diff --git a/app.js b/app.js
index 93c88eb45..5cee715dc 100644
--- a/app.js
+++ b/app.js
@@ -1,17 +1,8 @@
/* jshint -W117 */
/* application specific logic */
-var connection = null;
-var authenticatedUser = false;
-/* Initial "authentication required" dialog */
-var authDialog = null;
-/* Loop retry ID that wits for other user to create the room */
-var authRetryId = null;
-var activecall = null;
var nickname = null;
var focusMucJid = null;
-var roomName = null;
var ssrc2jid = {};
-var bridgeIsDown = false;
//TODO: this array must be removed when firefox implement multistream support
var notReceivedSSRCs = [];
@@ -27,674 +18,12 @@ var ssrc2videoType = {};
* @type {String}
*/
var focusedVideoInfo = null;
-var mutedAudios = {};
-/**
- * Remembers if we were muted by the focus.
- * @type {boolean}
- */
-var forceMuted = false;
-/**
- * Indicates if we have muted our audio before the conference has started.
- * @type {boolean}
- */
-var preMuted = false;
-
-var localVideoSrc = null;
-var flipXLocalVideo = true;
-var isFullScreen = false;
-var currentVideoWidth = null;
-var currentVideoHeight = null;
-
-var sessionTerminated = false;
function init() {
-
- RTC.addStreamListener(maybeDoJoin, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
RTC.start();
+ xmpp.start(UI.getCreadentials);
- var jid = document.getElementById('jid').value || config.hosts.anonymousdomain || config.hosts.domain || window.location.hostname;
- connect(jid);
-}
-
-function connect(jid, password) {
- connection = new Strophe.Connection(document.getElementById('boshURL').value || config.bosh || '/http-bind');
-
- var settings = UI.getSettings();
- var email = settings.email;
- var displayName = settings.displayName;
- if(email) {
- connection.emuc.addEmailToPresence(email);
- } else {
- connection.emuc.addUserIdToPresence(settings.uid);
- }
- if(displayName) {
- connection.emuc.addDisplayNameToPresence(displayName);
- }
-
- if (connection.disco) {
- // for chrome, add multistream cap
- }
- connection.jingle.pc_constraints = RTC.getPCConstraints();
- if (config.useIPv6) {
- // https://code.google.com/p/webrtc/issues/detail?id=2828
- if (!connection.jingle.pc_constraints.optional) connection.jingle.pc_constraints.optional = [];
- connection.jingle.pc_constraints.optional.push({googIPv6: true});
- }
-
- if(!password)
- password = document.getElementById('password').value;
-
- var anonymousConnectionFailed = false;
- connection.connect(jid, password, function (status, msg) {
- console.log('Strophe status changed to', Strophe.getStatusString(status));
- if (status === Strophe.Status.CONNECTED) {
- if (config.useStunTurn) {
- connection.jingle.getStunAndTurnCredentials();
- }
- document.getElementById('connect').disabled = true;
-
- console.info("My Jabber ID: " + connection.jid);
-
- if(password)
- authenticatedUser = true;
- maybeDoJoin();
- } else if (status === Strophe.Status.CONNFAIL) {
- if(msg === 'x-strophe-bad-non-anon-jid') {
- anonymousConnectionFailed = true;
- }
- } else if (status === Strophe.Status.DISCONNECTED) {
- if(anonymousConnectionFailed) {
- // prompt user for username and password
- $(document).trigger('passwordrequired.main');
- }
- } else if (status === Strophe.Status.AUTHFAIL) {
- // wrong password or username, prompt user
- $(document).trigger('passwordrequired.main');
-
- }
- });
-}
-
-
-
-function maybeDoJoin() {
- if (connection && connection.connected && Strophe.getResourceFromJid(connection.jid) // .connected is true while connecting?
- && (RTC.localAudio || RTC.localVideo)) {
- doJoin();
- }
-}
-
-function doJoin() {
- if (!roomName) {
- UI.generateRoomName();
- }
-
- Moderator.allocateConferenceFocus(
- roomName, doJoinAfterFocus);
-}
-
-function doJoinAfterFocus() {
-
- // Close authentication dialog if opened
- if (authDialog) {
- UI.messageHandler.closeDialog();
- authDialog = null;
- }
- // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice
- if (authRetryId) {
- window.clearTimeout(authRetryId);
- authRetryId = null;
- }
-
- var roomjid;
- roomjid = roomName;
-
- if (config.useNicks) {
- var nick = window.prompt('Your nickname (optional)');
- if (nick) {
- roomjid += '/' + nick;
- } else {
- roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
- }
- } else {
-
- var tmpJid = Strophe.getNodeFromJid(connection.jid);
-
- if(!authenticatedUser)
- tmpJid = tmpJid.substr(0, 8);
-
- roomjid += '/' + tmpJid;
- }
- connection.emuc.doJoin(roomjid);
-}
-
-function waitForRemoteVideo(selector, ssrc, stream, jid) {
- // XXX(gp) so, every call to this function is *always* preceded by a call
- // to the RTC.attachMediaStream() function but that call is *not* followed
- // by an update to the videoSrcToSsrc map!
- //
- // The above way of doing things results in video SRCs that don't correspond
- // to any SSRC for a short period of time (to be more precise, for as long
- // the waitForRemoteVideo takes to complete). This causes problems (see
- // bellow).
- //
- // I'm wondering why we need to do that; i.e. why call RTC.attachMediaStream()
- // a second time in here and only then update the videoSrcToSsrc map? Why
- // not simply update the videoSrcToSsrc map when the RTC.attachMediaStream()
- // is called the first time? I actually do that in the lastN changed event
- // handler because the "orphan" video SRC is causing troubles there. The
- // purpose of this method would then be to fire the "videoactive.jingle".
- //
- // Food for though I guess :-)
-
- if (selector.removed || !selector.parent().is(":visible")) {
- console.warn("Media removed before had started", selector);
- return;
- }
-
- if (stream.id === 'mixedmslabel') return;
-
- if (selector[0].currentTime > 0) {
- var videoStream = simulcast.getReceivingVideoStream(stream);
- RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
-
- // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
- // in order to get rid of too many maps
- if (ssrc && jid) {
- jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
- } else {
- console.warn("No ssrc given for jid", jid);
- }
-
- $(document).trigger('videoactive.jingle', [selector]);
- } else {
- setTimeout(function () {
- waitForRemoteVideo(selector, ssrc, stream, jid);
- }, 250);
- }
-}
-
-$(document).bind('remotestreamadded.jingle', function (event, data, sid) {
- waitForPresence(data, sid);
-});
-
-function waitForPresence(data, sid) {
- var sess = connection.jingle.sessions[sid];
-
- var thessrc;
-
- // look up an associated JID for a stream id
- if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
- // look only at a=ssrc: and _not_ at a=ssrc-group: lines
-
- var ssrclines
- = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc:');
- ssrclines = ssrclines.filter(function (line) {
- // NOTE(gp) previously we filtered on the mslabel, but that property
- // is not always present.
- // return line.indexOf('mslabel:' + data.stream.label) !== -1;
-
- return ((line.indexOf('msid:' + data.stream.id) !== -1));
- });
- if (ssrclines.length) {
- thessrc = ssrclines[0].substring(7).split(' ')[0];
-
- // We signal our streams (through Jingle to the focus) before we set
- // our presence (through which peers associate remote streams to
- // jids). So, it might arrive that a remote stream is added but
- // ssrc2jid is not yet updated and thus data.peerjid cannot be
- // successfully set. Here we wait for up to a second for the
- // presence to arrive.
-
- if (!ssrc2jid[thessrc]) {
- // TODO(gp) limit wait duration to 1 sec.
- setTimeout(function(d, s) {
- return function() {
- waitForPresence(d, s);
- }
- }(data, sid), 250);
- return;
- }
-
- // ok to overwrite the one from focus? might save work in colibri.js
- console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
- if (ssrc2jid[thessrc]) {
- data.peerjid = ssrc2jid[thessrc];
- }
- }
- }
-
- //TODO: this code should be removed when firefox implement multistream support
- if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
- {
- if((notReceivedSSRCs.length == 0) ||
- !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
- {
- // TODO(gp) limit wait duration to 1 sec.
- setTimeout(function(d, s) {
- return function() {
- waitForPresence(d, s);
- }
- }(data, sid), 250);
- return;
- }
-
- thessrc = notReceivedSSRCs.pop();
- if (ssrc2jid[thessrc]) {
- data.peerjid = ssrc2jid[thessrc];
- }
- }
-
- RTC.createRemoteStream(data, sid, thessrc);
-
- var isVideo = data.stream.getVideoTracks().length > 0;
- // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
- if (isVideo &&
- data.peerjid && sess.peerjid === data.peerjid &&
- data.stream.getVideoTracks().length === 0 &&
- RTC.localVideo.getTracks().length > 0) {
- //
- window.setTimeout(function () {
- sendKeyframe(sess.peerconnection);
- }, 3000);
- }
-}
-
-// an attempt to work around https://github.com/jitsi/jitmeet/issues/32
-function sendKeyframe(pc) {
- console.log('sendkeyframe', pc.iceConnectionState);
- if (pc.iceConnectionState !== 'connected') return; // safe...
- pc.setRemoteDescription(
- pc.remoteDescription,
- function () {
- pc.createAnswer(
- function (modifiedAnswer) {
- pc.setLocalDescription(
- modifiedAnswer,
- function () {
- // noop
- },
- function (error) {
- console.log('triggerKeyframe setLocalDescription failed', error);
- UI.messageHandler.showError();
- }
- );
- },
- function (error) {
- console.log('triggerKeyframe createAnswer failed', error);
- UI.messageHandler.showError();
- }
- );
- },
- function (error) {
- console.log('triggerKeyframe setRemoteDescription failed', error);
- UI.messageHandler.showError();
- }
- );
-}
-
-// Really mute video, i.e. dont even send black frames
-function muteVideo(pc, unmute) {
- // FIXME: this probably needs another of those lovely state safeguards...
- // which checks for iceconn == connected and sigstate == stable
- pc.setRemoteDescription(pc.remoteDescription,
- function () {
- pc.createAnswer(
- function (answer) {
- var sdp = new SDP(answer.sdp);
- if (sdp.media.length > 1) {
- if (unmute)
- sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
- else
- sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
- sdp.raw = sdp.session + sdp.media.join('');
- answer.sdp = sdp.raw;
- }
- pc.setLocalDescription(answer,
- function () {
- console.log('mute SLD ok');
- },
- function (error) {
- console.log('mute SLD error');
- UI.messageHandler.showError('Error',
- 'Oops! Something went wrong and we failed to ' +
- 'mute! (SLD Failure)');
- }
- );
- },
- function (error) {
- console.log(error);
- UI.messageHandler.showError();
- }
- );
- },
- function (error) {
- console.log('muteVideo SRD error');
- UI.messageHandler.showError('Error',
- 'Oops! Something went wrong and we failed to stop video!' +
- '(SRD Failure)');
-
- }
- );
-}
-
-$(document).bind('setLocalDescription.jingle', function (event, sid) {
- // put our ssrcs into presence so other clients can identify our stream
- var sess = connection.jingle.sessions[sid];
- var newssrcs = [];
- var media = simulcast.parseMedia(sess.peerconnection.localDescription);
- media.forEach(function (media) {
-
- if(Object.keys(media.sources).length > 0) {
- // TODO(gp) maybe exclude FID streams?
- Object.keys(media.sources).forEach(function (ssrc) {
- newssrcs.push({
- 'ssrc': ssrc,
- 'type': media.type,
- 'direction': media.direction
- });
- });
- }
- else if(sess.localStreamsSSRC && sess.localStreamsSSRC[media.type])
- {
- newssrcs.push({
- 'ssrc': sess.localStreamsSSRC[media.type],
- 'type': media.type,
- 'direction': media.direction
- });
- }
-
- });
-
- console.log('new ssrcs', newssrcs);
-
- // Have to clear presence map to get rid of removed streams
- connection.emuc.clearPresenceMedia();
-
- if (newssrcs.length > 0) {
- for (var i = 1; i <= newssrcs.length; i ++) {
- // Change video type to screen
- if (newssrcs[i-1].type === 'video' && desktopsharing.isUsingScreenStream()) {
- newssrcs[i-1].type = 'screen';
- }
- connection.emuc.addMediaToPresence(i,
- newssrcs[i-1].type, newssrcs[i-1].ssrc, newssrcs[i-1].direction);
- }
-
- connection.emuc.sendPresence();
- }
-});
-
-$(document).bind('iceconnectionstatechange.jingle', function (event, sid, session) {
- switch (session.peerconnection.iceConnectionState) {
- case 'checking':
- session.timeChecking = (new Date()).getTime();
- session.firstconnect = true;
- break;
- case 'completed': // on caller side
- case 'connected':
- if (session.firstconnect) {
- session.firstconnect = false;
- var metadata = {};
- metadata.setupTime = (new Date()).getTime() - session.timeChecking;
- session.peerconnection.getStats(function (res) {
- if(res && res.result) {
- res.result().forEach(function (report) {
- if (report.type == 'googCandidatePair' && report.stat('googActiveConnection') == 'true') {
- metadata.localCandidateType = report.stat('googLocalCandidateType');
- metadata.remoteCandidateType = report.stat('googRemoteCandidateType');
-
- // log pair as well so we can get nice pie charts
- metadata.candidatePair = report.stat('googLocalCandidateType') + ';' + report.stat('googRemoteCandidateType');
-
- if (report.stat('googRemoteAddress').indexOf('[') === 0) {
- metadata.ipv6 = true;
- }
- }
- });
- }
- });
- }
- break;
- }
-});
-
-$(document).bind('presence.muc', function (event, jid, info, pres) {
-
- //check if the video bridge is available
- if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
- bridgeIsDown = true;
- UI.messageHandler.showError("Error",
- "Jitsi Videobridge is currently unavailable. Please try again later!");
- }
-
- if (info.isFocus)
- {
- return;
- }
-
- // Remove old ssrcs coming from the jid
- Object.keys(ssrc2jid).forEach(function (ssrc) {
- if (ssrc2jid[ssrc] == jid) {
- delete ssrc2jid[ssrc];
- delete ssrc2videoType[ssrc];
- }
- });
-
- $(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
- //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
- var ssrcV = ssrc.getAttribute('ssrc');
- ssrc2jid[ssrcV] = jid;
- notReceivedSSRCs.push(ssrcV);
-
- var type = ssrc.getAttribute('type');
- ssrc2videoType[ssrcV] = type;
-
- // might need to update the direction if participant just went from sendrecv to recvonly
- if (type === 'video' || type === 'screen') {
- var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>video');
- switch (ssrc.getAttribute('direction')) {
- case 'sendrecv':
- el.show();
- break;
- case 'recvonly':
- el.hide();
- // FIXME: Check if we have to change large video
- //VideoLayout.updateLargeVideo(el);
- break;
- }
- }
- });
-
- var displayName = !config.displayJids
- ? info.displayName : Strophe.getResourceFromJid(jid);
-
- if (displayName && displayName.length > 0)
- $(document).trigger('displaynamechanged',
- [jid, displayName]);
- /*if (focus !== null && info.displayName !== null) {
- focus.setEndpointDisplayName(jid, info.displayName);
- }*/
-
- //check if the video bridge is available
- if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
- bridgeIsDown = true;
- UI.messageHandler.showError("Error",
- "Jitsi Videobridge is currently unavailable. Please try again later!");
- }
-
- var id = $(pres).find('>userID').text();
- var email = $(pres).find('>email');
- if(email.length > 0) {
- id = email.text();
- }
- UI.setUserAvatar(jid, id);
-
-});
-
-$(document).bind('kicked.muc', function (event, jid) {
- console.info(jid + " has been kicked from MUC!");
- if (connection.emuc.myroomjid === jid) {
- sessionTerminated = true;
- disposeConference(false);
- connection.emuc.doLeave();
- UI.messageHandler.openMessageDialog("Session Terminated",
- "Ouch! You have been kicked out of the meet!");
- }
-});
-
-$(document).bind('passwordrequired.main', function (event) {
- console.log('password is required');
-
- UI.messageHandler.openTwoButtonDialog(null,
- '
Password required
' +
- '' +
- '',
- true,
- "Ok",
- function (e, v, m, f) {
- if (v) {
- var username = document.getElementById('passwordrequired.username');
- var password = document.getElementById('passwordrequired.password');
-
- if (username.value !== null && password.value != null) {
- connect(username.value, password.value);
- }
- }
- },
- function (event) {
- document.getElementById('passwordrequired.username').focus();
- }
- );
-});
-
-/**
- * Checks if video identified by given src is desktop stream.
- * @param videoSrc eg.
- * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
- * @returns {boolean}
- */
-function isVideoSrcDesktop(jid) {
- // FIXME: fix this mapping mess...
- // figure out if large video is desktop stream or just a camera
-
- if(!jid)
- return false;
- var isDesktop = false;
- if (connection.emuc.myroomjid &&
- Strophe.getResourceFromJid(connection.emuc.myroomjid) === jid) {
- // local video
- isDesktop = desktopsharing.isUsingScreenStream();
- } else {
- // Do we have associations...
- var videoSsrc = jid2Ssrc[jid];
- if (videoSsrc) {
- var videoType = ssrc2videoType[videoSsrc];
- if (videoType) {
- // Finally there...
- isDesktop = videoType === 'screen';
- } else {
- console.error("No video type for ssrc: " + videoSsrc);
- }
- } else {
- console.error("No ssrc for jid: " + jid);
- }
- }
- return isDesktop;
-}
-
-/**
- * Mutes/unmutes the local video.
- *
- * @param mute true to mute the local video; otherwise, false
- * @param options an object which specifies optional arguments such as the
- * boolean key byUser with default value true which
- * specifies whether the method was initiated in response to a user command (in
- * contrast to an automatic decision taken by the application logic)
- */
-function setVideoMute(mute, options) {
- if (connection && RTC.localVideo) {
- if (activecall) {
- activecall.setVideoMute(
- mute,
- function (mute) {
- var video = $('#video');
- var communicativeClass = "icon-camera";
- var muteClass = "icon-camera icon-camera-disabled";
-
- if (mute) {
- video.removeClass(communicativeClass);
- video.addClass(muteClass);
- } else {
- video.removeClass(muteClass);
- video.addClass(communicativeClass);
- }
- connection.emuc.addVideoInfoToPresence(mute);
- connection.emuc.sendPresence();
- },
- options);
- }
- }
-}
-
-$(document).on('inlastnchanged', function (event, oldValue, newValue) {
- if (config.muteLocalVideoIfNotInLastN) {
- setVideoMute(!newValue, { 'byUser': false });
- }
-});
-
-/**
- * Mutes/unmutes the local video.
- */
-function toggleVideo() {
- buttonClick("#video", "icon-camera icon-camera-disabled");
-
- if (connection && activecall && RTC.localVideo ) {
- setVideoMute(!RTC.localVideo.isMuted());
- }
-}
-
-/**
- * Mutes / unmutes audio for the local participant.
- */
-function toggleAudio() {
- setAudioMuted(!RTC.localAudio.isMuted());
-}
-
-/**
- * Sets muted audio state for the local participant.
- */
-function setAudioMuted(mute) {
- if (!(connection && RTC.localAudio)) {
- preMuted = mute;
- // We still click the button.
- buttonClick("#mute", "icon-microphone icon-mic-disabled");
- return;
- }
-
- if (forceMuted && !mute) {
- console.info("Asking focus for unmute");
- connection.moderate.setMute(connection.emuc.myroomjid, mute);
- // FIXME: wait for result before resetting muted status
- forceMuted = false;
- }
-
- if (mute == RTC.localAudio.isMuted()) {
- // Nothing to do
- return;
- }
-
- // It is not clear what is the right way to handle multiple tracks.
- // So at least make sure that they are all muted or all unmuted and
- // that we send presence just once.
- RTC.localAudio.mute();
- // isMuted is the opposite of audioEnabled
- connection.emuc.addAudioInfoToPresence(mute);
- connection.emuc.sendPresence();
- UI.showLocalAudioIndicator(mute);
-
- buttonClick("#mute", "icon-microphone icon-mic-disabled");
}
@@ -706,60 +35,12 @@ $(document).ready(function () {
UI.start();
statistics.start();
- Moderator.init();
-
// Set default desktop sharing method
desktopsharing.init();
});
$(window).bind('beforeunload', function () {
- if (connection && connection.connected) {
- // ensure signout
- $.ajax({
- type: 'POST',
- url: config.bosh,
- async: false,
- cache: false,
- contentType: 'application/xml',
- data: "",
- success: function (data) {
- console.log('signed out');
- console.log(data);
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- console.log('signout error', textStatus + ' (' + errorThrown + ')');
- }
- });
- }
- disposeConference(true);
if(API.isEnabled())
API.dispose();
});
-function disposeConference(onUnload) {
- UI.onDisposeConference(onUnload);
- var handler = activecall;
- if (handler && handler.peerconnection) {
- // FIXME: probably removing streams is not required and close() should
- // be enough
- if (RTC.localAudio) {
- handler.peerconnection.removeStream(RTC.localAudio.getOriginalStream(), onUnload);
- }
- if (RTC.localVideo) {
- handler.peerconnection.removeStream(RTC.localVideo.getOriginalStream(), onUnload);
- }
- handler.peerconnection.close();
- }
- statistics.onDisposeConference(onUnload);
- activecall = null;
-}
-
-/**
- * Changes the style class of the element given by id.
- */
-function buttonClick(id, classname) {
- $(id).toggleClass(classname); // add the class to the clicked element
-}
diff --git a/estos_log.js b/estos_log.js
deleted file mode 100644
index f822fa7a1..000000000
--- a/estos_log.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/* global Strophe */
-Strophe.addConnectionPlugin('logger', {
- // logs raw stanzas and makes them available for download as JSON
- connection: null,
- log: [],
- init: function (conn) {
- this.connection = conn;
- this.connection.rawInput = this.log_incoming.bind(this);
- this.connection.rawOutput = this.log_outgoing.bind(this);
- },
- log_incoming: function (stanza) {
- this.log.push([new Date().getTime(), 'incoming', stanza]);
- },
- log_outgoing: function (stanza) {
- this.log.push([new Date().getTime(), 'outgoing', stanza]);
- },
-});
diff --git a/index.html b/index.html
index a50ef1b2c..2c4520ac1 100644
--- a/index.html
+++ b/index.html
@@ -11,16 +11,10 @@
-
-
-
-
-
-
@@ -29,22 +23,20 @@
+
-
-
+
+
-
-
+
-
-
diff --git a/keyboard_shortcut.js b/keyboard_shortcut.js
index 64be13994..b74d52cc3 100644
--- a/keyboard_shortcut.js
+++ b/keyboard_shortcut.js
@@ -14,20 +14,20 @@ var KeyboardShortcut = (function(my) {
77: {
character: "M",
id: "mutePopover",
- function: toggleAudio
+ function: UI.toggleAudio
},
84: {
character: "T",
function: function() {
if(!RTC.localAudio.isMuted()) {
- toggleAudio();
+ UI.toggleAudio();
}
}
},
86: {
character: "V",
id: "toggleVideoPopover",
- function: toggleVideo
+ function: UI.toggleVideo
}
};
@@ -53,7 +53,7 @@ var KeyboardShortcut = (function(my) {
if(!($(":focus").is("input[type=text]") || $(":focus").is("input[type=password]") || $(":focus").is("textarea"))) {
if(e.which === "T".charCodeAt(0)) {
if(RTC.localAudio.isMuted()) {
- toggleAudio();
+ UI.toggleAudio();
}
}
}
diff --git a/libs/modules/API.bundle.js b/libs/modules/API.bundle.js
index ca7317bc4..3c10be005 100644
--- a/libs/modules/API.bundle.js
+++ b/libs/modules/API.bundle.js
@@ -19,8 +19,8 @@
var commands =
{
displayName: UI.inputDisplayNameHandler,
- muteAudio: toggleAudio,
- muteVideo: toggleVideo,
+ muteAudio: UI.toggleAudio,
+ muteVideo: UI.toggleVideo,
toggleFilmStrip: UI.toggleFilmStrip,
toggleChat: UI.toggleChat,
toggleContactList: UI.toggleContactList
@@ -204,4 +204,5 @@ var API = {
module.exports = API;
},{}]},{},[1])(1)
-});
\ No newline at end of file
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL0FQSS9BUEkuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoqXG4gKiBJbXBsZW1lbnRzIEFQSSBjbGFzcyB0aGF0IGNvbW11bmljYXRlcyB3aXRoIGV4dGVybmFsIGFwaSBjbGFzc1xuICogYW5kIHByb3ZpZGVzIGludGVyZmFjZSB0byBhY2Nlc3MgSml0c2kgTWVldCBmZWF0dXJlcyBieSBleHRlcm5hbFxuICogYXBwbGljYXRpb25zIHRoYXQgZW1iZWQgSml0c2kgTWVldFxuICovXG5cblxuXG4vKipcbiAqIExpc3Qgb2YgdGhlIGF2YWlsYWJsZSBjb21tYW5kcy5cbiAqIEB0eXBlIHt7XG4gKiAgICAgICAgICAgICAgZGlzcGxheU5hbWU6IGlucHV0RGlzcGxheU5hbWVIYW5kbGVyLFxuICogICAgICAgICAgICAgIG11dGVBdWRpbzogdG9nZ2xlQXVkaW8sXG4gKiAgICAgICAgICAgICAgbXV0ZVZpZGVvOiB0b2dnbGVWaWRlbyxcbiAqICAgICAgICAgICAgICBmaWxtU3RyaXA6IHRvZ2dsZUZpbG1TdHJpcFxuICogICAgICAgICAgfX1cbiAqL1xudmFyIGNvbW1hbmRzID1cbntcbiAgICBkaXNwbGF5TmFtZTogVUkuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIsXG4gICAgbXV0ZUF1ZGlvOiBVSS50b2dnbGVBdWRpbyxcbiAgICBtdXRlVmlkZW86IFVJLnRvZ2dsZVZpZGVvLFxuICAgIHRvZ2dsZUZpbG1TdHJpcDogVUkudG9nZ2xlRmlsbVN0cmlwLFxuICAgIHRvZ2dsZUNoYXQ6IFVJLnRvZ2dsZUNoYXQsXG4gICAgdG9nZ2xlQ29udGFjdExpc3Q6IFVJLnRvZ2dsZUNvbnRhY3RMaXN0XG59O1xuXG5cbi8qKlxuICogTWFwcyB0aGUgc3VwcG9ydGVkIGV2ZW50cyBhbmQgdGhlaXIgc3RhdHVzXG4gKiAodHJ1ZSBpdCB0aGUgZXZlbnQgaXMgZW5hYmxlZCBhbmQgZmFsc2UgaWYgaXQgaXMgZGlzYWJsZWQpXG4gKiBAdHlwZSB7e1xuICogICAgICAgICAgICAgIGluY29taW5nTWVzc2FnZTogYm9vbGVhbixcbiAqICAgICAgICAgICAgICBvdXRnb2luZ01lc3NhZ2U6IGJvb2xlYW4sXG4gKiAgICAgICAgICAgICAgZGlzcGxheU5hbWVDaGFuZ2U6IGJvb2xlYW4sXG4gKiAgICAgICAgICAgICAgcGFydGljaXBhbnRKb2luZWQ6IGJvb2xlYW4sXG4gKiAgICAgICAgICAgICAgcGFydGljaXBhbnRMZWZ0OiBib29sZWFuXG4gKiAgICAgIH19XG4gKi9cbnZhciBldmVudHMgPVxue1xuICAgIGluY29taW5nTWVzc2FnZTogZmFsc2UsXG4gICAgb3V0Z29pbmdNZXNzYWdlOmZhbHNlLFxuICAgIGRpc3BsYXlOYW1lQ2hhbmdlOiBmYWxzZSxcbiAgICBwYXJ0aWNpcGFudEpvaW5lZDogZmFsc2UsXG4gICAgcGFydGljaXBhbnRMZWZ0OiBmYWxzZVxufTtcblxuLyoqXG4gKiBQcm9jZXNzZXMgY29tbWFuZHMgZnJvbSBleHRlcm5hbCBhcHBsaWNhaXRvbi5cbiAqIEBwYXJhbSBtZXNzYWdlIHRoZSBvYmplY3Qgd2l0aCB0aGUgY29tbWFuZFxuICovXG5mdW5jdGlvbiBwcm9jZXNzQ29tbWFuZChtZXNzYWdlKVxue1xuICAgIGlmKG1lc3NhZ2UuYWN0aW9uICE9IFwiZXhlY3V0ZVwiKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIlVua25vd24gYWN0aW9uIG9mIHRoZSBtZXNzYWdlXCIpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGZvcih2YXIga2V5IGluIG1lc3NhZ2UpXG4gICAge1xuICAgICAgICBpZihjb21tYW5kc1trZXldKVxuICAgICAgICAgICAgY29tbWFuZHNba2V5XS5hcHBseShudWxsLCBtZXNzYWdlW2tleV0pO1xuICAgIH1cbn1cblxuLyoqXG4gKiBQcm9jZXNzZXMgZXZlbnRzIG9iamVjdHMgZnJvbSBleHRlcm5hbCBhcHBsaWNhdGlvbnNcbiAqIEBwYXJhbSBldmVudCB0aGUgZXZlbnRcbiAqL1xuZnVuY3Rpb24gcHJvY2Vzc0V2ZW50KGV2ZW50KSB7XG4gICAgaWYoIWV2ZW50LmFjdGlvbilcbiAgICB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFdmVudCB3aXRoIG5vIGFjdGlvbiBpcyByZWNlaXZlZC5cIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgaSA9IDA7XG4gICAgc3dpdGNoKGV2ZW50LmFjdGlvbilcbiAgICB7XG4gICAgICAgIGNhc2UgXCJhZGRcIjpcbiAgICAgICAgICAgIGZvcig7IGkgPCBldmVudC5ldmVudHMubGVuZ3RoOyBpKyspXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgZXZlbnRzW2V2ZW50LmV2ZW50c1tpXV0gPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJyZW1vdmVcIjpcbiAgICAgICAgICAgIGZvcig7IGkgPCBldmVudC5ldmVudHMubGVuZ3RoOyBpKyspXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgZXZlbnRzW2V2ZW50LmV2ZW50c1tpXV0gPSBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIlVua25vd24gYWN0aW9uIGZvciBldmVudC5cIik7XG4gICAgfVxuXG59XG5cbi8qKlxuICogU2VuZHMgbWVzc2FnZSB0byB0aGUgZXh0ZXJuYWwgYXBwbGljYXRpb24uXG4gKiBAcGFyYW0gb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIHNlbmRNZXNzYWdlKG9iamVjdCkge1xuICAgIHdpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UoSlNPTi5zdHJpbmdpZnkob2JqZWN0KSwgXCIqXCIpO1xufVxuXG4vKipcbiAqIFByb2Nlc3NlcyBhIG1lc3NhZ2UgZXZlbnQgZnJvbSB0aGUgZXh0ZXJuYWwgYXBwbGljYXRpb25cbiAqIEBwYXJhbSBldmVudCB0aGUgbWVzc2FnZSBldmVudFxuICovXG5mdW5jdGlvbiBwcm9jZXNzTWVzc2FnZShldmVudClcbntcbiAgICB2YXIgbWVzc2FnZTtcbiAgICB0cnkge1xuICAgICAgICBtZXNzYWdlID0gSlNPTi5wYXJzZShldmVudC5kYXRhKTtcbiAgICB9IGNhdGNoIChlKSB7fVxuXG4gICAgaWYoIW1lc3NhZ2UudHlwZSlcbiAgICAgICAgcmV0dXJuO1xuICAgIHN3aXRjaCAobWVzc2FnZS50eXBlKVxuICAgIHtcbiAgICAgICAgY2FzZSBcImNvbW1hbmRcIjpcbiAgICAgICAgICAgIHByb2Nlc3NDb21tYW5kKG1lc3NhZ2UpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJldmVudFwiOlxuICAgICAgICAgICAgcHJvY2Vzc0V2ZW50KG1lc3NhZ2UpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5rbm93biB0eXBlIG9mIHRoZSBtZXNzYWdlXCIpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgIH1cblxufVxuXG52YXIgQVBJID0ge1xuICAgIC8qKlxuICAgICAqIENoZWNrIHdoZXRoZXIgdGhlIEFQSSBzaG91bGQgYmUgZW5hYmxlZCBvciBub3QuXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAgICovXG4gICAgaXNFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBoYXNoID0gbG9jYXRpb24uaGFzaDtcbiAgICAgICAgaWYoaGFzaCAmJiBoYXNoLmluZGV4T2YoXCJleHRlcm5hbFwiKSA+IC0xICYmIHdpbmRvdy5wb3N0TWVzc2FnZSlcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplcyB0aGUgQVBJQ29ubmVjdG9yLiBTZXR1cHMgbWVzc2FnZSBldmVudCBsaXN0ZW5lcnMgdGhhdCB3aWxsXG4gICAgICogcmVjZWl2ZSBpbmZvcm1hdGlvbiBmcm9tIGV4dGVybmFsIGFwcGxpY2F0aW9ucyB0aGF0IGVtYmVkIEppdHNpIE1lZXQuXG4gICAgICogSXQgYWxzbyBzZW5kcyBhIG1lc3NhZ2UgdG8gdGhlIGV4dGVybmFsIGFwcGxpY2F0aW9uIHRoYXQgQVBJQ29ubmVjdG9yXG4gICAgICogaXMgaW5pdGlhbGl6ZWQuXG4gICAgICovXG4gICAgaW5pdDogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAod2luZG93LmFkZEV2ZW50TGlzdGVuZXIpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJyxcbiAgICAgICAgICAgICAgICBwcm9jZXNzTWVzc2FnZSwgZmFsc2UpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgd2luZG93LmF0dGFjaEV2ZW50KCdvbm1lc3NhZ2UnLCBwcm9jZXNzTWVzc2FnZSk7XG4gICAgICAgIH1cbiAgICAgICAgc2VuZE1lc3NhZ2Uoe3R5cGU6IFwic3lzdGVtXCIsIGxvYWRlZDogdHJ1ZX0pO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIHdoZXRoZXIgdGhlIGV2ZW50IGlzIGVuYWJsZWQgb3Qgbm90LlxuICAgICAqIEBwYXJhbSBuYW1lIHRoZSBuYW1lIG9mIHRoZSBldmVudC5cbiAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgKi9cbiAgICBpc0V2ZW50RW5hYmxlZDogZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgcmV0dXJuIGV2ZW50c1tuYW1lXTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU2VuZHMgZXZlbnQgb2JqZWN0IHRvIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvbiB0aGF0IGhhcyBiZWVuIHN1YnNjcmliZWRcbiAgICAgKiBmb3IgdGhhdCBldmVudC5cbiAgICAgKiBAcGFyYW0gbmFtZSB0aGUgbmFtZSBldmVudFxuICAgICAqIEBwYXJhbSBvYmplY3QgZGF0YSBhc3NvY2lhdGVkIHdpdGggdGhlIGV2ZW50XG4gICAgICovXG4gICAgdHJpZ2dlckV2ZW50OiBmdW5jdGlvbiAobmFtZSwgb2JqZWN0KSB7XG4gICAgICAgIGlmKHRoaXMuaXNFbmFibGVkKCkgJiYgdGhpcy5pc0V2ZW50RW5hYmxlZChuYW1lKSlcbiAgICAgICAgICAgIHNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICB0eXBlOiBcImV2ZW50XCIsIGFjdGlvbjogXCJyZXN1bHRcIiwgZXZlbnQ6IG5hbWUsIHJlc3VsdDogb2JqZWN0fSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJlbW92ZXMgdGhlIGxpc3RlbmVycy5cbiAgICAgKi9cbiAgICBkaXNwb3NlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIixcbiAgICAgICAgICAgICAgICBwcm9jZXNzTWVzc2FnZSwgZmFsc2UpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgd2luZG93LmRldGFjaEV2ZW50KCdvbm1lc3NhZ2UnLCBwcm9jZXNzTWVzc2FnZSk7XG4gICAgICAgIH1cblxuICAgIH1cblxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEFQSTsiXX0=
diff --git a/libs/modules/RTC.bundle.js b/libs/modules/RTC.bundle.js
index cf4ab3e03..bf8405b4f 100644
--- a/libs/modules/RTC.bundle.js
+++ b/libs/modules/RTC.bundle.js
@@ -1,5 +1,5 @@
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.RTC=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;ovideo');
+ switch (stream.direction) {
+ case 'sendrecv':
+ el.show();
+ break;
+ case 'recvonly':
+ el.hide();
+ // FIXME: Check if we have to change large video
+ //VideoLayout.updateLargeVideo(el);
+ break;
+ }
+ }
+ }
-
+ });
+ xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged);
+ xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined);
}
function bindEvents()
@@ -118,10 +153,6 @@ function bindEvents()
function () {
VideoLayout.resizeLargeVideoContainer();
VideoLayout.positionLarge();
- isFullScreen = document.fullScreen ||
- document.mozFullScreen ||
- document.webkitIsFullScreen;
-
}
);
@@ -256,11 +287,6 @@ UI.start = function () {
};
-
-UI.setUserAvatar = function (jid, id) {
- Avatar.setUserAvatar(jid, id);
-};
-
UI.toggleSmileys = function () {
Chat.toggleSmileys();
};
@@ -279,7 +305,7 @@ UI.updateChatConversation = function (from, displayName, message) {
return Chat.updateChatConversation(from, displayName, message);
};
-UI.onMucJoined = function (jid, info) {
+function onMucJoined(jid, info) {
Toolbar.updateRoomUrl(window.location.href);
document.getElementById('localNick').appendChild(
document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)')
@@ -294,15 +320,14 @@ UI.onMucJoined = function (jid, info) {
// Show authenticate button if needed
Toolbar.showAuthenticateButton(
- Moderator.isExternalAuthEnabled() && !Moderator.isModerator());
+ xmpp.isExternalAuthEnabled() && !xmpp.isModerator());
var displayName = !config.displayJids
? info.displayName : Strophe.getResourceFromJid(jid);
if (displayName)
- $(document).trigger('displaynamechanged',
- ['localVideoContainer', displayName + ' (me)']);
-};
+ onDisplayNameChanged('localVideoContainer', displayName + ' (me)');
+}
UI.initEtherpad = function (name) {
Etherpad.init(name);
@@ -358,23 +383,19 @@ UI.toggleContactList = function () {
UI.onLocalRoleChange = function (jid, info, pres) {
console.info("My role changed, new role: " + info.role);
- var isModerator = Moderator.isModerator();
+ var isModerator = xmpp.isModerator();
VideoLayout.showModeratorIndicator();
Toolbar.showAuthenticateButton(
- Moderator.isExternalAuthEnabled() && !isModerator);
+ xmpp.isExternalAuthEnabled() && !isModerator);
if (isModerator) {
- Toolbar.closeAuthenticationWindow();
+ Authentication.closeAuthenticationWindow();
messageHandler.notify(
'Me', 'connected', 'Moderator rights granted !');
}
};
-UI.onDisposeConference = function (unload) {
- Toolbar.showAuthenticateButton(false);
-};
-
UI.onModeratorStatusChanged = function (isModerator) {
Toolbar.showSipCallButton(isModerator);
@@ -415,40 +436,11 @@ UI.onPasswordReqiured = function (callback) {
);
};
-UI.onAuthenticationRequired = function () {
- // This is the loop that will wait for the room to be created by
- // someone else. 'auth_required.moderator' will bring us back here.
- authRetryId = window.setTimeout(
- function () {
- Moderator.allocateConferenceFocus(roomName, doJoinAfterFocus);
- }, 5000);
- // Show prompt only if it's not open
- if (authDialog !== null) {
- return;
- }
- // extract room name from 'room@muc.server.net'
- var room = roomName.substr(0, roomName.indexOf('@'));
-
- authDialog = messageHandler.openDialog(
- 'Stop',
- 'Authentication is required to create room: ' + room +
- ' You can either authenticate to create the room or ' +
- 'just wait for someone else to do so.',
- true,
- {
- Authenticate: 'authNow'
- },
- function (onSubmitEvent, submitValue) {
-
- // Do not close the dialog yet
- onSubmitEvent.preventDefault();
-
- // Open login popup
- if (submitValue === 'authNow') {
- Toolbar.authenticateClicked();
- }
- }
- );
+UI.onAuthenticationRequired = function (intervalCallback) {
+ Authentication.openAuthenticationDialog(
+ roomName, intervalCallback, function () {
+ Toolbar.authenticateClicked();
+ });
};
UI.setRecordingButtonState = function (state) {
@@ -512,6 +504,8 @@ UI.showLocalAudioIndicator = function (mute) {
};
UI.generateRoomName = function() {
+ if(roomName)
+ return roomName;
var roomnode = null;
var path = window.location.pathname;
@@ -541,6 +535,7 @@ UI.generateRoomName = function() {
}
roomName = roomnode + '@' + config.hosts.muc;
+ return roomName;
};
@@ -557,30 +552,151 @@ UI.dockToolbar = function (isDock) {
return ToolbarToggler.dockToolbar(isDock);
};
+UI.getCreadentials = function () {
+ return {
+ bosh: document.getElementById('boshURL').value,
+ password: document.getElementById('password').value,
+ jid: document.getElementById('jid').value
+ };
+};
+
+UI.disableConnect = function () {
+ document.getElementById('connect').disabled = true;
+};
+
+UI.showLoginPopup = function(callback)
+{
+ console.log('password is required');
+
+ UI.messageHandler.openTwoButtonDialog(null,
+ '
Password required
' +
+ '' +
+ '',
+ true,
+ "Ok",
+ function (e, v, m, f) {
+ if (v) {
+ var username = document.getElementById('passwordrequired.username');
+ var password = document.getElementById('passwordrequired.password');
+
+ if (username.value !== null && password.value != null) {
+ callback(username.value, password.value);
+ }
+ }
+ },
+ function (event) {
+ document.getElementById('passwordrequired.username').focus();
+ }
+ );
+}
+
+UI.checkForNicknameAndJoin = function () {
+
+ Authentication.closeAuthenticationDialog();
+ Authentication.stopInterval();
+
+ var nick = null;
+ if (config.useNicks) {
+ nick = window.prompt('Your nickname (optional)');
+ }
+ xmpp.joinRooom(roomName, config.useNicks, nick);
+}
+
+
function dump(elem, filename) {
elem = elem.parentNode;
elem.download = filename || 'meetlog.json';
elem.href = 'data:application/json;charset=utf-8,\n';
- var data = {};
- if (connection.jingle) {
- data = connection.jingle.populateData();
- }
+ var data = xmpp.populateData();
var metadata = {};
metadata.time = new Date();
metadata.url = window.location.href;
metadata.ua = navigator.userAgent;
- if (connection.logger) {
- metadata.xmpp = connection.logger.log;
+ var log = xmpp.getLogger();
+ if (log) {
+ metadata.xmpp = log;
}
data.metadata = metadata;
elem.href += encodeURIComponent(JSON.stringify(data, null, ' '));
return false;
}
+UI.getRoomName = function () {
+ return roomName;
+}
+
+/**
+ * Mutes/unmutes the local video.
+ *
+ * @param mute true to mute the local video; otherwise, false
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision taken by the application logic)
+ */
+function setVideoMute(mute, options) {
+ xmpp.setVideoMute(
+ mute,
+ function (mute) {
+ var video = $('#video');
+ var communicativeClass = "icon-camera";
+ var muteClass = "icon-camera icon-camera-disabled";
+
+ if (mute) {
+ video.removeClass(communicativeClass);
+ video.addClass(muteClass);
+ } else {
+ video.removeClass(muteClass);
+ video.addClass(communicativeClass);
+ }
+ },
+ options);
+}
+
+/**
+ * Mutes/unmutes the local video.
+ */
+UI.toggleVideo = function () {
+ UIUtil.buttonClick("#video", "icon-camera icon-camera-disabled");
+
+ setVideoMute(!RTC.localVideo.isMuted());
+};
+
+/**
+ * Mutes / unmutes audio for the local participant.
+ */
+UI.toggleAudio = function() {
+ UI.setAudioMuted(!RTC.localAudio.isMuted());
+};
+
+/**
+ * Sets muted audio state for the local participant.
+ */
+UI.setAudioMuted = function (mute) {
+
+ if(!xmpp.setAudioMute(mute, function () {
+ UI.showLocalAudioIndicator(mute);
+
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ }))
+ {
+ // We still click the button.
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ return;
+ }
+
+}
+
+UI.onLastNChanged = function (oldValue, newValue) {
+ if (config.muteLocalVideoIfNotInLastN) {
+ setVideoMute(!newValue, { 'byUser': false });
+ }
+}
+
module.exports = UI;
-},{"./audio_levels/AudioLevels.js":2,"./avatar/Avatar":4,"./etherpad/Etherpad.js":5,"./prezi/Prezi.js":6,"./side_pannels/SidePanelToggler":7,"./side_pannels/chat/Chat.js":8,"./side_pannels/contactlist/ContactList":12,"./side_pannels/settings/Settings":13,"./side_pannels/settings/SettingsMenu":14,"./toolbars/BottomToolbar":15,"./toolbars/toolbar":17,"./toolbars/toolbartoggler":18,"./util/MessageHandler":20,"./videolayout/VideoLayout.js":23,"./welcome_page/RoomnameGenerator":24,"./welcome_page/WelcomePage":25}],2:[function(require,module,exports){
+},{"./audio_levels/AudioLevels.js":2,"./authentication/Authentication":4,"./avatar/Avatar":5,"./etherpad/Etherpad.js":6,"./prezi/Prezi.js":7,"./side_pannels/SidePanelToggler":8,"./side_pannels/chat/Chat.js":9,"./side_pannels/contactlist/ContactList":13,"./side_pannels/settings/Settings":14,"./side_pannels/settings/SettingsMenu":15,"./toolbars/BottomToolbar":16,"./toolbars/toolbar":18,"./toolbars/toolbartoggler":19,"./util/MessageHandler":21,"./util/UIUtil":22,"./videolayout/VideoLayout.js":24,"./welcome_page/RoomnameGenerator":25,"./welcome_page/WelcomePage":26}],2:[function(require,module,exports){
var CanvasUtil = require("./CanvasUtils");
/**
@@ -670,10 +786,10 @@ var AudioLevels = (function(my) {
drawContext.drawImage(canvasCache, 0, 0);
if(resourceJid === AudioLevels.LOCAL_LEVEL) {
- if(!connection.emuc.myroomjid) {
+ if(!xmpp.myJid()) {
return;
}
- resourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ resourceJid = xmpp.myResource();
}
if(resourceJid === largeVideoResourceJid) {
@@ -804,8 +920,8 @@ var AudioLevels = (function(my) {
function getVideoSpanId(resourceJid) {
var videoSpanId = null;
if (resourceJid === AudioLevels.LOCAL_LEVEL
- || (connection.emuc.myroomjid && resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid)))
+ || (xmpp.myResource() && resourceJid
+ === xmpp.myResource()))
videoSpanId = 'localVideoContainer';
else
videoSpanId = 'participant_' + resourceJid;
@@ -958,6 +1074,91 @@ var CanvasUtil = (function(my) {
module.exports = CanvasUtil;
},{}],4:[function(require,module,exports){
+/* Initial "authentication required" dialog */
+var authDialog = null;
+/* Loop retry ID that wits for other user to create the room */
+var authRetryId = null;
+var authenticationWindow = null;
+
+var Authentication = {
+ openAuthenticationDialog: function (roomName, intervalCallback, callback) {
+ // This is the loop that will wait for the room to be created by
+ // someone else. 'auth_required.moderator' will bring us back here.
+ authRetryId = window.setTimeout(intervalCallback , 5000);
+ // Show prompt only if it's not open
+ if (authDialog !== null) {
+ return;
+ }
+ // extract room name from 'room@muc.server.net'
+ var room = roomName.substr(0, roomName.indexOf('@'));
+
+ authDialog = messageHandler.openDialog(
+ 'Stop',
+ 'Authentication is required to create room: ' + room +
+ ' You can either authenticate to create the room or ' +
+ 'just wait for someone else to do so.',
+ true,
+ {
+ Authenticate: 'authNow'
+ },
+ function (onSubmitEvent, submitValue) {
+
+ // Do not close the dialog yet
+ onSubmitEvent.preventDefault();
+
+ // Open login popup
+ if (submitValue === 'authNow') {
+ callback();
+ }
+ }
+ );
+ },
+ closeAuthenticationWindow:function () {
+ if (authenticationWindow) {
+ authenticationWindow.close();
+ authenticationWindow = null;
+ }
+ },
+ focusAuthenticationWindow: function () {
+ // If auth window exists just bring it to the front
+ if (authenticationWindow) {
+ authenticationWindow.focus();
+ return;
+ }
+ },
+ closeAuthenticationDialog: function () {
+ // Close authentication dialog if opened
+ if (authDialog) {
+ UI.messageHandler.closeDialog();
+ authDialog = null;
+ }
+ },
+ createAuthenticationWindow: function (callback, url) {
+ authenticationWindow = messageHandler.openCenteredPopup(
+ url, 910, 660,
+ // On closed
+ function () {
+ // Close authentication dialog if opened
+ if (authDialog) {
+ messageHandler.closeDialog();
+ authDialog = null;
+ }
+ callback();
+ authenticationWindow = null;
+ });
+ return authenticationWindow;
+ },
+ stopInterval: function () {
+ // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice
+ if (authRetryId) {
+ window.clearTimeout(authRetryId);
+ authRetryId = null;
+ }
+ }
+};
+
+module.exports = Authentication;
+},{}],5:[function(require,module,exports){
var Settings = require("../side_pannels/settings/Settings");
var users = {};
@@ -972,7 +1173,7 @@ function setVisibility(selector, show) {
function isUserMuted(jid) {
// XXX(gp) we may want to rename this method to something like
// isUserStreaming, for example.
- if (jid && jid != connection.emuc.myroomjid) {
+ if (jid && jid != xmpp.myJid()) {
var resource = Strophe.getResourceFromJid(jid);
if (!require("../videolayout/VideoLayout").isInLastN(resource)) {
return true;
@@ -986,7 +1187,7 @@ function isUserMuted(jid) {
}
function getGravatarUrl(id, size) {
- if(id === connection.emuc.myroomjid || !id) {
+ if(id === xmpp.myJid() || !id) {
id = Settings.getSettings().uid;
}
return 'https://www.gravatar.com/avatar/' +
@@ -1017,7 +1218,7 @@ var Avatar = {
// set the avatar in the settings menu if it is local user and get the
// local video container
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
$('#avatar').get(0).src = thumbUrl;
thumbnail = $('#localVideoContainer');
}
@@ -1060,7 +1261,7 @@ var Avatar = {
var video = $('#participant_' + resourceJid + '>video');
var avatar = $('#avatar_' + resourceJid);
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
video = $('#localVideoWrapper>video');
}
if (show === undefined || show === null) {
@@ -1090,7 +1291,7 @@ var Avatar = {
*/
updateActiveSpeakerAvatarSrc: function (jid) {
if (!jid) {
- jid = connection.emuc.findJidFromResource(
+ jid = xmpp.findJidFromResource(
require("../videolayout/VideoLayout").getLargeVideoState().userResourceJid);
}
var avatar = $("#activeSpeakerAvatar")[0];
@@ -1112,8 +1313,8 @@ var Avatar = {
module.exports = Avatar;
-},{"../side_pannels/settings/Settings":13,"../videolayout/VideoLayout":23}],5:[function(require,module,exports){
-/* global $, config, connection, dockToolbar, Moderator,
+},{"../side_pannels/settings/Settings":14,"../videolayout/VideoLayout":24}],6:[function(require,module,exports){
+/* global $, config, dockToolbar,
setLargeVideoVisible, Util */
var VideoLayout = require("../videolayout/VideoLayout");
@@ -1145,8 +1346,7 @@ function resize() {
* Shares the Etherpad name with other participants.
*/
function shareEtherpad() {
- connection.emuc.addEtherpadToPresence(etherpadName);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("etherpad", etherpadName);
}
/**
@@ -1309,7 +1509,7 @@ var Etherpad = {
module.exports = Etherpad;
-},{"../prezi/Prezi":6,"../util/UIUtil":21,"../videolayout/VideoLayout":23}],6:[function(require,module,exports){
+},{"../prezi/Prezi":7,"../util/UIUtil":22,"../videolayout/VideoLayout":24}],7:[function(require,module,exports){
var ToolbarToggler = require("../toolbars/ToolbarToggler");
var UIUtil = require("../util/UIUtil");
var VideoLayout = require("../videolayout/VideoLayout");
@@ -1342,7 +1542,7 @@ var Prezi = {
* to load.
*/
openPreziDialog: function() {
- var myprezi = connection.emuc.getPrezi(connection.emuc.myroomjid);
+ var myprezi = xmpp.getPrezi();
if (myprezi) {
messageHandler.openTwoButtonDialog("Remove Prezi",
"Are you sure you would like to remove your Prezi?",
@@ -1350,8 +1550,7 @@ var Prezi = {
"Remove",
function(e,v,m,f) {
if(v) {
- connection.emuc.removePreziFromPresence();
- connection.emuc.sendPresence();
+ xmpp.removePreziFromPresence();
}
}
);
@@ -1403,9 +1602,7 @@ var Prezi = {
return false;
}
else {
- connection.emuc
- .addPreziToPresence(urlValue, 0);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("prezi", urlValue);
$.prompt.close();
}
}
@@ -1463,7 +1660,7 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
VideoLayout.resizeThumbnails();
var controlsEnabled = false;
- if (jid === connection.emuc.myroomjid)
+ if (jid === xmpp.myJid())
controlsEnabled = true;
setPresentationVisible(true);
@@ -1503,15 +1700,14 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
console.log("prezi status", event.value);
if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
- if (jid != connection.emuc.myroomjid)
+ if (jid != xmpp.myJid())
preziPlayer.flyToStep(currentSlide);
}
});
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
console.log("event value", event.value);
- connection.emuc.addCurrentSlideToPresence(event.value);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("preziSlide", event.value);
});
$("#" + elementId).css( 'background-image',
@@ -1665,13 +1861,14 @@ $(window).resize(function () {
module.exports = Prezi;
-},{"../toolbars/ToolbarToggler":16,"../util/MessageHandler":20,"../util/UIUtil":21,"../videolayout/VideoLayout":23}],7:[function(require,module,exports){
+},{"../toolbars/ToolbarToggler":17,"../util/MessageHandler":21,"../util/UIUtil":22,"../videolayout/VideoLayout":24}],8:[function(require,module,exports){
var Chat = require("./chat/Chat");
var ContactList = require("./contactlist/ContactList");
var Settings = require("./settings/Settings");
var SettingsMenu = require("./settings/SettingsMenu");
var VideoLayout = require("../videolayout/VideoLayout");
var ToolbarToggler = require("../toolbars/ToolbarToggler");
+var UIUtil = require("../util/UIUtil");
/**
* Toggler for the chat, contact list, settings menu, etc..
@@ -1778,7 +1975,7 @@ var PanelToggler = (function(my) {
* @param onClose function to be called if the window is going to be closed
*/
var toggle = function(object, selector, onOpenComplete, onOpen, onClose) {
- buttonClick(buttons[selector], "active");
+ UIUtil.buttonClick(buttons[selector], "active");
if (object.isVisible()) {
$("#toast-container").animate({
@@ -1808,7 +2005,7 @@ var PanelToggler = (function(my) {
if(currentlyOpen) {
var current = $(currentlyOpen);
- buttonClick(buttons[currentlyOpen], "active");
+ UIUtil.buttonClick(buttons[currentlyOpen], "active");
current.css('z-index', 4);
setTimeout(function () {
current.css('display', 'none');
@@ -1921,8 +2118,8 @@ var PanelToggler = (function(my) {
}(PanelToggler || {}));
module.exports = PanelToggler;
-},{"../toolbars/ToolbarToggler":16,"../videolayout/VideoLayout":23,"./chat/Chat":8,"./contactlist/ContactList":12,"./settings/Settings":13,"./settings/SettingsMenu":14}],8:[function(require,module,exports){
-/* global $, Util, connection, nickname:true, showToolbar */
+},{"../toolbars/ToolbarToggler":17,"../util/UIUtil":22,"../videolayout/VideoLayout":24,"./chat/Chat":9,"./contactlist/ContactList":13,"./settings/Settings":14,"./settings/SettingsMenu":15}],9:[function(require,module,exports){
+/* global $, Util, nickname:true, showToolbar */
var Replacement = require("./Replacement");
var CommandsProcessor = require("./Commands");
var ToolbarToggler = require("../../toolbars/ToolbarToggler");
@@ -2108,8 +2305,7 @@ var Chat = (function (my) {
nickname = val;
window.localStorage.displayname = nickname;
- connection.emuc.addDisplayNameToPresence(nickname);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("displayName", nickname);
Chat.setChatConversationMode(true);
@@ -2132,7 +2328,7 @@ var Chat = (function (my) {
else
{
var message = Util.escapeHtml(value);
- connection.emuc.sendMessage(message, nickname);
+ xmpp.sendChatMessage(message, nickname);
}
}
});
@@ -2158,7 +2354,7 @@ var Chat = (function (my) {
my.updateChatConversation = function (from, displayName, message) {
var divClassName = '';
- if (connection.emuc.myroomjid === from) {
+ if (xmpp.myJid() === from) {
divClassName = "localuser";
}
else {
@@ -2282,7 +2478,7 @@ var Chat = (function (my) {
return my;
}(Chat || {}));
module.exports = Chat;
-},{"../../toolbars/ToolbarToggler":16,"../SidePanelToggler":7,"./Commands":9,"./Replacement":10,"./smileys.json":11}],9:[function(require,module,exports){
+},{"../../toolbars/ToolbarToggler":17,"../SidePanelToggler":8,"./Commands":10,"./Replacement":11,"./smileys.json":12}],10:[function(require,module,exports){
/**
* List with supported commands. The keys are the names of the commands and
* the value is the function that processes the message.
@@ -2317,7 +2513,7 @@ function getCommand(message)
function processTopic(commandArguments)
{
var topic = Util.escapeHtml(commandArguments);
- connection.emuc.setSubject(topic);
+ xmpp.setSubject(topic);
}
/**
@@ -2378,7 +2574,7 @@ CommandsProcessor.prototype.processCommand = function()
};
module.exports = CommandsProcessor;
-},{}],10:[function(require,module,exports){
+},{}],11:[function(require,module,exports){
var Smileys = require("./smileys.json");
/**
* Processes links and smileys in "body"
@@ -2442,7 +2638,7 @@ module.exports = {
linkify: linkify
};
-},{"./smileys.json":11}],11:[function(require,module,exports){
+},{"./smileys.json":12}],12:[function(require,module,exports){
module.exports={
"smileys": {
"smiley1": ":)",
@@ -2492,7 +2688,7 @@ module.exports={
}
}
-},{}],12:[function(require,module,exports){
+},{}],13:[function(require,module,exports){
var numberOfContacts = 0;
var notificationInterval;
@@ -2541,23 +2737,6 @@ function createDisplayNameParagraph(displayName) {
}
-/**
- * Indicates that the display name has changed.
- */
-$(document).bind( 'displaynamechanged',
- function (event, peerJid, displayName) {
- if (peerJid === 'localVideoContainer')
- peerJid = connection.emuc.myroomjid;
-
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contactName = $('#contactlist #' + resourceJid + '>p');
-
- if (contactName && displayName && displayName.length > 0)
- contactName.html(displayName);
- });
-
-
function stopGlowing(glower) {
window.clearInterval(notificationInterval);
notificationInterval = false;
@@ -2622,7 +2801,7 @@ var ContactList = {
var clElement = contactlist.get(0);
- if (resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid)
+ if (resourceJid === xmpp.myResource()
&& $('#contactlist>ul .title')[0].nextSibling.nextSibling) {
clElement.insertBefore(newContact,
$('#contactlist>ul .title')[0].nextSibling.nextSibling);
@@ -2677,11 +2856,23 @@ var ContactList = {
} else {
contact.removeClass('clickable');
}
+ },
+
+ onDisplayNameChange: function (peerJid, displayName) {
+ if (peerJid === 'localVideoContainer')
+ peerJid = xmpp.myJid();
+
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactName = $('#contactlist #' + resourceJid + '>p');
+
+ if (contactName && displayName && displayName.length > 0)
+ contactName.html(displayName);
}
};
module.exports = ContactList;
-},{}],13:[function(require,module,exports){
+},{}],14:[function(require,module,exports){
var email = '';
var displayName = '';
var userId;
@@ -2741,7 +2932,7 @@ var Settings =
module.exports = Settings;
-},{}],14:[function(require,module,exports){
+},{}],15:[function(require,module,exports){
var Avatar = require("../../avatar/Avatar");
var Settings = require("./Settings");
@@ -2754,16 +2945,15 @@ var SettingsMenu = {
if(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
- connection.emuc.addDisplayNameToPresence(displayName);
+ xmpp.addToPresence("displayName", displayName, true);
}
- connection.emuc.addEmailToPresence(newEmail);
+ xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(newEmail);
- connection.emuc.sendPresence();
- Avatar.setUserAvatar(connection.emuc.myroomjid, email);
+ Avatar.setUserAvatar(xmpp.myJid(), email);
},
isVisible: function() {
@@ -2773,18 +2963,19 @@ var SettingsMenu = {
setDisplayName: function(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
$('#setDisplayName').get(0).value = displayName;
+ },
+
+ onDisplayNameChange: function(peerJid, newDisplayName) {
+ if(peerJid === 'localVideoContainer' ||
+ peerJid === xmpp.myJid()) {
+ this.setDisplayName(newDisplayName);
+ }
}
};
-$(document).bind('displaynamechanged', function(event, peerJid, newDisplayName) {
- if(peerJid === 'localVideoContainer' ||
- peerJid === connection.emuc.myroomjid) {
- SettingsMenu.setDisplayName(newDisplayName);
- }
-});
module.exports = SettingsMenu;
-},{"../../avatar/Avatar":4,"./Settings":13}],15:[function(require,module,exports){
+},{"../../avatar/Avatar":5,"./Settings":14}],16:[function(require,module,exports){
var PanelToggler = require("../side_pannels/SidePanelToggler");
var buttonHandlers = {
@@ -2829,7 +3020,7 @@ var BottomToolbar = (function (my) {
module.exports = BottomToolbar;
-},{"../side_pannels/SidePanelToggler":7}],16:[function(require,module,exports){
+},{"../side_pannels/SidePanelToggler":8}],17:[function(require,module,exports){
/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
var toolbarTimeoutObject,
@@ -2899,7 +3090,7 @@ var ToolbarToggler = {
toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
}
- if (Moderator.isModerator())
+ if (xmpp.isModerator())
{
// TODO: Enable settings functionality.
// Need to uncomment the settings button in index.html.
@@ -2944,26 +3135,28 @@ var ToolbarToggler = {
};
module.exports = ToolbarToggler;
-},{}],17:[function(require,module,exports){
-/* global $, buttonClick, config, lockRoom, Moderator, roomName,
- setSharedKey, sharedKey, Util */
+},{}],18:[function(require,module,exports){
+/* global $, buttonClick, config, lockRoom,
+ setSharedKey, Util */
var messageHandler = require("../util/MessageHandler");
var BottomToolbar = require("./BottomToolbar");
var Prezi = require("../prezi/Prezi");
var Etherpad = require("../etherpad/Etherpad");
var PanelToggler = require("../side_pannels/SidePanelToggler");
+var Authentication = require("../authentication/Authentication");
+var UIUtil = require("../util/UIUtil");
var roomUrl = null;
var sharedKey = '';
-var authenticationWindow = null;
+var UI = null;
var buttonHandlers =
{
"toolbar_button_mute": function () {
- return toggleAudio();
+ return UI.toggleAudio();
},
"toolbar_button_camera": function () {
- return toggleVideo();
+ return UI.toggleVideo();
},
"toolbar_button_authentication": function () {
return Toolbar.authenticateClicked();
@@ -2991,7 +3184,7 @@ var buttonHandlers =
},
"toolbar_button_fullScreen": function()
{
- buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
+ UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
return Toolbar.toggleFullScreen();
},
"toolbar_button_sip": function () {
@@ -3006,9 +3199,7 @@ var buttonHandlers =
};
function hangup() {
- disposeConference();
- sessionTerminated = true;
- connection.emuc.doLeave();
+ xmpp.disposeConference();
if(config.enableWelcomePage)
{
setTimeout(function()
@@ -3037,7 +3228,29 @@ function hangup() {
*/
function toggleRecording() {
- Recording.toggleRecording();
+ xmpp.toggleRecording(function (callback) {
+ UI.messageHandler.openTwoButtonDialog(null,
+ '
Enter recording token
' +
+ '',
+ false,
+ "Save",
+ function (e, v, m, f) {
+ if (v) {
+ var token = document.getElementById('recordingToken');
+
+ if (token.value) {
+ callback(Util.escapeHtml(token.value));
+ }
+ }
+ },
+ function (event) {
+ document.getElementById('recordingToken').focus();
+ },
+ function () {
+ }
+ );
+ }, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
}
/**
@@ -3048,7 +3261,7 @@ function lockRoom(lock) {
if (lock)
currentSharedKey = sharedKey;
- connection.emuc.lockRoom(currentSharedKey, function (res) {
+ xmpp.lockRoom(currentSharedKey, function (res) {
// password is required
if (sharedKey)
{
@@ -3130,9 +3343,8 @@ function callSipButtonClicked()
if (v) {
var numberInput = document.getElementById('sipNumber');
if (numberInput.value) {
- connection.rayo.dial(
- numberInput.value, 'fromnumber',
- roomName, sharedKey);
+ xmpp.dial(numberInput.value, 'fromnumber',
+ UI.getRoomName(), sharedKey);
}
}
},
@@ -3144,9 +3356,10 @@ function callSipButtonClicked()
var Toolbar = (function (my) {
- my.init = function () {
+ my.init = function (ui) {
for(var k in buttonHandlers)
$("#" + k).click(buttonHandlers[k]);
+ UI = ui;
}
/**
@@ -3157,35 +3370,15 @@ var Toolbar = (function (my) {
sharedKey = sKey;
};
- my.closeAuthenticationWindow = function () {
- if (authenticationWindow) {
- authenticationWindow.close();
- authenticationWindow = null;
- }
- }
-
my.authenticateClicked = function () {
- // If auth window exists just bring it to the front
- if (authenticationWindow) {
- authenticationWindow.focus();
- return;
- }
+ Authentication.focusAuthenticationWindow();
// Get authentication URL
- Moderator.getAuthUrl(function (url) {
+ xmpp.getAuthUrl(UI.getRoomName(), function (url) {
// Open popup with authentication URL
- authenticationWindow = messageHandler.openCenteredPopup(
- url, 910, 660,
- // On closed
- function () {
- // Close authentication dialog if opened
- if (authDialog) {
- messageHandler.closeDialog();
- authDialog = null;
- }
- // On popup closed - retry room allocation
- Moderator.allocateConferenceFocus(roomName, doJoinAfterFocus);
- authenticationWindow = null;
- });
+ var authenticationWindow = Authentication.createAuthenticationWindow(function () {
+ // On popup closed - retry room allocation
+ xmpp.allocateConferenceFocus(UI.getRoomName(), UI.checkForNicknameAndJoin);
+ }, url);
if (!authenticationWindow) {
Toolbar.showAuthenticateButton(true);
messageHandler.openMessageDialog(
@@ -3226,7 +3419,7 @@ var Toolbar = (function (my) {
*/
my.openLockDialog = function () {
// Only the focus is able to set a shared key.
- if (!Moderator.isModerator()) {
+ if (!xmpp.isModerator()) {
if (sharedKey) {
messageHandler.openMessageDialog(null,
"This conversation is currently protected by" +
@@ -3383,14 +3576,14 @@ var Toolbar = (function (my) {
*/
my.unlockLockButton = function () {
if ($("#lockIcon").hasClass("icon-security-locked"))
- buttonClick("#lockIcon", "icon-security icon-security-locked");
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
* Updates the lock button state to locked.
*/
my.lockLockButton = function () {
if ($("#lockIcon").hasClass("icon-security"))
- buttonClick("#lockIcon", "icon-security icon-security-locked");
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
@@ -3433,7 +3626,7 @@ var Toolbar = (function (my) {
// Shows or hides SIP calls button
my.showSipCallButton = function (show) {
- if (Moderator.isSipGatewayEnabled() && show) {
+ if (xmpp.isSipGatewayEnabled() && show) {
$('#sipCallButton').css({display: "inline"});
} else {
$('#sipCallButton').css({display: "none"});
@@ -3461,9 +3654,9 @@ var Toolbar = (function (my) {
}(Toolbar || {}));
module.exports = Toolbar;
-},{"../etherpad/Etherpad":5,"../prezi/Prezi":6,"../side_pannels/SidePanelToggler":7,"../util/MessageHandler":20,"./BottomToolbar":15}],18:[function(require,module,exports){
-module.exports=require(16)
-},{}],19:[function(require,module,exports){
+},{"../authentication/Authentication":4,"../etherpad/Etherpad":6,"../prezi/Prezi":7,"../side_pannels/SidePanelToggler":8,"../util/MessageHandler":21,"../util/UIUtil":22,"./BottomToolbar":16}],19:[function(require,module,exports){
+module.exports=require(17)
+},{}],20:[function(require,module,exports){
var JitsiPopover = (function () {
/**
* Constructs new JitsiPopover and attaches it to the element
@@ -3587,7 +3780,7 @@ var JitsiPopover = (function () {
})();
module.exports = JitsiPopover;
-},{}],20:[function(require,module,exports){
+},{}],21:[function(require,module,exports){
/* global $, jQuery */
var messageHandler = (function(my) {
@@ -3756,7 +3949,7 @@ module.exports = messageHandler;
-},{}],21:[function(require,module,exports){
+},{}],22:[function(require,module,exports){
/**
* Created by hristo on 12/22/14.
*/
@@ -3770,10 +3963,17 @@ module.exports = {
= PanelToggler.isVisible() ? PanelToggler.getPanelSize()[0] : 0;
return window.innerWidth - rightPanelWidth;
+ },
+ /**
+ * Changes the style class of the element given by id.
+ */
+ buttonClick: function(id, classname) {
+ $(id).toggleClass(classname); // add the class to the clicked element
}
+
};
-},{"../side_pannels/SidePanelToggler":7}],22:[function(require,module,exports){
+},{"../side_pannels/SidePanelToggler":8}],23:[function(require,module,exports){
var JitsiPopover = require("../util/JitsiPopover");
/**
@@ -4184,7 +4384,7 @@ ConnectionIndicator.prototype.hideIndicator = function () {
};
module.exports = ConnectionIndicator;
-},{"../util/JitsiPopover":19}],23:[function(require,module,exports){
+},{"../util/JitsiPopover":20}],24:[function(require,module,exports){
var AudioLevels = require("../audio_levels/AudioLevels");
var Avatar = require("../avatar/Avatar");
var Chat = require("../side_pannels/chat/Chat");
@@ -4203,8 +4403,99 @@ var largeVideoState = {
newSrc: ''
};
+/**
+ * Indicates if we have muted our audio before the conference has started.
+ * @type {boolean}
+ */
+var preMuted = false;
+
+var mutedAudios = {};
+
+var flipXLocalVideo = true;
+var currentVideoWidth = null;
+var currentVideoHeight = null;
+
+var localVideoSrc = null;
+
var defaultLocalDisplayName = "Me";
+function videoactive( videoelem) {
+ if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
+ // ignore mixedmslabela0 and v0
+
+ videoelem.show();
+ VideoLayout.resizeThumbnails();
+
+ var videoParent = videoelem.parent();
+ var parentResourceJid = null;
+ if (videoParent)
+ parentResourceJid
+ = VideoLayout.getPeerContainerResourceJid(videoParent[0]);
+
+ // Update the large video to the last added video only if there's no
+ // current dominant, focused speaker or prezi playing or update it to
+ // the current dominant speaker.
+ if ((!focusedVideoInfo &&
+ !VideoLayout.getDominantSpeakerResourceJid() &&
+ !require("../prezi/Prezi").isPresentationVisible()) ||
+ (parentResourceJid &&
+ VideoLayout.getDominantSpeakerResourceJid() === parentResourceJid)) {
+ VideoLayout.updateLargeVideo(
+ RTC.getVideoSrc(videoelem[0]),
+ 1,
+ parentResourceJid);
+ }
+
+ VideoLayout.showModeratorIndicator();
+ }
+}
+
+function waitForRemoteVideo(selector, ssrc, stream, jid) {
+ // XXX(gp) so, every call to this function is *always* preceded by a call
+ // to the RTC.attachMediaStream() function but that call is *not* followed
+ // by an update to the videoSrcToSsrc map!
+ //
+ // The above way of doing things results in video SRCs that don't correspond
+ // to any SSRC for a short period of time (to be more precise, for as long
+ // the waitForRemoteVideo takes to complete). This causes problems (see
+ // bellow).
+ //
+ // I'm wondering why we need to do that; i.e. why call RTC.attachMediaStream()
+ // a second time in here and only then update the videoSrcToSsrc map? Why
+ // not simply update the videoSrcToSsrc map when the RTC.attachMediaStream()
+ // is called the first time? I actually do that in the lastN changed event
+ // handler because the "orphan" video SRC is causing troubles there. The
+ // purpose of this method would then be to fire the "videoactive.jingle".
+ //
+ // Food for though I guess :-)
+
+ if (selector.removed || !selector.parent().is(":visible")) {
+ console.warn("Media removed before had started", selector);
+ return;
+ }
+
+ if (stream.id === 'mixedmslabel') return;
+
+ if (selector[0].currentTime > 0) {
+ var videoStream = simulcast.getReceivingVideoStream(stream);
+ RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
+
+ // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
+ // in order to get rid of too many maps
+ if (ssrc && jid) {
+ jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
+ } else {
+ console.warn("No ssrc given for jid", jid);
+ }
+
+ videoactive(selector);
+ } else {
+ setTimeout(function () {
+ waitForRemoteVideo(selector, ssrc, stream, jid);
+ }, 250);
+ }
+}
+
/**
* Returns an array of the video horizontal and vertical indents,
* so that if fits its parent.
@@ -4381,7 +4672,7 @@ function getParticipantContainer(resourceJid)
if (!resourceJid)
return null;
- if (resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ if (resourceJid === xmpp.myResource())
return $("#localVideoContainer");
else
return $("#participant_" + resourceJid);
@@ -4457,7 +4748,8 @@ function addRemoteVideoMenu(jid, parentElement) {
event.preventDefault();
}
var isMute = mutedAudios[jid] == true;
- connection.moderate.setMute(jid, !isMute);
+ xmpp.setMute(jid, !isMute);
+
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
@@ -4479,7 +4771,7 @@ function addRemoteVideoMenu(jid, parentElement) {
var ejectLinkItem = document.createElement('a');
ejectLinkItem.innerHTML = ejectIndicator + ' Kick out';
ejectLinkItem.onclick = function(){
- connection.moderate.eject(jid);
+ xmpp.eject(jid);
popupmenuElement.setAttribute('style', 'display:none;');
};
@@ -4587,6 +4879,43 @@ function createModeratorIndicatorElement(parentElement) {
}
+/**
+ * Checks if video identified by given src is desktop stream.
+ * @param videoSrc eg.
+ * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
+ * @returns {boolean}
+ */
+function isVideoSrcDesktop(jid) {
+ // FIXME: fix this mapping mess...
+ // figure out if large video is desktop stream or just a camera
+
+ if(!jid)
+ return false;
+ var isDesktop = false;
+ if (xmpp.myJid() &&
+ xmpp.myResource() === jid) {
+ // local video
+ isDesktop = desktopsharing.isUsingScreenStream();
+ } else {
+ // Do we have associations...
+ var videoSsrc = jid2Ssrc[jid];
+ if (videoSsrc) {
+ var videoType = ssrc2videoType[videoSsrc];
+ if (videoType) {
+ // Finally there...
+ isDesktop = videoType === 'screen';
+ } else {
+ console.error("No video type for ssrc: " + videoSsrc);
+ }
+ } else {
+ console.error("No ssrc for jid: " + jid);
+ }
+ }
+ return isDesktop;
+}
+
+
+
var VideoLayout = (function (my) {
my.connectionIndicators = {};
@@ -4594,6 +4923,16 @@ var VideoLayout = (function (my) {
my.getVideoSize = getCameraVideoSize;
my.getVideoPosition = getCameraVideoPosition;
+ my.init = function () {
+ // Listen for large video size updates
+ document.getElementById('largeVideo')
+ .addEventListener('loadedmetadata', function (e) {
+ currentVideoWidth = this.videoWidth;
+ currentVideoHeight = this.videoHeight;
+ VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
+ });
+ };
+
my.isInLastN = function(resource) {
return lastNCount < 0 // lastN is disabled, return true
|| (lastNCount > 0 && lastNEndpointsCache.length == 0) // lastNEndpoints cache not built yet, return true
@@ -4609,7 +4948,10 @@ var VideoLayout = (function (my) {
document.getElementById('localAudio').autoplay = true;
document.getElementById('localAudio').volume = 0;
if (preMuted) {
- setAudioMuted(true);
+ if(!UI.setAudioMuted(true))
+ {
+ preMuted = mute;
+ }
preMuted = false;
}
};
@@ -4646,14 +4988,14 @@ var VideoLayout = (function (my) {
VideoLayout.handleVideoThumbClicked(
RTC.getVideoSrc(localVideo),
false,
- Strophe.getResourceFromJid(connection.emuc.myroomjid));
+ xmpp.myResource());
});
$('#localVideoContainer').click(function (event) {
event.stopPropagation();
VideoLayout.handleVideoThumbClicked(
RTC.getVideoSrc(localVideo),
false,
- Strophe.getResourceFromJid(connection.emuc.myroomjid));
+ xmpp.myResource());
});
// Add hover handler
@@ -4683,11 +5025,8 @@ var VideoLayout = (function (my) {
localVideoSrc = RTC.getVideoSrc(localVideo);
- var myResourceJid = null;
- if(connection.emuc.myroomjid)
- {
- myResourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
- }
+ var myResourceJid = xmpp.myResource();
+
VideoLayout.updateLargeVideo(localVideoSrc, 0,
myResourceJid);
@@ -4726,7 +5065,7 @@ var VideoLayout = (function (my) {
{
if(container.id == "localVideoWrapper")
{
- jid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ jid = xmpp.myResource();
}
else
{
@@ -4804,9 +5143,9 @@ var VideoLayout = (function (my) {
largeVideoState.isVisible = $('#largeVideo').is(':visible');
largeVideoState.isDesktop = isVideoSrcDesktop(resourceJid);
if(jid2Ssrc[largeVideoState.userResourceJid] ||
- (connection && connection.emuc.myroomjid &&
+ (xmpp.myResource() &&
largeVideoState.userResourceJid ===
- Strophe.getResourceFromJid(connection.emuc.myroomjid))) {
+ xmpp.myResource())) {
largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
} else {
largeVideoState.oldResourceJid = null;
@@ -4830,7 +5169,7 @@ var VideoLayout = (function (my) {
var doUpdate = function () {
Avatar.updateActiveSpeakerAvatarSrc(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.userResourceJid));
if (!userChanged && largeVideoState.preload &&
@@ -4910,7 +5249,7 @@ var VideoLayout = (function (my) {
if(userChanged) {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.oldResourceJid));
}
@@ -4925,7 +5264,7 @@ var VideoLayout = (function (my) {
}
} else {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.userResourceJid));
}
@@ -5064,7 +5403,7 @@ var VideoLayout = (function (my) {
focusedVideoInfo = null;
if(focusResourceJid) {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(focusResourceJid));
+ xmpp.findJidFromResource(focusResourceJid));
}
}
}
@@ -5136,7 +5475,7 @@ var VideoLayout = (function (my) {
// If the peerJid is null then this video span couldn't be directly
// associated with a participant (this could happen in the case of prezi).
- if (Moderator.isModerator() && peerJid !== null)
+ if (xmpp.isModerator() && peerJid !== null)
addRemoteVideoMenu(peerJid, container);
remotes.appendChild(container);
@@ -5321,13 +5660,13 @@ var VideoLayout = (function (my) {
if (state == 'show')
{
// peerContainer.css('-webkit-filter', '');
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
Avatar.showUserAvatar(jid, false);
}
else // if (state == 'avatar')
{
// peerContainer.css('-webkit-filter', 'grayscale(100%)');
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
Avatar.showUserAvatar(jid, true);
}
}
@@ -5353,8 +5692,7 @@ var VideoLayout = (function (my) {
if (name && nickname !== name) {
nickname = name;
window.localStorage.displayname = nickname;
- connection.emuc.addDisplayNameToPresence(nickname);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("displayName", nickname);
Chat.setChatConversationMode(true);
}
@@ -5425,7 +5763,7 @@ var VideoLayout = (function (my) {
*/
my.showModeratorIndicator = function () {
- var isModerator = Moderator.isModerator();
+ var isModerator = xmpp.isModerator();
if (isModerator) {
var indicatorSpan = $('#localVideoContainer .focusindicator');
@@ -5434,7 +5772,10 @@ var VideoLayout = (function (my) {
createModeratorIndicatorElement(indicatorSpan[0]);
}
}
- Object.keys(connection.emuc.members).forEach(function (jid) {
+
+ var members = xmpp.getMembers();
+
+ Object.keys(members).forEach(function (jid) {
if (Strophe.getResourceFromJid(jid) === 'focus') {
// Skip server side focus
@@ -5450,7 +5791,7 @@ var VideoLayout = (function (my) {
return;
}
- var member = connection.emuc.members[jid];
+ var member = members[jid];
if (member.role === 'moderator') {
// Remove menu if peer is moderator
@@ -5622,7 +5963,7 @@ var VideoLayout = (function (my) {
var videoSpanId = null;
var videoContainerId = null;
if (resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid)) {
+ === xmpp.myResource()) {
videoSpanId = 'localVideoWrapper';
videoContainerId = 'localVideoContainer';
}
@@ -5665,7 +6006,7 @@ var VideoLayout = (function (my) {
}
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(resourceJid));
+ xmpp.findJidFromResource(resourceJid));
}
};
@@ -5790,7 +6131,7 @@ var VideoLayout = (function (my) {
lastNPickupJid = jid;
$(document).trigger("pinnedendpointchanged", [jid]);
}
- } else if (jid == connection.emuc.myroomjid) {
+ } else if (jid == xmpp.myJid()) {
$("#localVideoContainer").click();
}
}
@@ -5802,13 +6143,13 @@ var VideoLayout = (function (my) {
$(document).bind('audiomuted.muc', function (event, jid, isMuted) {
/*
// FIXME: but focus can not mute in this case ? - check
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
// The local mute indicator is controlled locally
return;
}*/
var videoSpanId = null;
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
@@ -5817,7 +6158,7 @@ var VideoLayout = (function (my) {
mutedAudios[jid] = isMuted;
- if (Moderator.isModerator()) {
+ if (xmpp.isModerator()) {
VideoLayout.updateRemoteVideoMenu(jid, isMuted);
}
@@ -5835,7 +6176,7 @@ var VideoLayout = (function (my) {
Avatar.showUserAvatar(jid, isMuted);
var videoSpanId = null;
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
@@ -5849,11 +6190,11 @@ var VideoLayout = (function (my) {
/**
* Display name changed.
*/
- $(document).bind('displaynamechanged',
- function (event, jid, displayName, status) {
+ my.onDisplayNameChanged =
+ function (jid, displayName, status) {
var name = null;
if (jid === 'localVideoContainer'
- || jid === connection.emuc.myroomjid) {
+ || jid === xmpp.myJid()) {
name = nickname;
setDisplayName('localVideoContainer',
displayName);
@@ -5867,10 +6208,10 @@ var VideoLayout = (function (my) {
}
if(jid === 'localVideoContainer')
- jid = connection.emuc.myroomjid;
+ jid = xmpp.myJid();
if(!name || name != displayName)
API.triggerEvent("displayNameChange",{jid: jid, displayname: displayName});
- });
+ };
/**
* On dominant speaker changed event.
@@ -5878,7 +6219,7 @@ var VideoLayout = (function (my) {
$(document).bind('dominantspeakerchanged', function (event, resourceJid) {
// We ignore local user events.
if (resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ === xmpp.myResource())
return;
// Update the current dominant speaker.
@@ -6009,7 +6350,7 @@ var VideoLayout = (function (my) {
if (!isVisible) {
console.log("Add to last N", resourceJid);
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
var mediaStream = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
var sel = $('#participant_' + resourceJid + '>video');
@@ -6042,7 +6383,7 @@ var VideoLayout = (function (my) {
var resource, container, src;
var myResource
- = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ = xmpp.myResource();
// Find out which endpoint to show in the large video.
for (var i = 0; i < lastNEndpoints.length; i++) {
@@ -6066,37 +6407,6 @@ var VideoLayout = (function (my) {
}
});
- $(document).bind('videoactive.jingle', function (event, videoelem) {
- if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
- // ignore mixedmslabela0 and v0
-
- videoelem.show();
- VideoLayout.resizeThumbnails();
-
- var videoParent = videoelem.parent();
- var parentResourceJid = null;
- if (videoParent)
- parentResourceJid
- = VideoLayout.getPeerContainerResourceJid(videoParent[0]);
-
- // Update the large video to the last added video only if there's no
- // current dominant, focused speaker or prezi playing or update it to
- // the current dominant speaker.
- if ((!focusedVideoInfo &&
- !VideoLayout.getDominantSpeakerResourceJid() &&
- !require("../prezi/Prezi").isPresentationVisible()) ||
- (parentResourceJid &&
- VideoLayout.getDominantSpeakerResourceJid() === parentResourceJid)) {
- VideoLayout.updateLargeVideo(
- RTC.getVideoSrc(videoelem[0]),
- 1,
- parentResourceJid);
- }
-
- VideoLayout.showModeratorIndicator();
- }
- });
-
$(document).bind('simulcastlayerschanging', function (event, endpointSimulcastLayers) {
endpointSimulcastLayers.forEach(function (esl) {
@@ -6117,13 +6427,13 @@ var VideoLayout = (function (my) {
// Get session and stream from primary ssrc.
var res = simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
- var session = res.session;
+ var sid = res.sid;
var electedStream = res.stream;
- if (session && electedStream) {
+ if (sid && electedStream) {
var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
- console.info([esl, primarySSRC, msid, session, electedStream]);
+ console.info([esl, primarySSRC, msid, sid, electedStream]);
var msidParts = msid.split(' ');
@@ -6143,7 +6453,7 @@ var VideoLayout = (function (my) {
}
} else {
- console.error('Could not find a stream or a session.', session, electedStream);
+ console.error('Could not find a stream or a session.', sid, electedStream);
}
});
});
@@ -6175,17 +6485,17 @@ var VideoLayout = (function (my) {
// Get session and stream from primary ssrc.
var res = simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
- var session = res.session;
+ var sid = res.sid;
var electedStream = res.stream;
- if (session && electedStream) {
+ if (sid && electedStream) {
var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
console.info('Switching simulcast substream.');
- console.info([esl, primarySSRC, msid, session, electedStream]);
+ console.info([esl, primarySSRC, msid, sid, electedStream]);
var msidParts = msid.split(' ');
- var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
+ var selRemoteVideo = $(['#', 'remoteVideo_', sid, '_', msidParts[0]].join(''));
var updateLargeVideo = (Strophe.getResourceFromJid(ssrc2jid[primarySSRC])
== largeVideoState.userResourceJid);
@@ -6222,7 +6532,7 @@ var VideoLayout = (function (my) {
}
var videoId;
- if(resource == Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ if(resource == xmpp.myResource())
{
videoId = "localVideoContainer";
}
@@ -6235,7 +6545,7 @@ var VideoLayout = (function (my) {
connectionIndicator.updatePopoverData();
} else {
- console.error('Could not find a stream or a session.', session, electedStream);
+ console.error('Could not find a stream or a sid.', sid, electedStream);
}
});
});
@@ -6250,8 +6560,8 @@ var VideoLayout = (function (my) {
if(object.resolution !== null)
{
resolution = object.resolution;
- object.resolution = resolution[connection.emuc.myroomjid];
- delete resolution[connection.emuc.myroomjid];
+ object.resolution = resolution[xmpp.myJid()];
+ delete resolution[xmpp.myJid()];
}
updateStatsIndicator("localVideoContainer", percent, object);
for(var jid in resolution)
@@ -6312,7 +6622,7 @@ var VideoLayout = (function (my) {
}(VideoLayout || {}));
module.exports = VideoLayout;
-},{"../audio_levels/AudioLevels":2,"../avatar/Avatar":4,"../etherpad/Etherpad":5,"../prezi/Prezi":6,"../side_pannels/chat/Chat":8,"../side_pannels/contactlist/ContactList":12,"../util/UIUtil":21,"./ConnectionIndicator":22}],24:[function(require,module,exports){
+},{"../audio_levels/AudioLevels":2,"../avatar/Avatar":5,"../etherpad/Etherpad":6,"../prezi/Prezi":7,"../side_pannels/chat/Chat":9,"../side_pannels/contactlist/ContactList":13,"../util/UIUtil":22,"./ConnectionIndicator":23}],25:[function(require,module,exports){
//var nouns = [
//];
var pluralNouns = [
@@ -6493,7 +6803,7 @@ var RoomNameGenerator = {
module.exports = RoomNameGenerator;
-},{}],25:[function(require,module,exports){
+},{}],26:[function(require,module,exports){
var animateTimeout, updateTimeout;
var RoomNameGenerator = require("./RoomnameGenerator");
@@ -6597,5 +6907,6 @@ function setupWelcomePage()
}
module.exports = setupWelcomePage;
-},{"./RoomnameGenerator":24}]},{},[1])(1)
-});
\ No newline at end of file
+},{"./RoomnameGenerator":25}]},{},[1])(1)
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL1VJLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS9hdWRpb19sZXZlbHMvQXVkaW9MZXZlbHMuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL2F1ZGlvX2xldmVscy9DYW52YXNVdGlscy5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvYXV0aGVudGljYXRpb24vQXV0aGVudGljYXRpb24uanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL2F2YXRhci9BdmF0YXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL2V0aGVycGFkL0V0aGVycGFkLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS9wcmV6aS9QcmV6aS5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3NpZGVfcGFubmVscy9jaGF0L0NoYXQuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3NpZGVfcGFubmVscy9jaGF0L0NvbW1hbmRzLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY2hhdC9SZXBsYWNlbWVudC5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL2NoYXQvc21pbGV5cy5qc29uIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY29udGFjdGxpc3QvQ29udGFjdExpc3QuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3NpZGVfcGFubmVscy9zZXR0aW5ncy9TZXR0aW5ncy5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL3NldHRpbmdzL1NldHRpbmdzTWVudS5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvdG9vbGJhcnMvQm90dG9tVG9vbGJhci5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3Rvb2xiYXJzL3Rvb2xiYXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3V0aWwvSml0c2lQb3BvdmVyLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS91dGlsL01lc3NhZ2VIYW5kbGVyLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy9VSS91dGlsL1VJVXRpbC5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvdmlkZW9sYXlvdXQvQ29ubmVjdGlvbkluZGljYXRvci5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXQuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL1VJL3dlbGNvbWVfcGFnZS9Sb29tbmFtZUdlbmVyYXRvci5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMvVUkvd2VsY29tZV9wYWdlL1dlbGNvbWVQYWdlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeHJCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdlFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5R0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25GQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6SkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOVZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9QQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDcmdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2S0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6WkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1ckVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwidmFyIFVJID0ge307XG5cbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0LmpzXCIpO1xudmFyIEF1ZGlvTGV2ZWxzID0gcmVxdWlyZShcIi4vYXVkaW9fbGV2ZWxzL0F1ZGlvTGV2ZWxzLmpzXCIpO1xudmFyIFByZXppID0gcmVxdWlyZShcIi4vcHJlemkvUHJlemkuanNcIik7XG52YXIgRXRoZXJwYWQgPSByZXF1aXJlKFwiLi9ldGhlcnBhZC9FdGhlcnBhZC5qc1wiKTtcbnZhciBDaGF0ID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL2NoYXQvQ2hhdC5qc1wiKTtcbnZhciBUb29sYmFyID0gcmVxdWlyZShcIi4vdG9vbGJhcnMvdG9vbGJhclwiKTtcbnZhciBUb29sYmFyVG9nZ2xlciA9IHJlcXVpcmUoXCIuL3Rvb2xiYXJzL3Rvb2xiYXJ0b2dnbGVyXCIpO1xudmFyIEJvdHRvbVRvb2xiYXIgPSByZXF1aXJlKFwiLi90b29sYmFycy9Cb3R0b21Ub29sYmFyXCIpO1xudmFyIENvbnRhY3RMaXN0ID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL2NvbnRhY3RsaXN0L0NvbnRhY3RMaXN0XCIpO1xudmFyIEF2YXRhciA9IHJlcXVpcmUoXCIuL2F2YXRhci9BdmF0YXJcIik7XG4vL3ZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFNldHRpbmdzTWVudSA9IHJlcXVpcmUoXCIuL3NpZGVfcGFubmVscy9zZXR0aW5ncy9TZXR0aW5nc01lbnVcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi9zaWRlX3Bhbm5lbHMvc2V0dGluZ3MvU2V0dGluZ3NcIik7XG52YXIgUGFuZWxUb2dnbGVyID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG52YXIgUm9vbU5hbWVHZW5lcmF0b3IgPSByZXF1aXJlKFwiLi93ZWxjb21lX3BhZ2UvUm9vbW5hbWVHZW5lcmF0b3JcIik7XG5VSS5tZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgbWVzc2FnZUhhbmRsZXIgPSBVSS5tZXNzYWdlSGFuZGxlcjtcbnZhciBBdXRoZW50aWNhdGlvbiAgPSByZXF1aXJlKFwiLi9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvblwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi91dGlsL1VJVXRpbFwiKTtcblxuLy92YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xudmFyIHJvb21OYW1lID0gbnVsbDtcblxuXG5mdW5jdGlvbiBzZXR1cFByZXppKClcbntcbiAgICAkKFwiI3JlbG9hZFByZXNlbnRhdGlvbkxpbmtcIikuY2xpY2soZnVuY3Rpb24oKVxuICAgIHtcbiAgICAgICAgUHJlemkucmVsb2FkUHJlc2VudGF0aW9uKCk7XG4gICAgfSk7XG59XG5cbmZ1bmN0aW9uIHNldHVwQ2hhdCgpXG57XG4gICAgQ2hhdC5pbml0KCk7XG4gICAgJChcIiN0b2dnbGVfc21pbGV5c1wiKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICAgQ2hhdC50b2dnbGVTbWlsZXlzKCk7XG4gICAgfSk7XG59XG5cbmZ1bmN0aW9uIHNldHVwVG9vbGJhcnMoKSB7XG4gICAgVG9vbGJhci5pbml0KFVJKTtcbiAgICBUb29sYmFyLnNldHVwQnV0dG9uc0Zyb21Db25maWcoKTtcbiAgICBCb3R0b21Ub29sYmFyLmluaXQoKTtcbn1cblxuZnVuY3Rpb24gc3RyZWFtSGFuZGxlcihzdHJlYW0pIHtcbiAgICBzd2l0Y2ggKHN0cmVhbS50eXBlKVxuICAgIHtcbiAgICAgICAgY2FzZSBcImF1ZGlvXCI6XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbEF1ZGlvKHN0cmVhbSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInZpZGVvXCI6XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFZpZGVvKHN0cmVhbSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInN0cmVhbVwiOlxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY2hhbmdlTG9jYWxTdHJlYW0oc3RyZWFtKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGVza3RvcFwiOlxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY2hhbmdlTG9jYWxWaWRlbyhzdHJlYW0pO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBvbkRpc3Bvc2VDb25mZXJlbmNlKHVubG9hZCkge1xuICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbihmYWxzZSk7XG59O1xuXG5mdW5jdGlvbiBvbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgQ29udGFjdExpc3Qub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBTZXR0aW5nc01lbnUub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBWaWRlb0xheW91dC5vbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKTtcbn1cblxuZnVuY3Rpb24gcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgUlRDLmFkZFN0cmVhbUxpc3RlbmVyKHN0cmVhbUhhbmRsZXIsIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DUkVBVEVEKTtcblxuICAgIFJUQy5hZGRTdHJlYW1MaXN0ZW5lcihzdHJlYW1IYW5kbGVyLCBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ0hBTkdFRCk7XG4gICAgUlRDLmFkZFN0cmVhbUxpc3RlbmVyKGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgVmlkZW9MYXlvdXQub25SZW1vdGVTdHJlYW1BZGRlZChzdHJlYW0pO1xuICAgIH0sIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9SRU1PVEVfQ1JFQVRFRCk7XG5cbiAgICBWaWRlb0xheW91dC5pbml0KCk7XG5cbiAgICBzdGF0aXN0aWNzLmFkZEF1ZGlvTGV2ZWxMaXN0ZW5lcihmdW5jdGlvbihqaWQsIGF1ZGlvTGV2ZWwpXG4gICAge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQ7XG4gICAgICAgIGlmKGppZCA9PT0gc3RhdGlzdGljcy5MT0NBTF9KSUQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc291cmNlSmlkID0gQXVkaW9MZXZlbHMuTE9DQUxfTEVWRUw7XG4gICAgICAgICAgICBpZihSVEMubG9jYWxBdWRpby5pc011dGVkKCkpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbCA9IDA7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBBdWRpb0xldmVscy51cGRhdGVBdWRpb0xldmVsKHJlc291cmNlSmlkLCBhdWRpb0xldmVsLFxuICAgICAgICAgICAgVUkuZ2V0TGFyZ2VWaWRlb1N0YXRlKCkudXNlclJlc291cmNlSmlkKTtcbiAgICB9KTtcbiAgICBkZXNrdG9wc2hhcmluZy5hZGRMaXN0ZW5lcihmdW5jdGlvbiAoKSB7XG4gICAgICAgIFRvb2xiYXJUb2dnbGVyLnNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbigpO1xuICAgIH0sIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5JTklUKTtcbiAgICBkZXNrdG9wc2hhcmluZy5hZGRMaXN0ZW5lcihcbiAgICAgICAgVG9vbGJhci5jaGFuZ2VEZXNrdG9wU2hhcmluZ0J1dHRvblN0YXRlLFxuICAgICAgICBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuU1dJVENISU5HX0RPTkUpO1xuICAgIHhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5ESVNQT1NFX0NPTkZFUkVOQ0UsIG9uRGlzcG9zZUNvbmZlcmVuY2UpO1xuICAgIHhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5LSUNLRUQsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2coXCJTZXNzaW9uIFRlcm1pbmF0ZWRcIixcbiAgICAgICAgICAgIFwiT3VjaCEgWW91IGhhdmUgYmVlbiBraWNrZWQgb3V0IG9mIHRoZSBtZWV0IVwiKTtcbiAgICB9KTtcbiAgICB4bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQlJJREdFX0RPV04sIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKFwiRXJyb3JcIixcbiAgICAgICAgICAgIFwiSml0c2kgVmlkZW9icmlkZ2UgaXMgY3VycmVudGx5IHVuYXZhaWxhYmxlLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyIVwiKTtcbiAgICB9KTtcbiAgICB4bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuVVNFUl9JRF9DSEFOR0VELCBBdmF0YXIuc2V0VXNlckF2YXRhcik7XG4gICAgeG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkNIQU5HRURfU1RSRUFNUywgZnVuY3Rpb24gKGppZCwgY2hhbmdlZFN0cmVhbXMpIHtcbiAgICAgICAgZm9yKHN0cmVhbSBpbiBjaGFuZ2VkU3RyZWFtcylcbiAgICAgICAge1xuICAgICAgICAgICAgLy8gbWlnaHQgbmVlZCB0byB1cGRhdGUgdGhlIGRpcmVjdGlvbiBpZiBwYXJ0aWNpcGFudCBqdXN0IHdlbnQgZnJvbSBzZW5kcmVjdiB0byByZWN2b25seVxuICAgICAgICAgICAgaWYgKHN0cmVhbS50eXBlID09PSAndmlkZW8nIHx8IHN0cmVhbS50eXBlID09PSAnc2NyZWVuJykge1xuICAgICAgICAgICAgICAgIHZhciBlbCA9ICQoJyNwYXJ0aWNpcGFudF8nICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgKyAnPnZpZGVvJyk7XG4gICAgICAgICAgICAgICAgc3dpdGNoIChzdHJlYW0uZGlyZWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgJ3NlbmRyZWN2JzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICBjYXNlICdyZWN2b25seSc6XG4gICAgICAgICAgICAgICAgICAgICAgICBlbC5oaWRlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogQ2hlY2sgaWYgd2UgaGF2ZSB0byBjaGFuZ2UgbGFyZ2UgdmlkZW9cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vVmlkZW9MYXlvdXQudXBkYXRlTGFyZ2VWaWRlbyhlbCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgIH0pO1xuICAgIHhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5ESVNQTEFZX05BTUVfQ0hBTkdFRCwgb25EaXNwbGF5TmFtZUNoYW5nZWQpO1xuICAgIHhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5NVUNfSk9JTkVELCBvbk11Y0pvaW5lZCk7XG59XG5cbmZ1bmN0aW9uIGJpbmRFdmVudHMoKVxue1xuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgYW5kIHJlcG9zaXRpb25zIHZpZGVvcyBpbiBmdWxsIHNjcmVlbiBtb2RlLlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLm9uKCd3ZWJraXRmdWxsc2NyZWVuY2hhbmdlIG1vemZ1bGxzY3JlZW5jaGFuZ2UgZnVsbHNjcmVlbmNoYW5nZScsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnJlc2l6ZUxhcmdlVmlkZW9Db250YWluZXIoKTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoKTtcbiAgICAgICAgfVxuICAgICk7XG5cbiAgICAkKHdpbmRvdykucmVzaXplKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplTGFyZ2VWaWRlb0NvbnRhaW5lcigpO1xuICAgICAgICBWaWRlb0xheW91dC5wb3NpdGlvbkxhcmdlKCk7XG4gICAgfSk7XG59XG5cblVJLnN0YXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgIGRvY3VtZW50LnRpdGxlID0gaW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FO1xuICAgIGlmKGNvbmZpZy5lbmFibGVXZWxjb21lUGFnZSAmJiB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWUgPT0gXCIvXCIgJiZcbiAgICAgICAgKCF3aW5kb3cubG9jYWxTdG9yYWdlLndlbGNvbWVQYWdlRGlzYWJsZWQgfHwgd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkID09IFwiZmFsc2VcIikpXG4gICAge1xuICAgICAgICAkKFwiI3ZpZGVvY29uZmVyZW5jZV9wYWdlXCIpLmhpZGUoKTtcbiAgICAgICAgdmFyIHNldHVwV2VsY29tZVBhZ2UgPSByZXF1aXJlKFwiLi93ZWxjb21lX3BhZ2UvV2VsY29tZVBhZ2VcIik7XG4gICAgICAgIHNldHVwV2VsY29tZVBhZ2UoKTtcblxuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0pJVFNJX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgbGVmdFdhdGVybWFya0RpdlxuICAgICAgICAgICAgPSAkKFwiI2xhcmdlVmlkZW9Db250YWluZXIgZGl2W2NsYXNzPSd3YXRlcm1hcmsgbGVmdHdhdGVybWFyayddXCIpO1xuXG4gICAgICAgIGxlZnRXYXRlcm1hcmtEaXYuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgICAgIGxlZnRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgID0gaW50ZXJmYWNlQ29uZmlnLkpJVFNJX1dBVEVSTUFSS19MSU5LO1xuICAgIH1cblxuICAgIGlmIChpbnRlcmZhY2VDb25maWcuU0hPV19CUkFORF9XQVRFUk1BUkspIHtcbiAgICAgICAgdmFyIHJpZ2h0V2F0ZXJtYXJrRGl2XG4gICAgICAgICAgICA9ICQoXCIjbGFyZ2VWaWRlb0NvbnRhaW5lciBkaXZbY2xhc3M9J3dhdGVybWFyayByaWdodHdhdGVybWFyayddXCIpO1xuXG4gICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmNzcyh7ZGlzcGxheTogJ2Jsb2NrJ30pO1xuICAgICAgICByaWdodFdhdGVybWFya0Rpdi5wYXJlbnQoKS5nZXQoMCkuaHJlZlxuICAgICAgICAgICAgPSBpbnRlcmZhY2VDb25maWcuQlJBTkRfV0FURVJNQVJLX0xJTks7XG4gICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmdldCgwKS5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2VcbiAgICAgICAgICAgID0gXCJ1cmwoaW1hZ2VzL3JpZ2h0d2F0ZXJtYXJrLnBuZylcIjtcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLlNIT1dfUE9XRVJFRF9CWSkge1xuICAgICAgICAkKFwiI2xhcmdlVmlkZW9Db250YWluZXI+YVtjbGFzcz0ncG93ZXJlZGJ5J11cIikuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgfVxuXG4gICAgJChcIiN3ZWxjb21lX3BhZ2VcIikuaGlkZSgpO1xuXG4gICAgJCgnYm9keScpLnBvcG92ZXIoeyBzZWxlY3RvcjogJ1tkYXRhLXRvZ2dsZT1wb3BvdmVyXScsXG4gICAgICAgIHRyaWdnZXI6ICdjbGljayBob3ZlcicsXG4gICAgICAgIGNvbnRlbnQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKFwiY29udGVudFwiKSArXG4gICAgICAgICAgICAgICAgS2V5Ym9hcmRTaG9ydGN1dC5nZXRTaG9ydGN1dCh0aGlzLmdldEF0dHJpYnV0ZShcInNob3J0Y3V0XCIpKTtcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIFZpZGVvTGF5b3V0LnJlc2l6ZUxhcmdlVmlkZW9Db250YWluZXIoKTtcbiAgICAkKFwiI3ZpZGVvc3BhY2VcIikubW91c2Vtb3ZlKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXJUb2dnbGVyLnNob3dUb29sYmFyKCk7XG4gICAgfSk7XG4gICAgLy8gU2V0IHRoZSBkZWZhdWx0cyBmb3IgcHJvbXB0IGRpYWxvZ3MuXG4gICAgalF1ZXJ5LnByb21wdC5zZXREZWZhdWx0cyh7cGVyc2lzdGVudDogZmFsc2V9KTtcblxuLy8gICAgS2V5Ym9hcmRTaG9ydGN1dC5pbml0KCk7XG4gICAgcmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICBiaW5kRXZlbnRzKCk7XG4gICAgc2V0dXBQcmV6aSgpO1xuICAgIHNldHVwVG9vbGJhcnMoKTtcbiAgICBzZXR1cENoYXQoKTtcblxuICAgIGRvY3VtZW50LnRpdGxlID0gaW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FO1xuXG4gICAgJChcIiNkb3dubG9hZGxvZ1wiKS5jbGljayhmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgZHVtcChldmVudC50YXJnZXQpO1xuICAgIH0pO1xuXG4gICAgaWYoY29uZmlnLmVuYWJsZVdlbGNvbWVQYWdlICYmIHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSA9PSBcIi9cIiAmJlxuICAgICAgICAoIXdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZCB8fCB3aW5kb3cubG9jYWxTdG9yYWdlLndlbGNvbWVQYWdlRGlzYWJsZWQgPT0gXCJmYWxzZVwiKSlcbiAgICB7XG4gICAgICAgICQoXCIjdmlkZW9jb25mZXJlbmNlX3BhZ2VcIikuaGlkZSgpO1xuICAgICAgICB2YXIgc2V0dXBXZWxjb21lUGFnZSA9IHJlcXVpcmUoXCIuL3dlbGNvbWVfcGFnZS9XZWxjb21lUGFnZVwiKTtcbiAgICAgICAgc2V0dXBXZWxjb21lUGFnZSgpO1xuXG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAkKFwiI3dlbGNvbWVfcGFnZVwiKS5oaWRlKCk7XG5cbiAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGFyZ2VWaWRlbycpLnZvbHVtZSA9IDA7XG5cbiAgICBpZiAoISQoJyNzZXR0aW5ncycpLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdpbml0Jyk7XG4gICAgICAgIGluaXQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBsb2dpbkluZm8ub25zdWJtaXQgPSBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgaWYgKGUucHJldmVudERlZmF1bHQpIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICQoJyNzZXR0aW5ncycpLmhpZGUoKTtcbiAgICAgICAgICAgIGluaXQoKTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICB0b2FzdHIub3B0aW9ucyA9IHtcbiAgICAgICAgXCJjbG9zZUJ1dHRvblwiOiB0cnVlLFxuICAgICAgICBcImRlYnVnXCI6IGZhbHNlLFxuICAgICAgICBcInBvc2l0aW9uQ2xhc3NcIjogXCJub3RpZmljYXRpb24tYm90dG9tLXJpZ2h0XCIsXG4gICAgICAgIFwib25jbGlja1wiOiBudWxsLFxuICAgICAgICBcInNob3dEdXJhdGlvblwiOiBcIjMwMFwiLFxuICAgICAgICBcImhpZGVEdXJhdGlvblwiOiBcIjEwMDBcIixcbiAgICAgICAgXCJ0aW1lT3V0XCI6IFwiMjAwMFwiLFxuICAgICAgICBcImV4dGVuZGVkVGltZU91dFwiOiBcIjEwMDBcIixcbiAgICAgICAgXCJzaG93RWFzaW5nXCI6IFwic3dpbmdcIixcbiAgICAgICAgXCJoaWRlRWFzaW5nXCI6IFwibGluZWFyXCIsXG4gICAgICAgIFwic2hvd01ldGhvZFwiOiBcImZhZGVJblwiLFxuICAgICAgICBcImhpZGVNZXRob2RcIjogXCJmYWRlT3V0XCIsXG4gICAgICAgIFwicmVwb3NpdGlvblwiOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmKFBhbmVsVG9nZ2xlci5pc1Zpc2libGUoKSkge1xuICAgICAgICAgICAgICAgICQoXCIjdG9hc3QtY29udGFpbmVyXCIpLmFkZENsYXNzKFwibm90aWZpY2F0aW9uLWJvdHRvbS1yaWdodC1jZW50ZXJcIik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICQoXCIjdG9hc3QtY29udGFpbmVyXCIpLnJlbW92ZUNsYXNzKFwibm90aWZpY2F0aW9uLWJvdHRvbS1yaWdodC1jZW50ZXJcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIFwibmV3ZXN0T25Ub3BcIjogZmFsc2VcbiAgICB9O1xuXG4gICAgJCgnI3NldHRpbmdzbWVudT5pbnB1dCcpLmtleXVwKGZ1bmN0aW9uKGV2ZW50KXtcbiAgICAgICAgaWYoZXZlbnQua2V5Q29kZSA9PT0gMTMpIHsvL2VudGVyXG4gICAgICAgICAgICBTZXR0aW5nc01lbnUudXBkYXRlKCk7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgICQoXCIjdXBkYXRlU2V0dGluZ3NcIikuY2xpY2soZnVuY3Rpb24gKCkge1xuICAgICAgICBTZXR0aW5nc01lbnUudXBkYXRlKCk7XG4gICAgfSk7XG5cbn07XG5cblVJLnRvZ2dsZVNtaWxleXMgPSBmdW5jdGlvbiAoKSB7XG4gICAgQ2hhdC50b2dnbGVTbWlsZXlzKCk7XG59O1xuXG5VSS5jaGF0QWRkRXJyb3IgPSBmdW5jdGlvbihlcnJvck1lc3NhZ2UsIG9yaWdpbmFsVGV4dClcbntcbiAgICByZXR1cm4gQ2hhdC5jaGF0QWRkRXJyb3IoZXJyb3JNZXNzYWdlLCBvcmlnaW5hbFRleHQpO1xufTtcblxuVUkuY2hhdFNldFN1YmplY3QgPSBmdW5jdGlvbih0ZXh0KVxue1xuICAgIHJldHVybiBDaGF0LmNoYXRTZXRTdWJqZWN0KHRleHQpO1xufTtcblxuVUkudXBkYXRlQ2hhdENvbnZlcnNhdGlvbiA9IGZ1bmN0aW9uIChmcm9tLCBkaXNwbGF5TmFtZSwgbWVzc2FnZSkge1xuICAgIHJldHVybiBDaGF0LnVwZGF0ZUNoYXRDb252ZXJzYXRpb24oZnJvbSwgZGlzcGxheU5hbWUsIG1lc3NhZ2UpO1xufTtcblxuZnVuY3Rpb24gb25NdWNKb2luZWQoamlkLCBpbmZvKSB7XG4gICAgVG9vbGJhci51cGRhdGVSb29tVXJsKHdpbmRvdy5sb2NhdGlvbi5ocmVmKTtcbiAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9jYWxOaWNrJykuYXBwZW5kQ2hpbGQoXG4gICAgICAgIGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgKyAnIChtZSknKVxuICAgICk7XG5cbiAgICB2YXIgc2V0dGluZ3MgPSBTZXR0aW5ncy5nZXRTZXR0aW5ncygpO1xuICAgIC8vIEFkZCBteXNlbGYgdG8gdGhlIGNvbnRhY3QgbGlzdC5cbiAgICBDb250YWN0TGlzdC5hZGRDb250YWN0KGppZCwgc2V0dGluZ3MuZW1haWwgfHwgc2V0dGluZ3MudWlkKTtcblxuICAgIC8vIE9uY2Ugd2UndmUgam9pbmVkIHRoZSBtdWMgc2hvdyB0aGUgdG9vbGJhclxuICAgIFRvb2xiYXJUb2dnbGVyLnNob3dUb29sYmFyKCk7XG5cbiAgICAvLyBTaG93IGF1dGhlbnRpY2F0ZSBidXR0b24gaWYgbmVlZGVkXG4gICAgVG9vbGJhci5zaG93QXV0aGVudGljYXRlQnV0dG9uKFxuICAgICAgICAgICAgeG1wcC5pc0V4dGVybmFsQXV0aEVuYWJsZWQoKSAmJiAheG1wcC5pc01vZGVyYXRvcigpKTtcblxuICAgIHZhciBkaXNwbGF5TmFtZSA9ICFjb25maWcuZGlzcGxheUppZHNcbiAgICAgICAgPyBpbmZvLmRpc3BsYXlOYW1lIDogU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcblxuICAgIGlmIChkaXNwbGF5TmFtZSlcbiAgICAgICAgb25EaXNwbGF5TmFtZUNoYW5nZWQoJ2xvY2FsVmlkZW9Db250YWluZXInLCBkaXNwbGF5TmFtZSArICcgKG1lKScpO1xufVxuXG5VSS5pbml0RXRoZXJwYWQgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIEV0aGVycGFkLmluaXQobmFtZSk7XG59O1xuXG5VSS5vbk11Y0xlZnQgPSBmdW5jdGlvbiAoamlkKSB7XG4gICAgY29uc29sZS5sb2coJ2xlZnQubXVjJywgamlkKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSAkKCcjcGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgK1xuICAgICAgICAnPi5kaXNwbGF5bmFtZScpLmh0bWwoKTtcbiAgICBtZXNzYWdlSGFuZGxlci5ub3RpZnkoZGlzcGxheU5hbWUgfHwgJ1NvbWVib2R5JyxcbiAgICAgICAgJ2Rpc2Nvbm5lY3RlZCcsXG4gICAgICAgICdkaXNjb25uZWN0ZWQnKTtcbiAgICAvLyBOZWVkIHRvIGNhbGwgdGhpcyB3aXRoIGEgc2xpZ2h0IGRlbGF5LCBvdGhlcndpc2UgdGhlIGVsZW1lbnQgY291bGRuJ3QgYmVcbiAgICAvLyBmb3VuZCBmb3Igc29tZSByZWFzb24uXG4gICAgLy8gWFhYKGdwKSBpdCB3b3JrcyBmaW5lIHdpdGhvdXQgdGhlIHRpbWVvdXQgZm9yIG1lICh3aXRoIENocm9tZSAzOCkuXG4gICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXG4gICAgICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgQ29udGFjdExpc3QucmVtb3ZlQ29udGFjdChqaWQpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVtb3ZlQ29ubmVjdGlvbkluZGljYXRvcihqaWQpO1xuICAgICAgICAgICAgLy8gaGlkZSBoZXJlLCB3YWl0IGZvciB2aWRlbyB0byBjbG9zZSBiZWZvcmUgcmVtb3ZpbmdcbiAgICAgICAgICAgICQoY29udGFpbmVyKS5oaWRlKCk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cbiAgICB9LCAxMCk7XG5cbiAgICAvLyBVbmxvY2sgbGFyZ2UgdmlkZW9cbiAgICBpZiAoZm9jdXNlZFZpZGVvSW5mbyAmJiBmb2N1c2VkVmlkZW9JbmZvLmppZCA9PT0gamlkKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5pbmZvKFwiRm9jdXNlZCB2aWRlbyBvd25lciBoYXMgbGVmdCB0aGUgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IG51bGw7XG4gICAgfVxuXG59O1xuXG5VSS5nZXRTZXR0aW5ncyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbn07XG5cblVJLnRvZ2dsZUZpbG1TdHJpcCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gQm90dG9tVG9vbGJhci50b2dnbGVGaWxtU3RyaXAoKTtcbn07XG5cblVJLnRvZ2dsZUNoYXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ2hhdCgpO1xufTtcblxuVUkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ29udGFjdExpc3QoKTtcbn07XG5cblVJLm9uTG9jYWxSb2xlQ2hhbmdlID0gZnVuY3Rpb24gKGppZCwgaW5mbywgcHJlcykge1xuXG4gICAgY29uc29sZS5pbmZvKFwiTXkgcm9sZSBjaGFuZ2VkLCBuZXcgcm9sZTogXCIgKyBpbmZvLnJvbGUpO1xuICAgIHZhciBpc01vZGVyYXRvciA9IHhtcHAuaXNNb2RlcmF0b3IoKTtcblxuICAgIFZpZGVvTGF5b3V0LnNob3dNb2RlcmF0b3JJbmRpY2F0b3IoKTtcbiAgICBUb29sYmFyLnNob3dBdXRoZW50aWNhdGVCdXR0b24oXG4gICAgICAgICAgICB4bXBwLmlzRXh0ZXJuYWxBdXRoRW5hYmxlZCgpICYmICFpc01vZGVyYXRvcik7XG5cbiAgICBpZiAoaXNNb2RlcmF0b3IpIHtcbiAgICAgICAgQXV0aGVudGljYXRpb24uY2xvc2VBdXRoZW50aWNhdGlvbldpbmRvdygpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5ub3RpZnkoXG4gICAgICAgICAgICAnTWUnLCAnY29ubmVjdGVkJywgJ01vZGVyYXRvciByaWdodHMgZ3JhbnRlZCAhJyk7XG4gICAgfVxufTtcblxuVUkub25Nb2RlcmF0b3JTdGF0dXNDaGFuZ2VkID0gZnVuY3Rpb24gKGlzTW9kZXJhdG9yKSB7XG5cbiAgICBUb29sYmFyLnNob3dTaXBDYWxsQnV0dG9uKGlzTW9kZXJhdG9yKTtcbiAgICBUb29sYmFyLnNob3dSZWNvcmRpbmdCdXR0b24oXG4gICAgICAgIGlzTW9kZXJhdG9yKTsgLy8mJlxuICAgIC8vIEZJWE1FOlxuICAgIC8vIFJlY29yZGluZyB2aXNpYmxlIGlmXG4gICAgLy8gdGhlcmUgYXJlIGF0IGxlYXN0IDIoKyAxIGZvY3VzKSBwYXJ0aWNpcGFudHNcbiAgICAvL09iamVjdC5rZXlzKGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzKS5sZW5ndGggPj0gMyk7XG5cbiAgICBpZiAoaXNNb2RlcmF0b3IgJiYgY29uZmlnLmV0aGVycGFkX2Jhc2UpIHtcbiAgICAgICAgRXRoZXJwYWQuaW5pdCgpO1xuICAgIH1cbn07XG5cblVJLm9uUGFzc3dvcmRSZXFpdXJlZCA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgIC8vIHBhc3N3b3JkIGlzIHJlcXVpcmVkXG4gICAgVG9vbGJhci5sb2NrTG9ja0J1dHRvbigpO1xuXG4gICAgbWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhudWxsLFxuICAgICAgICAgICAgJzxoMj5QYXNzd29yZCByZXF1aXJlZDwvaDI+JyArXG4gICAgICAgICAgICAnPGlucHV0IGlkPVwibG9ja0tleVwiIHR5cGU9XCJ0ZXh0XCIgcGxhY2Vob2xkZXI9XCJwYXNzd29yZFwiIGF1dG9mb2N1cz4nLFxuICAgICAgICB0cnVlLFxuICAgICAgICBcIk9rXCIsXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7fSxcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9ja0tleScpLmZvY3VzKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7XG4gICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgIHZhciBsb2NrS2V5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2tLZXknKTtcbiAgICAgICAgICAgICAgICBpZiAobG9ja0tleS52YWx1ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldFNoYXJlZEtleShsb2NrS2V5LnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sobG9ja0tleS52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgKTtcbn07XG5cblVJLm9uQXV0aGVudGljYXRpb25SZXF1aXJlZCA9IGZ1bmN0aW9uIChpbnRlcnZhbENhbGxiYWNrKSB7XG4gICAgQXV0aGVudGljYXRpb24ub3BlbkF1dGhlbnRpY2F0aW9uRGlhbG9nKFxuICAgICAgICByb29tTmFtZSwgaW50ZXJ2YWxDYWxsYmFjaywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgVG9vbGJhci5hdXRoZW50aWNhdGVDbGlja2VkKCk7XG4gICAgICAgIH0pO1xufTtcblxuVUkuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUgPSBmdW5jdGlvbiAoc3RhdGUpIHtcbiAgICBUb29sYmFyLnNldFJlY29yZGluZ0J1dHRvblN0YXRlKHN0YXRlKTtcbn07XG5cblVJLmlucHV0RGlzcGxheU5hbWVIYW5kbGVyID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgVmlkZW9MYXlvdXQuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIodmFsdWUpO1xufTtcblxuVUkub25NdWNFbnRlcmVkID0gZnVuY3Rpb24gKGppZCwgaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KGRpc3BsYXlOYW1lIHx8ICdTb21lYm9keScsXG4gICAgICAgICdjb25uZWN0ZWQnLFxuICAgICAgICAnY29ubmVjdGVkJyk7XG5cbiAgICAvLyBBZGQgUGVlcidzIGNvbnRhaW5lclxuICAgIFZpZGVvTGF5b3V0LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMoamlkLGlkKTtcbn07XG5cblVJLm9uTXVjUHJlc2VuY2VTdGF0dXMgPSBmdW5jdGlvbiAoIGppZCwgaW5mbykge1xuICAgIFZpZGVvTGF5b3V0LnNldFByZXNlbmNlU3RhdHVzKFxuICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpLCBpbmZvLnN0YXR1cyk7XG59O1xuXG5VSS5vbk11Y1JvbGVDaGFuZ2VkID0gZnVuY3Rpb24gKHJvbGUsIGRpc3BsYXlOYW1lKSB7XG4gICAgVmlkZW9MYXlvdXQuc2hvd01vZGVyYXRvckluZGljYXRvcigpO1xuXG4gICAgaWYgKHJvbGUgPT09ICdtb2RlcmF0b3InKSB7XG4gICAgICAgIHZhciBkaXNwbGF5TmFtZSA9IGRpc3BsYXlOYW1lO1xuICAgICAgICBpZiAoIWRpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICBkaXNwbGF5TmFtZSA9ICdTb21lYm9keSc7XG4gICAgICAgIH1cbiAgICAgICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KFxuICAgICAgICAgICAgZGlzcGxheU5hbWUsXG4gICAgICAgICAgICAnY29ubmVjdGVkJyxcbiAgICAgICAgICAgICAgICAnTW9kZXJhdG9yIHJpZ2h0cyBncmFudGVkIHRvICcgKyBkaXNwbGF5TmFtZSArICchJyk7XG4gICAgfVxufTtcblxuVUkudXBkYXRlTG9jYWxDb25uZWN0aW9uU3RhdHMgPSBmdW5jdGlvbihwZXJjZW50LCBzdGF0cylcbntcbiAgICBWaWRlb0xheW91dC51cGRhdGVMb2NhbENvbm5lY3Rpb25TdGF0cyhwZXJjZW50LCBzdGF0cyk7XG59O1xuXG5VSS51cGRhdGVDb25uZWN0aW9uU3RhdHMgPSBmdW5jdGlvbihqaWQsIHBlcmNlbnQsIHN0YXRzKVxue1xuICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUNvbm5lY3Rpb25TdGF0cyhqaWQsIHBlcmNlbnQsIHN0YXRzKTtcbn07XG5cblVJLm9uU3RhdHNTdG9wID0gZnVuY3Rpb24gKCkge1xuICAgIFZpZGVvTGF5b3V0Lm9uU3RhdHNTdG9wKCk7XG59O1xuXG5VSS5nZXRMYXJnZVZpZGVvU3RhdGUgPSBmdW5jdGlvbigpXG57XG4gICAgcmV0dXJuIFZpZGVvTGF5b3V0LmdldExhcmdlVmlkZW9TdGF0ZSgpO1xufTtcblxuVUkuc2hvd0xvY2FsQXVkaW9JbmRpY2F0b3IgPSBmdW5jdGlvbiAobXV0ZSkge1xuICAgIFZpZGVvTGF5b3V0LnNob3dMb2NhbEF1ZGlvSW5kaWNhdG9yKG11dGUpO1xufTtcblxuVUkuZ2VuZXJhdGVSb29tTmFtZSA9IGZ1bmN0aW9uKCkge1xuICAgIGlmKHJvb21OYW1lKVxuICAgICAgICByZXR1cm4gcm9vbU5hbWU7XG4gICAgdmFyIHJvb21ub2RlID0gbnVsbDtcbiAgICB2YXIgcGF0aCA9IHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZTtcblxuICAgIC8vIGRldGVybWluZGUgdGhlIHJvb20gbm9kZSBmcm9tIHRoZSB1cmxcbiAgICAvLyBUT0RPOiBqdXN0IHRoZSByb29tbm9kZSBvciB0aGUgd2hvbGUgYmFyZSBqaWQ/XG4gICAgaWYgKGNvbmZpZy5nZXRyb29tbm9kZSAmJiB0eXBlb2YgY29uZmlnLmdldHJvb21ub2RlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIGN1c3RvbSBmdW5jdGlvbiBtaWdodCBiZSByZXNwb25zaWJsZSBmb3IgZG9pbmcgdGhlIHB1c2hzdGF0ZVxuICAgICAgICByb29tbm9kZSA9IGNvbmZpZy5nZXRyb29tbm9kZShwYXRoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvKiBmYWxsIGJhY2sgdG8gZGVmYXVsdCBzdHJhdGVneVxuICAgICAgICAgKiB0aGlzIGlzIG1ha2luZyBhc3N1bXB0aW9ucyBhYm91dCBob3cgdGhlIFVSTC0+cm9vbSBtYXBwaW5nIGhhcHBlbnMuXG4gICAgICAgICAqIEl0IGN1cnJlbnRseSBhc3N1bWVzIGRlcGxveW1lbnQgYXQgcm9vdCwgd2l0aCBhIHJld3JpdGUgbGlrZSB0aGVcbiAgICAgICAgICogZm9sbG93aW5nIG9uZSAoZm9yIG5naW54KTpcbiAgICAgICAgIGxvY2F0aW9uIH4gXi8oW2EtekEtWjAtOV0rKSQge1xuICAgICAgICAgcmV3cml0ZSBeLyguKikkIC8gYnJlYWs7XG4gICAgICAgICB9XG4gICAgICAgICAqL1xuICAgICAgICBpZiAocGF0aC5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICByb29tbm9kZSA9IHBhdGguc3Vic3RyKDEpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgd29yZCA9IFJvb21OYW1lR2VuZXJhdG9yLmdlbmVyYXRlUm9vbVdpdGhvdXRTZXBhcmF0b3IoKTtcbiAgICAgICAgICAgIHJvb21ub2RlID0gd29yZC50b0xvd2VyQ2FzZSgpO1xuXG4gICAgICAgICAgICB3aW5kb3cuaGlzdG9yeS5wdXNoU3RhdGUoJ1ZpZGVvQ2hhdCcsXG4gICAgICAgICAgICAgICAgICAgICdSb29tOiAnICsgd29yZCwgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lICsgd29yZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByb29tTmFtZSA9IHJvb21ub2RlICsgJ0AnICsgY29uZmlnLmhvc3RzLm11YztcbiAgICByZXR1cm4gcm9vbU5hbWU7XG59O1xuXG5cblVJLmNvbm5lY3Rpb25JbmRpY2F0b3JTaG93TW9yZSA9IGZ1bmN0aW9uKGlkKVxue1xuICAgIHJldHVybiBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0uc2hvd01vcmUoKTtcbn07XG5cblVJLnNob3dUb29sYmFyID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBUb29sYmFyVG9nZ2xlci5zaG93VG9vbGJhcigpO1xufTtcblxuVUkuZG9ja1Rvb2xiYXIgPSBmdW5jdGlvbiAoaXNEb2NrKSB7XG4gICAgcmV0dXJuIFRvb2xiYXJUb2dnbGVyLmRvY2tUb29sYmFyKGlzRG9jayk7XG59O1xuXG5VSS5nZXRDcmVhZGVudGlhbHMgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgYm9zaDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Jvc2hVUkwnKS52YWx1ZSxcbiAgICAgICAgcGFzc3dvcmQ6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXNzd29yZCcpLnZhbHVlLFxuICAgICAgICBqaWQ6IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdqaWQnKS52YWx1ZVxuICAgIH07XG59O1xuXG5VSS5kaXNhYmxlQ29ubmVjdCA9IGZ1bmN0aW9uICgpIHtcbiAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY29ubmVjdCcpLmRpc2FibGVkID0gdHJ1ZTtcbn07XG5cblVJLnNob3dMb2dpblBvcHVwID0gZnVuY3Rpb24oY2FsbGJhY2spXG57XG4gICAgY29uc29sZS5sb2coJ3Bhc3N3b3JkIGlzIHJlcXVpcmVkJyk7XG5cbiAgICBVSS5tZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsXG4gICAgICAgICAgICAnPGgyPlBhc3N3b3JkIHJlcXVpcmVkPC9oMj4nICtcbiAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJwYXNzd29yZHJlcXVpcmVkLnVzZXJuYW1lXCIgdHlwZT1cInRleHRcIiBwbGFjZWhvbGRlcj1cInVzZXJAZG9tYWluLm5ldFwiIGF1dG9mb2N1cz4nICtcbiAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJwYXNzd29yZHJlcXVpcmVkLnBhc3N3b3JkXCIgdHlwZT1cInBhc3N3b3JkXCIgcGxhY2Vob2xkZXI9XCJ1c2VyIHBhc3N3b3JkXCI+JyxcbiAgICAgICAgdHJ1ZSxcbiAgICAgICAgXCJPa1wiLFxuICAgICAgICBmdW5jdGlvbiAoZSwgdiwgbSwgZikge1xuICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICB2YXIgdXNlcm5hbWUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGFzc3dvcmRyZXF1aXJlZC51c2VybmFtZScpO1xuICAgICAgICAgICAgICAgIHZhciBwYXNzd29yZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXNzd29yZHJlcXVpcmVkLnBhc3N3b3JkJyk7XG5cbiAgICAgICAgICAgICAgICBpZiAodXNlcm5hbWUudmFsdWUgIT09IG51bGwgJiYgcGFzc3dvcmQudmFsdWUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayh1c2VybmFtZS52YWx1ZSwgcGFzc3dvcmQudmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGFzc3dvcmRyZXF1aXJlZC51c2VybmFtZScpLmZvY3VzKCk7XG4gICAgICAgIH1cbiAgICApO1xufVxuXG5VSS5jaGVja0Zvck5pY2tuYW1lQW5kSm9pbiA9IGZ1bmN0aW9uICgpIHtcblxuICAgIEF1dGhlbnRpY2F0aW9uLmNsb3NlQXV0aGVudGljYXRpb25EaWFsb2coKTtcbiAgICBBdXRoZW50aWNhdGlvbi5zdG9wSW50ZXJ2YWwoKTtcblxuICAgIHZhciBuaWNrID0gbnVsbDtcbiAgICBpZiAoY29uZmlnLnVzZU5pY2tzKSB7XG4gICAgICAgIG5pY2sgPSB3aW5kb3cucHJvbXB0KCdZb3VyIG5pY2tuYW1lIChvcHRpb25hbCknKTtcbiAgICB9XG4gICAgeG1wcC5qb2luUm9vb20ocm9vbU5hbWUsIGNvbmZpZy51c2VOaWNrcywgbmljayk7XG59XG5cblxuZnVuY3Rpb24gZHVtcChlbGVtLCBmaWxlbmFtZSkge1xuICAgIGVsZW0gPSBlbGVtLnBhcmVudE5vZGU7XG4gICAgZWxlbS5kb3dubG9hZCA9IGZpbGVuYW1lIHx8ICdtZWV0bG9nLmpzb24nO1xuICAgIGVsZW0uaHJlZiA9ICdkYXRhOmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldD11dGYtOCxcXG4nO1xuICAgIHZhciBkYXRhID0geG1wcC5wb3B1bGF0ZURhdGEoKTtcbiAgICB2YXIgbWV0YWRhdGEgPSB7fTtcbiAgICBtZXRhZGF0YS50aW1lID0gbmV3IERhdGUoKTtcbiAgICBtZXRhZGF0YS51cmwgPSB3aW5kb3cubG9jYXRpb24uaHJlZjtcbiAgICBtZXRhZGF0YS51YSA9IG5hdmlnYXRvci51c2VyQWdlbnQ7XG4gICAgdmFyIGxvZyA9IHhtcHAuZ2V0TG9nZ2VyKCk7XG4gICAgaWYgKGxvZykge1xuICAgICAgICBtZXRhZGF0YS54bXBwID0gbG9nO1xuICAgIH1cbiAgICBkYXRhLm1ldGFkYXRhID0gbWV0YWRhdGE7XG4gICAgZWxlbS5ocmVmICs9IGVuY29kZVVSSUNvbXBvbmVudChKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAnICAnKSk7XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuXG5VSS5nZXRSb29tTmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gcm9vbU5hbWU7XG59XG5cbi8qKlxuICogTXV0ZXMvdW5tdXRlcyB0aGUgbG9jYWwgdmlkZW8uXG4gKlxuICogQHBhcmFtIG11dGUgPHR0PnRydWU8L3R0PiB0byBtdXRlIHRoZSBsb2NhbCB2aWRlbzsgb3RoZXJ3aXNlLCA8dHQ+ZmFsc2U8L3R0PlxuICogQHBhcmFtIG9wdGlvbnMgYW4gb2JqZWN0IHdoaWNoIHNwZWNpZmllcyBvcHRpb25hbCBhcmd1bWVudHMgc3VjaCBhcyB0aGVcbiAqIDx0dD5ib29sZWFuPC90dD4ga2V5IDx0dD5ieVVzZXI8L3R0PiB3aXRoIGRlZmF1bHQgdmFsdWUgPHR0PnRydWU8L3R0PiB3aGljaFxuICogc3BlY2lmaWVzIHdoZXRoZXIgdGhlIG1ldGhvZCB3YXMgaW5pdGlhdGVkIGluIHJlc3BvbnNlIHRvIGEgdXNlciBjb21tYW5kIChpblxuICogY29udHJhc3QgdG8gYW4gYXV0b21hdGljIGRlY2lzaW9uIHRha2VuIGJ5IHRoZSBhcHBsaWNhdGlvbiBsb2dpYylcbiAqL1xuZnVuY3Rpb24gc2V0VmlkZW9NdXRlKG11dGUsIG9wdGlvbnMpIHtcbiAgICB4bXBwLnNldFZpZGVvTXV0ZShcbiAgICAgICAgbXV0ZSxcbiAgICAgICAgZnVuY3Rpb24gKG11dGUpIHtcbiAgICAgICAgICAgIHZhciB2aWRlbyA9ICQoJyN2aWRlbycpO1xuICAgICAgICAgICAgdmFyIGNvbW11bmljYXRpdmVDbGFzcyA9IFwiaWNvbi1jYW1lcmFcIjtcbiAgICAgICAgICAgIHZhciBtdXRlQ2xhc3MgPSBcImljb24tY2FtZXJhIGljb24tY2FtZXJhLWRpc2FibGVkXCI7XG5cbiAgICAgICAgICAgIGlmIChtdXRlKSB7XG4gICAgICAgICAgICAgICAgdmlkZW8ucmVtb3ZlQ2xhc3MoY29tbXVuaWNhdGl2ZUNsYXNzKTtcbiAgICAgICAgICAgICAgICB2aWRlby5hZGRDbGFzcyhtdXRlQ2xhc3MpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB2aWRlby5yZW1vdmVDbGFzcyhtdXRlQ2xhc3MpO1xuICAgICAgICAgICAgICAgIHZpZGVvLmFkZENsYXNzKGNvbW11bmljYXRpdmVDbGFzcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIE11dGVzL3VubXV0ZXMgdGhlIGxvY2FsIHZpZGVvLlxuICovXG5VSS50b2dnbGVWaWRlbyA9IGZ1bmN0aW9uICgpIHtcbiAgICBVSVV0aWwuYnV0dG9uQ2xpY2soXCIjdmlkZW9cIiwgXCJpY29uLWNhbWVyYSBpY29uLWNhbWVyYS1kaXNhYmxlZFwiKTtcblxuICAgIHNldFZpZGVvTXV0ZSghUlRDLmxvY2FsVmlkZW8uaXNNdXRlZCgpKTtcbn07XG5cbi8qKlxuICogTXV0ZXMgLyB1bm11dGVzIGF1ZGlvIGZvciB0aGUgbG9jYWwgcGFydGljaXBhbnQuXG4gKi9cblVJLnRvZ2dsZUF1ZGlvID0gZnVuY3Rpb24oKSB7XG4gICAgVUkuc2V0QXVkaW9NdXRlZCghUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKTtcbn07XG5cbi8qKlxuICogU2V0cyBtdXRlZCBhdWRpbyBzdGF0ZSBmb3IgdGhlIGxvY2FsIHBhcnRpY2lwYW50LlxuICovXG5VSS5zZXRBdWRpb011dGVkID0gZnVuY3Rpb24gKG11dGUpIHtcblxuICAgIGlmKCF4bXBwLnNldEF1ZGlvTXV0ZShtdXRlLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIFVJLnNob3dMb2NhbEF1ZGlvSW5kaWNhdG9yKG11dGUpO1xuXG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNtdXRlXCIsIFwiaWNvbi1taWNyb3Bob25lIGljb24tbWljLWRpc2FibGVkXCIpO1xuICAgIH0pKVxuICAgIHtcbiAgICAgICAgLy8gV2Ugc3RpbGwgY2xpY2sgdGhlIGJ1dHRvbi5cbiAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI211dGVcIiwgXCJpY29uLW1pY3JvcGhvbmUgaWNvbi1taWMtZGlzYWJsZWRcIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbn1cblxuVUkub25MYXN0TkNoYW5nZWQgPSBmdW5jdGlvbiAob2xkVmFsdWUsIG5ld1ZhbHVlKSB7XG4gICAgaWYgKGNvbmZpZy5tdXRlTG9jYWxWaWRlb0lmTm90SW5MYXN0Tikge1xuICAgICAgICBzZXRWaWRlb011dGUoIW5ld1ZhbHVlLCB7ICdieVVzZXInOiBmYWxzZSB9KTtcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gVUk7XG5cbiIsInZhciBDYW52YXNVdGlsID0gcmVxdWlyZShcIi4vQ2FudmFzVXRpbHNcIik7XG5cbi8qKlxuICogVGhlIGF1ZGlvIExldmVscyBwbHVnaW4uXG4gKi9cbnZhciBBdWRpb0xldmVscyA9IChmdW5jdGlvbihteSkge1xuICAgIHZhciBhdWRpb0xldmVsQ2FudmFzQ2FjaGUgPSB7fTtcblxuICAgIG15LkxPQ0FMX0xFVkVMID0gJ2xvY2FsJztcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGF1ZGlvIGxldmVsIGNhbnZhcyBmb3IgdGhlIGdpdmVuIHBlZXJKaWQuIElmIHRoZSBjYW52YXNcbiAgICAgKiBkaWRuJ3QgZXhpc3Qgd2UgY3JlYXRlIGl0LlxuICAgICAqL1xuICAgIG15LnVwZGF0ZUF1ZGlvTGV2ZWxDYW52YXMgPSBmdW5jdGlvbiAocGVlckppZCwgVmlkZW9MYXlvdXQpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gbnVsbDtcbiAgICAgICAgaWYgKCFwZWVySmlkKVxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHZpZGVvU3BhbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHZpZGVvU3BhbklkKTtcblxuICAgICAgICBpZiAoIXZpZGVvU3Bhbikge1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkKVxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyBlbGVtZW50IGZvciBqaWRcIiwgcmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyBlbGVtZW50IGZvciBsb2NhbCB2aWRlby5cIik7XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBhdWRpb0xldmVsQ2FudmFzID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+Y2FudmFzJyk7XG5cbiAgICAgICAgdmFyIHZpZGVvU3BhY2VXaWR0aCA9ICQoJyNyZW1vdGVWaWRlb3MnKS53aWR0aCgpO1xuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9TcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHRodW1ibmFpbFdpZHRoID0gdGh1bWJuYWlsU2l6ZVswXTtcbiAgICAgICAgdmFyIHRodW1ibmFpbEhlaWdodCA9IHRodW1ibmFpbFNpemVbMV07XG5cbiAgICAgICAgaWYgKCFhdWRpb0xldmVsQ2FudmFzIHx8IGF1ZGlvTGV2ZWxDYW52YXMubGVuZ3RoID09PSAwKSB7XG5cbiAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMuY2xhc3NOYW1lID0gXCJhdWRpb2xldmVsXCI7XG4gICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzLnN0eWxlLmJvdHRvbSA9IFwiLVwiICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yICsgXCJweFwiO1xuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcy5zdHlsZS5sZWZ0ID0gXCItXCIgKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIgKyBcInB4XCI7XG4gICAgICAgICAgICByZXNpemVBdWRpb0xldmVsQ2FudmFzKCBhdWRpb0xldmVsQ2FudmFzLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KTtcblxuICAgICAgICAgICAgdmlkZW9TcGFuLmFwcGVuZENoaWxkKGF1ZGlvTGV2ZWxDYW52YXMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcyA9IGF1ZGlvTGV2ZWxDYW52YXMuZ2V0KDApO1xuXG4gICAgICAgICAgICByZXNpemVBdWRpb0xldmVsQ2FudmFzKCBhdWRpb0xldmVsQ2FudmFzLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBhdWRpbyBsZXZlbCBVSSBmb3IgdGhlIGdpdmVuIHJlc291cmNlSmlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSByZXNvdXJjZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCBmb3JcbiAgICAgKiB3aGljaCB3ZSBkcmF3IHRoZSBhdWRpbyBsZXZlbFxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBuZXdBdWRpbyBsZXZlbCB0byByZW5kZXJcbiAgICAgKi9cbiAgICBteS51cGRhdGVBdWRpb0xldmVsID0gZnVuY3Rpb24gKHJlc291cmNlSmlkLCBhdWRpb0xldmVsLCBsYXJnZVZpZGVvUmVzb3VyY2VKaWQpIHtcbiAgICAgICAgZHJhd0F1ZGlvTGV2ZWxDYW52YXMocmVzb3VyY2VKaWQsIGF1ZGlvTGV2ZWwpO1xuXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IGdldFZpZGVvU3BhbklkKHJlc291cmNlSmlkKTtcblxuICAgICAgICB2YXIgYXVkaW9MZXZlbENhbnZhcyA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPmNhbnZhcycpLmdldCgwKTtcblxuICAgICAgICBpZiAoIWF1ZGlvTGV2ZWxDYW52YXMpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdmFyIGRyYXdDb250ZXh0ID0gYXVkaW9MZXZlbENhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuXG4gICAgICAgIHZhciBjYW52YXNDYWNoZSA9IGF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF07XG5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xlYXJSZWN0ICgwLCAwLFxuICAgICAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMud2lkdGgsIGF1ZGlvTGV2ZWxDYW52YXMuaGVpZ2h0KTtcbiAgICAgICAgZHJhd0NvbnRleHQuZHJhd0ltYWdlKGNhbnZhc0NhY2hlLCAwLCAwKTtcblxuICAgICAgICBpZihyZXNvdXJjZUppZCA9PT0gQXVkaW9MZXZlbHMuTE9DQUxfTEVWRUwpIHtcbiAgICAgICAgICAgIGlmKCF4bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IHhtcHAubXlSZXNvdXJjZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYocmVzb3VyY2VKaWQgID09PSBsYXJnZVZpZGVvUmVzb3VyY2VKaWQpIHtcbiAgICAgICAgICAgIEF1ZGlvTGV2ZWxzLnVwZGF0ZUFjdGl2ZVNwZWFrZXJBdWRpb0xldmVsKGF1ZGlvTGV2ZWwpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIG15LnVwZGF0ZUFjdGl2ZVNwZWFrZXJBdWRpb0xldmVsID0gZnVuY3Rpb24oYXVkaW9MZXZlbCkge1xuICAgICAgICB2YXIgZHJhd0NvbnRleHQgPSAkKCcjYWN0aXZlU3BlYWtlckF1ZGlvTGV2ZWwnKVswXS5nZXRDb250ZXh0KCcyZCcpO1xuICAgICAgICB2YXIgciA9IGludGVyZmFjZUNvbmZpZy5BQ1RJVkVfU1BFQUtFUl9BVkFUQVJfU0laRSAvIDI7XG4gICAgICAgIHZhciBjZW50ZXIgPSAoaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFICsgcikgLyAyO1xuXG4gICAgICAgIC8vIFNhdmUgdGhlIHByZXZpb3VzIHN0YXRlIG9mIHRoZSBjb250ZXh0LlxuICAgICAgICBkcmF3Q29udGV4dC5zYXZlKCk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xlYXJSZWN0KDAsIDAsIDMwMCwgMzAwKTtcblxuICAgICAgICAvLyBEcmF3IGEgY2lyY2xlLlxuICAgICAgICBkcmF3Q29udGV4dC5hcmMoY2VudGVyLCBjZW50ZXIsIHIsIDAsIDIgKiBNYXRoLlBJKTtcblxuICAgICAgICAvLyBBZGQgYSBzaGFkb3cgYXJvdW5kIHRoZSBjaXJjbGVcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93Q29sb3IgPSBpbnRlcmZhY2VDb25maWcuU0hBRE9XX0NPTE9SO1xuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dCbHVyID0gZ2V0U2hhZG93TGV2ZWwoYXVkaW9MZXZlbCk7XG4gICAgICAgIGRyYXdDb250ZXh0LnNoYWRvd09mZnNldFggPSAwO1xuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dPZmZzZXRZID0gMDtcblxuICAgICAgICAvLyBGaWxsIHRoZSBzaGFwZS5cbiAgICAgICAgZHJhd0NvbnRleHQuZmlsbCgpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LnNhdmUoKTtcblxuICAgICAgICBkcmF3Q29udGV4dC5yZXN0b3JlKCk7XG5cblxuICAgICAgICBkcmF3Q29udGV4dC5hcmMoY2VudGVyLCBjZW50ZXIsIHIsIDAsIDIgKiBNYXRoLlBJKTtcblxuICAgICAgICBkcmF3Q29udGV4dC5jbGlwKCk7XG4gICAgICAgIGRyYXdDb250ZXh0LmNsZWFyUmVjdCgwLCAwLCAyNzcsIDIwMCk7XG5cbiAgICAgICAgLy8gUmVzdG9yZSB0aGUgcHJldmlvdXMgY29udGV4dCBzdGF0ZS5cbiAgICAgICAgZHJhd0NvbnRleHQucmVzdG9yZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRoZSBnaXZlbiBhdWRpbyBsZXZlbCBjYW52YXMgdG8gbWF0Y2ggdGhlIGdpdmVuIHRodW1ibmFpbCBzaXplLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHJlc2l6ZUF1ZGlvTGV2ZWxDYW52YXMoYXVkaW9MZXZlbENhbnZhcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRodW1ibmFpbFdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KSB7XG4gICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMud2lkdGggPSB0aHVtYm5haWxXaWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMuaGVpZ2h0ID0gdGh1bWJuYWlsSGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgYXVkaW8gbGV2ZWwgY2FudmFzIGludG8gdGhlIGNhY2hlZCBjYW52YXMgb2JqZWN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSByZXNvdXJjZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCBmb3JcbiAgICAgKiB3aGljaCB3ZSBkcmF3IHRoZSBhdWRpbyBsZXZlbFxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBuZXdBdWRpbyBsZXZlbCB0byByZW5kZXJcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBkcmF3QXVkaW9MZXZlbENhbnZhcyhyZXNvdXJjZUppZCwgYXVkaW9MZXZlbCkge1xuICAgICAgICBpZiAoIWF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF0pIHtcblxuICAgICAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gZ2V0VmlkZW9TcGFuSWQocmVzb3VyY2VKaWQpO1xuXG4gICAgICAgICAgICB2YXIgYXVkaW9MZXZlbENhbnZhc09yaWcgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5jYW52YXMnKS5nZXQoMCk7XG5cbiAgICAgICAgICAgIC8qXG4gICAgICAgICAgICAgKiBGSVhNRSBUZXN0aW5nIGhhcyBzaG93biB0aGF0IGF1ZGlvTGV2ZWxDYW52YXNPcmlnIG1heSBub3QgZXhpc3QuXG4gICAgICAgICAgICAgKiBJbiBzdWNoIGEgY2FzZSwgdGhlIG1ldGhvZCBDYW52YXNVdGlsLmNsb25lQ2FudmFzIG1heSB0aHJvdyBhblxuICAgICAgICAgICAgICogZXJyb3IuIFNpbmNlIGF1ZGlvIGxldmVscyBhcmUgZnJlcXVlbnRseSB1cGRhdGVkLCB0aGUgZXJyb3JzIGhhdmVcbiAgICAgICAgICAgICAqIGJlZW4gb2JzZXJ2ZWQgdG8gcGlsZSBpbnRvIHRoZSBjb25zb2xlLCBzdHJhaW4gdGhlIENQVS5cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgaWYgKGF1ZGlvTGV2ZWxDYW52YXNPcmlnKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF1cbiAgICAgICAgICAgICAgICAgICAgPSBDYW52YXNVdGlsLmNsb25lQ2FudmFzKGF1ZGlvTGV2ZWxDYW52YXNPcmlnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBjYW52YXMgPSBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdO1xuXG4gICAgICAgIGlmICghY2FudmFzKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBkcmF3Q29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LmNsZWFyUmVjdCgwLCAwLCBjYW52YXMud2lkdGgsIGNhbnZhcy5oZWlnaHQpO1xuXG4gICAgICAgIHZhciBzaGFkb3dMZXZlbCA9IGdldFNoYWRvd0xldmVsKGF1ZGlvTGV2ZWwpO1xuXG4gICAgICAgIGlmIChzaGFkb3dMZXZlbCA+IDApXG4gICAgICAgICAgICAvLyBkcmF3Q29udGV4dCwgeCwgeSwgdywgaCwgciwgc2hhZG93Q29sb3IsIHNoYWRvd0xldmVsXG4gICAgICAgICAgICBDYW52YXNVdGlsLmRyYXdSb3VuZFJlY3RHbG93KCAgIGRyYXdDb250ZXh0LFxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiwgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yLFxuICAgICAgICAgICAgICAgIGNhbnZhcy53aWR0aCAtIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEsXG4gICAgICAgICAgICAgICAgY2FudmFzLmhlaWdodCAtIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEsXG4gICAgICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19SQURJVVMsXG4gICAgICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLlNIQURPV19DT0xPUixcbiAgICAgICAgICAgICAgICBzaGFkb3dMZXZlbCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2hhZG93L2dsb3cgbGV2ZWwgZm9yIHRoZSBnaXZlbiBhdWRpbyBsZXZlbC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBhdWRpbyBsZXZlbCBmcm9tIHdoaWNoIHdlIGRldGVybWluZSB0aGUgc2hhZG93XG4gICAgICogbGV2ZWxcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBnZXRTaGFkb3dMZXZlbCAoYXVkaW9MZXZlbCkge1xuICAgICAgICB2YXIgc2hhZG93TGV2ZWwgPSAwO1xuXG4gICAgICAgIGlmIChhdWRpb0xldmVsIDw9IDAuMykge1xuICAgICAgICAgICAgc2hhZG93TGV2ZWwgPSBNYXRoLnJvdW5kKGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiooYXVkaW9MZXZlbC8wLjMpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChhdWRpb0xldmVsIDw9IDAuNikge1xuICAgICAgICAgICAgc2hhZG93TGV2ZWwgPSBNYXRoLnJvdW5kKGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiooKGF1ZGlvTGV2ZWwgLSAwLjMpIC8gMC4zKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBzaGFkb3dMZXZlbCA9IE1hdGgucm91bmQoaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yKigoYXVkaW9MZXZlbCAtIDAuNikgLyAwLjQpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gc2hhZG93TGV2ZWw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgdmlkZW8gc3BhbiBpZCBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiByZXNvdXJjZUppZCBvciBsb2NhbFxuICAgICAqIHVzZXIuXG4gICAgICovXG4gICAgZnVuY3Rpb24gZ2V0VmlkZW9TcGFuSWQocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gbnVsbDtcbiAgICAgICAgaWYgKHJlc291cmNlSmlkID09PSBBdWRpb0xldmVscy5MT0NBTF9MRVZFTFxuICAgICAgICAgICAgICAgIHx8ICh4bXBwLm15UmVzb3VyY2UoKSAmJiByZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgICAgICA9PT0geG1wcC5teVJlc291cmNlKCkpKVxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZDtcblxuICAgICAgICByZXR1cm4gdmlkZW9TcGFuSWQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSW5kaWNhdGVzIHRoYXQgdGhlIHJlbW90ZSB2aWRlbyBoYXMgYmVlbiByZXNpemVkLlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ3JlbW90ZXZpZGVvLnJlc2l6ZWQnLCBmdW5jdGlvbiAoZXZlbnQsIHdpZHRoLCBoZWlnaHQpIHtcbiAgICAgICAgdmFyIHJlc2l6ZWQgPSBmYWxzZTtcbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcz5zcGFuPmNhbnZhcycpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICB2YXIgY2FudmFzID0gJCh0aGlzKS5nZXQoMCk7XG4gICAgICAgICAgICBpZiAoY2FudmFzLndpZHRoICE9PSB3aWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEpIHtcbiAgICAgICAgICAgICAgICBjYW52YXMud2lkdGggPSB3aWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgICAgICAgICAgcmVzaXplZCA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChjYW52YXMuaGVpZ2ggIT09IGhlaWdodCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEpIHtcbiAgICAgICAgICAgICAgICBjYW52YXMuaGVpZ2h0ID0gaGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgICAgICByZXNpemVkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHJlc2l6ZWQpXG4gICAgICAgICAgICBPYmplY3Qua2V5cyhhdWRpb0xldmVsQ2FudmFzQ2FjaGUpLmZvckVhY2goZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhc0NhY2hlW3Jlc291cmNlSmlkXS53aWR0aFxuICAgICAgICAgICAgICAgICAgICA9IHdpZHRoICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdLmhlaWdodFxuICAgICAgICAgICAgICAgICAgICA9IGhlaWdodCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgICAgICB9KTtcbiAgICB9KTtcblxuICAgIHJldHVybiBteTtcblxufSkoQXVkaW9MZXZlbHMgfHwge30pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEF1ZGlvTGV2ZWxzOyIsIi8qKlxuICogVXRpbGl0eSBjbGFzcyBmb3IgZHJhd2luZyBjYW52YXMgc2hhcGVzLlxuICovXG52YXIgQ2FudmFzVXRpbCA9IChmdW5jdGlvbihteSkge1xuXG4gICAgLyoqXG4gICAgICogRHJhd3MgYSByb3VuZCByZWN0YW5nbGUgd2l0aCBhIGdsb3cuIFRoZSBnbG93V2lkdGggaW5kaWNhdGVzIHRoZSBkZXB0aFxuICAgICAqIG9mIHRoZSBnbG93LlxuICAgICAqXG4gICAgICogQHBhcmFtIGRyYXdDb250ZXh0IHRoZSBjb250ZXh0IG9mIHRoZSBjYW52YXMgdG8gZHJhdyB0b1xuICAgICAqIEBwYXJhbSB4IHRoZSB4IGNvb3JkaW5hdGUgb2YgdGhlIHJvdW5kIHJlY3RhbmdsZVxuICAgICAqIEBwYXJhbSB5IHRoZSB5IGNvb3JkaW5hdGUgb2YgdGhlIHJvdW5kIHJlY3RhbmdsZVxuICAgICAqIEBwYXJhbSB3IHRoZSB3aWR0aCBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIGggdGhlIGhlaWdodCBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIGdsb3dDb2xvciB0aGUgY29sb3Igb2YgdGhlIGdsb3dcbiAgICAgKiBAcGFyYW0gZ2xvd1dpZHRoIHRoZSB3aWR0aCBvZiB0aGUgZ2xvd1xuICAgICAqL1xuICAgIG15LmRyYXdSb3VuZFJlY3RHbG93XG4gICAgICAgID0gZnVuY3Rpb24oZHJhd0NvbnRleHQsIHgsIHksIHcsIGgsIHIsIGdsb3dDb2xvciwgZ2xvd1dpZHRoKSB7XG5cbiAgICAgICAgLy8gU2F2ZSB0aGUgcHJldmlvdXMgc3RhdGUgb2YgdGhlIGNvbnRleHQuXG4gICAgICAgIGRyYXdDb250ZXh0LnNhdmUoKTtcblxuICAgICAgICBpZiAodyA8IDIgKiByKSByID0gdyAvIDI7XG4gICAgICAgIGlmIChoIDwgMiAqIHIpIHIgPSBoIC8gMjtcblxuICAgICAgICAvLyBEcmF3IGEgcm91bmQgcmVjdGFuZ2xlLlxuICAgICAgICBkcmF3Q29udGV4dC5iZWdpblBhdGgoKTtcbiAgICAgICAgZHJhd0NvbnRleHQubW92ZVRvKHgrciwgeSk7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgrdywgeSwgICB4K3csIHkraCwgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgrdywgeStoLCB4LCAgIHkraCwgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgsICAgeStoLCB4LCAgIHksICAgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgsICAgeSwgICB4K3csIHksICAgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmNsb3NlUGF0aCgpO1xuXG4gICAgICAgIC8vIEFkZCBhIHNoYWRvdyBhcm91bmQgdGhlIHJlY3RhbmdsZVxuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dDb2xvciA9IGdsb3dDb2xvcjtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93Qmx1ciA9IGdsb3dXaWR0aDtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93T2Zmc2V0WCA9IDA7XG4gICAgICAgIGRyYXdDb250ZXh0LnNoYWRvd09mZnNldFkgPSAwO1xuXG4gICAgICAgIC8vIEZpbGwgdGhlIHNoYXBlLlxuICAgICAgICBkcmF3Q29udGV4dC5maWxsKCk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQuc2F2ZSgpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LnJlc3RvcmUoKTtcblxuLy8gICAgICAxKSBVbmNvbW1lbnQgdGhpcyBsaW5lIHRvIHVzZSBDb21wb3NpdGUgT3BlcmF0aW9uLCB3aGljaCBpcyBkb2luZyB0aGVcbi8vICAgICAgc2FtZSBhcyB0aGUgY2xpcCBmdW5jdGlvbiBiZWxvdyBhbmQgaXMgYWxzbyBhbnRpYWxpYXNpbmcgdGhlIHJvdW5kXG4vLyAgICAgIGJvcmRlciwgYnV0IGlzIHNhaWQgdG8gYmUgbGVzcyBmYXN0IHBlcmZvcm1hbmNlIHdpc2UuXG5cbi8vICAgICAgZHJhd0NvbnRleHQuZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSdkZXN0aW5hdGlvbi1vdXQnO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LmJlZ2luUGF0aCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5tb3ZlVG8oeCtyLCB5KTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5LCAgIHgrdywgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5K2gsIHgsICAgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5K2gsIHgsICAgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5LCAgIHgrdywgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuY2xvc2VQYXRoKCk7XG5cbi8vICAgICAgMikgVW5jb21tZW50IHRoaXMgbGluZSB0byB1c2UgQ29tcG9zaXRlIE9wZXJhdGlvbiwgd2hpY2ggaXMgZG9pbmcgdGhlXG4vLyAgICAgIHNhbWUgYXMgdGhlIGNsaXAgZnVuY3Rpb24gYmVsb3cgYW5kIGlzIGFsc28gYW50aWFsaWFzaW5nIHRoZSByb3VuZFxuLy8gICAgICBib3JkZXIsIGJ1dCBpcyBzYWlkIHRvIGJlIGxlc3MgZmFzdCBwZXJmb3JtYW5jZSB3aXNlLlxuXG4vLyAgICAgIGRyYXdDb250ZXh0LmZpbGwoKTtcblxuICAgICAgICAvLyBDb21tZW50IHRoZXNlIHR3byBsaW5lcyBpZiBjaG9vc2luZyB0byBkbyB0aGUgc2FtZSB3aXRoIGNvbXBvc2l0ZVxuICAgICAgICAvLyBvcGVyYXRpb24gYWJvdmUgMSBhbmQgMi5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xpcCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5jbGVhclJlY3QoMCwgMCwgMjc3LCAyMDApO1xuXG4gICAgICAgIC8vIFJlc3RvcmUgdGhlIHByZXZpb3VzIGNvbnRleHQgc3RhdGUuXG4gICAgICAgIGRyYXdDb250ZXh0LnJlc3RvcmUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xvbmVzIHRoZSBnaXZlbiBjYW52YXMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHRoZSBuZXcgY2xvbmVkIGNhbnZhcy5cbiAgICAgKi9cbiAgICBteS5jbG9uZUNhbnZhcyA9IGZ1bmN0aW9uIChvbGRDYW52YXMpIHtcbiAgICAgICAgLypcbiAgICAgICAgICogRklYTUUgVGVzdGluZyBoYXMgc2hvd24gdGhhdCBvbGRDYW52YXMgbWF5IG5vdCBleGlzdC4gSW4gc3VjaCBhIGNhc2UsXG4gICAgICAgICAqIHRoZSBtZXRob2QgQ2FudmFzVXRpbC5jbG9uZUNhbnZhcyBtYXkgdGhyb3cgYW4gZXJyb3IuIFNpbmNlIGF1ZGlvXG4gICAgICAgICAqIGxldmVscyBhcmUgZnJlcXVlbnRseSB1cGRhdGVkLCB0aGUgZXJyb3JzIGhhdmUgYmVlbiBvYnNlcnZlZCB0byBwaWxlXG4gICAgICAgICAqIGludG8gdGhlIGNvbnNvbGUsIHN0cmFpbiB0aGUgQ1BVLlxuICAgICAgICAgKi9cbiAgICAgICAgaWYgKCFvbGRDYW52YXMpXG4gICAgICAgICAgICByZXR1cm4gb2xkQ2FudmFzO1xuXG4gICAgICAgIC8vY3JlYXRlIGEgbmV3IGNhbnZhc1xuICAgICAgICB2YXIgbmV3Q2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICAgIHZhciBjb250ZXh0ID0gbmV3Q2FudmFzLmdldENvbnRleHQoJzJkJyk7XG5cbiAgICAgICAgLy9zZXQgZGltZW5zaW9uc1xuICAgICAgICBuZXdDYW52YXMud2lkdGggPSBvbGRDYW52YXMud2lkdGg7XG4gICAgICAgIG5ld0NhbnZhcy5oZWlnaHQgPSBvbGRDYW52YXMuaGVpZ2h0O1xuXG4gICAgICAgIC8vYXBwbHkgdGhlIG9sZCBjYW52YXMgdG8gdGhlIG5ldyBvbmVcbiAgICAgICAgY29udGV4dC5kcmF3SW1hZ2Uob2xkQ2FudmFzLCAwLCAwKTtcblxuICAgICAgICAvL3JldHVybiB0aGUgbmV3IGNhbnZhc1xuICAgICAgICByZXR1cm4gbmV3Q2FudmFzO1xuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG59KShDYW52YXNVdGlsIHx8IHt9KTtcblxubW9kdWxlLmV4cG9ydHMgPSBDYW52YXNVdGlsOyIsIi8qIEluaXRpYWwgXCJhdXRoZW50aWNhdGlvbiByZXF1aXJlZFwiIGRpYWxvZyAqL1xudmFyIGF1dGhEaWFsb2cgPSBudWxsO1xuLyogTG9vcCByZXRyeSBJRCB0aGF0IHdpdHMgZm9yIG90aGVyIHVzZXIgdG8gY3JlYXRlIHRoZSByb29tICovXG52YXIgYXV0aFJldHJ5SWQgPSBudWxsO1xudmFyIGF1dGhlbnRpY2F0aW9uV2luZG93ID0gbnVsbDtcblxudmFyIEF1dGhlbnRpY2F0aW9uID0ge1xuICAgIG9wZW5BdXRoZW50aWNhdGlvbkRpYWxvZzogZnVuY3Rpb24gKHJvb21OYW1lLCBpbnRlcnZhbENhbGxiYWNrLCBjYWxsYmFjaykge1xuICAgICAgICAvLyBUaGlzIGlzIHRoZSBsb29wIHRoYXQgd2lsbCB3YWl0IGZvciB0aGUgcm9vbSB0byBiZSBjcmVhdGVkIGJ5XG4gICAgICAgIC8vIHNvbWVvbmUgZWxzZS4gJ2F1dGhfcmVxdWlyZWQubW9kZXJhdG9yJyB3aWxsIGJyaW5nIHVzIGJhY2sgaGVyZS5cbiAgICAgICAgYXV0aFJldHJ5SWQgPSB3aW5kb3cuc2V0VGltZW91dChpbnRlcnZhbENhbGxiYWNrICwgNTAwMCk7XG4gICAgICAgIC8vIFNob3cgcHJvbXB0IG9ubHkgaWYgaXQncyBub3Qgb3BlblxuICAgICAgICBpZiAoYXV0aERpYWxvZyAhPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIGV4dHJhY3Qgcm9vbSBuYW1lIGZyb20gJ3Jvb21AbXVjLnNlcnZlci5uZXQnXG4gICAgICAgIHZhciByb29tID0gcm9vbU5hbWUuc3Vic3RyKDAsIHJvb21OYW1lLmluZGV4T2YoJ0AnKSk7XG5cbiAgICAgICAgYXV0aERpYWxvZyA9IG1lc3NhZ2VIYW5kbGVyLm9wZW5EaWFsb2coXG4gICAgICAgICAgICAnU3RvcCcsXG4gICAgICAgICAgICAgICAgJ0F1dGhlbnRpY2F0aW9uIGlzIHJlcXVpcmVkIHRvIGNyZWF0ZSByb29tOjxici8+PGI+JyArIHJvb20gK1xuICAgICAgICAgICAgICAgICc8L2I+PC9icj4gWW91IGNhbiBlaXRoZXIgYXV0aGVudGljYXRlIHRvIGNyZWF0ZSB0aGUgcm9vbSBvciAnICtcbiAgICAgICAgICAgICAgICAnanVzdCB3YWl0IGZvciBzb21lb25lIGVsc2UgdG8gZG8gc28uJyxcbiAgICAgICAgICAgIHRydWUsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgQXV0aGVudGljYXRlOiAnYXV0aE5vdydcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAob25TdWJtaXRFdmVudCwgc3VibWl0VmFsdWUpIHtcblxuICAgICAgICAgICAgICAgIC8vIERvIG5vdCBjbG9zZSB0aGUgZGlhbG9nIHlldFxuICAgICAgICAgICAgICAgIG9uU3VibWl0RXZlbnQucHJldmVudERlZmF1bHQoKTtcblxuICAgICAgICAgICAgICAgIC8vIE9wZW4gbG9naW4gcG9wdXBcbiAgICAgICAgICAgICAgICBpZiAoc3VibWl0VmFsdWUgPT09ICdhdXRoTm93Jykge1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuICAgIGNsb3NlQXV0aGVudGljYXRpb25XaW5kb3c6ZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoYXV0aGVudGljYXRpb25XaW5kb3cpIHtcbiAgICAgICAgICAgIGF1dGhlbnRpY2F0aW9uV2luZG93LmNsb3NlKCk7XG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGZvY3VzQXV0aGVudGljYXRpb25XaW5kb3c6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gSWYgYXV0aCB3aW5kb3cgZXhpc3RzIGp1c3QgYnJpbmcgaXQgdG8gdGhlIGZyb250XG4gICAgICAgIGlmIChhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgYXV0aGVudGljYXRpb25XaW5kb3cuZm9jdXMoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgY2xvc2VBdXRoZW50aWNhdGlvbkRpYWxvZzogZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBDbG9zZSBhdXRoZW50aWNhdGlvbiBkaWFsb2cgaWYgb3BlbmVkXG4gICAgICAgIGlmIChhdXRoRGlhbG9nKSB7XG4gICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5jbG9zZURpYWxvZygpO1xuICAgICAgICAgICAgYXV0aERpYWxvZyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGNyZWF0ZUF1dGhlbnRpY2F0aW9uV2luZG93OiBmdW5jdGlvbiAoY2FsbGJhY2ssIHVybCkge1xuICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG1lc3NhZ2VIYW5kbGVyLm9wZW5DZW50ZXJlZFBvcHVwKFxuICAgICAgICAgICAgdXJsLCA5MTAsIDY2MCxcbiAgICAgICAgICAgIC8vIE9uIGNsb3NlZFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIC8vIENsb3NlIGF1dGhlbnRpY2F0aW9uIGRpYWxvZyBpZiBvcGVuZWRcbiAgICAgICAgICAgICAgICBpZiAoYXV0aERpYWxvZykge1xuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5jbG9zZURpYWxvZygpO1xuICAgICAgICAgICAgICAgICAgICBhdXRoRGlhbG9nID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGF1dGhlbnRpY2F0aW9uV2luZG93O1xuICAgIH0sXG4gICAgc3RvcEludGVydmFsOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIENsZWFyIHJldHJ5IGludGVydmFsLCBzbyB0aGF0IHdlIGRvbid0IGNhbGwgJ2RvSm9pbkFmdGVyRm9jdXMnIHR3aWNlXG4gICAgICAgIGlmIChhdXRoUmV0cnlJZCkge1xuICAgICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dChhdXRoUmV0cnlJZCk7XG4gICAgICAgICAgICBhdXRoUmV0cnlJZCA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEF1dGhlbnRpY2F0aW9uOyIsInZhciBTZXR0aW5ncyA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvc2V0dGluZ3MvU2V0dGluZ3NcIik7XG5cbnZhciB1c2VycyA9IHt9O1xudmFyIGFjdGl2ZVNwZWFrZXJKaWQ7XG5cbmZ1bmN0aW9uIHNldFZpc2liaWxpdHkoc2VsZWN0b3IsIHNob3cpIHtcbiAgICBpZiAoc2VsZWN0b3IgJiYgc2VsZWN0b3IubGVuZ3RoID4gMCkge1xuICAgICAgICBzZWxlY3Rvci5jc3MoXCJ2aXNpYmlsaXR5XCIsIHNob3cgPyBcInZpc2libGVcIiA6IFwiaGlkZGVuXCIpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gaXNVc2VyTXV0ZWQoamlkKSB7XG4gICAgLy8gWFhYKGdwKSB3ZSBtYXkgd2FudCB0byByZW5hbWUgdGhpcyBtZXRob2QgdG8gc29tZXRoaW5nIGxpa2VcbiAgICAvLyBpc1VzZXJTdHJlYW1pbmcsIGZvciBleGFtcGxlLlxuICAgIGlmIChqaWQgJiYgamlkICE9IHhtcHAubXlKaWQoKSkge1xuICAgICAgICB2YXIgcmVzb3VyY2UgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgICAgICBpZiAoIXJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKS5pc0luTGFzdE4ocmVzb3VyY2UpKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmICghUlRDLnJlbW90ZVN0cmVhbXNbamlkXSB8fCAhUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV0pIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiBSVEMucmVtb3RlU3RyZWFtc1tqaWRdW01lZGlhU3RyZWFtVHlwZS5WSURFT19UWVBFXS5tdXRlZDtcbn1cblxuZnVuY3Rpb24gZ2V0R3JhdmF0YXJVcmwoaWQsIHNpemUpIHtcbiAgICBpZihpZCA9PT0geG1wcC5teUppZCgpIHx8ICFpZCkge1xuICAgICAgICBpZCA9IFNldHRpbmdzLmdldFNldHRpbmdzKCkudWlkO1xuICAgIH1cbiAgICByZXR1cm4gJ2h0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvJyArXG4gICAgICAgIE1ENS5oZXhkaWdlc3QoaWQudHJpbSgpLnRvTG93ZXJDYXNlKCkpICtcbiAgICAgICAgXCI/ZD13YXZhdGFyJnNpemU9XCIgKyAoc2l6ZSB8fCBcIjMwXCIpO1xufVxuXG52YXIgQXZhdGFyID0ge1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgdXNlcidzIGF2YXRhciBpbiB0aGUgc2V0dGluZ3MgbWVudShpZiBsb2NhbCB1c2VyKSwgY29udGFjdCBsaXN0XG4gICAgICogYW5kIHRodW1ibmFpbFxuICAgICAqIEBwYXJhbSBqaWQgamlkIG9mIHRoZSB1c2VyXG4gICAgICogQHBhcmFtIGlkIGVtYWlsIG9yIHVzZXJJRCB0byBiZSB1c2VkIGFzIGEgaGFzaFxuICAgICAqL1xuICAgIHNldFVzZXJBdmF0YXI6IGZ1bmN0aW9uIChqaWQsIGlkKSB7XG4gICAgICAgIGlmIChpZCkge1xuICAgICAgICAgICAgaWYgKHVzZXJzW2ppZF0gPT09IGlkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdXNlcnNbamlkXSA9IGlkO1xuICAgICAgICB9XG4gICAgICAgIHZhciB0aHVtYlVybCA9IGdldEdyYXZhdGFyVXJsKHVzZXJzW2ppZF0gfHwgamlkLCAxMDApO1xuICAgICAgICB2YXIgY29udGFjdExpc3RVcmwgPSBnZXRHcmF2YXRhclVybCh1c2Vyc1tqaWRdIHx8IGppZCk7XG4gICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIHZhciB0aHVtYm5haWwgPSAkKCcjcGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcbiAgICAgICAgdmFyIGF2YXRhciA9ICQoJyNhdmF0YXJfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAvLyBzZXQgdGhlIGF2YXRhciBpbiB0aGUgc2V0dGluZ3MgbWVudSBpZiBpdCBpcyBsb2NhbCB1c2VyIGFuZCBnZXQgdGhlXG4gICAgICAgIC8vIGxvY2FsIHZpZGVvIGNvbnRhaW5lclxuICAgICAgICBpZiAoamlkID09PSB4bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgICQoJyNhdmF0YXInKS5nZXQoMCkuc3JjID0gdGh1bWJVcmw7XG4gICAgICAgICAgICB0aHVtYm5haWwgPSAkKCcjbG9jYWxWaWRlb0NvbnRhaW5lcicpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2V0IHRoZSBhdmF0YXIgaW4gdGhlIGNvbnRhY3QgbGlzdFxuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyMnICsgcmVzb3VyY2VKaWQgKyAnPmltZycpO1xuICAgICAgICBpZiAoY29udGFjdCAmJiBjb250YWN0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnRhY3QuZ2V0KDApLnNyYyA9IGNvbnRhY3RMaXN0VXJsO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2V0IHRoZSBhdmF0YXIgaW4gdGhlIHRodW1ibmFpbFxuICAgICAgICBpZiAoYXZhdGFyICYmIGF2YXRhci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBhdmF0YXJbMF0uc3JjID0gdGh1bWJVcmw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGh1bWJuYWlsICYmIHRodW1ibmFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgYXZhdGFyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gICAgICAgICAgICAgICAgYXZhdGFyLmlkID0gJ2F2YXRhcl8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICAgICAgYXZhdGFyLmNsYXNzTmFtZSA9ICd1c2VyQXZhdGFyJztcbiAgICAgICAgICAgICAgICBhdmF0YXIuc3JjID0gdGh1bWJVcmw7XG4gICAgICAgICAgICAgICAgdGh1bWJuYWlsLmFwcGVuZChhdmF0YXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy9pZiB0aGUgdXNlciBpcyB0aGUgY3VycmVudCBhY3RpdmUgc3BlYWtlciAtIHVwZGF0ZSB0aGUgYWN0aXZlIHNwZWFrZXJcbiAgICAgICAgLy8gYXZhdGFyXG4gICAgICAgIGlmIChqaWQgPT09IGFjdGl2ZVNwZWFrZXJKaWQpIHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlQWN0aXZlU3BlYWtlckF2YXRhclNyYyhqaWQpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIG9yIHNob3dzIHRoZSB1c2VyJ3MgYXZhdGFyXG4gICAgICogQHBhcmFtIGppZCBqaWQgb2YgdGhlIHVzZXJcbiAgICAgKiBAcGFyYW0gc2hvdyB3aGV0aGVyIHdlIHNob3VsZCBzaG93IHRoZSBhdmF0YXIgb3Igbm90XG4gICAgICogdmlkZW8gYmVjYXVzZSB0aGVyZSBpcyBubyBkb21pbmFudCBzcGVha2VyIGFuZCBubyBmb2N1c2VkIHNwZWFrZXJcbiAgICAgKi9cbiAgICBzaG93VXNlckF2YXRhcjogZnVuY3Rpb24gKGppZCwgc2hvdykge1xuICAgICAgICBpZiAodXNlcnNbamlkXSkge1xuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgICAgIHZhciB2aWRlbyA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQgKyAnPnZpZGVvJyk7XG4gICAgICAgICAgICB2YXIgYXZhdGFyID0gJCgnI2F2YXRhcl8nICsgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgICAgICBpZiAoamlkID09PSB4bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgICAgICB2aWRlbyA9ICQoJyNsb2NhbFZpZGVvV3JhcHBlcj52aWRlbycpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHNob3cgPT09IHVuZGVmaW5lZCB8fCBzaG93ID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgc2hvdyA9IGlzVXNlck11dGVkKGppZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vaWYgdGhlIHVzZXIgaXMgdGhlIGN1cnJlbnRseSBmb2N1c2VkLCB0aGUgZG9taW5hbnQgc3BlYWtlciBvciBpZlxuICAgICAgICAgICAgLy90aGVyZSBpcyBubyBmb2N1c2VkIGFuZCBubyBkb21pbmFudCBzcGVha2VyIGFuZCB0aGUgbGFyZ2UgdmlkZW8gaXNcbiAgICAgICAgICAgIC8vY3VycmVudGx5IHNob3duXG4gICAgICAgICAgICBpZiAoYWN0aXZlU3BlYWtlckppZCA9PT0gamlkICYmIHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKS5pc0xhcmdlVmlkZW9PblRvcCgpKSB7XG4gICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eSgkKFwiI2xhcmdlVmlkZW9cIiksICFzaG93KTtcbiAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KCQoJyNhY3RpdmVTcGVha2VyJyksIHNob3cpO1xuICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkoYXZhdGFyLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eSh2aWRlbywgZmFsc2UpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAodmlkZW8gJiYgdmlkZW8ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KHZpZGVvLCAhc2hvdyk7XG4gICAgICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkoYXZhdGFyLCBzaG93KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgc3JjIG9mIHRoZSBhY3RpdmUgc3BlYWtlciBhdmF0YXJcbiAgICAgKiBAcGFyYW0gamlkIG9mIHRoZSBjdXJyZW50IGFjdGl2ZSBzcGVha2VyXG4gICAgICovXG4gICAgdXBkYXRlQWN0aXZlU3BlYWtlckF2YXRhclNyYzogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZiAoIWppZCkge1xuICAgICAgICAgICAgamlkID0geG1wcC5maW5kSmlkRnJvbVJlc291cmNlKFxuICAgICAgICAgICAgICAgIHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKS5nZXRMYXJnZVZpZGVvU3RhdGUoKS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBhdmF0YXIgPSAkKFwiI2FjdGl2ZVNwZWFrZXJBdmF0YXJcIilbMF07XG4gICAgICAgIHZhciB1cmwgPSBnZXRHcmF2YXRhclVybCh1c2Vyc1tqaWRdLFxuICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFKTtcbiAgICAgICAgaWYgKGppZCA9PT0gYWN0aXZlU3BlYWtlckppZCAmJiBhdmF0YXIuc3JjID09PSB1cmwpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhY3RpdmVTcGVha2VySmlkID0gamlkO1xuICAgICAgICB2YXIgaXNNdXRlZCA9IGlzVXNlck11dGVkKGppZCk7XG4gICAgICAgIGlmIChqaWQgJiYgaXNNdXRlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgYXZhdGFyLnNyYyA9IHVybDtcbiAgICAgICAgICAgIHNldFZpc2liaWxpdHkoJChcIiNsYXJnZVZpZGVvXCIpLCAhaXNNdXRlZCk7XG4gICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoamlkLCBpc011dGVkKTtcbiAgICAgICAgfVxuICAgIH1cblxufTtcblxuXG5tb2R1bGUuZXhwb3J0cyA9IEF2YXRhcjsiLCIvKiBnbG9iYWwgJCwgY29uZmlnLCBkb2NrVG9vbGJhcixcbiAgIHNldExhcmdlVmlkZW9WaXNpYmxlLCBVdGlsICovXG5cbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKTtcbnZhciBQcmV6aSA9IHJlcXVpcmUoXCIuLi9wcmV6aS9QcmV6aVwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vdXRpbC9VSVV0aWxcIik7XG5cbnZhciBldGhlcnBhZE5hbWUgPSBudWxsO1xudmFyIGV0aGVycGFkSUZyYW1lID0gbnVsbDtcbnZhciBkb21haW4gPSBudWxsO1xudmFyIG9wdGlvbnMgPSBcIj9zaG93Q29udHJvbHM9dHJ1ZSZzaG93Q2hhdD1mYWxzZSZzaG93TGluZU51bWJlcnM9dHJ1ZSZ1c2VNb25vc3BhY2VGb250PWZhbHNlXCI7XG5cblxuLyoqXG4gKiBSZXNpemVzIHRoZSBldGhlcnBhZC5cbiAqL1xuZnVuY3Rpb24gcmVzaXplKCkge1xuICAgIGlmICgkKCcjZXRoZXJwYWQ+aWZyYW1lJykubGVuZ3RoKSB7XG4gICAgICAgIHZhciByZW1vdGVWaWRlb3MgPSAkKCcjcmVtb3RlVmlkZW9zJyk7XG4gICAgICAgIHZhciBhdmFpbGFibGVIZWlnaHRcbiAgICAgICAgICAgID0gd2luZG93LmlubmVySGVpZ2h0IC0gcmVtb3RlVmlkZW9zLm91dGVySGVpZ2h0KCk7XG4gICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IFVJVXRpbC5nZXRBdmFpbGFibGVWaWRlb1dpZHRoKCk7XG5cbiAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLndpZHRoKGF2YWlsYWJsZVdpZHRoKTtcbiAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmhlaWdodChhdmFpbGFibGVIZWlnaHQpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBTaGFyZXMgdGhlIEV0aGVycGFkIG5hbWUgd2l0aCBvdGhlciBwYXJ0aWNpcGFudHMuXG4gKi9cbmZ1bmN0aW9uIHNoYXJlRXRoZXJwYWQoKSB7XG4gICAgeG1wcC5hZGRUb1ByZXNlbmNlKFwiZXRoZXJwYWRcIiwgZXRoZXJwYWROYW1lKTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBFdGhlcnBhZCBidXR0b24gYW5kIGFkZHMgaXQgdG8gdGhlIHRvb2xiYXIuXG4gKi9cbmZ1bmN0aW9uIGVuYWJsZUV0aGVycGFkQnV0dG9uKCkge1xuICAgIGlmICghJCgnI2V0aGVycGFkQnV0dG9uJykuaXMoXCI6dmlzaWJsZVwiKSlcbiAgICAgICAgJCgnI2V0aGVycGFkQnV0dG9uJykuY3NzKHtkaXNwbGF5OiAnaW5saW5lLWJsb2NrJ30pO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIElGcmFtZSBmb3IgdGhlIGV0aGVycGFkLlxuICovXG5mdW5jdGlvbiBjcmVhdGVJRnJhbWUoKSB7XG4gICAgZXRoZXJwYWRJRnJhbWUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpZnJhbWUnKTtcbiAgICBldGhlcnBhZElGcmFtZS5zcmMgPSBkb21haW4gKyBldGhlcnBhZE5hbWUgKyBvcHRpb25zO1xuICAgIGV0aGVycGFkSUZyYW1lLmZyYW1lQm9yZGVyID0gMDtcbiAgICBldGhlcnBhZElGcmFtZS5zY3JvbGxpbmcgPSBcIm5vXCI7XG4gICAgZXRoZXJwYWRJRnJhbWUud2lkdGggPSAkKCcjbGFyZ2VWaWRlb0NvbnRhaW5lcicpLndpZHRoKCkgfHwgNjQwO1xuICAgIGV0aGVycGFkSUZyYW1lLmhlaWdodCA9ICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykuaGVpZ2h0KCkgfHwgNDgwO1xuICAgIGV0aGVycGFkSUZyYW1lLnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAndmlzaWJpbGl0eTogaGlkZGVuOycpO1xuXG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2V0aGVycGFkJykuYXBwZW5kQ2hpbGQoZXRoZXJwYWRJRnJhbWUpO1xuXG4gICAgZXRoZXJwYWRJRnJhbWUub25sb2FkID0gZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgZG9jdW1lbnQuZG9tYWluID0gZG9jdW1lbnQuZG9tYWluO1xuICAgICAgICBidWJibGVJZnJhbWVNb3VzZU1vdmUoZXRoZXJwYWRJRnJhbWUpO1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgLy8gdGhlIGlmcmFtZXMgaW5zaWRlIG9mIHRoZSBldGhlcnBhZCBhcmVcbiAgICAgICAgICAgIC8vIG5vdCB5ZXQgbG9hZGVkIHdoZW4gdGhlIGV0aGVycGFkIGlmcmFtZSBpcyBsb2FkZWRcbiAgICAgICAgICAgIHZhciBvdXRlciA9IGV0aGVycGFkSUZyYW1lLlxuICAgICAgICAgICAgICAgIGNvbnRlbnREb2N1bWVudC5nZXRFbGVtZW50c0J5TmFtZShcImFjZV9vdXRlclwiKVswXTtcbiAgICAgICAgICAgIGJ1YmJsZUlmcmFtZU1vdXNlTW92ZShvdXRlcik7XG4gICAgICAgICAgICB2YXIgaW5uZXIgPSBvdXRlci5cbiAgICAgICAgICAgICAgICBjb250ZW50RG9jdW1lbnQuZ2V0RWxlbWVudHNCeU5hbWUoXCJhY2VfaW5uZXJcIilbMF07XG4gICAgICAgICAgICBidWJibGVJZnJhbWVNb3VzZU1vdmUoaW5uZXIpO1xuICAgICAgICB9LCAyMDAwKTtcbiAgICB9O1xufVxuXG5mdW5jdGlvbiBidWJibGVJZnJhbWVNb3VzZU1vdmUoaWZyYW1lKXtcbiAgICB2YXIgZXhpc3RpbmdPbk1vdXNlTW92ZSA9IGlmcmFtZS5jb250ZW50V2luZG93Lm9ubW91c2Vtb3ZlO1xuICAgIGlmcmFtZS5jb250ZW50V2luZG93Lm9ubW91c2Vtb3ZlID0gZnVuY3Rpb24oZSl7XG4gICAgICAgIGlmKGV4aXN0aW5nT25Nb3VzZU1vdmUpIGV4aXN0aW5nT25Nb3VzZU1vdmUoZSk7XG4gICAgICAgIHZhciBldnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudChcIk1vdXNlRXZlbnRzXCIpO1xuICAgICAgICB2YXIgYm91bmRpbmdDbGllbnRSZWN0ID0gaWZyYW1lLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBldnQuaW5pdE1vdXNlRXZlbnQoXG4gICAgICAgICAgICBcIm1vdXNlbW92ZVwiLFxuICAgICAgICAgICAgdHJ1ZSwgLy8gYnViYmxlc1xuICAgICAgICAgICAgZmFsc2UsIC8vIG5vdCBjYW5jZWxhYmxlXG4gICAgICAgICAgICB3aW5kb3csXG4gICAgICAgICAgICBlLmRldGFpbCxcbiAgICAgICAgICAgIGUuc2NyZWVuWCxcbiAgICAgICAgICAgIGUuc2NyZWVuWSxcbiAgICAgICAgICAgICAgICBlLmNsaWVudFggKyBib3VuZGluZ0NsaWVudFJlY3QubGVmdCxcbiAgICAgICAgICAgICAgICBlLmNsaWVudFkgKyBib3VuZGluZ0NsaWVudFJlY3QudG9wLFxuICAgICAgICAgICAgZS5jdHJsS2V5LFxuICAgICAgICAgICAgZS5hbHRLZXksXG4gICAgICAgICAgICBlLnNoaWZ0S2V5LFxuICAgICAgICAgICAgZS5tZXRhS2V5LFxuICAgICAgICAgICAgZS5idXR0b24sXG4gICAgICAgICAgICBudWxsIC8vIG5vIHJlbGF0ZWQgZWxlbWVudFxuICAgICAgICApO1xuICAgICAgICBpZnJhbWUuZGlzcGF0Y2hFdmVudChldnQpO1xuICAgIH07XG59XG5cblxuLyoqXG4gKiBPbiB2aWRlbyBzZWxlY3RlZCBldmVudC5cbiAqL1xuJChkb2N1bWVudCkuYmluZCgndmlkZW8uc2VsZWN0ZWQnLCBmdW5jdGlvbiAoZXZlbnQsIGlzUHJlc2VudGF0aW9uKSB7XG4gICAgaWYgKGNvbmZpZy5ldGhlcnBhZF9iYXNlICYmIGV0aGVycGFkSUZyYW1lICYmIGV0aGVycGFkSUZyYW1lLnN0eWxlLnZpc2liaWxpdHkgIT09ICdoaWRkZW4nKVxuICAgICAgICBFdGhlcnBhZC50b2dnbGVFdGhlcnBhZChpc1ByZXNlbnRhdGlvbik7XG59KTtcblxuXG52YXIgRXRoZXJwYWQgPSB7XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZXMgdGhlIGV0aGVycGFkLlxuICAgICAqL1xuICAgIGluaXQ6IGZ1bmN0aW9uIChuYW1lKSB7XG5cbiAgICAgICAgaWYgKGNvbmZpZy5ldGhlcnBhZF9iYXNlICYmICFldGhlcnBhZE5hbWUpIHtcblxuICAgICAgICAgICAgZG9tYWluID0gY29uZmlnLmV0aGVycGFkX2Jhc2U7XG5cbiAgICAgICAgICAgIGlmICghbmFtZSkge1xuICAgICAgICAgICAgICAgIC8vIEluIGNhc2Ugd2UncmUgdGhlIGZvY3VzIHdlIGdlbmVyYXRlIHRoZSBuYW1lLlxuICAgICAgICAgICAgICAgIGV0aGVycGFkTmFtZSA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZyg3KSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdfJyArIChuZXcgRGF0ZSgpLmdldFRpbWUoKSkudG9TdHJpbmcoKTtcbiAgICAgICAgICAgICAgICBzaGFyZUV0aGVycGFkKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgZXRoZXJwYWROYW1lID0gbmFtZTtcblxuICAgICAgICAgICAgZW5hYmxlRXRoZXJwYWRCdXR0b24oKTtcblxuICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiBSZXNpemVzIHRoZSBldGhlcnBhZCwgd2hlbiB0aGUgd2luZG93IGlzIHJlc2l6ZWQuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICQod2luZG93KS5yZXNpemUoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJlc2l6ZSgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogT3BlbnMvaGlkZXMgdGhlIEV0aGVycGFkLlxuICAgICAqL1xuICAgIHRvZ2dsZUV0aGVycGFkOiBmdW5jdGlvbiAoaXNQcmVzZW50YXRpb24pIHtcbiAgICAgICAgaWYgKCFldGhlcnBhZElGcmFtZSlcbiAgICAgICAgICAgIGNyZWF0ZUlGcmFtZSgpO1xuXG4gICAgICAgIHZhciBsYXJnZVZpZGVvID0gbnVsbDtcbiAgICAgICAgaWYgKFByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKVxuICAgICAgICAgICAgbGFyZ2VWaWRlbyA9ICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJyk7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGxhcmdlVmlkZW8gPSAkKCcjbGFyZ2VWaWRlbycpO1xuXG4gICAgICAgIGlmICgkKCcjZXRoZXJwYWQ+aWZyYW1lJykuY3NzKCd2aXNpYmlsaXR5JykgPT09ICdoaWRkZW4nKSB7XG4gICAgICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcbiAgICAgICAgICAgIGxhcmdlVmlkZW8uZmFkZU91dCgzMDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAoUHJlemkuaXNQcmVzZW50YXRpb25WaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlby5jc3Moe29wYWNpdHk6ICcwJ30pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldExhcmdlVmlkZW9WaXNpYmxlKGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmZhZGVJbigzMDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLmJhY2tncm91bmQgPSAnI2VlZWVlZSc7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkJykuY3NzKHt6SW5kZXg6IDJ9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKCQoJyNldGhlcnBhZD5pZnJhbWUnKSkge1xuICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmZhZGVPdXQoMzAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgICAgICAkKCcjZXRoZXJwYWQnKS5jc3Moe3pJbmRleDogMH0pO1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuYmFja2dyb3VuZCA9ICdibGFjayc7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgaWYgKCFpc1ByZXNlbnRhdGlvbikge1xuICAgICAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZUluKDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zZXRMYXJnZVZpZGVvVmlzaWJsZSh0cnVlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXNpemUoKTtcbiAgICB9LFxuXG4gICAgaXNWaXNpYmxlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGV0aGVycGFkSWZyYW1lID0gJCgnI2V0aGVycGFkPmlmcmFtZScpO1xuICAgICAgICByZXR1cm4gZXRoZXJwYWRJZnJhbWUgJiYgZXRoZXJwYWRJZnJhbWUuaXMoJzp2aXNpYmxlJyk7XG4gICAgfVxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEV0aGVycGFkO1xuIiwidmFyIFRvb2xiYXJUb2dnbGVyID0gcmVxdWlyZShcIi4uL3Rvb2xiYXJzL1Rvb2xiYXJUb2dnbGVyXCIpO1xudmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi91dGlsL1VJVXRpbFwiKTtcbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKTtcbnZhciBtZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuLi91dGlsL01lc3NhZ2VIYW5kbGVyXCIpO1xuXG52YXIgcHJlemlQbGF5ZXIgPSBudWxsO1xuXG52YXIgUHJlemkgPSB7XG5cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZHMgdGhlIGN1cnJlbnQgcHJlc2VudGF0aW9uLlxuICAgICAqL1xuICAgIHJlbG9hZFByZXNlbnRhdGlvbjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBpZnJhbWUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChwcmV6aVBsYXllci5vcHRpb25zLnByZXppSWQpO1xuICAgICAgICBpZnJhbWUuc3JjID0gaWZyYW1lLnNyYztcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyA8dHQ+dHJ1ZTwvdHQ+IGlmIHRoZSBwcmVzZW50YXRpb24gaXMgdmlzaWJsZSwgPHR0PmZhbHNlPC90dD4gLVxuICAgICAqIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBpc1ByZXNlbnRhdGlvblZpc2libGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuICgkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpICE9IG51bGxcbiAgICAgICAgICAgICAgICAmJiAkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpLmNzcygnb3BhY2l0eScpID09IDEpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB0aGUgUHJlemkgZGlhbG9nLCBmcm9tIHdoaWNoIHRoZSB1c2VyIGNvdWxkIGNob29zZSBhIHByZXNlbnRhdGlvblxuICAgICAqIHRvIGxvYWQuXG4gICAgICovXG4gICAgb3BlblByZXppRGlhbG9nOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIG15cHJlemkgPSB4bXBwLmdldFByZXppKCk7XG4gICAgICAgIGlmIChteXByZXppKSB7XG4gICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKFwiUmVtb3ZlIFByZXppXCIsXG4gICAgICAgICAgICAgICAgXCJBcmUgeW91IHN1cmUgeW91IHdvdWxkIGxpa2UgdG8gcmVtb3ZlIHlvdXIgUHJlemk/XCIsXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgXCJSZW1vdmVcIixcbiAgICAgICAgICAgICAgICBmdW5jdGlvbihlLHYsbSxmKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtcHAucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAocHJlemlQbGF5ZXIgIT0gbnVsbCkge1xuICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhcIlNoYXJlIGEgUHJlemlcIixcbiAgICAgICAgICAgICAgICBcIkFub3RoZXIgcGFydGljaXBhbnQgaXMgYWxyZWFkeSBzaGFyaW5nIGEgUHJlemkuXCIgK1xuICAgICAgICAgICAgICAgICAgICBcIlRoaXMgY29uZmVyZW5jZSBhbGxvd3Mgb25seSBvbmUgUHJlemkgYXQgYSB0aW1lLlwiLFxuICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgIFwiT2tcIixcbiAgICAgICAgICAgICAgICBmdW5jdGlvbihlLHYsbSxmKSB7XG4gICAgICAgICAgICAgICAgICAgICQucHJvbXB0LmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHZhciBvcGVuUHJlemlTdGF0ZSA9IHtcbiAgICAgICAgICAgICAgICBzdGF0ZTA6IHtcbiAgICAgICAgICAgICAgICAgICAgaHRtbDogICAnPGgyPlNoYXJlIGEgUHJlemk8L2gyPicgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJwcmV6aVVybFwiIHR5cGU9XCJ0ZXh0XCIgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3BsYWNlaG9sZGVyPVwiZS5nLiAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaHR0cDovL3ByZXppLmNvbS93ejd2aGp5Y2w3ZTYvbXktcHJlemlcIiBhdXRvZm9jdXM+JyxcbiAgICAgICAgICAgICAgICAgICAgcGVyc2lzdGVudDogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGJ1dHRvbnM6IHsgXCJTaGFyZVwiOiB0cnVlICwgXCJDYW5jZWxcIjogZmFsc2V9LFxuICAgICAgICAgICAgICAgICAgICBkZWZhdWx0QnV0dG9uOiAxLFxuICAgICAgICAgICAgICAgICAgICBzdWJtaXQ6IGZ1bmN0aW9uKGUsdixtLGYpe1xuICAgICAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYodilcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcHJlemlVcmwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncHJlemlVcmwnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwcmV6aVVybC52YWx1ZSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciB1cmxWYWx1ZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBlbmNvZGVVUkkoVXRpbC5lc2NhcGVIdG1sKHByZXppVXJsLnZhbHVlKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHVybFZhbHVlLmluZGV4T2YoJ2h0dHA6Ly9wcmV6aS5jb20vJykgIT0gMFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJiYgdXJsVmFsdWUuaW5kZXhPZignaHR0cHM6Ly9wcmV6aS5jb20vJykgIT0gMClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuZ29Ub1N0YXRlKCdzdGF0ZTEnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBwcmVzSWRUbXAgPSB1cmxWYWx1ZS5zdWJzdHJpbmcoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybFZhbHVlLmluZGV4T2YoXCJwcmV6aS5jb20vXCIpICsgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpc0FscGhhbnVtZXJpYyhwcmVzSWRUbXApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IHByZXNJZFRtcC5pbmRleE9mKCcvJykgPCAyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuZ29Ub1N0YXRlKCdzdGF0ZTEnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bXBwLmFkZFRvUHJlc2VuY2UoXCJwcmV6aVwiLCB1cmxWYWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5jbG9zZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBzdGF0ZTE6IHtcbiAgICAgICAgICAgICAgICAgICAgaHRtbDogICAnPGgyPlNoYXJlIGEgUHJlemk8L2gyPicgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdQbGVhc2UgcHJvdmlkZSBhIGNvcnJlY3QgcHJlemkgbGluay4nLFxuICAgICAgICAgICAgICAgICAgICBwZXJzaXN0ZW50OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgYnV0dG9uczogeyBcIkJhY2tcIjogdHJ1ZSwgXCJDYW5jZWxcIjogZmFsc2UgfSxcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMSxcbiAgICAgICAgICAgICAgICAgICAgc3VibWl0OmZ1bmN0aW9uKGUsdixtLGYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHY9PTApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5nb1RvU3RhdGUoJ3N0YXRlMCcpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHZhciBmb2N1c1ByZXppVXJsID0gIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ByZXppVXJsJykuZm9jdXMoKTtcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIub3BlbkRpYWxvZ1dpdGhTdGF0ZXMob3BlblByZXppU3RhdGUsIGZvY3VzUHJlemlVcmwsIGZvY3VzUHJlemlVcmwpO1xuICAgICAgICB9XG4gICAgfVxuXG59O1xuXG4vKipcbiAqIEEgbmV3IHByZXNlbnRhdGlvbiBoYXMgYmVlbiBhZGRlZC5cbiAqXG4gKiBAcGFyYW0gZXZlbnQgdGhlIGV2ZW50IGluZGljYXRpbmcgdGhlIGFkZCBvZiBhIHByZXNlbnRhdGlvblxuICogQHBhcmFtIGppZCB0aGUgamlkIGZyb20gd2hpY2ggdGhlIHByZXNlbnRhdGlvbiB3YXMgYWRkZWRcbiAqIEBwYXJhbSBwcmVzVXJsIHVybCBvZiB0aGUgcHJlc2VudGF0aW9uXG4gKiBAcGFyYW0gY3VycmVudFNsaWRlIHRoZSBjdXJyZW50IHNsaWRlIHRvIHdoaWNoIHdlIHNob3VsZCBtb3ZlXG4gKi9cbmZ1bmN0aW9uIHByZXNlbnRhdGlvbkFkZGVkKGV2ZW50LCBqaWQsIHByZXNVcmwsIGN1cnJlbnRTbGlkZSkge1xuICAgIGNvbnNvbGUubG9nKFwicHJlc2VudGF0aW9uIGFkZGVkXCIsIHByZXNVcmwpO1xuXG4gICAgdmFyIHByZXNJZCA9IGdldFByZXNlbnRhdGlvbklkKHByZXNVcmwpO1xuXG4gICAgdmFyIGVsZW1lbnRJZCA9ICdwYXJ0aWNpcGFudF8nXG4gICAgICAgICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKVxuICAgICAgICArICdfJyArIHByZXNJZDtcblxuICAgIC8vIFdlIGV4cGxpY2l0bHkgZG9uJ3Qgc3BlY2lmeSB0aGUgcGVlciBqaWQgaGVyZSwgYmVjYXVzZSB3ZSBkb24ndCB3YW50XG4gICAgLy8gdGhpcyB2aWRlbyB0byBiZSBkZWFsdCB3aXRoIGFzIGEgcGVlciByZWxhdGVkIG9uZSAoZm9yIGV4YW1wbGUgd2VcbiAgICAvLyBkb24ndCB3YW50IHRvIHNob3cgYSBtdXRlL2tpY2sgbWVudSBmb3IgdGhpcyBvbmUsIGV0Yy4pLlxuICAgIFZpZGVvTGF5b3V0LmFkZFJlbW90ZVZpZGVvQ29udGFpbmVyKG51bGwsIGVsZW1lbnRJZCk7XG4gICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuXG4gICAgdmFyIGNvbnRyb2xzRW5hYmxlZCA9IGZhbHNlO1xuICAgIGlmIChqaWQgPT09IHhtcHAubXlKaWQoKSlcbiAgICAgICAgY29udHJvbHNFbmFibGVkID0gdHJ1ZTtcblxuICAgIHNldFByZXNlbnRhdGlvblZpc2libGUodHJ1ZSk7XG4gICAgJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS5ob3ZlcihcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoUHJlemkuaXNQcmVzZW50YXRpb25WaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVsb2FkQnV0dG9uUmlnaHQgPSB3aW5kb3cuaW5uZXJXaWR0aFxuICAgICAgICAgICAgICAgICAgICAtICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykub2Zmc2V0KCkubGVmdFxuICAgICAgICAgICAgICAgICAgICAtICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykud2lkdGgoKTtcblxuICAgICAgICAgICAgICAgICQoJyNyZWxvYWRQcmVzZW50YXRpb24nKS5jc3MoeyAgcmlnaHQ6IHJlbG9hZEJ1dHRvblJpZ2h0LFxuICAgICAgICAgICAgICAgICAgICBkaXNwbGF5OidpbmxpbmUtYmxvY2snfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgaWYgKCFQcmV6aS5pc1ByZXNlbnRhdGlvblZpc2libGUoKSlcbiAgICAgICAgICAgICAgICAkKCcjcmVsb2FkUHJlc2VudGF0aW9uJykuY3NzKHtkaXNwbGF5Oidub25lJ30pO1xuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgdmFyIGUgPSBldmVudC50b0VsZW1lbnQgfHwgZXZlbnQucmVsYXRlZFRhcmdldDtcblxuICAgICAgICAgICAgICAgIGlmIChlICYmIGUuaWQgIT0gJ3JlbG9hZFByZXNlbnRhdGlvbicgJiYgZS5pZCAhPSAnaGVhZGVyJylcbiAgICAgICAgICAgICAgICAgICAgJCgnI3JlbG9hZFByZXNlbnRhdGlvbicpLmNzcyh7ZGlzcGxheTonbm9uZSd9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICBwcmV6aVBsYXllciA9IG5ldyBQcmV6aVBsYXllcihcbiAgICAgICAgJ3ByZXNlbnRhdGlvbicsXG4gICAgICAgIHtwcmV6aUlkOiBwcmVzSWQsXG4gICAgICAgICAgICB3aWR0aDogZ2V0UHJlc2VudGF0aW9uV2lkdGgoKSxcbiAgICAgICAgICAgIGhlaWdodDogZ2V0UHJlc2VudGF0aW9uSGVpaGd0KCksXG4gICAgICAgICAgICBjb250cm9sczogY29udHJvbHNFbmFibGVkLFxuICAgICAgICAgICAgZGVidWc6IHRydWVcbiAgICAgICAgfSk7XG5cbiAgICAkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpLmF0dHIoJ2lkJywgcHJlemlQbGF5ZXIub3B0aW9ucy5wcmV6aUlkKTtcblxuICAgIHByZXppUGxheWVyLm9uKFByZXppUGxheWVyLkVWRU5UX1NUQVRVUywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJwcmV6aSBzdGF0dXNcIiwgZXZlbnQudmFsdWUpO1xuICAgICAgICBpZiAoZXZlbnQudmFsdWUgPT0gUHJlemlQbGF5ZXIuU1RBVFVTX0NPTlRFTlRfUkVBRFkpIHtcbiAgICAgICAgICAgIGlmIChqaWQgIT0geG1wcC5teUppZCgpKVxuICAgICAgICAgICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50U2xpZGUpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICBwcmV6aVBsYXllci5vbihQcmV6aVBsYXllci5FVkVOVF9DVVJSRU5UX1NURVAsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiZXZlbnQgdmFsdWVcIiwgZXZlbnQudmFsdWUpO1xuICAgICAgICB4bXBwLmFkZFRvUHJlc2VuY2UoXCJwcmV6aVNsaWRlXCIsIGV2ZW50LnZhbHVlKTtcbiAgICB9KTtcblxuICAgICQoXCIjXCIgKyBlbGVtZW50SWQpLmNzcyggJ2JhY2tncm91bmQtaW1hZ2UnLFxuICAgICAgICAndXJsKC4uL2ltYWdlcy9hdmF0YXJwcmV6aS5wbmcpJyk7XG4gICAgJChcIiNcIiArIGVsZW1lbnRJZCkuY2xpY2soXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNldFByZXNlbnRhdGlvblZpc2libGUodHJ1ZSk7XG4gICAgICAgIH1cbiAgICApO1xufTtcblxuLyoqXG4gKiBBIHByZXNlbnRhdGlvbiBoYXMgYmVlbiByZW1vdmVkLlxuICpcbiAqIEBwYXJhbSBldmVudCB0aGUgZXZlbnQgaW5kaWNhdGluZyB0aGUgcmVtb3ZlIG9mIGEgcHJlc2VudGF0aW9uXG4gKiBAcGFyYW0gamlkIHRoZSBqaWQgZm9yIHdoaWNoIHRoZSBwcmVzZW50YXRpb24gd2FzIHJlbW92ZWRcbiAqIEBwYXJhbSB0aGUgdXJsIG9mIHRoZSBwcmVzZW50YXRpb25cbiAqL1xuZnVuY3Rpb24gcHJlc2VudGF0aW9uUmVtb3ZlZChldmVudCwgamlkLCBwcmVzVXJsKSB7XG4gICAgY29uc29sZS5sb2coJ3ByZXNlbnRhdGlvbiByZW1vdmVkJywgcHJlc1VybCk7XG4gICAgdmFyIHByZXNJZCA9IGdldFByZXNlbnRhdGlvbklkKHByZXNVcmwpO1xuICAgIHNldFByZXNlbnRhdGlvblZpc2libGUoZmFsc2UpO1xuICAgICQoJyNwYXJ0aWNpcGFudF8nXG4gICAgICAgICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKVxuICAgICAgICArICdfJyArIHByZXNJZCkucmVtb3ZlKCk7XG4gICAgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5yZW1vdmUoKTtcbiAgICBpZiAocHJlemlQbGF5ZXIgIT0gbnVsbCkge1xuICAgICAgICBwcmV6aVBsYXllci5kZXN0cm95KCk7XG4gICAgICAgIHByZXppUGxheWVyID0gbnVsbDtcbiAgICB9XG59O1xuXG4vKipcbiAqIEluZGljYXRlcyBpZiB0aGUgZ2l2ZW4gc3RyaW5nIGlzIGFuIGFscGhhbnVtZXJpYyBzdHJpbmcuXG4gKiBOb3RlIHRoYXQgc29tZSBzcGVjaWFsIGNoYXJhY3RlcnMgYXJlIGFsc28gYWxsb3dlZCAoLSwgXyAsIC8sICYsID8sID0sIDspIGZvciB0aGVcbiAqIHB1cnBvc2Ugb2YgY2hlY2tpbmcgVVJJcy5cbiAqL1xuZnVuY3Rpb24gaXNBbHBoYW51bWVyaWModW5zYWZlVGV4dCkge1xuICAgIHZhciByZWdleCA9IC9eW2EtejAtOS1fXFwvJlxcPz07XSskL2k7XG4gICAgcmV0dXJuIHJlZ2V4LnRlc3QodW5zYWZlVGV4dCk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgcHJlc2VudGF0aW9uIGlkIGZyb20gdGhlIGdpdmVuIHVybC5cbiAqL1xuZnVuY3Rpb24gZ2V0UHJlc2VudGF0aW9uSWQgKHByZXNVcmwpIHtcbiAgICB2YXIgcHJlc0lkVG1wID0gcHJlc1VybC5zdWJzdHJpbmcocHJlc1VybC5pbmRleE9mKFwicHJlemkuY29tL1wiKSArIDEwKTtcbiAgICByZXR1cm4gcHJlc0lkVG1wLnN1YnN0cmluZygwLCBwcmVzSWRUbXAuaW5kZXhPZignLycpKTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBwcmVzZW50YXRpb24gd2lkdGguXG4gKi9cbmZ1bmN0aW9uIGdldFByZXNlbnRhdGlvbldpZHRoKCkge1xuICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IFVJVXRpbC5nZXRBdmFpbGFibGVWaWRlb1dpZHRoKCk7XG4gICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IGdldFByZXNlbnRhdGlvbkhlaWhndCgpO1xuXG4gICAgdmFyIGFzcGVjdFJhdGlvID0gMTYuMCAvIDkuMDtcbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0IDwgYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbykge1xuICAgICAgICBhdmFpbGFibGVXaWR0aCA9IE1hdGguZmxvb3IoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8pO1xuICAgIH1cbiAgICByZXR1cm4gYXZhaWxhYmxlV2lkdGg7XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgcHJlc2VudGF0aW9uIGhlaWdodC5cbiAqL1xuZnVuY3Rpb24gZ2V0UHJlc2VudGF0aW9uSGVpaGd0KCkge1xuICAgIHZhciByZW1vdGVWaWRlb3MgPSAkKCcjcmVtb3RlVmlkZW9zJyk7XG4gICAgcmV0dXJuIHdpbmRvdy5pbm5lckhlaWdodCAtIHJlbW90ZVZpZGVvcy5vdXRlckhlaWdodCgpO1xufVxuXG4vKipcbiAqIFJlc2l6ZXMgdGhlIHByZXNlbnRhdGlvbiBpZnJhbWUuXG4gKi9cbmZ1bmN0aW9uIHJlc2l6ZSgpIHtcbiAgICBpZiAoJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKSkge1xuICAgICAgICAkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpLndpZHRoKGdldFByZXNlbnRhdGlvbldpZHRoKCkpO1xuICAgICAgICAkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpLmhlaWdodChnZXRQcmVzZW50YXRpb25IZWloZ3QoKSk7XG4gICAgfVxufVxuXG4vKipcbiAqIFNob3dzL2hpZGVzIGEgcHJlc2VudGF0aW9uLlxuICovXG5mdW5jdGlvbiBzZXRQcmVzZW50YXRpb25WaXNpYmxlKHZpc2libGUpIHtcbiAgICB2YXIgcHJlemkgPSAkKCcjcHJlc2VudGF0aW9uPmlmcmFtZScpO1xuICAgIGlmICh2aXNpYmxlKSB7XG4gICAgICAgIC8vIFRyaWdnZXIgdGhlIHZpZGVvLnNlbGVjdGVkIGV2ZW50IHRvIGluZGljYXRlIGEgY2hhbmdlIGluIHRoZVxuICAgICAgICAvLyBsYXJnZSB2aWRlby5cbiAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcInZpZGVvLnNlbGVjdGVkXCIsIFt0cnVlXSk7XG5cbiAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5mYWRlT3V0KDMwMCk7XG4gICAgICAgIHByZXppLmZhZGVJbigzMDAsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcHJlemkuY3NzKHtvcGFjaXR5OicxJ30pO1xuICAgICAgICAgICAgVG9vbGJhclRvZ2dsZXIuZG9ja1Rvb2xiYXIodHJ1ZSk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5zZXRMYXJnZVZpZGVvVmlzaWJsZShmYWxzZSk7XG4gICAgICAgIH0pO1xuICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGlmIChwcmV6aS5jc3MoJ29wYWNpdHknKSA9PSAnMScpIHtcbiAgICAgICAgICAgIHByZXppLmZhZGVPdXQoMzAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcHJlemkuY3NzKHtvcGFjaXR5OicwJ30pO1xuICAgICAgICAgICAgICAgICQoJyNyZWxvYWRQcmVzZW50YXRpb24nKS5jc3Moe2Rpc3BsYXk6J25vbmUnfSk7XG4gICAgICAgICAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5mYWRlSW4oMzAwLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2V0TGFyZ2VWaWRlb1Zpc2libGUodHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgIFRvb2xiYXJUb2dnbGVyLmRvY2tUb29sYmFyKGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIFByZXNlbnRhdGlvbiBoYXMgYmVlbiByZW1vdmVkLlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCdwcmVzZW50YXRpb25yZW1vdmVkLm11YycsIHByZXNlbnRhdGlvblJlbW92ZWQpO1xuXG4vKipcbiAqIFByZXNlbnRhdGlvbiBoYXMgYmVlbiBhZGRlZC5cbiAqL1xuJChkb2N1bWVudCkuYmluZCgncHJlc2VudGF0aW9uYWRkZWQubXVjJywgcHJlc2VudGF0aW9uQWRkZWQpO1xuXG4vKlxuICogSW5kaWNhdGVzIHByZXNlbnRhdGlvbiBzbGlkZSBjaGFuZ2UuXG4gKi9cbiQoZG9jdW1lbnQpLmJpbmQoJ2dvdG9zbGlkZS5tdWMnLCBmdW5jdGlvbiAoZXZlbnQsIGppZCwgcHJlc1VybCwgY3VycmVudCkge1xuICAgIGlmIChwcmV6aVBsYXllciAmJiBwcmV6aVBsYXllci5nZXRDdXJyZW50U3RlcCgpICE9IGN1cnJlbnQpIHtcbiAgICAgICAgcHJlemlQbGF5ZXIuZmx5VG9TdGVwKGN1cnJlbnQpO1xuXG4gICAgICAgIHZhciBhbmltYXRpb25TdGVwc0FycmF5ID0gcHJlemlQbGF5ZXIuZ2V0QW5pbWF0aW9uQ291bnRPblN0ZXBzKCk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcGFyc2VJbnQoYW5pbWF0aW9uU3RlcHNBcnJheVtjdXJyZW50XSk7IGkrKykge1xuICAgICAgICAgICAgcHJlemlQbGF5ZXIuZmx5VG9TdGVwKGN1cnJlbnQsIGkpO1xuICAgICAgICB9XG4gICAgfVxufSk7XG5cbi8qKlxuICogT24gdmlkZW8gc2VsZWN0ZWQgZXZlbnQuXG4gKi9cbiQoZG9jdW1lbnQpLmJpbmQoJ3ZpZGVvLnNlbGVjdGVkJywgZnVuY3Rpb24gKGV2ZW50LCBpc1ByZXNlbnRhdGlvbikge1xuICAgIGlmICghaXNQcmVzZW50YXRpb24gJiYgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKSkge1xuICAgICAgICBzZXRQcmVzZW50YXRpb25WaXNpYmxlKGZhbHNlKTtcbiAgICB9XG59KTtcblxuJCh3aW5kb3cpLnJlc2l6ZShmdW5jdGlvbiAoKSB7XG4gICAgcmVzaXplKCk7XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBQcmV6aTtcbiIsInZhciBDaGF0ID0gcmVxdWlyZShcIi4vY2hhdC9DaGF0XCIpO1xudmFyIENvbnRhY3RMaXN0ID0gcmVxdWlyZShcIi4vY29udGFjdGxpc3QvQ29udGFjdExpc3RcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi9zZXR0aW5ncy9TZXR0aW5nc1wiKTtcbnZhciBTZXR0aW5nc01lbnUgPSByZXF1aXJlKFwiLi9zZXR0aW5ncy9TZXR0aW5nc01lbnVcIik7XG52YXIgVmlkZW9MYXlvdXQgPSByZXF1aXJlKFwiLi4vdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXRcIik7XG52YXIgVG9vbGJhclRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXJcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uL3V0aWwvVUlVdGlsXCIpO1xuXG4vKipcbiAqIFRvZ2dsZXIgZm9yIHRoZSBjaGF0LCBjb250YWN0IGxpc3QsIHNldHRpbmdzIG1lbnUsIGV0Yy4uXG4gKi9cbnZhciBQYW5lbFRvZ2dsZXIgPSAoZnVuY3Rpb24obXkpIHtcblxuICAgIHZhciBjdXJyZW50bHlPcGVuID0gbnVsbDtcbiAgICB2YXIgYnV0dG9ucyA9IHtcbiAgICAgICAgJyNjaGF0c3BhY2UnOiAnI2NoYXRCb3R0b21CdXR0b24nLFxuICAgICAgICAnI2NvbnRhY3RsaXN0JzogJyNjb250YWN0TGlzdEJ1dHRvbicsXG4gICAgICAgICcjc2V0dGluZ3NtZW51JzogJyNzZXR0aW5nc0J1dHRvbidcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVzaXplcyB0aGUgdmlkZW8gYXJlYVxuICAgICAqIEBwYXJhbSBpc0Nsb3Npbmcgd2hldGhlciB0aGUgc2lkZSBwYW5lbCBpcyBnb2luZyB0byBiZSBjbG9zZWQgb3IgaXMgZ29pbmcgdG8gb3BlbiAvIHJlbWFpbiBvcGVuZWRcbiAgICAgKiBAcGFyYW0gY29tcGxldGVGdW5jdGlvbiBhIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSB2aWRlbyBzcGFjZSBpcyByZXNpemVkXG4gICAgICovXG4gICAgdmFyIHJlc2l6ZVZpZGVvQXJlYSA9IGZ1bmN0aW9uKGlzQ2xvc2luZywgY29tcGxldGVGdW5jdGlvbikge1xuICAgICAgICB2YXIgdmlkZW9zcGFjZSA9ICQoJyN2aWRlb3NwYWNlJyk7XG5cbiAgICAgICAgdmFyIHBhbmVsU2l6ZSA9IGlzQ2xvc2luZyA/IFswLCAwXSA6IFBhbmVsVG9nZ2xlci5nZXRQYW5lbFNpemUoKTtcbiAgICAgICAgdmFyIHZpZGVvc3BhY2VXaWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoIC0gcGFuZWxTaXplWzBdO1xuICAgICAgICB2YXIgdmlkZW9zcGFjZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcbiAgICAgICAgdmFyIHZpZGVvU2l6ZVxuICAgICAgICAgICAgPSBWaWRlb0xheW91dC5nZXRWaWRlb1NpemUobnVsbCwgbnVsbCwgdmlkZW9zcGFjZVdpZHRoLCB2aWRlb3NwYWNlSGVpZ2h0KTtcbiAgICAgICAgdmFyIHZpZGVvV2lkdGggPSB2aWRlb1NpemVbMF07XG4gICAgICAgIHZhciB2aWRlb0hlaWdodCA9IHZpZGVvU2l6ZVsxXTtcbiAgICAgICAgdmFyIHZpZGVvUG9zaXRpb24gPSBWaWRlb0xheW91dC5nZXRWaWRlb1Bvc2l0aW9uKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICB2aWRlb0hlaWdodCxcbiAgICAgICAgICAgIHZpZGVvc3BhY2VXaWR0aCxcbiAgICAgICAgICAgIHZpZGVvc3BhY2VIZWlnaHQpO1xuICAgICAgICB2YXIgaG9yaXpvbnRhbEluZGVudCA9IHZpZGVvUG9zaXRpb25bMF07XG4gICAgICAgIHZhciB2ZXJ0aWNhbEluZGVudCA9IHZpZGVvUG9zaXRpb25bMV07XG5cbiAgICAgICAgdmFyIHRodW1ibmFpbFNpemUgPSBWaWRlb0xheW91dC5jYWxjdWxhdGVUaHVtYm5haWxTaXplKHZpZGVvc3BhY2VXaWR0aCk7XG4gICAgICAgIHZhciB0aHVtYm5haWxzV2lkdGggPSB0aHVtYm5haWxTaXplWzBdO1xuICAgICAgICB2YXIgdGh1bWJuYWlsc0hlaWdodCA9IHRodW1ibmFpbFNpemVbMV07XG4gICAgICAgIC8vZm9yIGNoYXRcblxuICAgICAgICB2aWRlb3NwYWNlLmFuaW1hdGUoe1xuICAgICAgICAgICAgICAgIHJpZ2h0OiBwYW5lbFNpemVbMF0sXG4gICAgICAgICAgICAgICAgd2lkdGg6IHZpZGVvc3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHZpZGVvc3BhY2VIZWlnaHRcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDAsXG4gICAgICAgICAgICAgICAgY29tcGxldGU6IGNvbXBsZXRlRnVuY3Rpb25cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICQoJyNyZW1vdGVWaWRlb3MnKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHRodW1ibmFpbHNIZWlnaHRcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICQoJyNyZW1vdGVWaWRlb3M+c3BhbicpLmFuaW1hdGUoe1xuICAgICAgICAgICAgICAgIGhlaWdodDogdGh1bWJuYWlsc0hlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogdGh1bWJuYWlsc1dpZHRoXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoXG4gICAgICAgICAgICAgICAgICAgICAgICBcInJlbW90ZXZpZGVvLnJlc2l6ZWRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFt0aHVtYm5haWxzV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsc0hlaWdodF0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgd2lkdGg6IHZpZGVvc3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHZpZGVvc3BhY2VIZWlnaHRcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICQoJyNsYXJnZVZpZGVvJykuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgd2lkdGg6IHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgaGVpZ2h0OiB2aWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICB0b3A6IHZlcnRpY2FsSW5kZW50LFxuICAgICAgICAgICAgICAgIGJvdHRvbTogdmVydGljYWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgbGVmdDogaG9yaXpvbnRhbEluZGVudCxcbiAgICAgICAgICAgICAgICByaWdodDogaG9yaXpvbnRhbEluZGVudFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFRvZ2dsZXMgdGhlIHdpbmRvd3MgaW4gdGhlIHNpZGUgcGFuZWxcbiAgICAgKiBAcGFyYW0gb2JqZWN0IHRoZSB3aW5kb3cgdGhhdCBzaG91bGQgYmUgc2hvd25cbiAgICAgKiBAcGFyYW0gc2VsZWN0b3IgdGhlIHNlbGVjdG9yIGZvciB0aGUgZWxlbWVudCBjb250YWluaW5nIHRoZSBwYW5lbFxuICAgICAqIEBwYXJhbSBvbk9wZW5Db21wbGV0ZSBmdW5jdGlvbiB0byBiZSBjYWxsZWQgd2hlbiB0aGUgcGFuZWwgaXMgb3BlbmVkXG4gICAgICogQHBhcmFtIG9uT3BlbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgaWYgdGhlIHdpbmRvdyBpcyBnb2luZyB0byBiZSBvcGVuZWRcbiAgICAgKiBAcGFyYW0gb25DbG9zZSBmdW5jdGlvbiB0byBiZSBjYWxsZWQgaWYgdGhlIHdpbmRvdyBpcyBnb2luZyB0byBiZSBjbG9zZWRcbiAgICAgKi9cbiAgICB2YXIgdG9nZ2xlID0gZnVuY3Rpb24ob2JqZWN0LCBzZWxlY3Rvciwgb25PcGVuQ29tcGxldGUsIG9uT3Blbiwgb25DbG9zZSkge1xuICAgICAgICBVSVV0aWwuYnV0dG9uQ2xpY2soYnV0dG9uc1tzZWxlY3Rvcl0sIFwiYWN0aXZlXCIpO1xuXG4gICAgICAgIGlmIChvYmplY3QuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICQoXCIjdG9hc3QtY29udGFpbmVyXCIpLmFuaW1hdGUoe1xuICAgICAgICAgICAgICAgICAgICByaWdodDogJzVweCdcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAkKHNlbGVjdG9yKS5oaWRlKFwic2xpZGVcIiwge1xuICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogXCJyaWdodFwiLFxuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGlmKHR5cGVvZiBvbkNsb3NlID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICAgICAgICBvbkNsb3NlKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGN1cnJlbnRseU9wZW4gPSBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gVW5kb2NrIHRoZSB0b29sYmFyIHdoZW4gdGhlIGNoYXQgaXMgc2hvd24gYW5kIGlmIHdlJ3JlIGluIGFcbiAgICAgICAgICAgIC8vIHZpZGVvIG1vZGUuXG4gICAgICAgICAgICBpZiAoVmlkZW9MYXlvdXQuaXNMYXJnZVZpZGVvVmlzaWJsZSgpKSB7XG4gICAgICAgICAgICAgICAgVG9vbGJhclRvZ2dsZXIuZG9ja1Rvb2xiYXIoZmFsc2UpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZihjdXJyZW50bHlPcGVuKSB7XG4gICAgICAgICAgICAgICAgdmFyIGN1cnJlbnQgPSAkKGN1cnJlbnRseU9wZW4pO1xuICAgICAgICAgICAgICAgIFVJVXRpbC5idXR0b25DbGljayhidXR0b25zW2N1cnJlbnRseU9wZW5dLCBcImFjdGl2ZVwiKTtcbiAgICAgICAgICAgICAgICBjdXJyZW50LmNzcygnei1pbmRleCcsIDQpO1xuICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNzcygnZGlzcGxheScsICdub25lJyk7XG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY3NzKCd6LWluZGV4JywgNSk7XG4gICAgICAgICAgICAgICAgfSwgNTAwKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgJChcIiN0b2FzdC1jb250YWluZXJcIikuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIHJpZ2h0OiAoUGFuZWxUb2dnbGVyLmdldFBhbmVsU2l6ZSgpWzBdICsgNSkgKyAncHgnXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgJChzZWxlY3Rvcikuc2hvdyhcInNsaWRlXCIsIHtcbiAgICAgICAgICAgICAgICBkaXJlY3Rpb246IFwicmlnaHRcIixcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMCxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZTogb25PcGVuQ29tcGxldGVcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaWYodHlwZW9mIG9uT3BlbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgb25PcGVuKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGN1cnJlbnRseU9wZW4gPSBzZWxlY3RvcjtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyAvIGNsb3NlcyB0aGUgY2hhdCBhcmVhLlxuICAgICAqL1xuICAgIG15LnRvZ2dsZUNoYXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGNoYXRDb21wbGV0ZUZ1bmN0aW9uID0gQ2hhdC5pc1Zpc2libGUoKSA/XG4gICAgICAgICAgICBmdW5jdGlvbigpIHt9IDogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgQ2hhdC5zY3JvbGxDaGF0VG9Cb3R0b20oKTtcbiAgICAgICAgICAgICQoJyNjaGF0c3BhY2UnKS50cmlnZ2VyKCdzaG93bicpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHJlc2l6ZVZpZGVvQXJlYShDaGF0LmlzVmlzaWJsZSgpLCBjaGF0Q29tcGxldGVGdW5jdGlvbik7XG5cbiAgICAgICAgdG9nZ2xlKENoYXQsXG4gICAgICAgICAgICAnI2NoYXRzcGFjZScsXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgLy8gUmVxdWVzdCB0aGUgZm9jdXMgaW4gdGhlIG5pY2tuYW1lIGZpZWxkIG9yIHRoZSBjaGF0IGlucHV0IGZpZWxkLlxuICAgICAgICAgICAgICAgIGlmICgkKCcjbmlja25hbWUnKS5jc3MoJ3Zpc2liaWxpdHknKSA9PT0gJ3Zpc2libGUnKSB7XG4gICAgICAgICAgICAgICAgICAgICQoJyNuaWNraW5wdXQnKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICQoJyN1c2VybXNnJykuZm9jdXMoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgIENoYXQucmVzaXplQ2hhdCxcbiAgICAgICAgICAgIG51bGwpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyAvIGNsb3NlcyB0aGUgY29udGFjdCBsaXN0IGFyZWEuXG4gICAgICovXG4gICAgbXkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBjb21wbGV0ZUZ1bmN0aW9uID0gQ29udGFjdExpc3QuaXNWaXNpYmxlKCkgP1xuICAgICAgICAgICAgZnVuY3Rpb24oKSB7fSA6IGZ1bmN0aW9uICgpIHsgJCgnI2NvbnRhY3RsaXN0JykudHJpZ2dlcignc2hvd24nKTt9O1xuICAgICAgICByZXNpemVWaWRlb0FyZWEoQ29udGFjdExpc3QuaXNWaXNpYmxlKCksIGNvbXBsZXRlRnVuY3Rpb24pO1xuXG4gICAgICAgIHRvZ2dsZShDb250YWN0TGlzdCxcbiAgICAgICAgICAgICcjY29udGFjdGxpc3QnLFxuICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIENvbnRhY3RMaXN0LnNldFZpc3VhbE5vdGlmaWNhdGlvbihmYWxzZSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgbnVsbCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIC8gY2xvc2VzIHRoZSBzZXR0aW5ncyBtZW51XG4gICAgICovXG4gICAgbXkudG9nZ2xlU2V0dGluZ3NNZW51ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJlc2l6ZVZpZGVvQXJlYShTZXR0aW5nc01lbnUuaXNWaXNpYmxlKCksIGZ1bmN0aW9uICgpe30pO1xuICAgICAgICB0b2dnbGUoU2V0dGluZ3NNZW51LFxuICAgICAgICAgICAgJyNzZXR0aW5nc21lbnUnLFxuICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIHZhciBzZXR0aW5ncyA9IFNldHRpbmdzLmdldFNldHRpbmdzKCk7XG4gICAgICAgICAgICAgICAgJCgnI3NldERpc3BsYXlOYW1lJykuZ2V0KDApLnZhbHVlID0gc2V0dGluZ3MuZGlzcGxheU5hbWU7XG4gICAgICAgICAgICAgICAgJCgnI3NldEVtYWlsJykuZ2V0KDApLnZhbHVlID0gc2V0dGluZ3MuZW1haWw7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgbnVsbCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHNpemUgb2YgdGhlIHNpZGUgcGFuZWwuXG4gICAgICovXG4gICAgbXkuZ2V0UGFuZWxTaXplID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgYXZhaWxhYmxlSGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuICAgICAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSB3aW5kb3cuaW5uZXJXaWR0aDtcblxuICAgICAgICB2YXIgcGFuZWxXaWR0aCA9IDIwMDtcbiAgICAgICAgaWYgKGF2YWlsYWJsZVdpZHRoICogMC4yIDwgMjAwKSB7XG4gICAgICAgICAgICBwYW5lbFdpZHRoID0gYXZhaWxhYmxlV2lkdGggKiAwLjI7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gW3BhbmVsV2lkdGgsIGF2YWlsYWJsZUhlaWdodF07XG4gICAgfTtcblxuICAgIG15LmlzVmlzaWJsZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gKENoYXQuaXNWaXNpYmxlKCkgfHwgQ29udGFjdExpc3QuaXNWaXNpYmxlKCkgfHwgU2V0dGluZ3NNZW51LmlzVmlzaWJsZSgpKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIG15O1xuXG59KFBhbmVsVG9nZ2xlciB8fCB7fSkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFBhbmVsVG9nZ2xlcjsiLCIvKiBnbG9iYWwgJCwgVXRpbCwgbmlja25hbWU6dHJ1ZSwgc2hvd1Rvb2xiYXIgKi9cbnZhciBSZXBsYWNlbWVudCA9IHJlcXVpcmUoXCIuL1JlcGxhY2VtZW50XCIpO1xudmFyIENvbW1hbmRzUHJvY2Vzc29yID0gcmVxdWlyZShcIi4vQ29tbWFuZHNcIik7XG52YXIgVG9vbGJhclRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vLi4vdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXJcIik7XG52YXIgc21pbGV5cyA9IHJlcXVpcmUoXCIuL3NtaWxleXMuanNvblwiKS5zbWlsZXlzO1xuXG52YXIgbm90aWZpY2F0aW9uSW50ZXJ2YWwgPSBmYWxzZTtcbnZhciB1bnJlYWRNZXNzYWdlcyA9IDA7XG5cblxuLyoqXG4gKiBTaG93cy9oaWRlcyBhIHZpc3VhbCBub3RpZmljYXRpb24sIGluZGljYXRpbmcgdGhhdCBhIG1lc3NhZ2UgaGFzIGFycml2ZWQuXG4gKi9cbmZ1bmN0aW9uIHNldFZpc3VhbE5vdGlmaWNhdGlvbihzaG93KSB7XG4gICAgdmFyIHVucmVhZE1zZ0VsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndW5yZWFkTWVzc2FnZXMnKTtcbiAgICB2YXIgdW5yZWFkTXNnQm90dG9tRWxlbWVudFxuICAgICAgICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdib3R0b21VbnJlYWRNZXNzYWdlcycpO1xuXG4gICAgdmFyIGdsb3dlciA9ICQoJyNjaGF0QnV0dG9uJyk7XG4gICAgdmFyIGJvdHRvbUdsb3dlciA9ICQoJyNjaGF0Qm90dG9tQnV0dG9uJyk7XG5cbiAgICBpZiAodW5yZWFkTWVzc2FnZXMpIHtcbiAgICAgICAgdW5yZWFkTXNnRWxlbWVudC5pbm5lckhUTUwgPSB1bnJlYWRNZXNzYWdlcy50b1N0cmluZygpO1xuICAgICAgICB1bnJlYWRNc2dCb3R0b21FbGVtZW50LmlubmVySFRNTCA9IHVucmVhZE1lc3NhZ2VzLnRvU3RyaW5nKCk7XG5cbiAgICAgICAgVG9vbGJhclRvZ2dsZXIuZG9ja1Rvb2xiYXIodHJ1ZSk7XG5cbiAgICAgICAgdmFyIGNoYXRCdXR0b25FbGVtZW50XG4gICAgICAgICAgICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjaGF0QnV0dG9uJykucGFyZW50Tm9kZTtcbiAgICAgICAgdmFyIGxlZnRJbmRlbnQgPSAoVXRpbC5nZXRUZXh0V2lkdGgoY2hhdEJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFV0aWwuZ2V0VGV4dFdpZHRoKHVucmVhZE1zZ0VsZW1lbnQpKSAvIDI7XG4gICAgICAgIHZhciB0b3BJbmRlbnQgPSAoVXRpbC5nZXRUZXh0SGVpZ2h0KGNoYXRCdXR0b25FbGVtZW50KSAtXG4gICAgICAgICAgICBVdGlsLmdldFRleHRIZWlnaHQodW5yZWFkTXNnRWxlbWVudCkpIC8gMiAtIDM7XG5cbiAgICAgICAgdW5yZWFkTXNnRWxlbWVudC5zZXRBdHRyaWJ1dGUoXG4gICAgICAgICAgICAnc3R5bGUnLFxuICAgICAgICAgICAgICAgICd0b3A6JyArIHRvcEluZGVudCArXG4gICAgICAgICAgICAgICAgJzsgbGVmdDonICsgbGVmdEluZGVudCArICc7Jyk7XG5cbiAgICAgICAgdmFyIGNoYXRCb3R0b21CdXR0b25FbGVtZW50XG4gICAgICAgICAgICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjaGF0Qm90dG9tQnV0dG9uJykucGFyZW50Tm9kZTtcbiAgICAgICAgdmFyIGJvdHRvbUxlZnRJbmRlbnQgPSAoVXRpbC5nZXRUZXh0V2lkdGgoY2hhdEJvdHRvbUJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFV0aWwuZ2V0VGV4dFdpZHRoKHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQpKSAvIDI7XG4gICAgICAgIHZhciBib3R0b21Ub3BJbmRlbnQgPSAoVXRpbC5nZXRUZXh0SGVpZ2h0KGNoYXRCb3R0b21CdXR0b25FbGVtZW50KSAtXG4gICAgICAgICAgICBVdGlsLmdldFRleHRIZWlnaHQodW5yZWFkTXNnQm90dG9tRWxlbWVudCkpIC8gMiAtIDI7XG5cbiAgICAgICAgdW5yZWFkTXNnQm90dG9tRWxlbWVudC5zZXRBdHRyaWJ1dGUoXG4gICAgICAgICAgICAnc3R5bGUnLFxuICAgICAgICAgICAgICAgICd0b3A6JyArIGJvdHRvbVRvcEluZGVudCArXG4gICAgICAgICAgICAgICAgJzsgbGVmdDonICsgYm90dG9tTGVmdEluZGVudCArICc7Jyk7XG5cblxuICAgICAgICBpZiAoIWdsb3dlci5oYXNDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpKSB7XG4gICAgICAgICAgICBnbG93ZXIucmVtb3ZlQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgICAgICAgICAgZ2xvd2VyLmFkZENsYXNzKCdpY29uLWNoYXQtc2ltcGxlJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHVucmVhZE1zZ0VsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpO1xuICAgICAgICBnbG93ZXIuYWRkQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgIH1cblxuICAgIGlmIChzaG93ICYmICFub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBnbG93ZXIudG9nZ2xlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgYm90dG9tR2xvd2VyLnRvZ2dsZUNsYXNzKCdhY3RpdmUgZ2xvd2luZycpO1xuICAgICAgICB9LCA4MDApO1xuICAgIH1cbiAgICBlbHNlIGlmICghc2hvdyAmJiBub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChub3RpZmljYXRpb25JbnRlcnZhbCk7XG4gICAgICAgIG5vdGlmaWNhdGlvbkludGVydmFsID0gZmFsc2U7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgIGJvdHRvbUdsb3dlci5yZW1vdmVDbGFzcygnZ2xvd2luZycpO1xuICAgICAgICBib3R0b21HbG93ZXIuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgIH1cbn1cblxuXG4vKipcbiAqIFJldHVybnMgdGhlIGN1cnJlbnQgdGltZSBpbiB0aGUgZm9ybWF0IGl0IGlzIHNob3duIHRvIHRoZSB1c2VyXG4gKiBAcmV0dXJucyB7c3RyaW5nfVxuICovXG5mdW5jdGlvbiBnZXRDdXJyZW50VGltZSgpIHtcbiAgICB2YXIgbm93ICAgICA9IG5ldyBEYXRlKCk7XG4gICAgdmFyIGhvdXIgICAgPSBub3cuZ2V0SG91cnMoKTtcbiAgICB2YXIgbWludXRlICA9IG5vdy5nZXRNaW51dGVzKCk7XG4gICAgdmFyIHNlY29uZCAgPSBub3cuZ2V0U2Vjb25kcygpO1xuICAgIGlmKGhvdXIudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgaG91ciA9ICcwJytob3VyO1xuICAgIH1cbiAgICBpZihtaW51dGUudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgbWludXRlID0gJzAnK21pbnV0ZTtcbiAgICB9XG4gICAgaWYoc2Vjb25kLnRvU3RyaW5nKCkubGVuZ3RoID09PSAxKSB7XG4gICAgICAgIHNlY29uZCA9ICcwJytzZWNvbmQ7XG4gICAgfVxuICAgIHJldHVybiBob3VyKyc6JyttaW51dGUrJzonK3NlY29uZDtcbn1cblxuZnVuY3Rpb24gdG9nZ2xlU21pbGV5cygpXG57XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c0NvbnRhaW5lcicpO1xuICAgIGlmKCFzbWlsZXlzLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgIHNtaWxleXMuc2hvdyhcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHNtaWxleXMuaGlkZShcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH1cbiAgICAkKCcjdXNlcm1zZycpLmZvY3VzKCk7XG59XG5cbmZ1bmN0aW9uIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBudW1iZXIpIHtcbiAgICBzbWlsZXkub25jbGljayA9IGZ1bmN0aW9uIGFkZFNtaWxleVRvTWVzc2FnZSgpIHtcbiAgICAgICAgdmFyIHVzZXJtc2cgPSAkKCcjdXNlcm1zZycpO1xuICAgICAgICB2YXIgbWVzc2FnZSA9IHVzZXJtc2cudmFsKCk7XG4gICAgICAgIG1lc3NhZ2UgKz0gc21pbGV5c1snc21pbGV5JyArIG51bWJlcl07XG4gICAgICAgIHVzZXJtc2cudmFsKG1lc3NhZ2UpO1xuICAgICAgICB1c2VybXNnLmdldCgwKS5zZXRTZWxlY3Rpb25SYW5nZShtZXNzYWdlLmxlbmd0aCwgbWVzc2FnZS5sZW5ndGgpO1xuICAgICAgICB0b2dnbGVTbWlsZXlzKCk7XG4gICAgICAgIHVzZXJtc2cuZm9jdXMoKTtcbiAgICB9O1xufVxuXG4vKipcbiAqIEFkZHMgdGhlIHNtaWxleXMgY29udGFpbmVyIHRvIHRoZSBjaGF0XG4gKi9cbmZ1bmN0aW9uIGFkZFNtaWxleXMoKSB7XG4gICAgdmFyIHNtaWxleXNDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBzbWlsZXlzQ29udGFpbmVyLmlkID0gJ3NtaWxleXNDb250YWluZXInO1xuICAgIGZvcih2YXIgaSA9IDE7IGkgPD0gMjE7IGkrKykge1xuICAgICAgICB2YXIgc21pbGV5Q29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNtaWxleUNvbnRhaW5lci5pZCA9ICdzbWlsZXknICsgaTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmNsYXNzTmFtZSA9ICdzbWlsZXlDb250YWluZXInO1xuICAgICAgICB2YXIgc21pbGV5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gICAgICAgIHNtaWxleS5zcmMgPSAnaW1hZ2VzL3NtaWxleXMvc21pbGV5JyArIGkgKyAnLnN2Zyc7XG4gICAgICAgIHNtaWxleS5jbGFzc05hbWUgPSAgJ3NtaWxleSc7XG4gICAgICAgIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBpKTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmFwcGVuZENoaWxkKHNtaWxleSk7XG4gICAgICAgIHNtaWxleXNDb250YWluZXIuYXBwZW5kQ2hpbGQoc21pbGV5Q29udGFpbmVyKTtcbiAgICB9XG5cbiAgICAkKFwiI2NoYXRzcGFjZVwiKS5hcHBlbmQoc21pbGV5c0NvbnRhaW5lcik7XG59XG5cbi8qKlxuICogUmVzaXplcyB0aGUgY2hhdCBjb252ZXJzYXRpb24uXG4gKi9cbmZ1bmN0aW9uIHJlc2l6ZUNoYXRDb252ZXJzYXRpb24oKSB7XG4gICAgdmFyIG1zZ2FyZWFIZWlnaHQgPSAkKCcjdXNlcm1zZycpLm91dGVySGVpZ2h0KCk7XG4gICAgdmFyIGNoYXRzcGFjZSA9ICQoJyNjaGF0c3BhY2UnKTtcbiAgICB2YXIgd2lkdGggPSBjaGF0c3BhY2Uud2lkdGgoKTtcbiAgICB2YXIgY2hhdCA9ICQoJyNjaGF0Y29udmVyc2F0aW9uJyk7XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c2FyZWEnKTtcblxuICAgIHNtaWxleXMuaGVpZ2h0KG1zZ2FyZWFIZWlnaHQpO1xuICAgICQoXCIjc21pbGV5c1wiKS5jc3MoJ2JvdHRvbScsIChtc2dhcmVhSGVpZ2h0IC0gMjYpIC8gMik7XG4gICAgJCgnI3NtaWxleXNDb250YWluZXInKS5jc3MoJ2JvdHRvbScsIG1zZ2FyZWFIZWlnaHQpO1xuICAgIGNoYXQud2lkdGgod2lkdGggLSAxMCk7XG4gICAgY2hhdC5oZWlnaHQod2luZG93LmlubmVySGVpZ2h0IC0gMTUgLSBtc2dhcmVhSGVpZ2h0KTtcbn1cblxuLyoqXG4gKiBDaGF0IHJlbGF0ZWQgdXNlciBpbnRlcmZhY2UuXG4gKi9cbnZhciBDaGF0ID0gKGZ1bmN0aW9uIChteSkge1xuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemVzIGNoYXQgcmVsYXRlZCBpbnRlcmZhY2UuXG4gICAgICovXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHN0b3JlZERpc3BsYXlOYW1lID0gd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZTtcbiAgICAgICAgaWYgKHN0b3JlZERpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICBuaWNrbmFtZSA9IHN0b3JlZERpc3BsYXlOYW1lO1xuXG4gICAgICAgICAgICBDaGF0LnNldENoYXRDb252ZXJzYXRpb25Nb2RlKHRydWUpO1xuICAgICAgICB9XG5cbiAgICAgICAgJCgnI25pY2tpbnB1dCcpLmtleWRvd24oZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQua2V5Q29kZSA9PT0gMTMpIHtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIHZhciB2YWwgPSBVdGlsLmVzY2FwZUh0bWwodGhpcy52YWx1ZSk7XG4gICAgICAgICAgICAgICAgdGhpcy52YWx1ZSA9ICcnO1xuICAgICAgICAgICAgICAgIGlmICghbmlja25hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgbmlja25hbWUgPSB2YWw7XG4gICAgICAgICAgICAgICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2UuZGlzcGxheW5hbWUgPSBuaWNrbmFtZTtcblxuICAgICAgICAgICAgICAgICAgICB4bXBwLmFkZFRvUHJlc2VuY2UoXCJkaXNwbGF5TmFtZVwiLCBuaWNrbmFtZSk7XG5cbiAgICAgICAgICAgICAgICAgICAgQ2hhdC5zZXRDaGF0Q29udmVyc2F0aW9uTW9kZSh0cnVlKTtcblxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICAkKCcjdXNlcm1zZycpLmtleWRvd24oZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQua2V5Q29kZSA9PT0gMTMpIHtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIHZhciB2YWx1ZSA9IHRoaXMudmFsdWU7XG4gICAgICAgICAgICAgICAgJCgnI3VzZXJtc2cnKS52YWwoJycpLnRyaWdnZXIoJ2F1dG9zaXplLnJlc2l6ZScpO1xuICAgICAgICAgICAgICAgIHRoaXMuZm9jdXMoKTtcbiAgICAgICAgICAgICAgICB2YXIgY29tbWFuZCA9IG5ldyBDb21tYW5kc1Byb2Nlc3Nvcih2YWx1ZSk7XG4gICAgICAgICAgICAgICAgaWYoY29tbWFuZC5pc0NvbW1hbmQoKSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGNvbW1hbmQucHJvY2Vzc0NvbW1hbmQoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG1lc3NhZ2UgPSBVdGlsLmVzY2FwZUh0bWwodmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICB4bXBwLnNlbmRDaGF0TWVzc2FnZShtZXNzYWdlLCBuaWNrbmFtZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICB2YXIgb25UZXh0QXJlYVJlc2l6ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJlc2l6ZUNoYXRDb252ZXJzYXRpb24oKTtcbiAgICAgICAgICAgIENoYXQuc2Nyb2xsQ2hhdFRvQm90dG9tKCk7XG4gICAgICAgIH07XG4gICAgICAgICQoJyN1c2VybXNnJykuYXV0b3NpemUoe2NhbGxiYWNrOiBvblRleHRBcmVhUmVzaXplfSk7XG5cbiAgICAgICAgJChcIiNjaGF0c3BhY2VcIikuYmluZChcInNob3duXCIsXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdW5yZWFkTWVzc2FnZXMgPSAwO1xuICAgICAgICAgICAgICAgIHNldFZpc3VhbE5vdGlmaWNhdGlvbihmYWxzZSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICBhZGRTbWlsZXlzKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEFwcGVuZHMgdGhlIGdpdmVuIG1lc3NhZ2UgdG8gdGhlIGNoYXQgY29udmVyc2F0aW9uLlxuICAgICAqL1xuICAgIG15LnVwZGF0ZUNoYXRDb252ZXJzYXRpb24gPSBmdW5jdGlvbiAoZnJvbSwgZGlzcGxheU5hbWUsIG1lc3NhZ2UpIHtcbiAgICAgICAgdmFyIGRpdkNsYXNzTmFtZSA9ICcnO1xuXG4gICAgICAgIGlmICh4bXBwLm15SmlkKCkgPT09IGZyb20pIHtcbiAgICAgICAgICAgIGRpdkNsYXNzTmFtZSA9IFwibG9jYWx1c2VyXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBkaXZDbGFzc05hbWUgPSBcInJlbW90ZXVzZXJcIjtcblxuICAgICAgICAgICAgaWYgKCFDaGF0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgICAgICAgICAgdW5yZWFkTWVzc2FnZXMrKztcbiAgICAgICAgICAgICAgICBVdGlsLnBsYXlTb3VuZE5vdGlmaWNhdGlvbignY2hhdE5vdGlmaWNhdGlvbicpO1xuICAgICAgICAgICAgICAgIHNldFZpc3VhbE5vdGlmaWNhdGlvbih0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHJlcGxhY2UgbGlua3MgYW5kIHNtaWxleXNcbiAgICAgICAgLy8gU3Ryb3BoZSBhbHJlYWR5IGVzY2FwZXMgc3BlY2lhbCBzeW1ib2xzIG9uIHNlbmRpbmcsXG4gICAgICAgIC8vIHNvIHdlIGVzY2FwZSBoZXJlIG9ubHkgdGFncyB0byBhdm9pZCBkb3VibGUgJmFtcDtcbiAgICAgICAgdmFyIGVzY01lc3NhZ2UgPSBtZXNzYWdlLnJlcGxhY2UoLzwvZywgJyZsdDsnKS5cbiAgICAgICAgICAgIHJlcGxhY2UoLz4vZywgJyZndDsnKS5yZXBsYWNlKC9cXG4vZywgJzxici8+Jyk7XG4gICAgICAgIHZhciBlc2NEaXNwbGF5TmFtZSA9IFV0aWwuZXNjYXBlSHRtbChkaXNwbGF5TmFtZSk7XG4gICAgICAgIG1lc3NhZ2UgPSBSZXBsYWNlbWVudC5wcm9jZXNzUmVwbGFjZW1lbnRzKGVzY01lc3NhZ2UpO1xuXG4gICAgICAgIHZhciBtZXNzYWdlQ29udGFpbmVyID1cbiAgICAgICAgICAgICc8ZGl2IGNsYXNzPVwiY2hhdG1lc3NhZ2VcIj4nK1xuICAgICAgICAgICAgICAgICc8aW1nIHNyYz1cIi4uL2ltYWdlcy9jaGF0QXJyb3cuc3ZnXCIgY2xhc3M9XCJjaGF0QXJyb3dcIj4nICtcbiAgICAgICAgICAgICAgICAnPGRpdiBjbGFzcz1cInVzZXJuYW1lICcgKyBkaXZDbGFzc05hbWUgKydcIj4nICsgZXNjRGlzcGxheU5hbWUgK1xuICAgICAgICAgICAgICAgICc8L2Rpdj4nICsgJzxkaXYgY2xhc3M9XCJ0aW1lc3RhbXBcIj4nICsgZ2V0Q3VycmVudFRpbWUoKSArXG4gICAgICAgICAgICAgICAgJzwvZGl2PicgKyAnPGRpdiBjbGFzcz1cInVzZXJtZXNzYWdlXCI+JyArIG1lc3NhZ2UgKyAnPC9kaXY+JyArXG4gICAgICAgICAgICAnPC9kaXY+JztcblxuICAgICAgICAkKCcjY2hhdGNvbnZlcnNhdGlvbicpLmFwcGVuZChtZXNzYWdlQ29udGFpbmVyKTtcbiAgICAgICAgJCgnI2NoYXRjb252ZXJzYXRpb24nKS5hbmltYXRlKFxuICAgICAgICAgICAgICAgIHsgc2Nyb2xsVG9wOiAkKCcjY2hhdGNvbnZlcnNhdGlvbicpWzBdLnNjcm9sbEhlaWdodH0sIDEwMDApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBcHBlbmRzIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNvbnZlcnNhdGlvblxuICAgICAqIEBwYXJhbSBlcnJvck1lc3NhZ2UgdGhlIHJlY2VpdmVkIGVycm9yIG1lc3NhZ2UuXG4gICAgICogQHBhcmFtIG9yaWdpbmFsVGV4dCB0aGUgb3JpZ2luYWwgbWVzc2FnZS5cbiAgICAgKi9cbiAgICBteS5jaGF0QWRkRXJyb3IgPSBmdW5jdGlvbihlcnJvck1lc3NhZ2UsIG9yaWdpbmFsVGV4dClcbiAgICB7XG4gICAgICAgIGVycm9yTWVzc2FnZSA9IFV0aWwuZXNjYXBlSHRtbChlcnJvck1lc3NhZ2UpO1xuICAgICAgICBvcmlnaW5hbFRleHQgPSBVdGlsLmVzY2FwZUh0bWwob3JpZ2luYWxUZXh0KTtcblxuICAgICAgICAkKCcjY2hhdGNvbnZlcnNhdGlvbicpLmFwcGVuZChcbiAgICAgICAgICAgICc8ZGl2IGNsYXNzPVwiZXJyb3JNZXNzYWdlXCI+PGI+RXJyb3I6IDwvYj4nICsgJ1lvdXIgbWVzc2FnZScgK1xuICAgICAgICAgICAgKG9yaWdpbmFsVGV4dD8gKCcgXFxcIicrIG9yaWdpbmFsVGV4dCArICdcXFwiJykgOiBcIlwiKSArXG4gICAgICAgICAgICAnIHdhcyBub3Qgc2VudC4nICtcbiAgICAgICAgICAgIChlcnJvck1lc3NhZ2U/ICgnIFJlYXNvbjogJyArIGVycm9yTWVzc2FnZSkgOiAnJykgKyAgJzwvZGl2PicpO1xuICAgICAgICAkKCcjY2hhdGNvbnZlcnNhdGlvbicpLmFuaW1hdGUoXG4gICAgICAgICAgICB7IHNjcm9sbFRvcDogJCgnI2NoYXRjb252ZXJzYXRpb24nKVswXS5zY3JvbGxIZWlnaHR9LCAxMDAwKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgc3ViamVjdCB0byB0aGUgVUlcbiAgICAgKiBAcGFyYW0gc3ViamVjdCB0aGUgc3ViamVjdFxuICAgICAqL1xuICAgIG15LmNoYXRTZXRTdWJqZWN0ID0gZnVuY3Rpb24oc3ViamVjdClcbiAgICB7XG4gICAgICAgIGlmKHN1YmplY3QpXG4gICAgICAgICAgICBzdWJqZWN0ID0gc3ViamVjdC50cmltKCk7XG4gICAgICAgICQoJyNzdWJqZWN0JykuaHRtbChSZXBsYWNlbWVudC5saW5raWZ5KFV0aWwuZXNjYXBlSHRtbChzdWJqZWN0KSkpO1xuICAgICAgICBpZihzdWJqZWN0ID09PSBcIlwiKVxuICAgICAgICB7XG4gICAgICAgICAgICAkKFwiI3N1YmplY3RcIikuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgJChcIiNzdWJqZWN0XCIpLmNzcyh7ZGlzcGxheTogXCJibG9ja1wifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG5cblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGNoYXQgY29udmVyc2F0aW9uIG1vZGUuXG4gICAgICovXG4gICAgbXkuc2V0Q2hhdENvbnZlcnNhdGlvbk1vZGUgPSBmdW5jdGlvbiAoaXNDb252ZXJzYXRpb25Nb2RlKSB7XG4gICAgICAgIGlmIChpc0NvbnZlcnNhdGlvbk1vZGUpIHtcbiAgICAgICAgICAgICQoJyNuaWNrbmFtZScpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgICQoJyN1c2VybXNnJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgICQoJyNzbWlsZXlzYXJlYScpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcjdXNlcm1zZycpLmZvY3VzKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVzaXplcyB0aGUgY2hhdCBhcmVhLlxuICAgICAqL1xuICAgIG15LnJlc2l6ZUNoYXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBjaGF0U2l6ZSA9IHJlcXVpcmUoXCIuLi9TaWRlUGFuZWxUb2dnbGVyXCIpLmdldFBhbmVsU2l6ZSgpO1xuXG4gICAgICAgICQoJyNjaGF0c3BhY2UnKS53aWR0aChjaGF0U2l6ZVswXSk7XG4gICAgICAgICQoJyNjaGF0c3BhY2UnKS5oZWlnaHQoY2hhdFNpemVbMV0pO1xuXG4gICAgICAgIHJlc2l6ZUNoYXRDb252ZXJzYXRpb24oKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSW5kaWNhdGVzIGlmIHRoZSBjaGF0IGlzIGN1cnJlbnRseSB2aXNpYmxlLlxuICAgICAqL1xuICAgIG15LmlzVmlzaWJsZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuICQoJyNjaGF0c3BhY2UnKS5pcyhcIjp2aXNpYmxlXCIpO1xuICAgIH07XG4gICAgLyoqXG4gICAgICogU2hvd3MgYW5kIGhpZGVzIHRoZSB3aW5kb3cgd2l0aCB0aGUgc21pbGV5c1xuICAgICAqL1xuICAgIG15LnRvZ2dsZVNtaWxleXMgPSB0b2dnbGVTbWlsZXlzO1xuXG4gICAgLyoqXG4gICAgICogU2Nyb2xscyBjaGF0IHRvIHRoZSBib3R0b20uXG4gICAgICovXG4gICAgbXkuc2Nyb2xsQ2hhdFRvQm90dG9tID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgJCgnI2NoYXRjb252ZXJzYXRpb24nKS5zY3JvbGxUb3AoXG4gICAgICAgICAgICAgICAgJCgnI2NoYXRjb252ZXJzYXRpb24nKVswXS5zY3JvbGxIZWlnaHQpO1xuICAgICAgICB9LCA1KTtcbiAgICB9O1xuXG5cbiAgICByZXR1cm4gbXk7XG59KENoYXQgfHwge30pKTtcbm1vZHVsZS5leHBvcnRzID0gQ2hhdDsiLCIvKipcbiAqIExpc3Qgd2l0aCBzdXBwb3J0ZWQgY29tbWFuZHMuIFRoZSBrZXlzIGFyZSB0aGUgbmFtZXMgb2YgdGhlIGNvbW1hbmRzIGFuZFxuICogdGhlIHZhbHVlIGlzIHRoZSBmdW5jdGlvbiB0aGF0IHByb2Nlc3NlcyB0aGUgbWVzc2FnZS5cbiAqIEB0eXBlIHt7U3RyaW5nOiBmdW5jdGlvbn19XG4gKi9cbnZhciBjb21tYW5kcyA9IHtcbiAgICBcInRvcGljXCIgOiBwcm9jZXNzVG9waWNcbn07XG5cbi8qKlxuICogRXh0cmFjdHMgdGhlIGNvbW1hbmQgZnJvbSB0aGUgbWVzc2FnZS5cbiAqIEBwYXJhbSBtZXNzYWdlIHRoZSByZWNlaXZlZCBtZXNzYWdlXG4gKiBAcmV0dXJucyB7c3RyaW5nfSB0aGUgY29tbWFuZFxuICovXG5mdW5jdGlvbiBnZXRDb21tYW5kKG1lc3NhZ2UpXG57XG4gICAgaWYobWVzc2FnZSlcbiAgICB7XG4gICAgICAgIGZvcih2YXIgY29tbWFuZCBpbiBjb21tYW5kcylcbiAgICAgICAge1xuICAgICAgICAgICAgaWYobWVzc2FnZS5pbmRleE9mKFwiL1wiICsgY29tbWFuZCkgPT0gMClcbiAgICAgICAgICAgICAgICByZXR1cm4gY29tbWFuZDtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gXCJcIjtcbn07XG5cbi8qKlxuICogUHJvY2Vzc2VzIHRoZSBkYXRhIGZvciB0b3BpYyBjb21tYW5kLlxuICogQHBhcmFtIGNvbW1hbmRBcmd1bWVudHMgdGhlIGFyZ3VtZW50cyBvZiB0aGUgdG9waWMgY29tbWFuZC5cbiAqL1xuZnVuY3Rpb24gcHJvY2Vzc1RvcGljKGNvbW1hbmRBcmd1bWVudHMpXG57XG4gICAgdmFyIHRvcGljID0gVXRpbC5lc2NhcGVIdG1sKGNvbW1hbmRBcmd1bWVudHMpO1xuICAgIHhtcHAuc2V0U3ViamVjdCh0b3BpYyk7XG59XG5cbi8qKlxuICogQ29uc3RydWN0cyBuZXcgQ29tbWFuZFByb2NjZXNzb3IgaW5zdGFuY2UgZnJvbSBhIG1lc3NhZ2UgdGhhdFxuICogaGFuZGxlcyBjb21tYW5kcyByZWNlaXZlZCB2aWEgY2hhdCBtZXNzYWdlcy5cbiAqIEBwYXJhbSBtZXNzYWdlIHRoZSBtZXNzYWdlXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gQ29tbWFuZHNQcm9jZXNzb3IobWVzc2FnZSlcbntcblxuXG4gICAgdmFyIGNvbW1hbmQgPSBnZXRDb21tYW5kKG1lc3NhZ2UpO1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbmFtZSBvZiB0aGUgY29tbWFuZC5cbiAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSB0aGUgY29tbWFuZFxuICAgICAqL1xuICAgIHRoaXMuZ2V0Q29tbWFuZCA9IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIHJldHVybiBjb21tYW5kO1xuICAgIH07XG5cblxuICAgIHZhciBtZXNzYWdlQXJndW1lbnQgPSBtZXNzYWdlLnN1YnN0cihjb21tYW5kLmxlbmd0aCArIDIpO1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgYXJndW1lbnRzIG9mIHRoZSBjb21tYW5kLlxuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICovXG4gICAgdGhpcy5nZXRBcmd1bWVudCA9IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIHJldHVybiBtZXNzYWdlQXJndW1lbnQ7XG4gICAgfTtcbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciB0aGlzIGluc3RhbmNlIGlzIHZhbGlkIGNvbW1hbmQgb3Igbm90LlxuICogQHJldHVybnMge2Jvb2xlYW59XG4gKi9cbkNvbW1hbmRzUHJvY2Vzc29yLnByb3RvdHlwZS5pc0NvbW1hbmQgPSBmdW5jdGlvbigpXG57XG4gICAgaWYodGhpcy5nZXRDb21tYW5kKCkpXG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIHJldHVybiBmYWxzZTtcbn07XG5cbi8qKlxuICogUHJvY2Vzc2VzIHRoZSBjb21tYW5kLlxuICovXG5Db21tYW5kc1Byb2Nlc3Nvci5wcm90b3R5cGUucHJvY2Vzc0NvbW1hbmQgPSBmdW5jdGlvbigpXG57XG4gICAgaWYoIXRoaXMuaXNDb21tYW5kKCkpXG4gICAgICAgIHJldHVybjtcblxuICAgIGNvbW1hbmRzW3RoaXMuZ2V0Q29tbWFuZCgpXSh0aGlzLmdldEFyZ3VtZW50KCkpO1xuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IENvbW1hbmRzUHJvY2Vzc29yOyIsInZhciBTbWlsZXlzID0gcmVxdWlyZShcIi4vc21pbGV5cy5qc29uXCIpO1xuLyoqXG4gKiBQcm9jZXNzZXMgbGlua3MgYW5kIHNtaWxleXMgaW4gXCJib2R5XCJcbiAqL1xuZnVuY3Rpb24gcHJvY2Vzc1JlcGxhY2VtZW50cyhib2R5KVxue1xuICAgIC8vbWFrZSBsaW5rcyBjbGlja2FibGVcbiAgICBib2R5ID0gbGlua2lmeShib2R5KTtcblxuICAgIC8vYWRkIHNtaWxleXNcbiAgICBib2R5ID0gc21pbGlmeShib2R5KTtcblxuICAgIHJldHVybiBib2R5O1xufVxuXG4vKipcbiAqIEZpbmRzIGFuZCByZXBsYWNlcyBhbGwgbGlua3MgaW4gdGhlIGxpbmtzIGluIFwiYm9keVwiXG4gKiB3aXRoIHRoZWlyIDxhIGhyZWY9XCJcIj48L2E+XG4gKi9cbmZ1bmN0aW9uIGxpbmtpZnkoaW5wdXRUZXh0KVxue1xuICAgIHZhciByZXBsYWNlZFRleHQsIHJlcGxhY2VQYXR0ZXJuMSwgcmVwbGFjZVBhdHRlcm4yLCByZXBsYWNlUGF0dGVybjM7XG5cbiAgICAvL1VSTHMgc3RhcnRpbmcgd2l0aCBodHRwOi8vLCBodHRwczovLywgb3IgZnRwOi8vXG4gICAgcmVwbGFjZVBhdHRlcm4xID0gLyhcXGIoaHR0cHM/fGZ0cCk6XFwvXFwvWy1BLVowLTkrJkAjXFwvJT89fl98ITosLjtdKlstQS1aMC05KyZAI1xcLyU9fl98XSkvZ2ltO1xuICAgIHJlcGxhY2VkVGV4dCA9IGlucHV0VGV4dC5yZXBsYWNlKHJlcGxhY2VQYXR0ZXJuMSwgJzxhIGhyZWY9XCIkMVwiIHRhcmdldD1cIl9ibGFua1wiPiQxPC9hPicpO1xuXG4gICAgLy9VUkxzIHN0YXJ0aW5nIHdpdGggXCJ3d3cuXCIgKHdpdGhvdXQgLy8gYmVmb3JlIGl0LCBvciBpdCdkIHJlLWxpbmsgdGhlIG9uZXMgZG9uZSBhYm92ZSkuXG4gICAgcmVwbGFjZVBhdHRlcm4yID0gLyhefFteXFwvXSkod3d3XFwuW1xcU10rKFxcYnwkKSkvZ2ltO1xuICAgIHJlcGxhY2VkVGV4dCA9IHJlcGxhY2VkVGV4dC5yZXBsYWNlKHJlcGxhY2VQYXR0ZXJuMiwgJyQxPGEgaHJlZj1cImh0dHA6Ly8kMlwiIHRhcmdldD1cIl9ibGFua1wiPiQyPC9hPicpO1xuXG4gICAgLy9DaGFuZ2UgZW1haWwgYWRkcmVzc2VzIHRvIG1haWx0bzo6IGxpbmtzLlxuICAgIHJlcGxhY2VQYXR0ZXJuMyA9IC8oKFthLXpBLVowLTlcXC1cXF9cXC5dKStAW2EtekEtWlxcX10rPyhcXC5bYS16QS1aXXsyLDZ9KSspL2dpbTtcbiAgICByZXBsYWNlZFRleHQgPSByZXBsYWNlZFRleHQucmVwbGFjZShyZXBsYWNlUGF0dGVybjMsICc8YSBocmVmPVwibWFpbHRvOiQxXCI+JDE8L2E+Jyk7XG5cbiAgICByZXR1cm4gcmVwbGFjZWRUZXh0O1xufVxuXG4vKipcbiAqIFJlcGxhY2VzIGNvbW1vbiBzbWlsZXkgc3RyaW5ncyB3aXRoIGltYWdlc1xuICovXG5mdW5jdGlvbiBzbWlsaWZ5KGJvZHkpXG57XG4gICAgaWYoIWJvZHkpIHtcbiAgICAgICAgcmV0dXJuIGJvZHk7XG4gICAgfVxuXG4gICAgdmFyIHJlZ2V4cyA9IFNtaWxleXNbXCJyZWdleHNcIl07XG4gICAgZm9yKHZhciBzbWlsZXkgaW4gcmVnZXhzKSB7XG4gICAgICAgIGlmKHJlZ2V4cy5oYXNPd25Qcm9wZXJ0eShzbWlsZXkpKSB7XG4gICAgICAgICAgICBib2R5ID0gYm9keS5yZXBsYWNlKHJlZ2V4c1tzbWlsZXldLFxuICAgICAgICAgICAgICAgICAgICAnPGltZyBjbGFzcz1cInNtaWxleVwiIHNyYz1cImltYWdlcy9zbWlsZXlzLycgKyBzbWlsZXkgKyAnLnN2Z1wiPicpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGJvZHk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIHByb2Nlc3NSZXBsYWNlbWVudHM6IHByb2Nlc3NSZXBsYWNlbWVudHMsXG4gICAgbGlua2lmeTogbGlua2lmeVxufTtcbiIsIm1vZHVsZS5leHBvcnRzPXtcbiAgICBcInNtaWxleXNcIjoge1xuICAgICAgICBcInNtaWxleTFcIjogXCI6KVwiLFxuICAgICAgICBcInNtaWxleTJcIjogXCI6KFwiLFxuICAgICAgICBcInNtaWxleTNcIjogXCI6RFwiLFxuICAgICAgICBcInNtaWxleTRcIjogXCIoeSlcIixcbiAgICAgICAgXCJzbWlsZXk1XCI6IFwiIDpQXCIsXG4gICAgICAgIFwic21pbGV5NlwiOiBcIih3YXZlKVwiLFxuICAgICAgICBcInNtaWxleTdcIjogXCIoYmx1c2gpXCIsXG4gICAgICAgIFwic21pbGV5OFwiOiBcIihjaHVja2xlKVwiLFxuICAgICAgICBcInNtaWxleTlcIjogXCIoc2hvY2tlZClcIixcbiAgICAgICAgXCJzbWlsZXkxMFwiOiBcIjoqXCIsXG4gICAgICAgIFwic21pbGV5MTFcIjogXCIobilcIixcbiAgICAgICAgXCJzbWlsZXkxMlwiOiBcIihzZWFyY2gpXCIsXG4gICAgICAgIFwic21pbGV5MTNcIjogXCIgPDNcIixcbiAgICAgICAgXCJzbWlsZXkxNFwiOiBcIihvb3BzKVwiLFxuICAgICAgICBcInNtaWxleTE1XCI6IFwiKGFuZ3J5KVwiLFxuICAgICAgICBcInNtaWxleTE2XCI6IFwiKGFuZ2VsKVwiLFxuICAgICAgICBcInNtaWxleTE3XCI6IFwiKHNpY2spXCIsXG4gICAgICAgIFwic21pbGV5MThcIjogXCI7KFwiLFxuICAgICAgICBcInNtaWxleTE5XCI6IFwiKGJvbWIpXCIsXG4gICAgICAgIFwic21pbGV5MjBcIjogXCIoY2xhcClcIixcbiAgICAgICAgXCJzbWlsZXkyMVwiOiBcIiA7KVwiXG4gICAgfSxcbiAgICBcInJlZ2V4c1wiOiB7XG4gICAgICAgIFwic21pbGV5MlwiOiAvKDotXFwoXFwofDotXFwofDpcXChcXCh8OlxcKHxcXChzYWRcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTNcIjogLyg6LVxcKVxcKXw6XFwpXFwpfFxcKGxvbFxcKXw6LUR8OkQpL2dpLFxuICAgICAgICBcInNtaWxleTFcIjogLyg6LVxcKXw6XFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk0XCI6IC8oXFwoeVxcKXxcXChZXFwpfFxcKG9rXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk1XCI6IC8oOi1QfDpQfDotcHw6cCkvZ2ksXG4gICAgICAgIFwic21pbGV5NlwiOiAvKFxcKHdhdmVcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTdcIjogLyhcXChibHVzaFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5OFwiOiAvKFxcKGNodWNrbGVcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTlcIjogLyg6LTB8XFwoc2hvY2tlZFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTBcIjogLyg6LVxcKnw6XFwqfFxcKGtpc3NcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTExXCI6IC8oXFwoblxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTJcIjogLyhcXChzZWFyY2hcXCkpL2csXG4gICAgICAgIFwic21pbGV5MTNcIjogLyg8M3wmbHQ7M3wmYW1wO2x0OzN8XFwoTFxcKXxcXChsXFwpfFxcKEhcXCl8XFwoaFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTRcIjogLyhcXChvb3BzXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxNVwiOiAvKFxcKGFuZ3J5XFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxNlwiOiAvKFxcKGFuZ2VsXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxN1wiOiAvKFxcKHNpY2tcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTE4XCI6IC8oOy1cXChcXCh8O1xcKFxcKHw7LVxcKHw7XFwofDpcIlxcKHw6XCItXFwofDp+LVxcKHw6flxcKHxcXCh1cHNldFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTlcIjogLyhcXChib21iXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkyMFwiOiAvKFxcKGNsYXBcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTIxXCI6IC8oOy1cXCl8O1xcKXw7LVxcKVxcKXw7XFwpXFwpfDstRHw7RHxcXCh3aW5rXFwpKS9naVxuICAgIH1cbn1cbiIsIlxudmFyIG51bWJlck9mQ29udGFjdHMgPSAwO1xudmFyIG5vdGlmaWNhdGlvbkludGVydmFsO1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIG51bWJlciBvZiBwYXJ0aWNpcGFudHMgaW4gdGhlIGNvbnRhY3QgbGlzdCBidXR0b24gYW5kIHNldHNcbiAqIHRoZSBnbG93XG4gKiBAcGFyYW0gZGVsdGEgaW5kaWNhdGVzIHdoZXRoZXIgYSBuZXcgdXNlciBoYXMgam9pbmVkICgxKSBvciBzb21lb25lIGhhc1xuICogbGVmdCgtMSlcbiAqL1xuZnVuY3Rpb24gdXBkYXRlTnVtYmVyT2ZQYXJ0aWNpcGFudHMoZGVsdGEpIHtcbiAgICAvL3doZW4gdGhlIHVzZXIgaXMgYWxvbmUgd2UgZG9uJ3Qgc2hvdyB0aGUgbnVtYmVyIG9mIHBhcnRpY2lwYW50c1xuICAgIGlmKG51bWJlck9mQ29udGFjdHMgPT09IDApIHtcbiAgICAgICAgJChcIiNudW1iZXJPZlBhcnRpY2lwYW50c1wiKS50ZXh0KCcnKTtcbiAgICAgICAgbnVtYmVyT2ZDb250YWN0cyArPSBkZWx0YTtcbiAgICB9IGVsc2UgaWYobnVtYmVyT2ZDb250YWN0cyAhPT0gMCAmJiAhQ29udGFjdExpc3QuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgQ29udGFjdExpc3Quc2V0VmlzdWFsTm90aWZpY2F0aW9uKHRydWUpO1xuICAgICAgICBudW1iZXJPZkNvbnRhY3RzICs9IGRlbHRhO1xuICAgICAgICAkKFwiI251bWJlck9mUGFydGljaXBhbnRzXCIpLnRleHQobnVtYmVyT2ZDb250YWN0cyk7XG4gICAgfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGF2YXRhciBlbGVtZW50LlxuICpcbiAqIEByZXR1cm4gdGhlIG5ld2x5IGNyZWF0ZWQgYXZhdGFyIGVsZW1lbnRcbiAqL1xuZnVuY3Rpb24gY3JlYXRlQXZhdGFyKGlkKSB7XG4gICAgdmFyIGF2YXRhciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xuICAgIGF2YXRhci5jbGFzc05hbWUgPSBcImljb24tYXZhdGFyIGF2YXRhclwiO1xuICAgIGF2YXRhci5zcmMgPSBcImh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvXCIgKyBpZCArIFwiP2Q9d2F2YXRhciZzaXplPTMwXCI7XG5cbiAgICByZXR1cm4gYXZhdGFyO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGRpc3BsYXkgbmFtZSBwYXJhZ3JhcGguXG4gKlxuICogQHBhcmFtIGRpc3BsYXlOYW1lIHRoZSBkaXNwbGF5IG5hbWUgdG8gc2V0XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZURpc3BsYXlOYW1lUGFyYWdyYXBoKGRpc3BsYXlOYW1lKSB7XG4gICAgdmFyIHAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdwJyk7XG4gICAgcC5pbm5lclRleHQgPSBkaXNwbGF5TmFtZTtcblxuICAgIHJldHVybiBwO1xufVxuXG5cbmZ1bmN0aW9uIHN0b3BHbG93aW5nKGdsb3dlcikge1xuICAgIHdpbmRvdy5jbGVhckludGVydmFsKG5vdGlmaWNhdGlvbkludGVydmFsKTtcbiAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IGZhbHNlO1xuICAgIGdsb3dlci5yZW1vdmVDbGFzcygnZ2xvd2luZycpO1xuICAgIGlmICghQ29udGFjdExpc3QuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgZ2xvd2VyLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICB9XG59XG5cblxuLyoqXG4gKiBDb250YWN0IGxpc3QuXG4gKi9cbnZhciBDb250YWN0TGlzdCA9IHtcbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgaWYgdGhlIGNoYXQgaXMgY3VycmVudGx5IHZpc2libGUuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIDx0dD50cnVlPC90dD4gaWYgdGhlIGNoYXQgaXMgY3VycmVudGx5IHZpc2libGUsIDx0dD5mYWxzZTwvdHQ+IC1cbiAgICAgKiBvdGhlcndpc2VcbiAgICAgKi9cbiAgICBpc1Zpc2libGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuICQoJyNjb250YWN0bGlzdCcpLmlzKFwiOnZpc2libGVcIik7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEFkZHMgYSBjb250YWN0IGZvciB0aGUgZ2l2ZW4gcGVlckppZCBpZiBzdWNoIGRvZXNuJ3QgeWV0IGV4aXN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHBlZXJKaWQgdGhlIHBlZXJKaWQgY29ycmVzcG9uZGluZyB0byB0aGUgY29udGFjdFxuICAgICAqIEBwYXJhbSBpZCB0aGUgdXNlcidzIGVtYWlsIG9yIHVzZXJJZCB1c2VkIHRvIGdldCB0aGUgdXNlcidzIGF2YXRhclxuICAgICAqL1xuICAgIGVuc3VyZUFkZENvbnRhY3Q6IGZ1bmN0aW9uIChwZWVySmlkLCBpZCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyNjb250YWN0bGlzdD51bD5saVtpZD1cIicgKyByZXNvdXJjZUppZCArICdcIl0nKTtcblxuICAgICAgICBpZiAoIWNvbnRhY3QgfHwgY29udGFjdC5sZW5ndGggPD0gMClcbiAgICAgICAgICAgIENvbnRhY3RMaXN0LmFkZENvbnRhY3QocGVlckppZCwgaWQpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgY29udGFjdCBmb3IgdGhlIGdpdmVuIHBlZXIgamlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHBlZXJKaWQgdGhlIGppZCBvZiB0aGUgY29udGFjdCB0byBhZGRcbiAgICAgKiBAcGFyYW0gaWQgdGhlIGVtYWlsIG9yIHVzZXJJZCBvZiB0aGUgdXNlclxuICAgICAqL1xuICAgIGFkZENvbnRhY3Q6IGZ1bmN0aW9uIChwZWVySmlkLCBpZCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdGxpc3QgPSAkKCcjY29udGFjdGxpc3Q+dWwnKTtcblxuICAgICAgICB2YXIgbmV3Q29udGFjdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7XG4gICAgICAgIG5ld0NvbnRhY3QuaWQgPSByZXNvdXJjZUppZDtcbiAgICAgICAgbmV3Q29udGFjdC5jbGFzc05hbWUgPSBcImNsaWNrYWJsZVwiO1xuICAgICAgICBuZXdDb250YWN0Lm9uY2xpY2sgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5jdXJyZW50VGFyZ2V0LmNsYXNzTmFtZSA9PT0gXCJjbGlja2FibGVcIikge1xuICAgICAgICAgICAgICAgICQoQ29udGFjdExpc3QpLnRyaWdnZXIoJ2NvbnRhY3RjbGlja2VkJywgW3BlZXJKaWRdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBuZXdDb250YWN0LmFwcGVuZENoaWxkKGNyZWF0ZUF2YXRhcihpZCkpO1xuICAgICAgICBuZXdDb250YWN0LmFwcGVuZENoaWxkKGNyZWF0ZURpc3BsYXlOYW1lUGFyYWdyYXBoKFwiUGFydGljaXBhbnRcIikpO1xuXG4gICAgICAgIHZhciBjbEVsZW1lbnQgPSBjb250YWN0bGlzdC5nZXQoMCk7XG5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkID09PSB4bXBwLm15UmVzb3VyY2UoKVxuICAgICAgICAgICAgJiYgJCgnI2NvbnRhY3RsaXN0PnVsIC50aXRsZScpWzBdLm5leHRTaWJsaW5nLm5leHRTaWJsaW5nKSB7XG4gICAgICAgICAgICBjbEVsZW1lbnQuaW5zZXJ0QmVmb3JlKG5ld0NvbnRhY3QsXG4gICAgICAgICAgICAgICAgJCgnI2NvbnRhY3RsaXN0PnVsIC50aXRsZScpWzBdLm5leHRTaWJsaW5nLm5leHRTaWJsaW5nKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNsRWxlbWVudC5hcHBlbmRDaGlsZChuZXdDb250YWN0KTtcbiAgICAgICAgfVxuICAgICAgICB1cGRhdGVOdW1iZXJPZlBhcnRpY2lwYW50cygxKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhIGNvbnRhY3QgZm9yIHRoZSBnaXZlbiBwZWVyIGppZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBwZWVySmlkIHRoZSBwZWVySmlkIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNvbnRhY3QgdG8gcmVtb3ZlXG4gICAgICovXG4gICAgcmVtb3ZlQ29udGFjdDogZnVuY3Rpb24gKHBlZXJKaWQpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCk7XG5cbiAgICAgICAgdmFyIGNvbnRhY3QgPSAkKCcjY29udGFjdGxpc3Q+dWw+bGlbaWQ9XCInICsgcmVzb3VyY2VKaWQgKyAnXCJdJyk7XG5cbiAgICAgICAgaWYgKGNvbnRhY3QgJiYgY29udGFjdC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB2YXIgY29udGFjdGxpc3QgPSAkKCcjY29udGFjdGxpc3Q+dWwnKTtcblxuICAgICAgICAgICAgY29udGFjdGxpc3QuZ2V0KDApLnJlbW92ZUNoaWxkKGNvbnRhY3QuZ2V0KDApKTtcblxuICAgICAgICAgICAgdXBkYXRlTnVtYmVyT2ZQYXJ0aWNpcGFudHMoLTEpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIHNldFZpc3VhbE5vdGlmaWNhdGlvbjogZnVuY3Rpb24gKHNob3csIHN0b3BHbG93aW5nSW4pIHtcbiAgICAgICAgdmFyIGdsb3dlciA9ICQoJyNjb250YWN0TGlzdEJ1dHRvbicpO1xuXG4gICAgICAgIGlmIChzaG93ICYmICFub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICAgICAgbm90aWZpY2F0aW9uSW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGdsb3dlci50b2dnbGVDbGFzcygnYWN0aXZlIGdsb3dpbmcnKTtcbiAgICAgICAgICAgIH0sIDgwMCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoIXNob3cgJiYgbm90aWZpY2F0aW9uSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgIHN0b3BHbG93aW5nKGdsb3dlcik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0b3BHbG93aW5nSW4pIHtcbiAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHN0b3BHbG93aW5nKGdsb3dlcik7XG4gICAgICAgICAgICB9LCBzdG9wR2xvd2luZ0luKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBzZXRDbGlja2FibGU6IGZ1bmN0aW9uIChyZXNvdXJjZUppZCwgaXNDbGlja2FibGUpIHtcbiAgICAgICAgdmFyIGNvbnRhY3QgPSAkKCcjY29udGFjdGxpc3Q+dWw+bGlbaWQ9XCInICsgcmVzb3VyY2VKaWQgKyAnXCJdJyk7XG4gICAgICAgIGlmIChpc0NsaWNrYWJsZSkge1xuICAgICAgICAgICAgY29udGFjdC5hZGRDbGFzcygnY2xpY2thYmxlJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb250YWN0LnJlbW92ZUNsYXNzKCdjbGlja2FibGUnKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBvbkRpc3BsYXlOYW1lQ2hhbmdlOiBmdW5jdGlvbiAocGVlckppZCwgZGlzcGxheU5hbWUpIHtcbiAgICAgICAgaWYgKHBlZXJKaWQgPT09ICdsb2NhbFZpZGVvQ29udGFpbmVyJylcbiAgICAgICAgICAgIHBlZXJKaWQgPSB4bXBwLm15SmlkKCk7XG5cbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCk7XG5cbiAgICAgICAgdmFyIGNvbnRhY3ROYW1lID0gJCgnI2NvbnRhY3RsaXN0ICMnICsgcmVzb3VyY2VKaWQgKyAnPnAnKTtcblxuICAgICAgICBpZiAoY29udGFjdE5hbWUgJiYgZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgIGNvbnRhY3ROYW1lLmh0bWwoZGlzcGxheU5hbWUpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29udGFjdExpc3Q7IiwidmFyIGVtYWlsID0gJyc7XG52YXIgZGlzcGxheU5hbWUgPSAnJztcbnZhciB1c2VySWQ7XG5cblxuZnVuY3Rpb24gc3VwcG9ydHNMb2NhbFN0b3JhZ2UoKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuICdsb2NhbFN0b3JhZ2UnIGluIHdpbmRvdyAmJiB3aW5kb3cubG9jYWxTdG9yYWdlICE9PSBudWxsO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJsb2NhbHN0b3JhZ2UgaXMgbm90IHN1cHBvcnRlZFwiKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuXG5mdW5jdGlvbiBnZW5lcmF0ZVVuaXF1ZUlkKCkge1xuICAgIGZ1bmN0aW9uIF9wOCgpIHtcbiAgICAgICAgcmV0dXJuIChNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDE2KStcIjAwMDAwMDAwMFwiKS5zdWJzdHIoMiw4KTtcbiAgICB9XG4gICAgcmV0dXJuIF9wOCgpICsgX3A4KCkgKyBfcDgoKSArIF9wOCgpO1xufVxuXG5pZihzdXBwb3J0c0xvY2FsU3RvcmFnZSgpKSB7XG4gICAgaWYoIXdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQpIHtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCA9IGdlbmVyYXRlVW5pcXVlSWQoKTtcbiAgICAgICAgY29uc29sZS5sb2coXCJnZW5lcmF0ZWQgaWRcIiwgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCk7XG4gICAgfVxuICAgIHVzZXJJZCA9IHdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQgfHwgJyc7XG4gICAgZW1haWwgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmVtYWlsIHx8ICcnO1xuICAgIGRpc3BsYXlOYW1lID0gd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSB8fCAnJztcbn0gZWxzZSB7XG4gICAgY29uc29sZS5sb2coXCJsb2NhbCBzdG9yYWdlIGlzIG5vdCBzdXBwb3J0ZWRcIik7XG4gICAgdXNlcklkID0gZ2VuZXJhdGVVbmlxdWVJZCgpO1xufVxuXG52YXIgU2V0dGluZ3MgPVxue1xuICAgIHNldERpc3BsYXlOYW1lOiBmdW5jdGlvbiAobmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgZGlzcGxheU5hbWUgPSBuZXdEaXNwbGF5TmFtZTtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSA9IGRpc3BsYXlOYW1lO1xuICAgICAgICByZXR1cm4gZGlzcGxheU5hbWU7XG4gICAgfSxcbiAgICBzZXRFbWFpbDogZnVuY3Rpb24obmV3RW1haWwpXG4gICAge1xuICAgICAgICBlbWFpbCA9IG5ld0VtYWlsO1xuICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLmVtYWlsID0gbmV3RW1haWw7XG4gICAgICAgIHJldHVybiBlbWFpbDtcbiAgICB9LFxuICAgIGdldFNldHRpbmdzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBlbWFpbDogZW1haWwsXG4gICAgICAgICAgICBkaXNwbGF5TmFtZTogZGlzcGxheU5hbWUsXG4gICAgICAgICAgICB1aWQ6IHVzZXJJZFxuICAgICAgICB9O1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2V0dGluZ3M7XG4iLCJ2YXIgQXZhdGFyID0gcmVxdWlyZShcIi4uLy4uL2F2YXRhci9BdmF0YXJcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi9TZXR0aW5nc1wiKTtcblxuXG52YXIgU2V0dGluZ3NNZW51ID0ge1xuXG4gICAgdXBkYXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIG5ld0Rpc3BsYXlOYW1lID0gVXRpbC5lc2NhcGVIdG1sKCQoJyNzZXREaXNwbGF5TmFtZScpLmdldCgwKS52YWx1ZSk7XG4gICAgICAgIHZhciBuZXdFbWFpbCA9IFV0aWwuZXNjYXBlSHRtbCgkKCcjc2V0RW1haWwnKS5nZXQoMCkudmFsdWUpO1xuXG4gICAgICAgIGlmKG5ld0Rpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSBTZXR0aW5ncy5zZXREaXNwbGF5TmFtZShuZXdEaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICB4bXBwLmFkZFRvUHJlc2VuY2UoXCJkaXNwbGF5TmFtZVwiLCBkaXNwbGF5TmFtZSwgdHJ1ZSk7XG4gICAgICAgIH1cblxuXG4gICAgICAgIHhtcHAuYWRkVG9QcmVzZW5jZShcImVtYWlsXCIsIG5ld0VtYWlsKTtcbiAgICAgICAgdmFyIGVtYWlsID0gU2V0dGluZ3Muc2V0RW1haWwobmV3RW1haWwpO1xuXG5cbiAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIoeG1wcC5teUppZCgpLCBlbWFpbCk7XG4gICAgfSxcblxuICAgIGlzVmlzaWJsZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAkKCcjc2V0dGluZ3NtZW51JykuaXMoJzp2aXNpYmxlJyk7XG4gICAgfSxcblxuICAgIHNldERpc3BsYXlOYW1lOiBmdW5jdGlvbihuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSBTZXR0aW5ncy5zZXREaXNwbGF5TmFtZShuZXdEaXNwbGF5TmFtZSk7XG4gICAgICAgICQoJyNzZXREaXNwbGF5TmFtZScpLmdldCgwKS52YWx1ZSA9IGRpc3BsYXlOYW1lO1xuICAgIH0sXG5cbiAgICBvbkRpc3BsYXlOYW1lQ2hhbmdlOiBmdW5jdGlvbihwZWVySmlkLCBuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICBpZihwZWVySmlkID09PSAnbG9jYWxWaWRlb0NvbnRhaW5lcicgfHxcbiAgICAgICAgICAgIHBlZXJKaWQgPT09IHhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgdGhpcy5zZXREaXNwbGF5TmFtZShuZXdEaXNwbGF5TmFtZSk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gU2V0dGluZ3NNZW51OyIsInZhciBQYW5lbFRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG5cbnZhciBidXR0b25IYW5kbGVycyA9IHtcbiAgICBcImJvdHRvbV90b29sYmFyX2NvbnRhY3RfbGlzdFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ29udGFjdExpc3QoKTtcbiAgICB9LFxuICAgIFwiYm90dG9tX3Rvb2xiYXJfZmlsbV9zdHJpcFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEJvdHRvbVRvb2xiYXIudG9nZ2xlRmlsbVN0cmlwKCk7XG4gICAgfSxcbiAgICBcImJvdHRvbV90b29sYmFyX2NoYXRcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICBCb3R0b21Ub29sYmFyLnRvZ2dsZUNoYXQoKTtcbiAgICB9XG59O1xuXG52YXIgQm90dG9tVG9vbGJhciA9IChmdW5jdGlvbiAobXkpIHtcbiAgICBteS5pbml0ID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBmb3IodmFyIGsgaW4gYnV0dG9uSGFuZGxlcnMpXG4gICAgICAgICAgICAkKFwiI1wiICsgaykuY2xpY2soYnV0dG9uSGFuZGxlcnNba10pO1xuICAgIH07XG5cbiAgICBteS50b2dnbGVDaGF0ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIFBhbmVsVG9nZ2xlci50b2dnbGVDaGF0KCk7XG4gICAgfTtcblxuICAgIG15LnRvZ2dsZUNvbnRhY3RMaXN0ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIFBhbmVsVG9nZ2xlci50b2dnbGVDb250YWN0TGlzdCgpO1xuICAgIH07XG5cbiAgICBteS50b2dnbGVGaWxtU3RyaXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGZpbG1zdHJpcCA9ICQoXCIjcmVtb3RlVmlkZW9zXCIpO1xuICAgICAgICBmaWxtc3RyaXAudG9nZ2xlQ2xhc3MoXCJoaWRkZW5cIik7XG4gICAgfTtcblxuICAgICQoZG9jdW1lbnQpLmJpbmQoXCJyZW1vdGV2aWRlby5yZXNpemVkXCIsIGZ1bmN0aW9uIChldmVudCwgd2lkdGgsIGhlaWdodCkge1xuICAgICAgICB2YXIgYm90dG9tID0gKGhlaWdodCAtICQoJyNib3R0b21Ub29sYmFyJykub3V0ZXJIZWlnaHQoKSkvMiArIDE4O1xuXG4gICAgICAgICQoJyNib3R0b21Ub29sYmFyJykuY3NzKHtib3R0b206IGJvdHRvbSArICdweCd9KTtcbiAgICB9KTtcblxuICAgIHJldHVybiBteTtcbn0oQm90dG9tVG9vbGJhciB8fCB7fSkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEJvdHRvbVRvb2xiYXI7XG4iLCIvKiBnbG9iYWwgJCwgaW50ZXJmYWNlQ29uZmlnLCBNb2RlcmF0b3IsIERlc2t0b3BTdHJlYW1pbmcuc2hvd0Rlc2t0b3BTaGFyaW5nQnV0dG9uICovXG5cbnZhciB0b29sYmFyVGltZW91dE9iamVjdCxcbiAgICB0b29sYmFyVGltZW91dCA9IGludGVyZmFjZUNvbmZpZy5JTklUSUFMX1RPT0xCQVJfVElNRU9VVDtcblxuZnVuY3Rpb24gc2hvd0Rlc2t0b3BTaGFyaW5nQnV0dG9uKCkge1xuICAgIGlmIChkZXNrdG9wc2hhcmluZy5pc0Rlc2t0b3BTaGFyaW5nRW5hYmxlZCgpKSB7XG4gICAgICAgICQoJyNkZXNrdG9wc2hhcmluZycpLmNzcyh7ZGlzcGxheTogXCJpbmxpbmVcIn0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgICQoJyNkZXNrdG9wc2hhcmluZycpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICB9XG59XG5cbi8qKlxuICogSGlkZXMgdGhlIHRvb2xiYXIuXG4gKi9cbmZ1bmN0aW9uIGhpZGVUb29sYmFyKCkge1xuICAgIHZhciBoZWFkZXIgPSAkKFwiI2hlYWRlclwiKSxcbiAgICAgICAgYm90dG9tVG9vbGJhciA9ICQoXCIjYm90dG9tVG9vbGJhclwiKTtcbiAgICB2YXIgaXNUb29sYmFySG92ZXIgPSBmYWxzZTtcbiAgICBoZWFkZXIuZmluZCgnKicpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaWQgPSAkKHRoaXMpLmF0dHIoJ2lkJyk7XG4gICAgICAgIGlmICgkKFwiI1wiICsgaWQgKyBcIjpob3ZlclwiKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBpc1Rvb2xiYXJIb3ZlciA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAoJChcIiNib3R0b21Ub29sYmFyOmhvdmVyXCIpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgaXNUb29sYmFySG92ZXIgPSB0cnVlO1xuICAgIH1cblxuICAgIGNsZWFyVGltZW91dCh0b29sYmFyVGltZW91dE9iamVjdCk7XG4gICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBudWxsO1xuXG4gICAgaWYgKCFpc1Rvb2xiYXJIb3Zlcikge1xuICAgICAgICBoZWFkZXIuaGlkZShcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcInVwXCIsIGR1cmF0aW9uOiAzMDB9KTtcbiAgICAgICAgJCgnI3N1YmplY3QnKS5hbmltYXRlKHt0b3A6IFwiLT00MFwifSwgMzAwKTtcbiAgICAgICAgaWYgKCQoXCIjcmVtb3RlVmlkZW9zXCIpLmhhc0NsYXNzKFwiaGlkZGVuXCIpKSB7XG4gICAgICAgICAgICBib3R0b21Ub29sYmFyLmhpZGUoXG4gICAgICAgICAgICAgICAgXCJzbGlkZVwiLCB7ZGlyZWN0aW9uOiBcInJpZ2h0XCIsIGR1cmF0aW9uOiAzMDB9KTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBzZXRUaW1lb3V0KGhpZGVUb29sYmFyLCB0b29sYmFyVGltZW91dCk7XG4gICAgfVxufVxuXG52YXIgVG9vbGJhclRvZ2dsZXIgPSB7XG4gICAgLyoqXG4gICAgICogU2hvd3MgdGhlIG1haW4gdG9vbGJhci5cbiAgICAgKi9cbiAgICBzaG93VG9vbGJhcjogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaGVhZGVyID0gJChcIiNoZWFkZXJcIiksXG4gICAgICAgICAgICBib3R0b21Ub29sYmFyID0gJChcIiNib3R0b21Ub29sYmFyXCIpO1xuICAgICAgICBpZiAoIWhlYWRlci5pcygnOnZpc2libGUnKSB8fCAhYm90dG9tVG9vbGJhci5pcyhcIjp2aXNpYmxlXCIpKSB7XG4gICAgICAgICAgICBoZWFkZXIuc2hvdyhcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcInVwXCIsIGR1cmF0aW9uOiAzMDB9KTtcbiAgICAgICAgICAgICQoJyNzdWJqZWN0JykuYW5pbWF0ZSh7dG9wOiBcIis9NDBcIn0sIDMwMCk7XG4gICAgICAgICAgICBpZiAoIWJvdHRvbVRvb2xiYXIuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICAgICAgICAgIGJvdHRvbVRvb2xiYXIuc2hvdyhcbiAgICAgICAgICAgICAgICAgICAgXCJzbGlkZVwiLCB7ZGlyZWN0aW9uOiBcInJpZ2h0XCIsIGR1cmF0aW9uOiAzMDB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRvb2xiYXJUaW1lb3V0T2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRvb2xiYXJUaW1lb3V0T2JqZWN0KTtcbiAgICAgICAgICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IHNldFRpbWVvdXQoaGlkZVRvb2xiYXIsIHRvb2xiYXJUaW1lb3V0KTtcbiAgICAgICAgICAgIHRvb2xiYXJUaW1lb3V0ID0gaW50ZXJmYWNlQ29uZmlnLlRPT0xCQVJfVElNRU9VVDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh4bXBwLmlzTW9kZXJhdG9yKCkpXG4gICAgICAgIHtcbi8vICAgICAgICAgICAgVE9ETzogRW5hYmxlIHNldHRpbmdzIGZ1bmN0aW9uYWxpdHkuXG4vLyAgICAgICAgICAgICAgICAgIE5lZWQgdG8gdW5jb21tZW50IHRoZSBzZXR0aW5ncyBidXR0b24gaW4gaW5kZXguaHRtbC5cbi8vICAgICAgICAgICAgJCgnI3NldHRpbmdzQnV0dG9uJykuY3NzKHt2aXNpYmlsaXR5OlwidmlzaWJsZVwifSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTaG93L2hpZGUgZGVza3RvcCBzaGFyaW5nIGJ1dHRvblxuICAgICAgICBzaG93RGVza3RvcFNoYXJpbmdCdXR0b24oKTtcbiAgICB9LFxuXG5cbiAgICAvKipcbiAgICAgKiBEb2Nrcy91bmRvY2tzIHRoZSB0b29sYmFyLlxuICAgICAqXG4gICAgICogQHBhcmFtIGlzRG9jayBpbmRpY2F0ZXMgd2hhdCBvcGVyYXRpb24gdG8gcGVyZm9ybVxuICAgICAqL1xuICAgIGRvY2tUb29sYmFyOiBmdW5jdGlvbiAoaXNEb2NrKSB7XG4gICAgICAgIGlmIChpc0RvY2spIHtcbiAgICAgICAgICAgIC8vIEZpcnN0IG1ha2Ugc3VyZSB0aGUgdG9vbGJhciBpcyBzaG93bi5cbiAgICAgICAgICAgIGlmICghJCgnI2hlYWRlcicpLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaG93VG9vbGJhcigpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBUaGVuIGNsZWFyIHRoZSB0aW1lIG91dCwgdG8gZG9jayB0aGUgdG9vbGJhci5cbiAgICAgICAgICAgIGlmICh0b29sYmFyVGltZW91dE9iamVjdCkge1xuICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0b29sYmFyVGltZW91dE9iamVjdCk7XG4gICAgICAgICAgICAgICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKCEkKCcjaGVhZGVyJykuaXMoJzp2aXNpYmxlJykpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNob3dUb29sYmFyKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IHNldFRpbWVvdXQoaGlkZVRvb2xiYXIsIHRvb2xiYXJUaW1lb3V0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBzaG93RGVza3RvcFNoYXJpbmdCdXR0b246IHNob3dEZXNrdG9wU2hhcmluZ0J1dHRvblxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFRvb2xiYXJUb2dnbGVyOyIsIi8qIGdsb2JhbCAkLCBidXR0b25DbGljaywgY29uZmlnLCBsb2NrUm9vbSxcbiAgIHNldFNoYXJlZEtleSwgVXRpbCAqL1xudmFyIG1lc3NhZ2VIYW5kbGVyID0gcmVxdWlyZShcIi4uL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgQm90dG9tVG9vbGJhciA9IHJlcXVpcmUoXCIuL0JvdHRvbVRvb2xiYXJcIik7XG52YXIgUHJlemkgPSByZXF1aXJlKFwiLi4vcHJlemkvUHJlemlcIik7XG52YXIgRXRoZXJwYWQgPSByZXF1aXJlKFwiLi4vZXRoZXJwYWQvRXRoZXJwYWRcIik7XG52YXIgUGFuZWxUb2dnbGVyID0gcmVxdWlyZShcIi4uL3NpZGVfcGFubmVscy9TaWRlUGFuZWxUb2dnbGVyXCIpO1xudmFyIEF1dGhlbnRpY2F0aW9uID0gcmVxdWlyZShcIi4uL2F1dGhlbnRpY2F0aW9uL0F1dGhlbnRpY2F0aW9uXCIpO1xudmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi91dGlsL1VJVXRpbFwiKTtcblxudmFyIHJvb21VcmwgPSBudWxsO1xudmFyIHNoYXJlZEtleSA9ICcnO1xudmFyIFVJID0gbnVsbDtcblxudmFyIGJ1dHRvbkhhbmRsZXJzID1cbntcbiAgICBcInRvb2xiYXJfYnV0dG9uX211dGVcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gVUkudG9nZ2xlQXVkaW8oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fY2FtZXJhXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFVJLnRvZ2dsZVZpZGVvKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2F1dGhlbnRpY2F0aW9uXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXIuYXV0aGVudGljYXRlQ2xpY2tlZCgpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9yZWNvcmRcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdG9nZ2xlUmVjb3JkaW5nKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3NlY3VyaXR5XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXIub3BlbkxvY2tEaWFsb2coKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fbGlua1wiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBUb29sYmFyLm9wZW5MaW5rRGlhbG9nKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2NoYXRcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gQm90dG9tVG9vbGJhci50b2dnbGVDaGF0KCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3ByZXppXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFByZXppLm9wZW5QcmV6aURpYWxvZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9ldGhlcnBhZFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBFdGhlcnBhZC50b2dnbGVFdGhlcnBhZCgwKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fZGVza3RvcHNoYXJpbmdcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gZGVza3RvcHNoYXJpbmcudG9nZ2xlU2NyZWVuU2hhcmluZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9mdWxsU2NyZWVuXCI6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNmdWxsU2NyZWVuXCIsIFwiaWNvbi1mdWxsLXNjcmVlbiBpY29uLWV4aXQtZnVsbC1zY3JlZW5cIik7XG4gICAgICAgIHJldHVybiBUb29sYmFyLnRvZ2dsZUZ1bGxTY3JlZW4oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fc2lwXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNhbGxTaXBCdXR0b25DbGlja2VkKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3NldHRpbmdzXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZVNldHRpbmdzTWVudSgpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9oYW5ndXBcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gaGFuZ3VwKCk7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gaGFuZ3VwKCkge1xuICAgIHhtcHAuZGlzcG9zZUNvbmZlcmVuY2UoKTtcbiAgICBpZihjb25maWcuZW5hYmxlV2VsY29tZVBhZ2UpXG4gICAge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKClcbiAgICAgICAge1xuICAgICAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkID0gZmFsc2U7XG4gICAgICAgICAgICB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWUgPSBcIi9cIjtcbiAgICAgICAgfSwgMTAwMDApO1xuXG4gICAgfVxuXG4gICAgVUkubWVzc2FnZUhhbmRsZXIub3BlbkRpYWxvZyhcbiAgICAgICAgXCJTZXNzaW9uIFRlcm1pbmF0ZWRcIixcbiAgICAgICAgXCJZb3UgaHVuZyB1cCB0aGUgY2FsbFwiLFxuICAgICAgICB0cnVlLFxuICAgICAgICB7IFwiSm9pbiBhZ2FpblwiOiB0cnVlIH0sXG4gICAgICAgIGZ1bmN0aW9uKGV2ZW50LCB2YWx1ZSwgbWVzc2FnZSwgZm9ybVZhbHMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5yZWxvYWQoKTtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cbi8qKlxuICogU3RhcnRzIG9yIHN0b3BzIHRoZSByZWNvcmRpbmcgZm9yIHRoZSBjb25mZXJlbmNlLlxuICovXG5cbmZ1bmN0aW9uIHRvZ2dsZVJlY29yZGluZygpIHtcbiAgICB4bXBwLnRvZ2dsZVJlY29yZGluZyhmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICc8aDI+RW50ZXIgcmVjb3JkaW5nIHRva2VuPC9oMj4nICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwicmVjb3JkaW5nVG9rZW5cIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAgICAgICAgICdwbGFjZWhvbGRlcj1cInRva2VuXCIgYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgIFwiU2F2ZVwiLFxuICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdG9rZW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVjb3JkaW5nVG9rZW4nKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAodG9rZW4udmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKFV0aWwuZXNjYXBlSHRtbCh0b2tlbi52YWx1ZSkpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZWNvcmRpbmdUb2tlbicpLmZvY3VzKCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sIFRvb2xiYXIuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUsIFRvb2xiYXIuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUpO1xufVxuXG4vKipcbiAqIExvY2tzIC8gdW5sb2NrcyB0aGUgcm9vbS5cbiAqL1xuZnVuY3Rpb24gbG9ja1Jvb20obG9jaykge1xuICAgIHZhciBjdXJyZW50U2hhcmVkS2V5ID0gJyc7XG4gICAgaWYgKGxvY2spXG4gICAgICAgIGN1cnJlbnRTaGFyZWRLZXkgPSBzaGFyZWRLZXk7XG5cbiAgICB4bXBwLmxvY2tSb29tKGN1cnJlbnRTaGFyZWRLZXksIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgLy8gcGFzc3dvcmQgaXMgcmVxdWlyZWRcbiAgICAgICAgaWYgKHNoYXJlZEtleSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldCByb29tIHBhc3N3b3JkJyk7XG4gICAgICAgICAgICBUb29sYmFyLmxvY2tMb2NrQnV0dG9uKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygncmVtb3ZlZCByb29tIHBhc3N3b3JkJyk7XG4gICAgICAgICAgICBUb29sYmFyLnVubG9ja0xvY2tCdXR0b24oKTtcbiAgICAgICAgfVxuICAgIH0sIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdzZXR0aW5nIHBhc3N3b3JkIGZhaWxlZCcsIGVycik7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcignTG9jayBmYWlsZWQnLFxuICAgICAgICAgICAgJ0ZhaWxlZCB0byBsb2NrIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgIGVycik7XG4gICAgICAgIFRvb2xiYXIuc2V0U2hhcmVkS2V5KCcnKTtcbiAgICB9LCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGNvbnNvbGUud2Fybigncm9vbSBwYXNzd29yZHMgbm90IHN1cHBvcnRlZCcpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ1dhcm5pbmcnLFxuICAgICAgICAgICAgJ1Jvb20gcGFzc3dvcmRzIGFyZSBjdXJyZW50bHkgbm90IHN1cHBvcnRlZC4nKTtcbiAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkoJycpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBJbnZpdGUgcGFydGljaXBhbnRzIHRvIGNvbmZlcmVuY2UuXG4gKi9cbmZ1bmN0aW9uIGludml0ZVBhcnRpY2lwYW50cygpIHtcbiAgICBpZiAocm9vbVVybCA9PT0gbnVsbClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgdmFyIHNoYXJlZEtleVRleHQgPSBcIlwiO1xuICAgIGlmIChzaGFyZWRLZXkgJiYgc2hhcmVkS2V5Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgc2hhcmVkS2V5VGV4dCA9XG4gICAgICAgICAgICBcIlRoaXMgY29uZmVyZW5jZSBpcyBwYXNzd29yZCBwcm90ZWN0ZWQuIFBsZWFzZSB1c2UgdGhlIFwiICtcbiAgICAgICAgICAgIFwiZm9sbG93aW5nIHBpbiB3aGVuIGpvaW5pbmc6JTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICAgICAgc2hhcmVkS2V5ICsgXCIlMEQlMEElMEQlMEFcIjtcbiAgICB9XG5cbiAgICB2YXIgY29uZmVyZW5jZU5hbWUgPSByb29tVXJsLnN1YnN0cmluZyhyb29tVXJsLmxhc3RJbmRleE9mKCcvJykgKyAxKTtcbiAgICB2YXIgc3ViamVjdCA9IFwiSW52aXRhdGlvbiB0byBhIFwiICsgaW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FICsgXCIgKFwiICsgY29uZmVyZW5jZU5hbWUgKyBcIilcIjtcbiAgICB2YXIgYm9keSA9IFwiSGV5IHRoZXJlLCBJJTI3ZCBsaWtlIHRvIGludml0ZSB5b3UgdG8gYSBcIiArIGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRSArXG4gICAgICAgIFwiIGNvbmZlcmVuY2UgSSUyN3ZlIGp1c3Qgc2V0IHVwLiUwRCUwQSUwRCUwQVwiICtcbiAgICAgICAgXCJQbGVhc2UgY2xpY2sgb24gdGhlIGZvbGxvd2luZyBsaW5rIGluIG9yZGVyXCIgK1xuICAgICAgICBcIiB0byBqb2luIHRoZSBjb25mZXJlbmNlLiUwRCUwQSUwRCUwQVwiICtcbiAgICAgICAgcm9vbVVybCArXG4gICAgICAgIFwiJTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICBzaGFyZWRLZXlUZXh0ICtcbiAgICAgICAgXCJOb3RlIHRoYXQgXCIgKyBpbnRlcmZhY2VDb25maWcuQVBQX05BTUUgKyBcIiBpcyBjdXJyZW50bHlcIiArXG4gICAgICAgIFwiIG9ubHkgc3VwcG9ydGVkIGJ5IENocm9taXVtLFwiICtcbiAgICAgICAgXCIgR29vZ2xlIENocm9tZSBhbmQgT3BlcmEsIHNvIHlvdSBuZWVkXCIgK1xuICAgICAgICBcIiB0byBiZSB1c2luZyBvbmUgb2YgdGhlc2UgYnJvd3NlcnMuJTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICBcIlRhbGsgdG8geW91IGluIGEgc2VjIVwiO1xuXG4gICAgaWYgKHdpbmRvdy5sb2NhbFN0b3JhZ2UuZGlzcGxheW5hbWUpIHtcbiAgICAgICAgYm9keSArPSBcIiUwRCUwQSUwRCUwQVwiICsgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZTtcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLklOVklUQVRJT05fUE9XRVJFRF9CWSkge1xuICAgICAgICBib2R5ICs9IFwiJTBEJTBBJTBEJTBBLS0lMEQlMEFwb3dlcmVkIGJ5IGppdHNpLm9yZ1wiO1xuICAgIH1cblxuICAgIHdpbmRvdy5vcGVuKFwibWFpbHRvOj9zdWJqZWN0PVwiICsgc3ViamVjdCArIFwiJmJvZHk9XCIgKyBib2R5LCAnX2JsYW5rJyk7XG59XG5cbmZ1bmN0aW9uIGNhbGxTaXBCdXR0b25DbGlja2VkKClcbntcbiAgICB2YXIgZGVmYXVsdE51bWJlclxuICAgICAgICA9IGNvbmZpZy5kZWZhdWx0U2lwTnVtYmVyID8gY29uZmlnLmRlZmF1bHRTaXBOdW1iZXIgOiAnJztcblxuICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCxcbiAgICAgICAgJzxoMj5FbnRlciBTSVAgbnVtYmVyPC9oMj4nICtcbiAgICAgICAgJzxpbnB1dCBpZD1cInNpcE51bWJlclwiIHR5cGU9XCJ0ZXh0XCInICtcbiAgICAgICAgJyB2YWx1ZT1cIicgKyBkZWZhdWx0TnVtYmVyICsgJ1wiIGF1dG9mb2N1cz4nLFxuICAgICAgICBmYWxzZSxcbiAgICAgICAgXCJEaWFsXCIsXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7XG4gICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgIHZhciBudW1iZXJJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzaXBOdW1iZXInKTtcbiAgICAgICAgICAgICAgICBpZiAobnVtYmVySW5wdXQudmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgeG1wcC5kaWFsKG51bWJlcklucHV0LnZhbHVlLCAnZnJvbW51bWJlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICBVSS5nZXRSb29tTmFtZSgpLCBzaGFyZWRLZXkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2lwTnVtYmVyJykuZm9jdXMoKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cbnZhciBUb29sYmFyID0gKGZ1bmN0aW9uIChteSkge1xuXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICh1aSkge1xuICAgICAgICBmb3IodmFyIGsgaW4gYnV0dG9uSGFuZGxlcnMpXG4gICAgICAgICAgICAkKFwiI1wiICsgaykuY2xpY2soYnV0dG9uSGFuZGxlcnNba10pO1xuICAgICAgICBVSSA9IHVpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldHMgc2hhcmVkIGtleVxuICAgICAqIEBwYXJhbSBzS2V5IHRoZSBzaGFyZWQga2V5XG4gICAgICovXG4gICAgbXkuc2V0U2hhcmVkS2V5ID0gZnVuY3Rpb24gKHNLZXkpIHtcbiAgICAgICAgc2hhcmVkS2V5ID0gc0tleTtcbiAgICB9O1xuXG4gICAgbXkuYXV0aGVudGljYXRlQ2xpY2tlZCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQXV0aGVudGljYXRpb24uZm9jdXNBdXRoZW50aWNhdGlvbldpbmRvdygpO1xuICAgICAgICAvLyBHZXQgYXV0aGVudGljYXRpb24gVVJMXG4gICAgICAgIHhtcHAuZ2V0QXV0aFVybChVSS5nZXRSb29tTmFtZSgpLCBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgICAgICAvLyBPcGVuIHBvcHVwIHdpdGggYXV0aGVudGljYXRpb24gVVJMXG4gICAgICAgICAgICB2YXIgYXV0aGVudGljYXRpb25XaW5kb3cgPSBBdXRoZW50aWNhdGlvbi5jcmVhdGVBdXRoZW50aWNhdGlvbldpbmRvdyhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgLy8gT24gcG9wdXAgY2xvc2VkIC0gcmV0cnkgcm9vbSBhbGxvY2F0aW9uXG4gICAgICAgICAgICAgICAgeG1wcC5hbGxvY2F0ZUNvbmZlcmVuY2VGb2N1cyhVSS5nZXRSb29tTmFtZSgpLCBVSS5jaGVja0Zvck5pY2tuYW1lQW5kSm9pbik7XG4gICAgICAgICAgICB9LCB1cmwpO1xuICAgICAgICAgICAgaWYgKCFhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbih0cnVlKTtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhcbiAgICAgICAgICAgICAgICAgICAgbnVsbCwgXCJZb3VyIGJyb3dzZXIgaXMgYmxvY2tpbmcgcG9wdXAgd2luZG93cyBmcm9tIHRoaXMgc2l0ZS5cIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBQbGVhc2UgZW5hYmxlIHBvcHVwcyBpbiB5b3VyIGJyb3dzZXIgc2VjdXJpdHkgc2V0dGluZ3NcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBhbmQgdHJ5IGFnYWluLlwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIHJvb20gaW52aXRlIHVybC5cbiAgICAgKi9cbiAgICBteS51cGRhdGVSb29tVXJsID0gZnVuY3Rpb24gKG5ld1Jvb21VcmwpIHtcbiAgICAgICAgcm9vbVVybCA9IG5ld1Jvb21Vcmw7XG5cbiAgICAgICAgLy8gSWYgdGhlIGludml0ZSBkaWFsb2cgaGFzIGJlZW4gYWxyZWFkeSBvcGVuZWQgd2UgdXBkYXRlIHRoZSBpbmZvcm1hdGlvbi5cbiAgICAgICAgdmFyIGludml0ZUxpbmsgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52aXRlTGlua1JlZicpO1xuICAgICAgICBpZiAoaW52aXRlTGluaykge1xuICAgICAgICAgICAgaW52aXRlTGluay52YWx1ZSA9IHJvb21Vcmw7XG4gICAgICAgICAgICBpbnZpdGVMaW5rLnNlbGVjdCgpO1xuICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2pxaV9zdGF0ZTBfYnV0dG9uSW52aXRlJykuZGlzYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlcyBhbmQgZW5hYmxlcyBzb21lIG9mIHRoZSBidXR0b25zLlxuICAgICAqL1xuICAgIG15LnNldHVwQnV0dG9uc0Zyb21Db25maWcgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChjb25maWcuZGlzYWJsZVByZXppKVxuICAgICAgICB7XG4gICAgICAgICAgICAkKFwiI3ByZXppX2J1dHRvblwiKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIGxvY2sgcm9vbSBkaWFsb2cuXG4gICAgICovXG4gICAgbXkub3BlbkxvY2tEaWFsb2cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIE9ubHkgdGhlIGZvY3VzIGlzIGFibGUgdG8gc2V0IGEgc2hhcmVkIGtleS5cbiAgICAgICAgaWYgKCF4bXBwLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgIGlmIChzaGFyZWRLZXkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJUaGlzIGNvbnZlcnNhdGlvbiBpcyBjdXJyZW50bHkgcHJvdGVjdGVkIGJ5XCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgYSBwYXNzd29yZC4gT25seSB0aGUgb3duZXIgb2YgdGhlIGNvbmZlcmVuY2VcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBjb3VsZCBzZXQgYSBwYXNzd29yZC5cIixcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIFwiUGFzc3dvcmRcIik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5NZXNzYWdlRGlhbG9nKG51bGwsXG4gICAgICAgICAgICAgICAgICAgIFwiVGhpcyBjb252ZXJzYXRpb24gaXNuJ3QgY3VycmVudGx5IHByb3RlY3RlZCBieVwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGEgcGFzc3dvcmQuIE9ubHkgdGhlIG93bmVyIG9mIHRoZSBjb25mZXJlbmNlXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgY291bGQgc2V0IGEgcGFzc3dvcmQuXCIsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBcIlBhc3N3b3JkXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHNoYXJlZEtleSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCxcbiAgICAgICAgICAgICAgICAgICAgXCJBcmUgeW91IHN1cmUgeW91IHdvdWxkIGxpa2UgdG8gcmVtb3ZlIHlvdXIgcGFzc3dvcmQ/XCIsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBcIlJlbW92ZVwiLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZSwgdikge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldFNoYXJlZEtleSgnJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9ja1Jvb20oZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAnPGgyPlNldCBhIHBhc3N3b3JkIHRvIGxvY2sgeW91ciByb29tPC9oMj4nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJsb2NrS2V5XCIgdHlwZT1cInRleHRcIicgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJ3BsYWNlaG9sZGVyPVwieW91ciBwYXNzd29yZFwiIGF1dG9mb2N1cz4nLFxuICAgICAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgXCJTYXZlXCIsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlLCB2KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsb2NrS2V5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2tLZXknKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChsb2NrS2V5LnZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvb2xiYXIuc2V0U2hhcmVkS2V5KFV0aWwuZXNjYXBlSHRtbChsb2NrS2V5LnZhbHVlKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2tSb29tKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2tLZXknKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB0aGUgaW52aXRlIGxpbmsgZGlhbG9nLlxuICAgICAqL1xuICAgIG15Lm9wZW5MaW5rRGlhbG9nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW52aXRlTGluaztcbiAgICAgICAgaWYgKHJvb21VcmwgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGludml0ZUxpbmsgPSBcIllvdXIgY29uZmVyZW5jZSBpcyBjdXJyZW50bHkgYmVpbmcgY3JlYXRlZC4uLlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW52aXRlTGluayA9IGVuY29kZVVSSShyb29tVXJsKTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKFxuICAgICAgICAgICAgXCJTaGFyZSB0aGlzIGxpbmsgd2l0aCBldmVyeW9uZSB5b3Ugd2FudCB0byBpbnZpdGVcIixcbiAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJpbnZpdGVMaW5rUmVmXCIgdHlwZT1cInRleHRcIiB2YWx1ZT1cIicgK1xuICAgICAgICAgICAgICAgIGludml0ZUxpbmsgKyAnXCIgb25jbGljaz1cInRoaXMuc2VsZWN0KCk7XCIgcmVhZG9ubHk+JyxcbiAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgXCJJbnZpdGVcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlLCB2KSB7XG4gICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJvb21VcmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGludml0ZVBhcnRpY2lwYW50cygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocm9vbVVybCkge1xuICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52aXRlTGlua1JlZicpLnNlbGVjdCgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdqcWlfc3RhdGUwX2J1dHRvbkludml0ZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIHNldHRpbmdzIGRpYWxvZy5cbiAgICAgKi9cbiAgICBteS5vcGVuU2V0dGluZ3NEaWFsb2cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXG4gICAgICAgICAgICAnPGgyPkNvbmZpZ3VyZSB5b3VyIGNvbmZlcmVuY2U8L2gyPicgK1xuICAgICAgICAgICAgICAgICc8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgaWQ9XCJpbml0TXV0ZWRcIj4nICtcbiAgICAgICAgICAgICAgICAnUGFydGljaXBhbnRzIGpvaW4gbXV0ZWQ8YnIvPicgK1xuICAgICAgICAgICAgICAgICc8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgaWQ9XCJyZXF1aXJlTmlja25hbWVzXCI+JyArXG4gICAgICAgICAgICAgICAgJ1JlcXVpcmUgbmlja25hbWVzPGJyLz48YnIvPicgK1xuICAgICAgICAgICAgICAgICdTZXQgYSBwYXNzd29yZCB0byBsb2NrIHlvdXIgcm9vbTonICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwibG9ja0tleVwiIHR5cGU9XCJ0ZXh0XCIgcGxhY2Vob2xkZXI9XCJ5b3VyIHBhc3N3b3JkXCInICtcbiAgICAgICAgICAgICAgICAnYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcIlNhdmVcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9ja0tleScpLmZvY3VzKCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYpIHtcbiAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICBpZiAoJCgnI2luaXRNdXRlZCcpLmlzKFwiOmNoZWNrZWRcIikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGl0IGlzIGNoZWNrZWRcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICgkKCcjcmVxdWlyZU5pY2tuYW1lcycpLmlzKFwiOmNoZWNrZWRcIikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGl0IGlzIGNoZWNrZWRcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAvKlxuICAgICAgICAgICAgICAgICAgICB2YXIgbG9ja0tleSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NrS2V5Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGxvY2tLZXkudmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldFNoYXJlZEtleShsb2NrS2V5LnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2tSb29tKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBUb2dnbGVzIHRoZSBhcHBsaWNhdGlvbiBpbiBhbmQgb3V0IG9mIGZ1bGwgc2NyZWVuIG1vZGVcbiAgICAgKiAoYS5rLmEuIHByZXNlbnRhdGlvbiBtb2RlIGluIENocm9tZSkuXG4gICAgICovXG4gICAgbXkudG9nZ2xlRnVsbFNjcmVlbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGZzRWxlbWVudCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcblxuICAgICAgICBpZiAoIWRvY3VtZW50Lm1vekZ1bGxTY3JlZW4gJiYgIWRvY3VtZW50LndlYmtpdElzRnVsbFNjcmVlbikge1xuICAgICAgICAgICAgLy9FbnRlciBGdWxsIFNjcmVlblxuICAgICAgICAgICAgaWYgKGZzRWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbikge1xuICAgICAgICAgICAgICAgIGZzRWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgZnNFbGVtZW50LndlYmtpdFJlcXVlc3RGdWxsU2NyZWVuKEVsZW1lbnQuQUxMT1dfS0VZQk9BUkRfSU5QVVQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy9FeGl0IEZ1bGwgU2NyZWVuXG4gICAgICAgICAgICBpZiAoZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbikge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50Lm1vekNhbmNlbEZ1bGxTY3JlZW4oKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQud2Via2l0Q2FuY2VsRnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgICAvKipcbiAgICAgKiBVbmxvY2tzIHRoZSBsb2NrIGJ1dHRvbiBzdGF0ZS5cbiAgICAgKi9cbiAgICBteS51bmxvY2tMb2NrQnV0dG9uID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoJChcIiNsb2NrSWNvblwiKS5oYXNDbGFzcyhcImljb24tc2VjdXJpdHktbG9ja2VkXCIpKVxuICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI2xvY2tJY29uXCIsIFwiaWNvbi1zZWN1cml0eSBpY29uLXNlY3VyaXR5LWxvY2tlZFwiKTtcbiAgICB9O1xuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGxvY2sgYnV0dG9uIHN0YXRlIHRvIGxvY2tlZC5cbiAgICAgKi9cbiAgICBteS5sb2NrTG9ja0J1dHRvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKCQoXCIjbG9ja0ljb25cIikuaGFzQ2xhc3MoXCJpY29uLXNlY3VyaXR5XCIpKVxuICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI2xvY2tJY29uXCIsIFwiaWNvbi1zZWN1cml0eSBpY29uLXNlY3VyaXR5LWxvY2tlZFwiKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3Mgb3IgaGlkZXMgYXV0aGVudGljYXRpb24gYnV0dG9uXG4gICAgICogQHBhcmFtIHNob3cgPHR0PnRydWU8L3R0PiB0byBzaG93IG9yIDx0dD5mYWxzZTwvdHQ+IHRvIGhpZGVcbiAgICAgKi9cbiAgICBteS5zaG93QXV0aGVudGljYXRlQnV0dG9uID0gZnVuY3Rpb24gKHNob3cpIHtcbiAgICAgICAgaWYgKHNob3cpIHtcbiAgICAgICAgICAgICQoJyNhdXRoZW50aWNhdGlvbicpLmNzcyh7ZGlzcGxheTogXCJpbmxpbmVcIn0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgJCgnI2F1dGhlbnRpY2F0aW9uJykuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8vIFNob3dzIG9yIGhpZGVzIHRoZSAncmVjb3JkaW5nJyBidXR0b24uXG4gICAgbXkuc2hvd1JlY29yZGluZ0J1dHRvbiA9IGZ1bmN0aW9uIChzaG93KSB7XG4gICAgICAgIGlmICghY29uZmlnLmVuYWJsZVJlY29yZGluZykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHNob3cpIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRpbmcnKS5jc3Moe2Rpc3BsYXk6IFwiaW5saW5lXCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRpbmcnKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLy8gU2V0cyB0aGUgc3RhdGUgb2YgdGhlIHJlY29yZGluZyBidXR0b25cbiAgICBteS5zZXRSZWNvcmRpbmdCdXR0b25TdGF0ZSA9IGZ1bmN0aW9uIChpc1JlY29yZGluZykge1xuICAgICAgICBpZiAoaXNSZWNvcmRpbmcpIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRCdXR0b24nKS5yZW1vdmVDbGFzcyhcImljb24tcmVjRW5hYmxlXCIpO1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLmFkZENsYXNzKFwiaWNvbi1yZWNFbmFibGUgYWN0aXZlXCIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLnJlbW92ZUNsYXNzKFwiaWNvbi1yZWNFbmFibGUgYWN0aXZlXCIpO1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLmFkZENsYXNzKFwiaWNvbi1yZWNFbmFibGVcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLy8gU2hvd3Mgb3IgaGlkZXMgU0lQIGNhbGxzIGJ1dHRvblxuICAgIG15LnNob3dTaXBDYWxsQnV0dG9uID0gZnVuY3Rpb24gKHNob3cpIHtcbiAgICAgICAgaWYgKHhtcHAuaXNTaXBHYXRld2F5RW5hYmxlZCgpICYmIHNob3cpIHtcbiAgICAgICAgICAgICQoJyNzaXBDYWxsQnV0dG9uJykuY3NzKHtkaXNwbGF5OiBcImlubGluZVwifSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAkKCcjc2lwQ2FsbEJ1dHRvbicpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBzdGF0ZSBvZiB0aGUgYnV0dG9uLiBUaGUgYnV0dG9uIGhhcyBibHVlIGdsb3cgaWYgZGVza3RvcFxuICAgICAqIHN0cmVhbWluZyBpcyBhY3RpdmUuXG4gICAgICogQHBhcmFtIGFjdGl2ZSB0aGUgc3RhdGUgb2YgdGhlIGRlc2t0b3Agc3RyZWFtaW5nLlxuICAgICAqL1xuICAgIG15LmNoYW5nZURlc2t0b3BTaGFyaW5nQnV0dG9uU3RhdGUgPSBmdW5jdGlvbiAoYWN0aXZlKSB7XG4gICAgICAgIHZhciBidXR0b24gPSAkKFwiI2Rlc2t0b3BzaGFyaW5nID4gYVwiKTtcbiAgICAgICAgaWYgKGFjdGl2ZSlcbiAgICAgICAge1xuICAgICAgICAgICAgYnV0dG9uLmFkZENsYXNzKFwiZ2xvd1wiKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ1dHRvbi5yZW1vdmVDbGFzcyhcImdsb3dcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIG15O1xufShUb29sYmFyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gVG9vbGJhcjsiLCJ2YXIgSml0c2lQb3BvdmVyID0gKGZ1bmN0aW9uICgpIHtcbiAgICAvKipcbiAgICAgKiBDb25zdHJ1Y3RzIG5ldyBKaXRzaVBvcG92ZXIgYW5kIGF0dGFjaGVzIGl0IHRvIHRoZSBlbGVtZW50XG4gICAgICogQHBhcmFtIGVsZW1lbnQganF1ZXJ5IHNlbGVjdG9yXG4gICAgICogQHBhcmFtIG9wdGlvbnMgdGhlIG9wdGlvbnMgZm9yIHRoZSBwb3BvdmVyLlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqL1xuICAgIGZ1bmN0aW9uIEppdHNpUG9wb3ZlcihlbGVtZW50LCBvcHRpb25zKVxuICAgIHtcbiAgICAgICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgICAgICAgc2tpbjogXCJ3aGl0ZVwiLFxuICAgICAgICAgICAgY29udGVudDogXCJcIlxuICAgICAgICB9O1xuICAgICAgICBpZihvcHRpb25zKVxuICAgICAgICB7XG4gICAgICAgICAgICBpZihvcHRpb25zLnNraW4pXG4gICAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLnNraW4gPSBvcHRpb25zLnNraW47XG5cbiAgICAgICAgICAgIGlmKG9wdGlvbnMuY29udGVudClcbiAgICAgICAgICAgICAgICB0aGlzLm9wdGlvbnMuY29udGVudCA9IG9wdGlvbnMuY29udGVudDtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZWxlbWVudElzSG92ZXJlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnBvcG92ZXJJc0hvdmVyZWQgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5wb3BvdmVyU2hvd24gPSBmYWxzZTtcblxuICAgICAgICBlbGVtZW50LmRhdGEoXCJqaXRzaV9wb3BvdmVyXCIsIHRoaXMpO1xuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50O1xuICAgICAgICB0aGlzLnRlbXBsYXRlID0gJyA8ZGl2IGNsYXNzPVwiaml0c2lwb3BvdmVyICcgKyB0aGlzLm9wdGlvbnMuc2tpbiArXG4gICAgICAgICAgICAnXCI+PGRpdiBjbGFzcz1cImFycm93XCI+PC9kaXY+PGRpdiBjbGFzcz1cImppdHNpcG9wb3Zlci1jb250ZW50XCI+PC9kaXY+JyArXG4gICAgICAgICAgICAnPGRpdiBjbGFzcz1cImppdHNpUG9wdXBtZW51UGFkZGluZ1wiPjwvZGl2PjwvZGl2Pic7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgdGhpcy5lbGVtZW50Lm9uKFwibW91c2VlbnRlclwiLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZWxmLmVsZW1lbnRJc0hvdmVyZWQgPSB0cnVlO1xuICAgICAgICAgICAgc2VsZi5zaG93KCk7XG4gICAgICAgIH0pLm9uKFwibW91c2VsZWF2ZVwiLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZWxmLmVsZW1lbnRJc0hvdmVyZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHNlbGYuaGlkZSgpO1xuICAgICAgICAgICAgfSwgMTApO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB0aGUgcG9wb3ZlclxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUuc2hvdyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdGhpcy5jcmVhdGVQb3BvdmVyKCk7XG4gICAgICAgIHRoaXMucG9wb3ZlclNob3duID0gdHJ1ZTtcblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyB0aGUgcG9wb3ZlclxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUuaGlkZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYoIXRoaXMuZWxlbWVudElzSG92ZXJlZCAmJiAhdGhpcy5wb3BvdmVySXNIb3ZlcmVkICYmIHRoaXMucG9wb3ZlclNob3duKVxuICAgICAgICB7XG4gICAgICAgICAgICB0aGlzLmZvcmNlSGlkZSgpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIHRoZSBwb3BvdmVyXG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS5mb3JjZUhpZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICQoXCIuaml0c2lwb3BvdmVyXCIpLnJlbW92ZSgpO1xuICAgICAgICB0aGlzLnBvcG92ZXJTaG93biA9IGZhbHNlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIHRoZSBwb3BvdmVyIGh0bWxcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLmNyZWF0ZVBvcG92ZXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICQoXCJib2R5XCIpLmFwcGVuZCh0aGlzLnRlbXBsYXRlKTtcbiAgICAgICAgJChcIi5qaXRzaXBvcG92ZXIgPiAuaml0c2lwb3BvdmVyLWNvbnRlbnRcIikuaHRtbCh0aGlzLm9wdGlvbnMuY29udGVudCk7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgJChcIi5qaXRzaXBvcG92ZXJcIikub24oXCJtb3VzZWVudGVyXCIsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYucG9wb3ZlcklzSG92ZXJlZCA9IHRydWU7XG4gICAgICAgIH0pLm9uKFwibW91c2VsZWF2ZVwiLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZWxmLnBvcG92ZXJJc0hvdmVyZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHNlbGYuaGlkZSgpO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnJlZnJlc2hQb3NpdGlvbigpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZWZyZXNoZXMgdGhlIHBvc2l0aW9uIG9mIHRoZSBwb3BvdmVyXG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS5yZWZyZXNoUG9zaXRpb24gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICQoXCIuaml0c2lwb3BvdmVyXCIpLnBvc2l0aW9uKHtcbiAgICAgICAgICAgIG15OiBcImJvdHRvbVwiLFxuICAgICAgICAgICAgYXQ6IFwidG9wXCIsXG4gICAgICAgICAgICBjb2xsaXNpb246IFwiZml0XCIsXG4gICAgICAgICAgICBvZjogdGhpcy5lbGVtZW50LFxuICAgICAgICAgICAgdXNpbmc6IGZ1bmN0aW9uIChwb3NpdGlvbiwgZWxlbWVudHMpIHtcbiAgICAgICAgICAgICAgICB2YXIgY2FsY0xlZnQgPSBlbGVtZW50cy50YXJnZXQubGVmdCAtIGVsZW1lbnRzLmVsZW1lbnQubGVmdCArIGVsZW1lbnRzLnRhcmdldC53aWR0aC8yO1xuICAgICAgICAgICAgICAgICQoXCIuaml0c2lwb3BvdmVyXCIpLmNzcyh7dG9wOiBwb3NpdGlvbi50b3AsIGxlZnQ6IHBvc2l0aW9uLmxlZnQsIGRpc3BsYXk6IFwidGFibGVcIn0pO1xuICAgICAgICAgICAgICAgICQoXCIuaml0c2lwb3BvdmVyID4gLmFycm93XCIpLmNzcyh7bGVmdDogY2FsY0xlZnR9KTtcbiAgICAgICAgICAgICAgICAkKFwiLmppdHNpcG9wb3ZlciA+IC5qaXRzaVBvcHVwbWVudVBhZGRpbmdcIikuY3NzKHtsZWZ0OiBjYWxjTGVmdCAtIDUwfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBjb250ZW50IG9mIHBvcG92ZXIuXG4gICAgICogQHBhcmFtIGNvbnRlbnQgbmV3IGNvbnRlbnRcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLnVwZGF0ZUNvbnRlbnQgPSBmdW5jdGlvbiAoY29udGVudCkge1xuICAgICAgICB0aGlzLm9wdGlvbnMuY29udGVudCA9IGNvbnRlbnQ7XG4gICAgICAgIGlmKCF0aGlzLnBvcG92ZXJTaG93bilcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgJChcIi5qaXRzaXBvcG92ZXJcIikucmVtb3ZlKCk7XG4gICAgICAgIHRoaXMuY3JlYXRlUG9wb3ZlcigpO1xuICAgIH07XG5cbiAgICByZXR1cm4gSml0c2lQb3BvdmVyO1xuXG5cbn0pKCk7XG5cbm1vZHVsZS5leHBvcnRzID0gSml0c2lQb3BvdmVyOyIsIi8qIGdsb2JhbCAkLCBqUXVlcnkgKi9cbnZhciBtZXNzYWdlSGFuZGxlciA9IChmdW5jdGlvbihteSkge1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MgYSBtZXNzYWdlIHRvIHRoZSB1c2VyLlxuICAgICAqXG4gICAgICogQHBhcmFtIHRpdGxlU3RyaW5nIHRoZSB0aXRsZSBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBtZXNzYWdlU3RyaW5nIHRoZSB0ZXh0IG9mIHRoZSBtZXNzYWdlXG4gICAgICovXG4gICAgbXkub3Blbk1lc3NhZ2VEaWFsb2cgPSBmdW5jdGlvbih0aXRsZVN0cmluZywgbWVzc2FnZVN0cmluZykge1xuICAgICAgICAkLnByb21wdChtZXNzYWdlU3RyaW5nLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHRpdGxlOiB0aXRsZVN0cmluZyxcbiAgICAgICAgICAgICAgICBwZXJzaXN0ZW50OiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIG1lc3NhZ2UgdG8gdGhlIHVzZXIgd2l0aCB0d28gYnV0dG9uczogZmlyc3QgaXMgZ2l2ZW4gYXMgYSBwYXJhbWV0ZXIgYW5kIHRoZSBzZWNvbmQgaXMgQ2FuY2VsLlxuICAgICAqXG4gICAgICogQHBhcmFtIHRpdGxlU3RyaW5nIHRoZSB0aXRsZSBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBtc2dTdHJpbmcgdGhlIHRleHQgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gcGVyc2lzdGVudCBib29sZWFuIHZhbHVlIHdoaWNoIGRldGVybWluZXMgd2hldGhlciB0aGUgbWVzc2FnZSBpcyBwZXJzaXN0ZW50IG9yIG5vdFxuICAgICAqIEBwYXJhbSBsZWZ0QnV0dG9uIHRoZSBmaXN0IGJ1dHRvbidzIHRleHRcbiAgICAgKiBAcGFyYW0gc3VibWl0RnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIG9uIHN1Ym1pdFxuICAgICAqIEBwYXJhbSBsb2FkZWRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBmdWxseSBsb2FkZWRcbiAgICAgKiBAcGFyYW0gY2xvc2VGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBjbG9zZWRcbiAgICAgKi9cbiAgICBteS5vcGVuVHdvQnV0dG9uRGlhbG9nID0gZnVuY3Rpb24odGl0bGVTdHJpbmcsIG1zZ1N0cmluZywgcGVyc2lzdGVudCwgbGVmdEJ1dHRvbixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VibWl0RnVuY3Rpb24sIGxvYWRlZEZ1bmN0aW9uLCBjbG9zZUZ1bmN0aW9uKSB7XG4gICAgICAgIHZhciBidXR0b25zID0ge307XG4gICAgICAgIGJ1dHRvbnNbbGVmdEJ1dHRvbl0gPSB0cnVlO1xuICAgICAgICBidXR0b25zLkNhbmNlbCA9IGZhbHNlO1xuICAgICAgICAkLnByb21wdChtc2dTdHJpbmcsIHtcbiAgICAgICAgICAgIHRpdGxlOiB0aXRsZVN0cmluZyxcbiAgICAgICAgICAgIHBlcnNpc3RlbnQ6IGZhbHNlLFxuICAgICAgICAgICAgYnV0dG9uczogYnV0dG9ucyxcbiAgICAgICAgICAgIGRlZmF1bHRCdXR0b246IDEsXG4gICAgICAgICAgICBsb2FkZWQ6IGxvYWRlZEZ1bmN0aW9uLFxuICAgICAgICAgICAgc3VibWl0OiBzdWJtaXRGdW5jdGlvbixcbiAgICAgICAgICAgIGNsb3NlOiBjbG9zZUZ1bmN0aW9uXG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIG1lc3NhZ2UgdG8gdGhlIHVzZXIgd2l0aCB0d28gYnV0dG9uczogZmlyc3QgaXMgZ2l2ZW4gYXMgYSBwYXJhbWV0ZXIgYW5kIHRoZSBzZWNvbmQgaXMgQ2FuY2VsLlxuICAgICAqXG4gICAgICogQHBhcmFtIHRpdGxlU3RyaW5nIHRoZSB0aXRsZSBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBtc2dTdHJpbmcgdGhlIHRleHQgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gcGVyc2lzdGVudCBib29sZWFuIHZhbHVlIHdoaWNoIGRldGVybWluZXMgd2hldGhlciB0aGUgbWVzc2FnZSBpcyBwZXJzaXN0ZW50IG9yIG5vdFxuICAgICAqIEBwYXJhbSBidXR0b25zIG9iamVjdCB3aXRoIHRoZSBidXR0b25zLiBUaGUga2V5cyBtdXN0IGJlIHRoZSBuYW1lIG9mIHRoZSBidXR0b24gYW5kIHZhbHVlIGlzIHRoZSB2YWx1ZVxuICAgICAqIHRoYXQgd2lsbCBiZSBwYXNzZWQgdG8gc3VibWl0RnVuY3Rpb25cbiAgICAgKiBAcGFyYW0gc3VibWl0RnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIG9uIHN1Ym1pdFxuICAgICAqIEBwYXJhbSBsb2FkZWRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBmdWxseSBsb2FkZWRcbiAgICAgKi9cbiAgICBteS5vcGVuRGlhbG9nID0gZnVuY3Rpb24gKHRpdGxlU3RyaW5nLCAgICBtc2dTdHJpbmcsIHBlcnNpc3RlbnQsIGJ1dHRvbnMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJtaXRGdW5jdGlvbiwgbG9hZGVkRnVuY3Rpb24pIHtcbiAgICAgICAgdmFyIGFyZ3MgPSB7XG4gICAgICAgICAgICB0aXRsZTogdGl0bGVTdHJpbmcsXG4gICAgICAgICAgICBwZXJzaXN0ZW50OiBwZXJzaXN0ZW50LFxuICAgICAgICAgICAgYnV0dG9uczogYnV0dG9ucyxcbiAgICAgICAgICAgIGRlZmF1bHRCdXR0b246IDEsXG4gICAgICAgICAgICBsb2FkZWQ6IGxvYWRlZEZ1bmN0aW9uLFxuICAgICAgICAgICAgc3VibWl0OiBzdWJtaXRGdW5jdGlvblxuICAgICAgICB9O1xuICAgICAgICBpZiAocGVyc2lzdGVudCkge1xuICAgICAgICAgICAgYXJncy5jbG9zZVRleHQgPSAnJztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gJC5wcm9tcHQobXNnU3RyaW5nLCBhcmdzKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xvc2VzIGN1cnJlbnRseSBvcGVuZWQgZGlhbG9nLlxuICAgICAqL1xuICAgIG15LmNsb3NlRGlhbG9nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAkLnByb21wdC5jbG9zZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIGRpYWxvZyB3aXRoIGRpZmZlcmVudCBzdGF0ZXMgdG8gdGhlIHVzZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gc3RhdGVzT2JqZWN0IG9iamVjdCBjb250YWluaW5nIGFsbCB0aGUgc3RhdGVzIG9mIHRoZSBkaWFsb2dcbiAgICAgKiBAcGFyYW0gbG9hZGVkRnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIGFmdGVyIHRoZSBwcm9tcHQgaXMgZnVsbHkgbG9hZGVkXG4gICAgICogQHBhcmFtIHN0YXRlQ2hhbmdlZEZ1bmN0aW9uIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBzdGF0ZSBvZiB0aGUgZGlhbG9nIGlzIGNoYW5nZWRcbiAgICAgKi9cbiAgICBteS5vcGVuRGlhbG9nV2l0aFN0YXRlcyA9IGZ1bmN0aW9uKHN0YXRlc09iamVjdCwgbG9hZGVkRnVuY3Rpb24sIHN0YXRlQ2hhbmdlZEZ1bmN0aW9uKSB7XG5cblxuICAgICAgICB2YXIgbXlQcm9tcHQgPSAkLnByb21wdChzdGF0ZXNPYmplY3QpO1xuXG4gICAgICAgIG15UHJvbXB0Lm9uKCdpbXByb21wdHU6bG9hZGVkJywgbG9hZGVkRnVuY3Rpb24pO1xuICAgICAgICBteVByb21wdC5vbignaW1wcm9tcHR1OnN0YXRlY2hhbmdlZCcsIHN0YXRlQ2hhbmdlZEZ1bmN0aW9uKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgbmV3IHBvcHVwIHdpbmRvdyBmb3IgZ2l2ZW4gPHR0PnVybDwvdHQ+IGNlbnRlcmVkIG92ZXIgY3VycmVudFxuICAgICAqIHdpbmRvdy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB1cmwgdGhlIFVSTCB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHBvcHVwIHdpbmRvd1xuICAgICAqIEBwYXJhbSB3IHRoZSB3aWR0aCBvZiB0aGUgcG9wdXAgd2luZG93XG4gICAgICogQHBhcmFtIGggdGhlIGhlaWdodCBvZiB0aGUgcG9wdXAgd2luZG93XG4gICAgICogQHBhcmFtIG9uUG9wdXBDbG9zZWQgb3B0aW9uYWwgY2FsbGJhY2sgZnVuY3Rpb24gY2FsbGVkIHdoZW4gcG9wdXAgd2luZG93XG4gICAgICogICAgICAgIGhhcyBiZWVuIGNsb3NlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHBvcHVwIHdpbmRvdyBvYmplY3QgaWYgb3BlbmVkIHN1Y2Nlc3NmdWxseSBvciB1bmRlZmluZWRcbiAgICAgKiAgICAgICAgICBpbiBjYXNlIHdlIGZhaWxlZCB0byBvcGVuIGl0KHBvcHVwIGJsb2NrZWQpXG4gICAgICovXG4gICAgbXkub3BlbkNlbnRlcmVkUG9wdXAgPSBmdW5jdGlvbiAodXJsLCB3LCBoLCBvblBvcHVwQ2xvc2VkKSB7XG4gICAgICAgIHZhciBsID0gd2luZG93LnNjcmVlblggKyAod2luZG93LmlubmVyV2lkdGggLyAyKSAtICh3IC8gMik7XG4gICAgICAgIHZhciB0ID0gd2luZG93LnNjcmVlblkgKyAod2luZG93LmlubmVySGVpZ2h0IC8gMikgLSAoaCAvIDIpO1xuICAgICAgICB2YXIgcG9wdXAgPSB3aW5kb3cub3BlbihcbiAgICAgICAgICAgIHVybCwgJ19ibGFuaycsXG4gICAgICAgICAgICAndG9wPScgKyB0ICsgJywgbGVmdD0nICsgbCArICcsIHdpZHRoPScgKyB3ICsgJywgaGVpZ2h0PScgKyBoICsgJycpO1xuICAgICAgICBpZiAocG9wdXAgJiYgb25Qb3B1cENsb3NlZCkge1xuICAgICAgICAgICAgdmFyIHBvbGxUaW1lciA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBvcHVwLmNsb3NlZCAhPT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LmNsZWFySW50ZXJ2YWwocG9sbFRpbWVyKTtcbiAgICAgICAgICAgICAgICAgICAgb25Qb3B1cENsb3NlZCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sIDIwMCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBvcHVwO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIGRpYWxvZyBwcm9tcHRpbmcgdGhlIHVzZXIgdG8gc2VuZCBhbiBlcnJvciByZXBvcnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGl0bGVTdHJpbmcgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1zZ1N0cmluZyB0aGUgdGV4dCBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBlcnJvciB0aGUgZXJyb3IgdGhhdCBpcyBiZWluZyByZXBvcnRlZFxuICAgICAqL1xuICAgIG15Lm9wZW5SZXBvcnREaWFsb2cgPSBmdW5jdGlvbih0aXRsZVN0cmluZywgbXNnU3RyaW5nLCBlcnJvcikge1xuICAgICAgICBteS5vcGVuTWVzc2FnZURpYWxvZyh0aXRsZVN0cmluZywgbXNnU3RyaW5nKTtcbiAgICAgICAgY29uc29sZS5sb2coZXJyb3IpO1xuICAgICAgICAvL0ZJWE1FIHNlbmQgdGhlIGVycm9yIHRvIHRoZSBzZXJ2ZXJcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogIFNob3dzIGFuIGVycm9yIGRpYWxvZyB0byB0aGUgdXNlci5cbiAgICAgKiBAcGFyYW0gdGl0bGUgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1lc3NhZ2UgdGhlIHRleHQgb2YgdGhlIG1lc3NhZmVcbiAgICAgKi9cbiAgICBteS5zaG93RXJyb3IgPSBmdW5jdGlvbih0aXRsZSwgbWVzc2FnZSkge1xuICAgICAgICBpZighKHRpdGxlIHx8IG1lc3NhZ2UpKSB7XG4gICAgICAgICAgICB0aXRsZSA9IHRpdGxlIHx8IFwiT29wcyFcIjtcbiAgICAgICAgICAgIG1lc3NhZ2UgPSBtZXNzYWdlIHx8IFwiVGhlcmUgd2FzIHNvbWUga2luZCBvZiBlcnJvclwiO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5NZXNzYWdlRGlhbG9nKHRpdGxlLCBtZXNzYWdlKTtcbiAgICB9O1xuXG4gICAgbXkubm90aWZ5ID0gZnVuY3Rpb24oZGlzcGxheU5hbWUsIGNscywgbWVzc2FnZSkge1xuICAgICAgICB0b2FzdHIuaW5mbyhcbiAgICAgICAgICAgICc8c3BhbiBjbGFzcz1cIm5pY2tuYW1lXCI+JyArXG4gICAgICAgICAgICAgICAgZGlzcGxheU5hbWUgK1xuICAgICAgICAgICAgJzwvc3Bhbj48YnI+JyArXG4gICAgICAgICAgICAnPHNwYW4gY2xhc3M9JyArIGNscyArICc+JyArXG4gICAgICAgICAgICAgICAgbWVzc2FnZSArXG4gICAgICAgICAgICAnPC9zcGFuPicpO1xuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG59KG1lc3NhZ2VIYW5kbGVyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gbWVzc2FnZUhhbmRsZXI7XG5cblxuIiwiLyoqXG4gKiBDcmVhdGVkIGJ5IGhyaXN0byBvbiAxMi8yMi8xNC5cbiAqL1xubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgYXZhaWxhYmxlIHZpZGVvIHdpZHRoLlxuICAgICAqL1xuICAgIGdldEF2YWlsYWJsZVZpZGVvV2lkdGg6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIFBhbmVsVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvU2lkZVBhbmVsVG9nZ2xlclwiKTtcbiAgICAgICAgdmFyIHJpZ2h0UGFuZWxXaWR0aFxuICAgICAgICAgICAgPSBQYW5lbFRvZ2dsZXIuaXNWaXNpYmxlKCkgPyBQYW5lbFRvZ2dsZXIuZ2V0UGFuZWxTaXplKClbMF0gOiAwO1xuXG4gICAgICAgIHJldHVybiB3aW5kb3cuaW5uZXJXaWR0aCAtIHJpZ2h0UGFuZWxXaWR0aDtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIENoYW5nZXMgdGhlIHN0eWxlIGNsYXNzIG9mIHRoZSBlbGVtZW50IGdpdmVuIGJ5IGlkLlxuICAgICAqL1xuICAgIGJ1dHRvbkNsaWNrOiBmdW5jdGlvbihpZCwgY2xhc3NuYW1lKSB7XG4gICAgICAgICQoaWQpLnRvZ2dsZUNsYXNzKGNsYXNzbmFtZSk7IC8vIGFkZCB0aGUgY2xhc3MgdG8gdGhlIGNsaWNrZWQgZWxlbWVudFxuICAgIH1cblxuXG59OyIsInZhciBKaXRzaVBvcG92ZXIgPSByZXF1aXJlKFwiLi4vdXRpbC9KaXRzaVBvcG92ZXJcIik7XG5cbi8qKlxuICogQ29uc3RydWN0cyBuZXcgY29ubmVjdGlvbiBpbmRpY2F0b3IuXG4gKiBAcGFyYW0gdmlkZW9Db250YWluZXIgdGhlIHZpZGVvIGNvbnRhaW5lciBhc3NvY2lhdGVkIHdpdGggdGhlIGluZGljYXRvci5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBDb25uZWN0aW9uSW5kaWNhdG9yKHZpZGVvQ29udGFpbmVyLCBqaWQsIFZpZGVvTGF5b3V0KVxue1xuICAgIHRoaXMudmlkZW9Db250YWluZXIgPSB2aWRlb0NvbnRhaW5lcjtcbiAgICB0aGlzLmJhbmR3aWR0aCA9IG51bGw7XG4gICAgdGhpcy5wYWNrZXRMb3NzID0gbnVsbDtcbiAgICB0aGlzLmJpdHJhdGUgPSBudWxsO1xuICAgIHRoaXMuc2hvd01vcmVWYWx1ZSA9IGZhbHNlO1xuICAgIHRoaXMucmVzb2x1dGlvbiA9IG51bGw7XG4gICAgdGhpcy50cmFuc3BvcnQgPSBbXTtcbiAgICB0aGlzLnBvcG92ZXIgPSBudWxsO1xuICAgIHRoaXMuamlkID0gamlkO1xuICAgIHRoaXMuY3JlYXRlKCk7XG4gICAgdGhpcy52aWRlb0xheW91dCA9IFZpZGVvTGF5b3V0O1xufVxuXG4vKipcbiAqIFZhbHVlcyBmb3IgdGhlIGNvbm5lY3Rpb24gcXVhbGl0eVxuICogQHR5cGUge3s5ODogc3RyaW5nLFxuICogICAgICAgICA4MTogc3RyaW5nLFxuICogICAgICAgICA2NDogc3RyaW5nLFxuICogICAgICAgICA0Nzogc3RyaW5nLFxuICogICAgICAgICAzMDogc3RyaW5nLFxuICogICAgICAgICAwOiBzdHJpbmd9fVxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLmNvbm5lY3Rpb25RdWFsaXR5VmFsdWVzID0ge1xuICAgIDk4OiBcIjE4cHhcIiwgLy9mdWxsXG4gICAgODE6IFwiMTVweFwiLC8vNCBiYXJzXG4gICAgNjQ6IFwiMTFweFwiLC8vMyBiYXJzXG4gICAgNDc6IFwiN3B4XCIsLy8yIGJhcnNcbiAgICAzMDogXCIzcHhcIiwvLzEgYmFyXG4gICAgMDogXCIwcHhcIi8vZW1wdHlcbn07XG5cbkNvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0SVAgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKDAsIHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSArIDEsIHZhbHVlLmxlbmd0aCk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFN0cmluZ0Zyb21BcnJheSA9IGZ1bmN0aW9uIChhcnJheSkge1xuICAgIHZhciByZXMgPSBcIlwiO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKylcbiAgICB7XG4gICAgICAgIHJlcyArPSAoaSA9PT0gMD8gXCJcIiA6IFwiLCBcIikgKyBhcnJheVtpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlcztcbn07XG5cbi8qKlxuICogR2VuZXJhdGVzIHRoZSBodG1sIGNvbnRlbnQuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSB0aGUgaHRtbCBjb250ZW50LlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5nZW5lcmF0ZVRleHQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGRvd25sb2FkQml0cmF0ZSwgdXBsb2FkQml0cmF0ZSwgcGFja2V0TG9zcywgcmVzb2x1dGlvbiwgaTtcblxuICAgIGlmKHRoaXMuYml0cmF0ZSA9PT0gbnVsbClcbiAgICB7XG4gICAgICAgIGRvd25sb2FkQml0cmF0ZSA9IFwiTi9BXCI7XG4gICAgICAgIHVwbG9hZEJpdHJhdGUgPSBcIk4vQVwiO1xuICAgIH1cbiAgICBlbHNlXG4gICAge1xuICAgICAgICBkb3dubG9hZEJpdHJhdGUgPVxuICAgICAgICAgICAgdGhpcy5iaXRyYXRlLmRvd25sb2FkPyB0aGlzLmJpdHJhdGUuZG93bmxvYWQgKyBcIiBLYnBzXCIgOiBcIk4vQVwiO1xuICAgICAgICB1cGxvYWRCaXRyYXRlID1cbiAgICAgICAgICAgIHRoaXMuYml0cmF0ZS51cGxvYWQ/IHRoaXMuYml0cmF0ZS51cGxvYWQgKyBcIiBLYnBzXCIgOiBcIk4vQVwiO1xuICAgIH1cblxuICAgIGlmKHRoaXMucGFja2V0TG9zcyA9PT0gbnVsbClcbiAgICB7XG4gICAgICAgIHBhY2tldExvc3MgPSBcIk4vQVwiO1xuICAgIH1cbiAgICBlbHNlXG4gICAge1xuXG4gICAgICAgIHBhY2tldExvc3MgPSBcIjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfZ3JlZW4nPiZkYXJyOzwvc3Bhbj5cIiArXG4gICAgICAgICAgICAodGhpcy5wYWNrZXRMb3NzLmRvd25sb2FkICE9PSBudWxsPyB0aGlzLnBhY2tldExvc3MuZG93bmxvYWQgOiBcIk4vQVwiKSArXG4gICAgICAgICAgICBcIiUgPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9vcmFuZ2UnPiZ1YXJyOzwvc3Bhbj5cIiArXG4gICAgICAgICAgICAodGhpcy5wYWNrZXRMb3NzLnVwbG9hZCAhPT0gbnVsbD8gdGhpcy5wYWNrZXRMb3NzLnVwbG9hZCA6IFwiTi9BXCIpICsgXCIlXCI7XG4gICAgfVxuXG4gICAgdmFyIHJlc29sdXRpb25WYWx1ZSA9IG51bGw7XG4gICAgaWYodGhpcy5yZXNvbHV0aW9uICYmIHRoaXMuamlkICE9IG51bGwpXG4gICAge1xuICAgICAgICB2YXIga2V5cyA9IE9iamVjdC5rZXlzKHRoaXMucmVzb2x1dGlvbik7XG4gICAgICAgIGlmKGtleXMubGVuZ3RoID09IDEpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGZvcih2YXIgc3NyYyBpbiB0aGlzLnJlc29sdXRpb24pXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlID0gdGhpcy5yZXNvbHV0aW9uW3NzcmNdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYoa2V5cy5sZW5ndGggPiAxKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgZGlzcGxheWVkU3NyYyA9IHNpbXVsY2FzdC5nZXRSZWNlaXZpbmdTU1JDKHRoaXMuamlkKTtcbiAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZSA9IHRoaXMucmVzb2x1dGlvbltkaXNwbGF5ZWRTc3JjXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmKHRoaXMuamlkID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiXCI7XG4gICAgICAgIGlmKHRoaXMucmVzb2x1dGlvbiA9PT0gbnVsbCB8fCAhT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKSB8fFxuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKS5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc29sdXRpb24gPSBcIk4vQVwiO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGZvcihpIGluIHRoaXMucmVzb2x1dGlvbilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uVmFsdWUgPSB0aGlzLnJlc29sdXRpb25baV07XG4gICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlLmhlaWdodCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uICs9IChyZXNvbHV0aW9uID09PSBcIlwiPyBcIlwiIDogXCIsIFwiKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoICsgXCJ4XCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgfVxuICAgIGVsc2UgaWYoIXJlc29sdXRpb25WYWx1ZSB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLmhlaWdodCB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHJlc29sdXRpb24gPSByZXNvbHV0aW9uVmFsdWUud2lkdGggKyBcInhcIiArIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgfVxuXG4gICAgdmFyIHJlc3VsdCA9IFwiPHRhYmxlIHN0eWxlPSd3aWR0aDoxMDAlJz5cIiArXG4gICAgICAgIFwiPHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJz5CaXRyYXRlOjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ncmVlbic+JmRhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgZG93bmxvYWRCaXRyYXRlICsgXCIgPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9vcmFuZ2UnPiZ1YXJyOzwvc3Bhbj5cIiArXG4gICAgICAgIHVwbG9hZEJpdHJhdGUgKyBcIjwvdGQ+XCIgK1xuICAgICAgICBcIjwvdHI+PHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJz5QYWNrZXQgbG9zczogPC9zcGFuPjwvdGQ+XCIgK1xuICAgICAgICBcIjx0ZD5cIiArIHBhY2tldExvc3MgICsgXCI8L3RkPlwiICtcbiAgICAgICAgXCI8L3RyPjx0cj5cIiArXG4gICAgICAgIFwiPHRkPjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZSc+UmVzb2x1dGlvbjo8L3NwYW4+PC90ZD5cIiArXG4gICAgICAgIFwiPHRkPlwiICsgcmVzb2x1dGlvbiArIFwiPC90ZD48L3RyPjwvdGFibGU+XCI7XG5cbiAgICBpZih0aGlzLnZpZGVvQ29udGFpbmVyLmlkID09IFwibG9jYWxWaWRlb0NvbnRhaW5lclwiKVxuICAgICAgICByZXN1bHQgKz0gXCI8ZGl2IGNsYXNzPVxcXCJqaXRzaXBvcG92ZXJfc2hvd21vcmVcXFwiIFwiICtcbiAgICAgICAgICAgIFwib25jbGljayA9IFxcXCJVSS5jb25uZWN0aW9uSW5kaWNhdG9yU2hvd01vcmUoJ1wiICtcbiAgICAgICAgICAgIHRoaXMudmlkZW9Db250YWluZXIuaWQgKyBcIicpXFxcIj5cIiArXG4gICAgICAgICAgICAodGhpcy5zaG93TW9yZVZhbHVlPyBcIlNob3cgbGVzc1wiIDogXCJTaG93IE1vcmVcIikgKyBcIjwvZGl2PjxiciAvPlwiO1xuXG4gICAgaWYodGhpcy5zaG93TW9yZVZhbHVlKVxuICAgIHtcbiAgICAgICAgdmFyIGRvd25sb2FkQmFuZHdpZHRoLCB1cGxvYWRCYW5kd2lkdGgsIHRyYW5zcG9ydDtcbiAgICAgICAgaWYodGhpcy5iYW5kd2lkdGggPT09IG51bGwpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGRvd25sb2FkQmFuZHdpZHRoID0gXCJOL0FcIjtcbiAgICAgICAgICAgIHVwbG9hZEJhbmR3aWR0aCA9IFwiTi9BXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBkb3dubG9hZEJhbmR3aWR0aCA9IHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkICsgXCIgS2Jwc1wiIDpcbiAgICAgICAgICAgICAgICBcIk4vQVwiO1xuICAgICAgICAgICAgdXBsb2FkQmFuZHdpZHRoID0gdGhpcy5iYW5kd2lkdGgudXBsb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLnVwbG9hZCArIFwiIEticHNcIiA6XG4gICAgICAgICAgICAgICAgXCJOL0FcIjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCF0aGlzLnRyYW5zcG9ydCB8fCB0aGlzLnRyYW5zcG9ydC5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRyYW5zcG9ydCA9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnPkFkZHJlc3M6PC9zcGFuPjwvdGQ+XCIgK1xuICAgICAgICAgICAgICAgIFwiPHRkPiBOL0E8L3RkPjwvdHI+XCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgZGF0YSA9IHtyZW1vdGVJUDogW10sIGxvY2FsSVA6W10sIHJlbW90ZVBvcnQ6W10sIGxvY2FsUG9ydDpbXX07XG4gICAgICAgICAgICBmb3IoaSA9IDA7IGkgPCB0aGlzLnRyYW5zcG9ydC5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB2YXIgaXAgPSAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRJUCh0aGlzLnRyYW5zcG9ydFtpXS5pcCk7XG4gICAgICAgICAgICAgICAgdmFyIHBvcnQgPSBDb25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQodGhpcy50cmFuc3BvcnRbaV0uaXApO1xuICAgICAgICAgICAgICAgIHZhciBsb2NhbElQID1cbiAgICAgICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRJUCh0aGlzLnRyYW5zcG9ydFtpXS5sb2NhbGlwKTtcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWxQb3J0ID1cbiAgICAgICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRQb3J0KHRoaXMudHJhbnNwb3J0W2ldLmxvY2FsaXApO1xuICAgICAgICAgICAgICAgIGlmKGRhdGEucmVtb3RlSVAuaW5kZXhPZihpcCkgPT0gLTEpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnJlbW90ZUlQLnB1c2goaXApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmKGRhdGEucmVtb3RlUG9ydC5pbmRleE9mKHBvcnQpID09IC0xKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZGF0YS5yZW1vdGVQb3J0LnB1c2gocG9ydCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYoZGF0YS5sb2NhbElQLmluZGV4T2YobG9jYWxJUCkgPT0gLTEpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLmxvY2FsSVAucHVzaChsb2NhbElQKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZihkYXRhLmxvY2FsUG9ydC5pbmRleE9mKGxvY2FsUG9ydCkgPT0gLTEpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLmxvY2FsUG9ydC5wdXNoKGxvY2FsUG9ydCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgbG9jYWxUcmFuc3BvcnQgPVxuICAgICAgICAgICAgICAgIFwiPHRyPjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnPkxvY2FsIGFkZHJlc3NcIiArXG4gICAgICAgICAgICAgICAgKGRhdGEubG9jYWxJUC5sZW5ndGggPiAxPyBcImVzXCIgOiBcIlwiKSArIFwiOiA8L3NwYW4+PC90ZD48dGQ+IFwiICtcbiAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldFN0cmluZ0Zyb21BcnJheShkYXRhLmxvY2FsSVApICtcbiAgICAgICAgICAgICAgICBcIjwvdGQ+PC90cj5cIjtcbiAgICAgICAgICAgIHRyYW5zcG9ydCA9XG4gICAgICAgICAgICAgICAgXCI8dHI+PHRkPjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZSc+UmVtb3RlIGFkZHJlc3NcIitcbiAgICAgICAgICAgICAgICAoZGF0YS5yZW1vdGVJUC5sZW5ndGggPiAxPyBcImVzXCIgOiBcIlwiKSArIFwiOjwvc3Bhbj48L3RkPjx0ZD4gXCIgK1xuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0U3RyaW5nRnJvbUFycmF5KGRhdGEucmVtb3RlSVApICtcbiAgICAgICAgICAgICAgICBcIjwvdGQ+PC90cj5cIjtcbiAgICAgICAgICAgIGlmKHRoaXMudHJhbnNwb3J0Lmxlbmd0aCA+IDEpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdHJhbnNwb3J0ICs9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZSc+UmVtb3RlIHBvcnRzOjwvc3Bhbj5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC90ZD48dGQ+XCI7XG4gICAgICAgICAgICAgICAgbG9jYWxUcmFuc3BvcnQgKz0gXCI8dHI+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjx0ZD5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJz5Mb2NhbCBwb3J0czo8L3NwYW4+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjwvdGQ+PHRkPlwiO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHRyYW5zcG9ydCArPVxuICAgICAgICAgICAgICAgICAgICBcIjx0cj5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPHRkPlwiICtcbiAgICAgICAgICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnPlJlbW90ZSBwb3J0Ojwvc3Bhbj5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC90ZD48dGQ+XCI7XG4gICAgICAgICAgICAgICAgbG9jYWxUcmFuc3BvcnQgKz1cbiAgICAgICAgICAgICAgICAgICAgXCI8dHI+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjx0ZD5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJz5Mb2NhbCBwb3J0Ojwvc3Bhbj5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC90ZD48dGQ+XCI7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRyYW5zcG9ydCArPVxuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0U3RyaW5nRnJvbUFycmF5KGRhdGEucmVtb3RlUG9ydCk7XG4gICAgICAgICAgICBsb2NhbFRyYW5zcG9ydCArPVxuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0U3RyaW5nRnJvbUFycmF5KGRhdGEubG9jYWxQb3J0KTtcbiAgICAgICAgICAgIHRyYW5zcG9ydCArPSBcIjwvdGQ+PC90cj5cIjtcbiAgICAgICAgICAgIHRyYW5zcG9ydCArPSBsb2NhbFRyYW5zcG9ydCArIFwiPC90ZD48L3RyPlwiO1xuICAgICAgICAgICAgdHJhbnNwb3J0ICs9XCI8dHI+XCIgK1xuICAgICAgICAgICAgICAgIFwiPHRkPjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZSc+VHJhbnNwb3J0Ojwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD5cIiArIHRoaXMudHJhbnNwb3J0WzBdLnR5cGUgKyBcIjwvdGQ+PC90cj5cIjtcblxuICAgICAgICB9XG5cbiAgICAgICAgcmVzdWx0ICs9IFwiPHRhYmxlICBzdHlsZT0nd2lkdGg6MTAwJSc+XCIgK1xuICAgICAgICAgICAgXCI8dHI+XCIgK1xuICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnPkVzdGltYXRlZCBiYW5kd2lkdGg6PC9zcGFuPlwiICtcbiAgICAgICAgICAgIFwiPC90ZD48dGQ+XCIgK1xuICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2dyZWVuJz4mZGFycjs8L3NwYW4+XCIgK1xuICAgICAgICAgICAgZG93bmxvYWRCYW5kd2lkdGggK1xuICAgICAgICAgICAgXCIgPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9vcmFuZ2UnPiZ1YXJyOzwvc3Bhbj5cIiArXG4gICAgICAgICAgICB1cGxvYWRCYW5kd2lkdGggKyBcIjwvdGQ+PC90cj5cIjtcblxuICAgICAgICByZXN1bHQgKz0gdHJhbnNwb3J0ICsgXCI8L3RhYmxlPlwiO1xuXG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qKlxuICogU2hvd3Mgb3IgaGlkZSB0aGUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbi5cbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUuc2hvd01vcmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5zaG93TW9yZVZhbHVlID0gIXRoaXMuc2hvd01vcmVWYWx1ZTtcbiAgICB0aGlzLnVwZGF0ZVBvcG92ZXJEYXRhKCk7XG59O1xuXG5cbmZ1bmN0aW9uIGNyZWF0ZUljb24oY2xhc3NlcylcbntcbiAgICB2YXIgaWNvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIpO1xuICAgIGZvcih2YXIgaSBpbiBjbGFzc2VzKVxuICAgIHtcbiAgICAgICAgaWNvbi5jbGFzc0xpc3QuYWRkKGNsYXNzZXNbaV0pO1xuICAgIH1cbiAgICBpY29uLmFwcGVuZENoaWxkKFxuICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiaVwiKSkuY2xhc3NMaXN0LmFkZChcImljb24tY29ubmVjdGlvblwiKTtcbiAgICByZXR1cm4gaWNvbjtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBpbmRpY2F0b3JcbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUuY3JlYXRlID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLmNsYXNzTmFtZSA9IFwiY29ubmVjdGlvbmluZGljYXRvclwiO1xuICAgIHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XG4gICAgdGhpcy52aWRlb0NvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIpO1xuICAgIHRoaXMucG9wb3ZlciA9IG5ldyBKaXRzaVBvcG92ZXIoXG4gICAgICAgICQoXCIjXCIgKyB0aGlzLnZpZGVvQ29udGFpbmVyLmlkICsgXCIgPiAuY29ubmVjdGlvbmluZGljYXRvclwiKSxcbiAgICAgICAge2NvbnRlbnQ6IFwiPGRpdiBjbGFzcz1cXFwiY29ubmVjdGlvbl9pbmZvXFxcIj5Db21lIGJhY2sgaGVyZSBmb3IgXCIgK1xuICAgICAgICAgICAgXCJjb25uZWN0aW9uIGluZm9ybWF0aW9uIG9uY2UgdGhlIGNvbmZlcmVuY2Ugc3RhcnRzPC9kaXY+XCIsXG4gICAgICAgICAgICBza2luOiBcImJsYWNrXCJ9KTtcblxuICAgIHRoaXMuZW1wdHlJY29uID0gdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLmFwcGVuZENoaWxkKFxuICAgICAgICBjcmVhdGVJY29uKFtcImNvbm5lY3Rpb25cIiwgXCJjb25uZWN0aW9uX2VtcHR5XCJdKSk7XG4gICAgdGhpcy5mdWxsSWNvbiA9IHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5hcHBlbmRDaGlsZChcbiAgICAgICAgY3JlYXRlSWNvbihbXCJjb25uZWN0aW9uXCIsIFwiY29ubmVjdGlvbl9mdWxsXCJdKSk7XG5cbn07XG5cbi8qKlxuICogUmVtb3ZlcyB0aGUgaW5kaWNhdG9yXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLnJlbW92ZSA9IGZ1bmN0aW9uKClcbntcbiAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIucmVtb3ZlKCk7XG4gICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xuXG59O1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGRhdGEgb2YgdGhlIGluZGljYXRvclxuICogQHBhcmFtIHBlcmNlbnQgdGhlIHBlcmNlbnQgb2YgY29ubmVjdGlvbiBxdWFsaXR5XG4gKiBAcGFyYW0gb2JqZWN0IHRoZSBzdGF0aXN0aWNzIGRhdGEuXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLnVwZGF0ZUNvbm5lY3Rpb25RdWFsaXR5ID1cbmZ1bmN0aW9uIChwZXJjZW50LCBvYmplY3QpIHtcblxuICAgIGlmKHBlcmNlbnQgPT09IG51bGwpXG4gICAge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwibm9uZVwiO1xuICAgICAgICB0aGlzLnBvcG92ZXIuZm9yY2VIaWRlKCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgaWYodGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPT0gXCJub25lXCIpIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gXCJibG9ja1wiO1xuICAgICAgICAgICAgdGhpcy52aWRlb0xheW91dC51cGRhdGVNdXRlUG9zaXRpb24odGhpcy52aWRlb0NvbnRhaW5lci5pZCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5iYW5kd2lkdGggPSBvYmplY3QuYmFuZHdpZHRoO1xuICAgIHRoaXMuYml0cmF0ZSA9IG9iamVjdC5iaXRyYXRlO1xuICAgIHRoaXMucGFja2V0TG9zcyA9IG9iamVjdC5wYWNrZXRMb3NzO1xuICAgIHRoaXMudHJhbnNwb3J0ID0gb2JqZWN0LnRyYW5zcG9ydDtcbiAgICBpZihvYmplY3QucmVzb2x1dGlvbilcbiAgICB7XG4gICAgICAgIHRoaXMucmVzb2x1dGlvbiA9IG9iamVjdC5yZXNvbHV0aW9uO1xuICAgIH1cbiAgICBmb3IodmFyIHF1YWxpdHkgaW4gQ29ubmVjdGlvbkluZGljYXRvci5jb25uZWN0aW9uUXVhbGl0eVZhbHVlcylcbiAgICB7XG4gICAgICAgIGlmKHBlcmNlbnQgPj0gcXVhbGl0eSlcbiAgICAgICAge1xuICAgICAgICAgICAgdGhpcy5mdWxsSWNvbi5zdHlsZS53aWR0aCA9XG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5jb25uZWN0aW9uUXVhbGl0eVZhbHVlc1txdWFsaXR5XTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnVwZGF0ZVBvcG92ZXJEYXRhKCk7XG59O1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIHJlc29sdXRpb25cbiAqIEBwYXJhbSByZXNvbHV0aW9uIHRoZSBuZXcgcmVzb2x1dGlvblxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS51cGRhdGVSZXNvbHV0aW9uID0gZnVuY3Rpb24gKHJlc29sdXRpb24pIHtcbiAgICB0aGlzLnJlc29sdXRpb24gPSByZXNvbHV0aW9uO1xuICAgIHRoaXMudXBkYXRlUG9wb3ZlckRhdGEoKTtcbn07XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgY29udGVudCBvZiB0aGUgcG9wb3ZlclxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS51cGRhdGVQb3BvdmVyRGF0YSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnBvcG92ZXIudXBkYXRlQ29udGVudChcbiAgICAgICAgICAgIFwiPGRpdiBjbGFzcz1cXFwiY29ubmVjdGlvbl9pbmZvXFxcIj5cIiArIHRoaXMuZ2VuZXJhdGVUZXh0KCkgKyBcIjwvZGl2PlwiKTtcbn07XG5cbi8qKlxuICogSGlkZXMgdGhlIHBvcG92ZXJcbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUuaGlkZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnBvcG92ZXIuZm9yY2VIaWRlKCk7XG59O1xuXG4vKipcbiAqIEhpZGVzIHRoZSBpbmRpY2F0b3JcbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUuaGlkZUluZGljYXRvciA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwibm9uZVwiO1xuICAgIGlmKHRoaXMucG9wb3ZlcilcbiAgICAgICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb25uZWN0aW9uSW5kaWNhdG9yOyIsInZhciBBdWRpb0xldmVscyA9IHJlcXVpcmUoXCIuLi9hdWRpb19sZXZlbHMvQXVkaW9MZXZlbHNcIik7XG52YXIgQXZhdGFyID0gcmVxdWlyZShcIi4uL2F2YXRhci9BdmF0YXJcIik7XG52YXIgQ2hhdCA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvY2hhdC9DaGF0XCIpO1xudmFyIENvbnRhY3RMaXN0ID0gcmVxdWlyZShcIi4uL3NpZGVfcGFubmVscy9jb250YWN0bGlzdC9Db250YWN0TGlzdFwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vdXRpbC9VSVV0aWxcIik7XG52YXIgQ29ubmVjdGlvbkluZGljYXRvciA9IHJlcXVpcmUoXCIuL0Nvbm5lY3Rpb25JbmRpY2F0b3JcIik7XG5cbnZhciBjdXJyZW50RG9taW5hbnRTcGVha2VyID0gbnVsbDtcbnZhciBsYXN0TkNvdW50ID0gY29uZmlnLmNoYW5uZWxMYXN0TjtcbnZhciBsb2NhbExhc3ROQ291bnQgPSBjb25maWcuY2hhbm5lbExhc3ROO1xudmFyIGxvY2FsTGFzdE5TZXQgPSBbXTtcbnZhciBsYXN0TkVuZHBvaW50c0NhY2hlID0gW107XG52YXIgbGFzdE5QaWNrdXBKaWQgPSBudWxsO1xudmFyIGxhcmdlVmlkZW9TdGF0ZSA9IHtcbiAgICB1cGRhdGVJblByb2dyZXNzOiBmYWxzZSxcbiAgICBuZXdTcmM6ICcnXG59O1xuXG4vKipcbiAqIEluZGljYXRlcyBpZiB3ZSBoYXZlIG11dGVkIG91ciBhdWRpbyBiZWZvcmUgdGhlIGNvbmZlcmVuY2UgaGFzIHN0YXJ0ZWQuXG4gKiBAdHlwZSB7Ym9vbGVhbn1cbiAqL1xudmFyIHByZU11dGVkID0gZmFsc2U7XG5cbnZhciBtdXRlZEF1ZGlvcyA9IHt9O1xuXG52YXIgZmxpcFhMb2NhbFZpZGVvID0gdHJ1ZTtcbnZhciBjdXJyZW50VmlkZW9XaWR0aCA9IG51bGw7XG52YXIgY3VycmVudFZpZGVvSGVpZ2h0ID0gbnVsbDtcblxudmFyIGxvY2FsVmlkZW9TcmMgPSBudWxsO1xuXG52YXIgZGVmYXVsdExvY2FsRGlzcGxheU5hbWUgPSBcIk1lXCI7XG5cbmZ1bmN0aW9uIHZpZGVvYWN0aXZlKCB2aWRlb2VsZW0pIHtcbiAgICBpZiAodmlkZW9lbGVtLmF0dHIoJ2lkJykuaW5kZXhPZignbWl4ZWRtc2xhYmVsJykgPT09IC0xKSB7XG4gICAgICAgIC8vIGlnbm9yZSBtaXhlZG1zbGFiZWxhMCBhbmQgdjBcblxuICAgICAgICB2aWRlb2VsZW0uc2hvdygpO1xuICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG5cbiAgICAgICAgdmFyIHZpZGVvUGFyZW50ID0gdmlkZW9lbGVtLnBhcmVudCgpO1xuICAgICAgICB2YXIgcGFyZW50UmVzb3VyY2VKaWQgPSBudWxsO1xuICAgICAgICBpZiAodmlkZW9QYXJlbnQpXG4gICAgICAgICAgICBwYXJlbnRSZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID0gVmlkZW9MYXlvdXQuZ2V0UGVlckNvbnRhaW5lclJlc291cmNlSmlkKHZpZGVvUGFyZW50WzBdKTtcblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGxhcmdlIHZpZGVvIHRvIHRoZSBsYXN0IGFkZGVkIHZpZGVvIG9ubHkgaWYgdGhlcmUncyBub1xuICAgICAgICAvLyBjdXJyZW50IGRvbWluYW50LCBmb2N1c2VkIHNwZWFrZXIgb3IgcHJlemkgcGxheWluZyBvciB1cGRhdGUgaXQgdG9cbiAgICAgICAgLy8gdGhlIGN1cnJlbnQgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgaWYgKCghZm9jdXNlZFZpZGVvSW5mbyAmJlxuICAgICAgICAgICAgIVZpZGVvTGF5b3V0LmdldERvbWluYW50U3BlYWtlclJlc291cmNlSmlkKCkgJiZcbiAgICAgICAgICAgICFyZXF1aXJlKFwiLi4vcHJlemkvUHJlemlcIikuaXNQcmVzZW50YXRpb25WaXNpYmxlKCkpIHx8XG4gICAgICAgICAgICAocGFyZW50UmVzb3VyY2VKaWQgJiZcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5nZXREb21pbmFudFNwZWFrZXJSZXNvdXJjZUppZCgpID09PSBwYXJlbnRSZXNvdXJjZUppZCkpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oXG4gICAgICAgICAgICAgICAgUlRDLmdldFZpZGVvU3JjKHZpZGVvZWxlbVswXSksXG4gICAgICAgICAgICAgICAgMSxcbiAgICAgICAgICAgICAgICBwYXJlbnRSZXNvdXJjZUppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBWaWRlb0xheW91dC5zaG93TW9kZXJhdG9ySW5kaWNhdG9yKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiB3YWl0Rm9yUmVtb3RlVmlkZW8oc2VsZWN0b3IsIHNzcmMsIHN0cmVhbSwgamlkKSB7XG4gICAgLy8gWFhYKGdwKSBzbywgZXZlcnkgY2FsbCB0byB0aGlzIGZ1bmN0aW9uIGlzICphbHdheXMqIHByZWNlZGVkIGJ5IGEgY2FsbFxuICAgIC8vIHRvIHRoZSBSVEMuYXR0YWNoTWVkaWFTdHJlYW0oKSBmdW5jdGlvbiBidXQgdGhhdCBjYWxsIGlzICpub3QqIGZvbGxvd2VkXG4gICAgLy8gYnkgYW4gdXBkYXRlIHRvIHRoZSB2aWRlb1NyY1RvU3NyYyBtYXAhXG4gICAgLy9cbiAgICAvLyBUaGUgYWJvdmUgd2F5IG9mIGRvaW5nIHRoaW5ncyByZXN1bHRzIGluIHZpZGVvIFNSQ3MgdGhhdCBkb24ndCBjb3JyZXNwb25kXG4gICAgLy8gdG8gYW55IFNTUkMgZm9yIGEgc2hvcnQgcGVyaW9kIG9mIHRpbWUgKHRvIGJlIG1vcmUgcHJlY2lzZSwgZm9yIGFzIGxvbmdcbiAgICAvLyB0aGUgd2FpdEZvclJlbW90ZVZpZGVvIHRha2VzIHRvIGNvbXBsZXRlKS4gVGhpcyBjYXVzZXMgcHJvYmxlbXMgKHNlZVxuICAgIC8vIGJlbGxvdykuXG4gICAgLy9cbiAgICAvLyBJJ20gd29uZGVyaW5nIHdoeSB3ZSBuZWVkIHRvIGRvIHRoYXQ7IGkuZS4gd2h5IGNhbGwgUlRDLmF0dGFjaE1lZGlhU3RyZWFtKClcbiAgICAvLyBhIHNlY29uZCB0aW1lIGluIGhlcmUgYW5kIG9ubHkgdGhlbiB1cGRhdGUgdGhlIHZpZGVvU3JjVG9Tc3JjIG1hcD8gV2h5XG4gICAgLy8gbm90IHNpbXBseSB1cGRhdGUgdGhlIHZpZGVvU3JjVG9Tc3JjIG1hcCB3aGVuIHRoZSBSVEMuYXR0YWNoTWVkaWFTdHJlYW0oKVxuICAgIC8vIGlzIGNhbGxlZCB0aGUgZmlyc3QgdGltZT8gSSBhY3R1YWxseSBkbyB0aGF0IGluIHRoZSBsYXN0TiBjaGFuZ2VkIGV2ZW50XG4gICAgLy8gaGFuZGxlciBiZWNhdXNlIHRoZSBcIm9ycGhhblwiIHZpZGVvIFNSQyBpcyBjYXVzaW5nIHRyb3VibGVzIHRoZXJlLiBUaGVcbiAgICAvLyBwdXJwb3NlIG9mIHRoaXMgbWV0aG9kIHdvdWxkIHRoZW4gYmUgdG8gZmlyZSB0aGUgXCJ2aWRlb2FjdGl2ZS5qaW5nbGVcIi5cbiAgICAvL1xuICAgIC8vIEZvb2QgZm9yIHRob3VnaCBJIGd1ZXNzIDotKVxuXG4gICAgaWYgKHNlbGVjdG9yLnJlbW92ZWQgfHwgIXNlbGVjdG9yLnBhcmVudCgpLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiTWVkaWEgcmVtb3ZlZCBiZWZvcmUgaGFkIHN0YXJ0ZWRcIiwgc2VsZWN0b3IpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHN0cmVhbS5pZCA9PT0gJ21peGVkbXNsYWJlbCcpIHJldHVybjtcblxuICAgIGlmIChzZWxlY3RvclswXS5jdXJyZW50VGltZSA+IDApIHtcbiAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gc2ltdWxjYXN0LmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKHN0cmVhbSk7XG4gICAgICAgIFJUQy5hdHRhY2hNZWRpYVN0cmVhbShzZWxlY3RvciwgdmlkZW9TdHJlYW0pOyAvLyBGSVhNRTogd2h5IGRvIGkgaGF2ZSB0byBkbyB0aGlzIGZvciBGRj9cblxuICAgICAgICAvLyBGSVhNRTogYWRkIGEgY2xhc3MgdGhhdCB3aWxsIGFzc29jaWF0ZSBwZWVyIEppZCwgdmlkZW8uc3JjLCBpdCdzIHNzcmMgYW5kIHZpZGVvIHR5cGVcbiAgICAgICAgLy8gICAgICAgIGluIG9yZGVyIHRvIGdldCByaWQgb2YgdG9vIG1hbnkgbWFwc1xuICAgICAgICBpZiAoc3NyYyAmJiBqaWQpIHtcbiAgICAgICAgICAgIGppZDJTc3JjW1N0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldID0gc3NyYztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIk5vIHNzcmMgZ2l2ZW4gZm9yIGppZFwiLCBqaWQpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmlkZW9hY3RpdmUoc2VsZWN0b3IpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgd2FpdEZvclJlbW90ZVZpZGVvKHNlbGVjdG9yLCBzc3JjLCBzdHJlYW0sIGppZCk7XG4gICAgICAgIH0sIDI1MCk7XG4gICAgfVxufVxuXG4vKipcbiAqIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHZpZGVvIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGluZGVudHMsXG4gKiBzbyB0aGF0IGlmIGZpdHMgaXRzIHBhcmVudC5cbiAqXG4gKiBAcmV0dXJuIGFuIGFycmF5IHdpdGggMiBlbGVtZW50cywgdGhlIGhvcml6b250YWwgaW5kZW50IGFuZCB0aGUgdmVydGljYWxcbiAqIGluZGVudFxuICovXG5mdW5jdGlvbiBnZXRDYW1lcmFWaWRlb1Bvc2l0aW9uKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICAvLyBQYXJlbnQgaGVpZ2h0IGlzbid0IGNvbXBsZXRlbHkgY2FsY3VsYXRlZCB3aGVuIHdlIHBvc2l0aW9uIHRoZSB2aWRlbyBpblxuICAgIC8vIGZ1bGwgc2NyZWVuIG1vZGUgYW5kIHRoaXMgaXMgd2h5IHdlIHVzZSB0aGUgc2NyZWVuIGhlaWdodCBpbiB0aGlzIGNhc2UuXG4gICAgLy8gTmVlZCB0byB0aGluayBpdCBmdXJ0aGVyIGF0IHNvbWUgcG9pbnQgYW5kIGltcGxlbWVudCBpdCBwcm9wZXJseS5cbiAgICB2YXIgaXNGdWxsU2NyZWVuID0gZG9jdW1lbnQuZnVsbFNjcmVlbiB8fFxuICAgICAgICBkb2N1bWVudC5tb3pGdWxsU2NyZWVuIHx8XG4gICAgICAgIGRvY3VtZW50LndlYmtpdElzRnVsbFNjcmVlbjtcbiAgICBpZiAoaXNGdWxsU2NyZWVuKVxuICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuXG4gICAgdmFyIGhvcml6b250YWxJbmRlbnQgPSAodmlkZW9TcGFjZVdpZHRoIC0gdmlkZW9XaWR0aCkgLyAyO1xuICAgIHZhciB2ZXJ0aWNhbEluZGVudCA9ICh2aWRlb1NwYWNlSGVpZ2h0IC0gdmlkZW9IZWlnaHQpIC8gMjtcblxuICAgIHJldHVybiBbaG9yaXpvbnRhbEluZGVudCwgdmVydGljYWxJbmRlbnRdO1xufVxuXG4vKipcbiAqIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHZpZGVvIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGluZGVudHMuXG4gKiBDZW50ZXJzIGhvcml6b250YWxseSBhbmQgdG9wIGFsaWducyB2ZXJ0aWNhbGx5LlxuICpcbiAqIEByZXR1cm4gYW4gYXJyYXkgd2l0aCAyIGVsZW1lbnRzLCB0aGUgaG9yaXpvbnRhbCBpbmRlbnQgYW5kIHRoZSB2ZXJ0aWNhbFxuICogaW5kZW50XG4gKi9cbmZ1bmN0aW9uIGdldERlc2t0b3BWaWRlb1Bvc2l0aW9uKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcblxuICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gKHZpZGVvU3BhY2VXaWR0aCAtIHZpZGVvV2lkdGgpIC8gMjtcblxuICAgIHZhciB2ZXJ0aWNhbEluZGVudCA9IDA7Ly8gVG9wIGFsaWduZWRcblxuICAgIHJldHVybiBbaG9yaXpvbnRhbEluZGVudCwgdmVydGljYWxJbmRlbnRdO1xufVxuXG5cbi8qKlxuICogUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgdmlkZW8gZGltZW5zaW9ucywgc28gdGhhdCBpdCBjb3ZlcnMgdGhlIHNjcmVlbi5cbiAqIEl0IGxlYXZlcyBubyBlbXB0eSBhcmVhcywgYnV0IHNvbWUgcGFydHMgb2YgdGhlIHZpZGVvIG1pZ2h0IG5vdCBiZSB2aXNpYmxlLlxuICpcbiAqIEByZXR1cm4gYW4gYXJyYXkgd2l0aCAyIGVsZW1lbnRzLCB0aGUgdmlkZW8gd2lkdGggYW5kIHRoZSB2aWRlbyBoZWlnaHRcbiAqL1xuZnVuY3Rpb24gZ2V0Q2FtZXJhVmlkZW9TaXplKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICBpZiAoIXZpZGVvV2lkdGgpXG4gICAgICAgIHZpZGVvV2lkdGggPSBjdXJyZW50VmlkZW9XaWR0aDtcbiAgICBpZiAoIXZpZGVvSGVpZ2h0KVxuICAgICAgICB2aWRlb0hlaWdodCA9IGN1cnJlbnRWaWRlb0hlaWdodDtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IHZpZGVvV2lkdGggLyB2aWRlb0hlaWdodDtcblxuICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KHZpZGVvV2lkdGgsIHZpZGVvU3BhY2VXaWR0aCk7XG4gICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IE1hdGgubWF4KHZpZGVvSGVpZ2h0LCB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgIGlmIChhdmFpbGFibGVXaWR0aCAvIGFzcGVjdFJhdGlvIDwgdmlkZW9TcGFjZUhlaWdodCkge1xuICAgICAgICBhdmFpbGFibGVIZWlnaHQgPSB2aWRlb1NwYWNlSGVpZ2h0O1xuICAgICAgICBhdmFpbGFibGVXaWR0aCA9IGF2YWlsYWJsZUhlaWdodCAqIGFzcGVjdFJhdGlvO1xuICAgIH1cblxuICAgIGlmIChhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbyA8IHZpZGVvU3BhY2VXaWR0aCkge1xuICAgICAgICBhdmFpbGFibGVXaWR0aCA9IHZpZGVvU3BhY2VXaWR0aDtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICByZXR1cm4gW2F2YWlsYWJsZVdpZHRoLCBhdmFpbGFibGVIZWlnaHRdO1xufVxuXG4vKipcbiAqIFNldHMgdGhlIGRpc3BsYXkgbmFtZSBmb3IgdGhlIGdpdmVuIHZpZGVvIHNwYW4gaWQuXG4gKi9cbmZ1bmN0aW9uIHNldERpc3BsYXlOYW1lKHZpZGVvU3BhbklkLCBkaXNwbGF5TmFtZSkge1xuICAgIHZhciBuYW1lU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uZGlzcGxheW5hbWUnKTtcbiAgICB2YXIgZGVmYXVsdExvY2FsRGlzcGxheU5hbWUgPSBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9MT0NBTF9ESVNQTEFZX05BTUU7XG5cbiAgICAvLyBJZiB3ZSBhbHJlYWR5IGhhdmUgYSBkaXNwbGF5IG5hbWUgZm9yIHRoaXMgdmlkZW8uXG4gICAgaWYgKG5hbWVTcGFuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdmFyIG5hbWVTcGFuRWxlbWVudCA9IG5hbWVTcGFuLmdldCgwKTtcblxuICAgICAgICBpZiAobmFtZVNwYW5FbGVtZW50LmlkID09PSAnbG9jYWxEaXNwbGF5TmFtZScgJiZcbiAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJykudGV4dCgpICE9PSBkaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCA+IDApXG4gICAgICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKS5odG1sKGRpc3BsYXlOYW1lICsgJyAobWUpJyk7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKS50ZXh0KGRlZmF1bHRMb2NhbERpc3BsYXlOYW1lKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGggPiAwKVxuICAgICAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnX25hbWUnKS5odG1sKGRpc3BsYXlOYW1lKTtcbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkICsgJ19uYW1lJykudGV4dChpbnRlcmZhY2VDb25maWcuREVGQVVMVF9SRU1PVEVfRElTUExBWV9OQU1FKTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBlZGl0QnV0dG9uID0gbnVsbDtcblxuICAgICAgICBuYW1lU3BhbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgbmFtZVNwYW4uY2xhc3NOYW1lID0gJ2Rpc3BsYXluYW1lJztcbiAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQobmFtZVNwYW4pO1xuXG4gICAgICAgIGlmICh2aWRlb1NwYW5JZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInKSB7XG4gICAgICAgICAgICBlZGl0QnV0dG9uID0gY3JlYXRlRWRpdERpc3BsYXlOYW1lQnV0dG9uKCk7XG4gICAgICAgICAgICBuYW1lU3Bhbi5pbm5lclRleHQgPSBkZWZhdWx0TG9jYWxEaXNwbGF5TmFtZTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIG5hbWVTcGFuLmlubmVyVGV4dCA9IGludGVyZmFjZUNvbmZpZy5ERUZBVUxUX1JFTU9URV9ESVNQTEFZX05BTUU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgbmFtZVNwYW4uaW5uZXJUZXh0ID0gZGlzcGxheU5hbWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWVkaXRCdXR0b24pIHtcbiAgICAgICAgICAgIG5hbWVTcGFuLmlkID0gdmlkZW9TcGFuSWQgKyAnX25hbWUnO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbmFtZVNwYW4uaWQgPSAnbG9jYWxEaXNwbGF5TmFtZSc7XG4gICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkKVswXS5hcHBlbmRDaGlsZChlZGl0QnV0dG9uKTtcblxuICAgICAgICAgICAgdmFyIGVkaXRhYmxlVGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XG4gICAgICAgICAgICBlZGl0YWJsZVRleHQuY2xhc3NOYW1lID0gJ2Rpc3BsYXluYW1lJztcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC50eXBlID0gJ3RleHQnO1xuICAgICAgICAgICAgZWRpdGFibGVUZXh0LmlkID0gJ2VkaXREaXNwbGF5TmFtZSc7XG5cbiAgICAgICAgICAgIGlmIChkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBlZGl0YWJsZVRleHQudmFsdWVcbiAgICAgICAgICAgICAgICAgICAgPSBkaXNwbGF5TmFtZS5zdWJzdHJpbmcoMCwgZGlzcGxheU5hbWUuaW5kZXhPZignIChtZSknKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgJ2Rpc3BsYXk6bm9uZTsnKTtcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC5zZXRBdHRyaWJ1dGUoJ3BsYWNlaG9sZGVyJywgJ2V4LiBKYW5lIFBpbmsnKTtcbiAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKGVkaXRhYmxlVGV4dCk7XG5cbiAgICAgICAgICAgICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyIC5kaXNwbGF5bmFtZScpXG4gICAgICAgICAgICAgICAgLmJpbmQoXCJjbGlja1wiLCBmdW5jdGlvbiAoZSkge1xuXG4gICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKS5oaWRlKCk7XG4gICAgICAgICAgICAgICAgICAgICQoJyNlZGl0RGlzcGxheU5hbWUnKS5zaG93KCk7XG4gICAgICAgICAgICAgICAgICAgICQoJyNlZGl0RGlzcGxheU5hbWUnKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykuc2VsZWN0KCk7XG5cbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLm9uZShcImZvY3Vzb3V0XCIsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5pbnB1dERpc3BsYXlOYW1lSGFuZGxlcih0aGlzLnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLm9uKCdrZXlkb3duJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlLmtleUNvZGUgPT09IDEzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmlucHV0RGlzcGxheU5hbWVIYW5kbGVyKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBHZXRzIHRoZSBzZWxlY3RvciBvZiB2aWRlbyB0aHVtYm5haWwgY29udGFpbmVyIGZvciB0aGUgdXNlciBpZGVudGlmaWVkIGJ5XG4gKiBnaXZlbiA8dHQ+dXNlckppZDwvdHQ+XG4gKiBAcGFyYW0gcmVzb3VyY2VKaWQgdXNlcidzIEppZCBmb3Igd2hvbSB3ZSB3YW50IHRvIGdldCB0aGUgdmlkZW8gY29udGFpbmVyLlxuICovXG5mdW5jdGlvbiBnZXRQYXJ0aWNpcGFudENvbnRhaW5lcihyZXNvdXJjZUppZClcbntcbiAgICBpZiAoIXJlc291cmNlSmlkKVxuICAgICAgICByZXR1cm4gbnVsbDtcblxuICAgIGlmIChyZXNvdXJjZUppZCA9PT0geG1wcC5teVJlc291cmNlKCkpXG4gICAgICAgIHJldHVybiAkKFwiI2xvY2FsVmlkZW9Db250YWluZXJcIik7XG4gICAgZWxzZVxuICAgICAgICByZXR1cm4gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkKTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBzaXplIGFuZCBwb3NpdGlvbiBvZiB0aGUgZ2l2ZW4gdmlkZW8gZWxlbWVudC5cbiAqXG4gKiBAcGFyYW0gdmlkZW8gdGhlIHZpZGVvIGVsZW1lbnQgdG8gcG9zaXRpb25cbiAqIEBwYXJhbSB3aWR0aCB0aGUgZGVzaXJlZCB2aWRlbyB3aWR0aFxuICogQHBhcmFtIGhlaWdodCB0aGUgZGVzaXJlZCB2aWRlbyBoZWlnaHRcbiAqIEBwYXJhbSBob3Jpem9udGFsSW5kZW50IHRoZSBsZWZ0IGFuZCByaWdodCBpbmRlbnRcbiAqIEBwYXJhbSB2ZXJ0aWNhbEluZGVudCB0aGUgdG9wIGFuZCBib3R0b20gaW5kZW50XG4gKi9cbmZ1bmN0aW9uIHBvc2l0aW9uVmlkZW8odmlkZW8sXG4gICAgICAgICAgICAgICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgIGhvcml6b250YWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2FsSW5kZW50KSB7XG4gICAgdmlkZW8ud2lkdGgod2lkdGgpO1xuICAgIHZpZGVvLmhlaWdodChoZWlnaHQpO1xuICAgIHZpZGVvLmNzcyh7ICB0b3A6IHZlcnRpY2FsSW5kZW50ICsgJ3B4JyxcbiAgICAgICAgYm90dG9tOiB2ZXJ0aWNhbEluZGVudCArICdweCcsXG4gICAgICAgIGxlZnQ6IGhvcml6b250YWxJbmRlbnQgKyAncHgnLFxuICAgICAgICByaWdodDogaG9yaXpvbnRhbEluZGVudCArICdweCd9KTtcbn1cblxuLyoqXG4gKiBBZGRzIHRoZSByZW1vdGUgdmlkZW8gbWVudSBlbGVtZW50IGZvciB0aGUgZ2l2ZW4gPHR0PmppZDwvdHQ+IGluIHRoZVxuICogZ2l2ZW4gPHR0PnBhcmVudEVsZW1lbnQ8L3R0Pi5cbiAqXG4gKiBAcGFyYW0gamlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZm9yIHdoaWNoIHdlJ3JlIGFkZGluZyBhIG1lbnUuXG4gKiBAcGFyYW0gcGFyZW50RWxlbWVudCB0aGUgcGFyZW50IGVsZW1lbnQgd2hlcmUgdGhpcyBtZW51IHdpbGwgYmUgYWRkZWRcbiAqL1xuZnVuY3Rpb24gYWRkUmVtb3RlVmlkZW9NZW51KGppZCwgcGFyZW50RWxlbWVudCkge1xuICAgIHZhciBzcGFuRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICBzcGFuRWxlbWVudC5jbGFzc05hbWUgPSAncmVtb3RldmlkZW9tZW51JztcblxuICAgIHBhcmVudEVsZW1lbnQuYXBwZW5kQ2hpbGQoc3BhbkVsZW1lbnQpO1xuXG4gICAgdmFyIG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgIG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdmYSBmYS1hbmdsZS1kb3duJztcbiAgICBtZW51RWxlbWVudC50aXRsZSA9ICdSZW1vdGUgdXNlciBjb250cm9scyc7XG4gICAgc3BhbkVsZW1lbnQuYXBwZW5kQ2hpbGQobWVudUVsZW1lbnQpO1xuXG4vLyAgICAgICAgPHVsIGNsYXNzPVwicG9wdXBtZW51XCI+XG4vLyAgICAgICAgPGxpPjxhIGhyZWY9XCIjXCI+TXV0ZTwvYT48L2xpPlxuLy8gICAgICAgIDxsaT48YSBocmVmPVwiI1wiPkVqZWN0PC9hPjwvbGk+XG4vLyAgICAgICAgPC91bD5cblxuICAgIHZhciBwb3B1cG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndWwnKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdwb3B1cG1lbnUnO1xuICAgIHBvcHVwbWVudUVsZW1lbnQuaWRcbiAgICAgICAgPSAncmVtb3RlX3BvcHVwbWVudV8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICBzcGFuRWxlbWVudC5hcHBlbmRDaGlsZChwb3B1cG1lbnVFbGVtZW50KTtcblxuICAgIHZhciBtdXRlTWVudUl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuICAgIHZhciBtdXRlTGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cbiAgICB2YXIgbXV0ZWRJbmRpY2F0b3IgPSBcIjxpIGNsYXNzPSdpY29uLW1pYy1kaXNhYmxlZCc+PC9pPlwiO1xuXG4gICAgaWYgKCFtdXRlZEF1ZGlvc1tqaWRdKSB7XG4gICAgICAgIG11dGVMaW5rSXRlbS5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArICdNdXRlJztcbiAgICAgICAgbXV0ZUxpbmtJdGVtLmNsYXNzTmFtZSA9ICdtdXRlbGluayc7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBtdXRlTGlua0l0ZW0uaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgKyAnIE11dGVkJztcbiAgICAgICAgbXV0ZUxpbmtJdGVtLmNsYXNzTmFtZSA9ICdtdXRlbGluayBkaXNhYmxlZCc7XG4gICAgfVxuXG4gICAgbXV0ZUxpbmtJdGVtLm9uY2xpY2sgPSBmdW5jdGlvbigpe1xuICAgICAgICBpZiAoJCh0aGlzKS5hdHRyKCdkaXNhYmxlZCcpICE9IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgaXNNdXRlID0gbXV0ZWRBdWRpb3NbamlkXSA9PSB0cnVlO1xuICAgICAgICB4bXBwLnNldE11dGUoamlkLCAhaXNNdXRlKTtcblxuICAgICAgICBwb3B1cG1lbnVFbGVtZW50LnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAnZGlzcGxheTpub25lOycpO1xuXG4gICAgICAgIGlmIChpc011dGUpIHtcbiAgICAgICAgICAgIHRoaXMuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgKyAnIE11dGVkJztcbiAgICAgICAgICAgIHRoaXMuY2xhc3NOYW1lID0gJ211dGVsaW5rIGRpc2FibGVkJztcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgKyAnIE11dGUnO1xuICAgICAgICAgICAgdGhpcy5jbGFzc05hbWUgPSAnbXV0ZWxpbmsnO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIG11dGVNZW51SXRlbS5hcHBlbmRDaGlsZChtdXRlTGlua0l0ZW0pO1xuICAgIHBvcHVwbWVudUVsZW1lbnQuYXBwZW5kQ2hpbGQobXV0ZU1lbnVJdGVtKTtcblxuICAgIHZhciBlamVjdEluZGljYXRvciA9IFwiPGkgY2xhc3M9J2ZhIGZhLWVqZWN0Jz48L2k+XCI7XG5cbiAgICB2YXIgZWplY3RNZW51SXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7XG4gICAgdmFyIGVqZWN0TGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgZWplY3RMaW5rSXRlbS5pbm5lckhUTUwgPSBlamVjdEluZGljYXRvciArICcgS2ljayBvdXQnO1xuICAgIGVqZWN0TGlua0l0ZW0ub25jbGljayA9IGZ1bmN0aW9uKCl7XG4gICAgICAgIHhtcHAuZWplY3QoamlkKTtcbiAgICAgICAgcG9wdXBtZW51RWxlbWVudC5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgJ2Rpc3BsYXk6bm9uZTsnKTtcbiAgICB9O1xuXG4gICAgZWplY3RNZW51SXRlbS5hcHBlbmRDaGlsZChlamVjdExpbmtJdGVtKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKGVqZWN0TWVudUl0ZW0pO1xuXG4gICAgdmFyIHBhZGRpbmdTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgIHBhZGRpbmdTcGFuLmNsYXNzTmFtZSA9ICdwb3B1cG1lbnVQYWRkaW5nJztcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKHBhZGRpbmdTcGFuKTtcbn1cblxuLyoqXG4gKiBSZW1vdmVzIHJlbW90ZSB2aWRlbyBtZW51IGVsZW1lbnQgZnJvbSB2aWRlbyBlbGVtZW50IGlkZW50aWZpZWQgYnlcbiAqIGdpdmVuIDx0dD52aWRlb0VsZW1lbnRJZDwvdHQ+LlxuICpcbiAqIEBwYXJhbSB2aWRlb0VsZW1lbnRJZCB0aGUgaWQgb2YgbG9jYWwgb3IgcmVtb3RlIHZpZGVvIGVsZW1lbnQuXG4gKi9cbmZ1bmN0aW9uIHJlbW92ZVJlbW90ZVZpZGVvTWVudSh2aWRlb0VsZW1lbnRJZCkge1xuICAgIHZhciBtZW51U3BhbiA9ICQoJyMnICsgdmlkZW9FbGVtZW50SWQgKyAnPnNwYW4ucmVtb3RldmlkZW9tZW51Jyk7XG4gICAgaWYgKG1lbnVTcGFuLmxlbmd0aCkge1xuICAgICAgICBtZW51U3Bhbi5yZW1vdmUoKTtcbiAgICB9XG59XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgZGF0YSBmb3IgdGhlIGluZGljYXRvclxuICogQHBhcmFtIGlkIHRoZSBpZCBvZiB0aGUgaW5kaWNhdG9yXG4gKiBAcGFyYW0gcGVyY2VudCB0aGUgcGVyY2VudCBmb3IgY29ubmVjdGlvbiBxdWFsaXR5XG4gKiBAcGFyYW0gb2JqZWN0IHRoZSBkYXRhXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZVN0YXRzSW5kaWNhdG9yKGlkLCBwZXJjZW50LCBvYmplY3QpIHtcbiAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0pXG4gICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2lkXS51cGRhdGVDb25uZWN0aW9uUXVhbGl0eShwZXJjZW50LCBvYmplY3QpO1xufVxuXG5cbi8qKlxuICogUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgdmlkZW8gZGltZW5zaW9ucywgc28gdGhhdCBpdCBrZWVwcyBpdCdzIGFzcGVjdFxuICogcmF0aW8gYW5kIGZpdHMgYXZhaWxhYmxlIGFyZWEgd2l0aCBpdCdzIGxhcmdlciBkaW1lbnNpb24uIFRoaXMgbWV0aG9kXG4gKiBlbnN1cmVzIHRoYXQgd2hvbGUgdmlkZW8gd2lsbCBiZSB2aXNpYmxlIGFuZCBjYW4gbGVhdmUgZW1wdHkgYXJlYXMuXG4gKlxuICogQHJldHVybiBhbiBhcnJheSB3aXRoIDIgZWxlbWVudHMsIHRoZSB2aWRlbyB3aWR0aCBhbmQgdGhlIHZpZGVvIGhlaWdodFxuICovXG5mdW5jdGlvbiBnZXREZXNrdG9wVmlkZW9TaXplKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICBpZiAoIXZpZGVvV2lkdGgpXG4gICAgICAgIHZpZGVvV2lkdGggPSBjdXJyZW50VmlkZW9XaWR0aDtcbiAgICBpZiAoIXZpZGVvSGVpZ2h0KVxuICAgICAgICB2aWRlb0hlaWdodCA9IGN1cnJlbnRWaWRlb0hlaWdodDtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IHZpZGVvV2lkdGggLyB2aWRlb0hlaWdodDtcblxuICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KHZpZGVvV2lkdGgsIHZpZGVvU3BhY2VXaWR0aCk7XG4gICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IE1hdGgubWF4KHZpZGVvSGVpZ2h0LCB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgIHZpZGVvU3BhY2VIZWlnaHQgLT0gJCgnI3JlbW90ZVZpZGVvcycpLm91dGVySGVpZ2h0KCk7XG5cbiAgICBpZiAoYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyA+PSB2aWRlb1NwYWNlSGVpZ2h0KVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gdmlkZW9TcGFjZUhlaWdodDtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8gPj0gdmlkZW9TcGFjZVdpZHRoKVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSB2aWRlb1NwYWNlV2lkdGg7XG4gICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW87XG4gICAgfVxuXG4gICAgcmV0dXJuIFthdmFpbGFibGVXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBlZGl0IGRpc3BsYXkgbmFtZSBidXR0b24uXG4gKlxuICogQHJldHVybnMgdGhlIGVkaXQgYnV0dG9uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUVkaXREaXNwbGF5TmFtZUJ1dHRvbigpIHtcbiAgICB2YXIgZWRpdEJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICBlZGl0QnV0dG9uLmNsYXNzTmFtZSA9ICdkaXNwbGF5bmFtZSc7XG4gICAgVXRpbC5zZXRUb29sdGlwKGVkaXRCdXR0b24sXG4gICAgICAgICdDbGljayB0byBlZGl0IHlvdXI8YnIvPmRpc3BsYXkgbmFtZScsXG4gICAgICAgIFwidG9wXCIpO1xuICAgIGVkaXRCdXR0b24uaW5uZXJIVE1MID0gJzxpIGNsYXNzPVwiZmEgZmEtcGVuY2lsXCI+PC9pPic7XG5cbiAgICByZXR1cm4gZWRpdEJ1dHRvbjtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBlbGVtZW50IGluZGljYXRpbmcgdGhlIG1vZGVyYXRvcihvd25lcikgb2YgdGhlIGNvbmZlcmVuY2UuXG4gKlxuICogQHBhcmFtIHBhcmVudEVsZW1lbnQgdGhlIHBhcmVudCBlbGVtZW50IHdoZXJlIHRoZSBvd25lciBpbmRpY2F0b3Igd2lsbFxuICogYmUgYWRkZWRcbiAqL1xuZnVuY3Rpb24gY3JlYXRlTW9kZXJhdG9ySW5kaWNhdG9yRWxlbWVudChwYXJlbnRFbGVtZW50KSB7XG4gICAgdmFyIG1vZGVyYXRvckluZGljYXRvciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2knKTtcbiAgICBtb2RlcmF0b3JJbmRpY2F0b3IuY2xhc3NOYW1lID0gJ2ZhIGZhLXN0YXInO1xuICAgIHBhcmVudEVsZW1lbnQuYXBwZW5kQ2hpbGQobW9kZXJhdG9ySW5kaWNhdG9yKTtcblxuICAgIFV0aWwuc2V0VG9vbHRpcChwYXJlbnRFbGVtZW50LFxuICAgICAgICBcIlRoZSBvd25lciBvZjxici8+dGhpcyBjb25mZXJlbmNlXCIsXG4gICAgICAgIFwidG9wXCIpO1xufVxuXG5cbi8qKlxuICogQ2hlY2tzIGlmIHZpZGVvIGlkZW50aWZpZWQgYnkgZ2l2ZW4gc3JjIGlzIGRlc2t0b3Agc3RyZWFtLlxuICogQHBhcmFtIHZpZGVvU3JjIGVnLlxuICogYmxvYjpodHRwcyUzQS8vcGF3ZWwuaml0c2kubmV0LzlhNDZlMGJkLTEzMWUtNGQxOC05YzE0LWE5MjY0ZThkYjM5NVxuICogQHJldHVybnMge2Jvb2xlYW59XG4gKi9cbmZ1bmN0aW9uIGlzVmlkZW9TcmNEZXNrdG9wKGppZCkge1xuICAgIC8vIEZJWE1FOiBmaXggdGhpcyBtYXBwaW5nIG1lc3MuLi5cbiAgICAvLyBmaWd1cmUgb3V0IGlmIGxhcmdlIHZpZGVvIGlzIGRlc2t0b3Agc3RyZWFtIG9yIGp1c3QgYSBjYW1lcmFcblxuICAgIGlmKCFqaWQpXG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB2YXIgaXNEZXNrdG9wID0gZmFsc2U7XG4gICAgaWYgKHhtcHAubXlKaWQoKSAmJlxuICAgICAgICB4bXBwLm15UmVzb3VyY2UoKSA9PT0gamlkKSB7XG4gICAgICAgIC8vIGxvY2FsIHZpZGVvXG4gICAgICAgIGlzRGVza3RvcCA9IGRlc2t0b3BzaGFyaW5nLmlzVXNpbmdTY3JlZW5TdHJlYW0oKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvLyBEbyB3ZSBoYXZlIGFzc29jaWF0aW9ucy4uLlxuICAgICAgICB2YXIgdmlkZW9Tc3JjID0gamlkMlNzcmNbamlkXTtcbiAgICAgICAgaWYgKHZpZGVvU3NyYykge1xuICAgICAgICAgICAgdmFyIHZpZGVvVHlwZSA9IHNzcmMydmlkZW9UeXBlW3ZpZGVvU3NyY107XG4gICAgICAgICAgICBpZiAodmlkZW9UeXBlKSB7XG4gICAgICAgICAgICAgICAgLy8gRmluYWxseSB0aGVyZS4uLlxuICAgICAgICAgICAgICAgIGlzRGVza3RvcCA9IHZpZGVvVHlwZSA9PT0gJ3NjcmVlbic7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyB0eXBlIGZvciBzc3JjOiBcIiArIHZpZGVvU3NyYyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiTm8gc3NyYyBmb3IgamlkOiBcIiArIGppZCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGlzRGVza3RvcDtcbn1cblxuXG5cbnZhciBWaWRlb0xheW91dCA9IChmdW5jdGlvbiAobXkpIHtcbiAgICBteS5jb25uZWN0aW9uSW5kaWNhdG9ycyA9IHt9O1xuXG4gICAgLy8gQnkgZGVmYXVsdCB3ZSB1c2UgY2FtZXJhXG4gICAgbXkuZ2V0VmlkZW9TaXplID0gZ2V0Q2FtZXJhVmlkZW9TaXplO1xuICAgIG15LmdldFZpZGVvUG9zaXRpb24gPSBnZXRDYW1lcmFWaWRlb1Bvc2l0aW9uO1xuXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gTGlzdGVuIGZvciBsYXJnZSB2aWRlbyBzaXplIHVwZGF0ZXNcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKVxuICAgICAgICAgICAgLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWRlZG1ldGFkYXRhJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9XaWR0aCA9IHRoaXMudmlkZW9XaWR0aDtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9IZWlnaHQgPSB0aGlzLnZpZGVvSGVpZ2h0O1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoY3VycmVudFZpZGVvV2lkdGgsIGN1cnJlbnRWaWRlb0hlaWdodCk7XG4gICAgICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgbXkuaXNJbkxhc3ROID0gZnVuY3Rpb24ocmVzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIGxhc3ROQ291bnQgPCAwIC8vIGxhc3ROIGlzIGRpc2FibGVkLCByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfHwgKGxhc3ROQ291bnQgPiAwICYmIGxhc3RORW5kcG9pbnRzQ2FjaGUubGVuZ3RoID09IDApIC8vIGxhc3RORW5kcG9pbnRzIGNhY2hlIG5vdCBidWlsdCB5ZXQsIHJldHVybiB0cnVlXG4gICAgICAgICAgICB8fCAobGFzdE5FbmRwb2ludHNDYWNoZSAmJiBsYXN0TkVuZHBvaW50c0NhY2hlLmluZGV4T2YocmVzb3VyY2UpICE9PSAtMSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFZpZGVvKHN0cmVhbSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsQXVkaW8gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgUlRDLmF0dGFjaE1lZGlhU3RyZWFtKCQoJyNsb2NhbEF1ZGlvJyksIHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpKTtcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2FsQXVkaW8nKS5hdXRvcGxheSA9IHRydWU7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NhbEF1ZGlvJykudm9sdW1lID0gMDtcbiAgICAgICAgaWYgKHByZU11dGVkKSB7XG4gICAgICAgICAgICBpZighVUkuc2V0QXVkaW9NdXRlZCh0cnVlKSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBwcmVNdXRlZCA9IG11dGU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwcmVNdXRlZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsVmlkZW8gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgdmFyIGZsaXBYID0gdHJ1ZTtcbiAgICAgICAgaWYoc3RyZWFtLnR5cGUgPT0gXCJkZXNrdG9wXCIpXG4gICAgICAgICAgICBmbGlwWCA9IGZhbHNlO1xuICAgICAgICB2YXIgbG9jYWxWaWRlbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJyk7XG4gICAgICAgIGxvY2FsVmlkZW8uaWQgPSAnbG9jYWxWaWRlb18nICtcbiAgICAgICAgICAgIFJUQy5nZXRTdHJlYW1JRChzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKSk7XG4gICAgICAgIGxvY2FsVmlkZW8uYXV0b3BsYXkgPSB0cnVlO1xuICAgICAgICBsb2NhbFZpZGVvLnZvbHVtZSA9IDA7IC8vIGlzIGl0IHJlcXVpcmVkIGlmIGF1ZGlvIGlzIHNlcGFyYXRlZCA/XG4gICAgICAgIGxvY2FsVmlkZW8ub25jb250ZXh0bWVudSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGZhbHNlOyB9O1xuXG4gICAgICAgIHZhciBsb2NhbFZpZGVvQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2FsVmlkZW9XcmFwcGVyJyk7XG4gICAgICAgIGxvY2FsVmlkZW9Db250YWluZXIuYXBwZW5kQ2hpbGQobG9jYWxWaWRlbyk7XG5cbiAgICAgICAgLy8gU2V0IGRlZmF1bHQgZGlzcGxheSBuYW1lLlxuICAgICAgICBzZXREaXNwbGF5TmFtZSgnbG9jYWxWaWRlb0NvbnRhaW5lcicpO1xuXG4gICAgICAgIGlmKCFWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tcImxvY2FsVmlkZW9Db250YWluZXJcIl0pIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW1wibG9jYWxWaWRlb0NvbnRhaW5lclwiXVxuICAgICAgICAgICAgICAgID0gbmV3IENvbm5lY3Rpb25JbmRpY2F0b3IoJChcIiNsb2NhbFZpZGVvQ29udGFpbmVyXCIpWzBdLCBudWxsLCBWaWRlb0xheW91dCk7XG4gICAgICAgIH1cblxuICAgICAgICBBdWRpb0xldmVscy51cGRhdGVBdWRpb0xldmVsQ2FudmFzKG51bGwsIFZpZGVvTGF5b3V0KTtcblxuICAgICAgICB2YXIgbG9jYWxWaWRlb1NlbGVjdG9yID0gJCgnIycgKyBsb2NhbFZpZGVvLmlkKTtcbiAgICAgICAgLy8gQWRkIGNsaWNrIGhhbmRsZXIgdG8gYm90aCB2aWRlbyBhbmQgdmlkZW8gd3JhcHBlciBlbGVtZW50cyBpbiBjYXNlXG4gICAgICAgIC8vIHRoZXJlJ3Mgbm8gdmlkZW8uXG4gICAgICAgIGxvY2FsVmlkZW9TZWxlY3Rvci5jbGljayhmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQoXG4gICAgICAgICAgICAgICAgUlRDLmdldFZpZGVvU3JjKGxvY2FsVmlkZW8pLFxuICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgIHhtcHAubXlSZXNvdXJjZSgpKTtcbiAgICAgICAgfSk7XG4gICAgICAgICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyJykuY2xpY2soZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgIFJUQy5nZXRWaWRlb1NyYyhsb2NhbFZpZGVvKSxcbiAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICB4bXBwLm15UmVzb3VyY2UoKSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIEFkZCBob3ZlciBoYW5kbGVyXG4gICAgICAgICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyJykuaG92ZXIoXG4gICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zaG93RGlzcGxheU5hbWUoJ2xvY2FsVmlkZW9Db250YWluZXInLCB0cnVlKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBpZiAoIVZpZGVvTGF5b3V0LmlzTGFyZ2VWaWRlb1Zpc2libGUoKVxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgUlRDLmdldFZpZGVvU3JjKGxvY2FsVmlkZW8pICE9PSBSVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpXG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSgnbG9jYWxWaWRlb0NvbnRhaW5lcicsIGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgICAgLy8gQWRkIHN0cmVhbSBlbmRlZCBoYW5kbGVyXG4gICAgICAgIHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLm9uZW5kZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBsb2NhbFZpZGVvQ29udGFpbmVyLnJlbW92ZUNoaWxkKGxvY2FsVmlkZW8pO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3ZlZFZpZGVvKFJUQy5nZXRWaWRlb1NyYyhsb2NhbFZpZGVvKSk7XG4gICAgICAgIH07XG4gICAgICAgIC8vIEZsaXAgdmlkZW8geCBheGlzIGlmIG5lZWRlZFxuICAgICAgICBmbGlwWExvY2FsVmlkZW8gPSBmbGlwWDtcbiAgICAgICAgaWYgKGZsaXBYKSB7XG4gICAgICAgICAgICBsb2NhbFZpZGVvU2VsZWN0b3IuYWRkQ2xhc3MoXCJmbGlwVmlkZW9YXCIpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEF0dGFjaCBXZWJSVEMgc3RyZWFtXG4gICAgICAgIHZhciB2aWRlb1N0cmVhbSA9IHNpbXVsY2FzdC5nZXRMb2NhbFZpZGVvU3RyZWFtKCk7XG4gICAgICAgIFJUQy5hdHRhY2hNZWRpYVN0cmVhbShsb2NhbFZpZGVvU2VsZWN0b3IsIHZpZGVvU3RyZWFtKTtcblxuICAgICAgICBsb2NhbFZpZGVvU3JjID0gUlRDLmdldFZpZGVvU3JjKGxvY2FsVmlkZW8pO1xuXG4gICAgICAgIHZhciBteVJlc291cmNlSmlkID0geG1wcC5teVJlc291cmNlKCk7XG5cbiAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTGFyZ2VWaWRlbyhsb2NhbFZpZGVvU3JjLCAwLFxuICAgICAgICAgICAgbXlSZXNvdXJjZUppZCk7XG5cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIGlmIHJlbW92ZWQgdmlkZW8gaXMgY3VycmVudGx5IGRpc3BsYXllZCBhbmQgdHJpZXMgdG8gZGlzcGxheVxuICAgICAqIGFub3RoZXIgb25lIGluc3RlYWQuXG4gICAgICogQHBhcmFtIHJlbW92ZWRWaWRlb1NyYyBzcmMgc3RyZWFtIGlkZW50aWZpZXIgb2YgdGhlIHZpZGVvLlxuICAgICAqL1xuICAgIG15LnVwZGF0ZVJlbW92ZWRWaWRlbyA9IGZ1bmN0aW9uKHJlbW92ZWRWaWRlb1NyYykge1xuICAgICAgICBpZiAocmVtb3ZlZFZpZGVvU3JjID09PSBSVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpIHtcbiAgICAgICAgICAgIC8vIHRoaXMgaXMgY3VycmVudGx5IGRpc3BsYXllZCBhcyBsYXJnZVxuICAgICAgICAgICAgLy8gcGljayB0aGUgbGFzdCB2aXNpYmxlIHZpZGVvIGluIHRoZSByb3dcbiAgICAgICAgICAgIC8vIGlmIG5vYm9keSBlbHNlIGlzIGxlZnQsIHRoaXMgcGlja3MgdGhlIGxvY2FsIHZpZGVvXG4gICAgICAgICAgICB2YXIgcGlja1xuICAgICAgICAgICAgICAgID0gJCgnI3JlbW90ZVZpZGVvcz5zcGFuW2lkIT1cIm1peGVkc3RyZWFtXCJdOnZpc2libGU6bGFzdD52aWRlbycpXG4gICAgICAgICAgICAgICAgICAgIC5nZXQoMCk7XG5cbiAgICAgICAgICAgIGlmICghcGljaykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkxhc3QgdmlzaWJsZSB2aWRlbyBubyBsb25nZXIgZXhpc3RzXCIpO1xuICAgICAgICAgICAgICAgIHBpY2sgPSAkKCcjcmVtb3RlVmlkZW9zPnNwYW5baWQhPVwibWl4ZWRzdHJlYW1cIl0+dmlkZW8nKS5nZXQoMCk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIXBpY2sgfHwgIVJUQy5nZXRWaWRlb1NyYyhwaWNrKSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUcnkgbG9jYWwgdmlkZW9cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiRmFsbGJhY2sgdG8gbG9jYWwgdmlkZW8uLi5cIik7XG4gICAgICAgICAgICAgICAgICAgIHBpY2sgPSAkKCcjcmVtb3RlVmlkZW9zPnNwYW4+c3Bhbj52aWRlbycpLmdldCgwKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG11dGUgaWYgbG9jYWx2aWRlb1xuICAgICAgICAgICAgaWYgKHBpY2spIHtcbiAgICAgICAgICAgICAgICB2YXIgY29udGFpbmVyID0gcGljay5wYXJlbnROb2RlO1xuICAgICAgICAgICAgICAgIHZhciBqaWQgPSBudWxsO1xuICAgICAgICAgICAgICAgIGlmKGNvbnRhaW5lcilcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGlmKGNvbnRhaW5lci5pZCA9PSBcImxvY2FsVmlkZW9XcmFwcGVyXCIpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGppZCA9IHhtcHAubXlSZXNvdXJjZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgamlkID0gVmlkZW9MYXlvdXQuZ2V0UGVlckNvbnRhaW5lclJlc291cmNlSmlkKGNvbnRhaW5lcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKFJUQy5nZXRWaWRlb1NyYyhwaWNrKSwgcGljay52b2x1bWUsIGppZCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkZhaWxlZCB0byBlbGVjdCBsYXJnZSB2aWRlb1wiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG4gICAgXG4gICAgbXkub25SZW1vdGVTdHJlYW1BZGRlZCA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgdmFyIGNvbnRhaW5lcjtcbiAgICAgICAgdmFyIHJlbW90ZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVtb3RlVmlkZW9zJyk7XG5cbiAgICAgICAgaWYgKHN0cmVhbS5wZWVyamlkKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5lbnN1cmVQZWVyQ29udGFpbmVyRXhpc3RzKHN0cmVhbS5wZWVyamlkKTtcblxuICAgICAgICAgICAgY29udGFpbmVyICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFxuICAgICAgICAgICAgICAgICAgICAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHN0cmVhbS5wZWVyamlkKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgaWQgPSBzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKS5pZDtcbiAgICAgICAgICAgIGlmIChpZCAhPT0gJ21peGVkbXNsYWJlbCdcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogZGVmYXVsdCBzdHJlYW0gaXMgYWRkZWQgYWx3YXlzIHdpdGggbmV3IGZvY3VzXG4gICAgICAgICAgICAgICAgLy8gKHRvIGJlIGludmVzdGlnYXRlZClcbiAgICAgICAgICAgICAgICAmJiBpZCAhPT0gJ2RlZmF1bHQnKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignY2FuIG5vdCBhc3NvY2lhdGUgc3RyZWFtJyxcbiAgICAgICAgICAgICAgICAgICAgaWQsXG4gICAgICAgICAgICAgICAgICAgICd3aXRoIGEgcGFydGljaXBhbnQnKTtcbiAgICAgICAgICAgICAgICAvLyBXZSBkb24ndCB3YW50IHRvIGFkZCBpdCBoZXJlIHNpbmNlIGl0IHdpbGwgY2F1c2UgdHJvdWJsZXNcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGSVhNRTogZm9yIHRoZSBtaXhlZCBtcyB3ZSBkb250IG5lZWQgYSB2aWRlbyAtLSBjdXJyZW50bHlcbiAgICAgICAgICAgIGNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5pZCA9ICdtaXhlZHN0cmVhbSc7XG4gICAgICAgICAgICBjb250YWluZXIuY2xhc3NOYW1lID0gJ3ZpZGVvY29udGFpbmVyJztcbiAgICAgICAgICAgIHJlbW90ZXMuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcbiAgICAgICAgICAgIFV0aWwucGxheVNvdW5kTm90aWZpY2F0aW9uKCd1c2VySm9pbmVkJyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY29udGFpbmVyKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5hZGRSZW1vdGVTdHJlYW1FbGVtZW50KCBjb250YWluZXIsXG4gICAgICAgICAgICAgICAgc3RyZWFtLnNpZCxcbiAgICAgICAgICAgICAgICBzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKSxcbiAgICAgICAgICAgICAgICBzdHJlYW0ucGVlcmppZCxcbiAgICAgICAgICAgICAgICBzdHJlYW0uc3NyYyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBteS5nZXRMYXJnZVZpZGVvU3RhdGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBsYXJnZVZpZGVvU3RhdGU7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGxhcmdlIHZpZGVvIHdpdGggdGhlIGdpdmVuIG5ldyB2aWRlbyBzb3VyY2UuXG4gICAgICovXG4gICAgbXkudXBkYXRlTGFyZ2VWaWRlbyA9IGZ1bmN0aW9uKG5ld1NyYywgdm9sLCByZXNvdXJjZUppZCkge1xuICAgICAgICBjb25zb2xlLmxvZygnaG92ZXIgaW4nLCBuZXdTcmMpO1xuXG4gICAgICAgIGlmIChSVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkgIT09IG5ld1NyYykge1xuXG4gICAgICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcbiAgICAgICAgICAgIC8vIER1ZSB0byB0aGUgc2ltdWxjYXN0IHRoZSBsb2NhbFZpZGVvU3JjIG1heSBoYXZlIGNoYW5nZWQgd2hlbiB0aGVcbiAgICAgICAgICAgIC8vIGZhZGVPdXQgZXZlbnQgdHJpZ2dlcnMuIEluIHRoYXQgY2FzZSB0aGUgZ2V0SmlkRnJvbVZpZGVvU3JjIGFuZFxuICAgICAgICAgICAgLy8gaXNWaWRlb1NyY0Rlc2t0b3AgbWV0aG9kcyB3aWxsIG5vdCBmdW5jdGlvbiBjb3JyZWN0bHkuXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gQWxzbywgYWdhaW4gZHVlIHRvIHRoZSBzaW11bGNhc3QsIHRoZSB1cGRhdGVMYXJnZVZpZGVvIG1ldGhvZCBjYW5cbiAgICAgICAgICAgIC8vIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyBhbG1vc3Qgc2ltdWx0YW5lb3VzbHkuIFRoZXJlZm9yZSwgd2VcbiAgICAgICAgICAgIC8vIHN0b3JlIHRoZSBzdGF0ZSBoZXJlIGFuZCB1cGRhdGUgb25seSBvbmNlLlxuXG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUubmV3U3JjID0gbmV3U3JjO1xuICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLmlzVmlzaWJsZSA9ICQoJyNsYXJnZVZpZGVvJykuaXMoJzp2aXNpYmxlJyk7XG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUuaXNEZXNrdG9wID0gaXNWaWRlb1NyY0Rlc2t0b3AocmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgaWYoamlkMlNzcmNbbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZF0gfHxcbiAgICAgICAgICAgICAgICAoeG1wcC5teVJlc291cmNlKCkgJiZcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCA9PT1cbiAgICAgICAgICAgICAgICAgICAgeG1wcC5teVJlc291cmNlKCkpKSB7XG4gICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQgPSByZXNvdXJjZUppZDtcblxuICAgICAgICAgICAgLy8gU2NyZWVuIHN0cmVhbSBpcyBhbHJlYWR5IHJvdGF0ZWRcbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5mbGlwWCA9IChuZXdTcmMgPT09IGxvY2FsVmlkZW9TcmMpICYmIGZsaXBYTG9jYWxWaWRlbztcblxuICAgICAgICAgICAgdmFyIHVzZXJDaGFuZ2VkID0gZmFsc2U7XG4gICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkICE9PSBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgdXNlckNoYW5nZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIC8vIHdlIHdhbnQgdGhlIG5vdGlmaWNhdGlvbiB0byB0cmlnZ2VyIGV2ZW4gaWYgdXNlckppZCBpcyB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgLy8gb3IgbnVsbC5cbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwic2VsZWN0ZWRlbmRwb2ludGNoYW5nZWRcIiwgW2xhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWRdKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFsYXJnZVZpZGVvU3RhdGUudXBkYXRlSW5Qcm9ncmVzcykge1xuICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51cGRhdGVJblByb2dyZXNzID0gdHJ1ZTtcblxuICAgICAgICAgICAgICAgIHZhciBkb1VwZGF0ZSA9IGZ1bmN0aW9uICgpIHtcblxuICAgICAgICAgICAgICAgICAgICBBdmF0YXIudXBkYXRlQWN0aXZlU3BlYWtlckF2YXRhclNyYyhcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCF1c2VyQ2hhbmdlZCAmJiBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgIT09IG51bGwgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgIFJUQy5nZXRWaWRlb1NyYygkKGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkKVswXSkgPT09IG5ld1NyYylcbiAgICAgICAgICAgICAgICAgICAge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ1N3aXRjaGluZyB0byBwcmVsb2FkZWQgdmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBhdHRyaWJ1dGVzID0gJCgnI2xhcmdlVmlkZW8nKS5wcm9wKFwiYXR0cmlidXRlc1wiKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gbG9vcCB0aHJvdWdoIGxhcmdlVmlkZW8gYXR0cmlidXRlcyBhbmQgYXBwbHkgdGhlbSBvblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJlbG9hZC5cbiAgICAgICAgICAgICAgICAgICAgICAgICQuZWFjaChhdHRyaWJ1dGVzLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubmFtZSAhPT0gJ2lkJyAmJiB0aGlzLm5hbWUgIT09ICdzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkLmF0dHIodGhpcy5uYW1lLCB0aGlzLnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQuYXBwZW5kVG8oJCgnI2xhcmdlVmlkZW9Db250YWluZXInKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmF0dHIoJ2lkJywgJ3ByZXZpb3VzTGFyZ2VWaWRlbycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQuYXR0cignaWQnLCAnbGFyZ2VWaWRlbycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJCgnI3ByZXZpb3VzTGFyZ2VWaWRlbycpLnJlbW92ZSgpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZC5vbignbG9hZGVkbWV0YWRhdGEnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRWaWRlb1dpZHRoID0gdGhpcy52aWRlb1dpZHRoO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRWaWRlb0hlaWdodCA9IHRoaXMudmlkZW9IZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQucG9zaXRpb25MYXJnZShjdXJyZW50VmlkZW9XaWR0aCwgY3VycmVudFZpZGVvSGVpZ2h0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRfc3NyYyA9IDA7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBSVEMuc2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSwgbGFyZ2VWaWRlb1N0YXRlLm5ld1NyYyk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICB2YXIgdmlkZW9UcmFuc2Zvcm0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGFyZ2VWaWRlbycpXG4gICAgICAgICAgICAgICAgICAgICAgICAuc3R5bGUud2Via2l0VHJhbnNmb3JtO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChsYXJnZVZpZGVvU3RhdGUuZmxpcFggJiYgdmlkZW9UcmFuc2Zvcm0gIT09ICdzY2FsZVgoLTEpJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKS5zdHlsZS53ZWJraXRUcmFuc2Zvcm1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IFwic2NhbGVYKC0xKVwiO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2UgaWYgKCFsYXJnZVZpZGVvU3RhdGUuZmxpcFggJiYgdmlkZW9UcmFuc2Zvcm0gPT09ICdzY2FsZVgoLTEpJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKS5zdHlsZS53ZWJraXRUcmFuc2Zvcm1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IFwibm9uZVwiO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gQ2hhbmdlIHRoZSB3YXkgd2UnbGwgYmUgbWVhc3VyaW5nIGFuZCBwb3NpdGlvbmluZyBsYXJnZSB2aWRlb1xuXG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmdldFZpZGVvU2l6ZSA9IGxhcmdlVmlkZW9TdGF0ZS5pc0Rlc2t0b3BcbiAgICAgICAgICAgICAgICAgICAgICAgID8gZ2V0RGVza3RvcFZpZGVvU2l6ZVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBnZXRDYW1lcmFWaWRlb1NpemU7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmdldFZpZGVvUG9zaXRpb24gPSBsYXJnZVZpZGVvU3RhdGUuaXNEZXNrdG9wXG4gICAgICAgICAgICAgICAgICAgICAgICA/IGdldERlc2t0b3BWaWRlb1Bvc2l0aW9uXG4gICAgICAgICAgICAgICAgICAgICAgICA6IGdldENhbWVyYVZpZGVvUG9zaXRpb247XG5cblxuICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGlmIHRoZSBsYXJnZSB2aWRlbyBpcyBjdXJyZW50bHkgdmlzaWJsZS5cbiAgICAgICAgICAgICAgICAgICAgLy8gRGlzYWJsZSBwcmV2aW91cyBkb21pbmFudCBzcGVha2VyIHZpZGVvLlxuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5lbmFibGVEb21pbmFudFNwZWFrZXIoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEVuYWJsZSBuZXcgZG9taW5hbnQgc3BlYWtlciBpbiB0aGUgcmVtb3RlIHZpZGVvcyBzZWN0aW9uLlxuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5hYmxlRG9taW5hbnRTcGVha2VyKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAodXNlckNoYW5nZWQgJiYgbGFyZ2VWaWRlb1N0YXRlLmlzVmlzaWJsZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gdXNpbmcgXCJ0aGlzXCIgc2hvdWxkIGJlIG9rIGJlY2F1c2Ugd2UncmUgY2FsbGVkXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBmcm9tIHdpdGhpbiB0aGUgZmFkZU91dCBldmVudC5cbiAgICAgICAgICAgICAgICAgICAgICAgICQodGhpcykuZmFkZUluKDMwMCk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZih1c2VyQ2hhbmdlZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXBkYXRlSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBpZiAodXNlckNoYW5nZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5mYWRlT3V0KDMwMCwgZG9VcGRhdGUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRvVXBkYXRlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKFxuICAgICAgICAgICAgICAgIHhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCkpO1xuICAgICAgICB9XG5cbiAgICB9O1xuXG4gICAgbXkuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQgPSBmdW5jdGlvbih2aWRlb1NyYyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vUGlubmVkRW5kcG9pbnRDaGFuZ2VkRXZlbnQsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb3VyY2VKaWQpIHtcbiAgICAgICAgLy8gUmVzdG9yZSBzdHlsZSBmb3IgcHJldmlvdXNseSBmb2N1c2VkIHZpZGVvXG4gICAgICAgIHZhciBvbGRDb250YWluZXIgPSBudWxsO1xuICAgICAgICBpZihmb2N1c2VkVmlkZW9JbmZvKSB7XG4gICAgICAgICAgICB2YXIgZm9jdXNSZXNvdXJjZUppZCA9IGZvY3VzZWRWaWRlb0luZm8ucmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICBvbGRDb250YWluZXIgPSBnZXRQYXJ0aWNpcGFudENvbnRhaW5lcihmb2N1c1Jlc291cmNlSmlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChvbGRDb250YWluZXIpIHtcbiAgICAgICAgICAgIG9sZENvbnRhaW5lci5yZW1vdmVDbGFzcyhcInZpZGVvQ29udGFpbmVyRm9jdXNlZFwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFVubG9jayBjdXJyZW50IGZvY3VzZWQuXG4gICAgICAgIGlmIChmb2N1c2VkVmlkZW9JbmZvICYmIGZvY3VzZWRWaWRlb0luZm8uc3JjID09PSB2aWRlb1NyYylcbiAgICAgICAge1xuICAgICAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IG51bGw7XG4gICAgICAgICAgICB2YXIgZG9taW5hbnRTcGVha2VyVmlkZW8gPSBudWxsO1xuICAgICAgICAgICAgLy8gRW5hYmxlIHRoZSBjdXJyZW50bHkgc2V0IGRvbWluYW50IHNwZWFrZXIuXG4gICAgICAgICAgICBpZiAoY3VycmVudERvbWluYW50U3BlYWtlcikge1xuICAgICAgICAgICAgICAgIGRvbWluYW50U3BlYWtlclZpZGVvXG4gICAgICAgICAgICAgICAgICAgID0gJCgnI3BhcnRpY2lwYW50XycgKyBjdXJyZW50RG9taW5hbnRTcGVha2VyICsgJz52aWRlbycpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZ2V0KDApO1xuXG4gICAgICAgICAgICAgICAgaWYgKGRvbWluYW50U3BlYWtlclZpZGVvKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oXG4gICAgICAgICAgICAgICAgICAgICAgICBSVEMuZ2V0VmlkZW9TcmMoZG9taW5hbnRTcGVha2VyVmlkZW8pLFxuICAgICAgICAgICAgICAgICAgICAgICAgMSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnREb21pbmFudFNwZWFrZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFub1Bpbm5lZEVuZHBvaW50Q2hhbmdlZEV2ZW50KSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcInBpbm5lZGVuZHBvaW50Y2hhbmdlZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIExvY2sgbmV3IHZpZGVvXG4gICAgICAgIGZvY3VzZWRWaWRlb0luZm8gPSB7XG4gICAgICAgICAgICBzcmM6IHZpZGVvU3JjLFxuICAgICAgICAgICAgcmVzb3VyY2VKaWQ6IHJlc291cmNlSmlkXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gVXBkYXRlIGZvY3VzZWQvcGlubmVkIGludGVyZmFjZS5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgY29udGFpbmVyID0gZ2V0UGFydGljaXBhbnRDb250YWluZXIocmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgY29udGFpbmVyLmFkZENsYXNzKFwidmlkZW9Db250YWluZXJGb2N1c2VkXCIpO1xuXG4gICAgICAgICAgICBpZiAoIW5vUGlubmVkRW5kcG9pbnRDaGFuZ2VkRXZlbnQpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwicGlubmVkZW5kcG9pbnRjaGFuZ2VkXCIsIFtyZXNvdXJjZUppZF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCQoJyNsYXJnZVZpZGVvJykuYXR0cignc3JjJykgPT09IHZpZGVvU3JjICYmXG4gICAgICAgICAgICBWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9PblRvcCgpKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBUcmlnZ2VycyBhIFwidmlkZW8uc2VsZWN0ZWRcIiBldmVudC4gVGhlIFwiZmFsc2VcIiBwYXJhbWV0ZXIgaW5kaWNhdGVzXG4gICAgICAgIC8vIHRoaXMgaXNuJ3QgYSBwcmV6aS5cbiAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcInZpZGVvLnNlbGVjdGVkXCIsIFtmYWxzZV0pO1xuXG4gICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8odmlkZW9TcmMsIDEsIHJlc291cmNlSmlkKTtcblxuICAgICAgICAkKCdhdWRpbycpLmVhY2goZnVuY3Rpb24gKGlkeCwgZWwpIHtcbiAgICAgICAgICAgIGlmIChlbC5pZC5pbmRleE9mKCdtaXhlZG1zbGFiZWwnKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICBlbC52b2x1bWUgPSAwO1xuICAgICAgICAgICAgICAgIGVsLnZvbHVtZSA9IDE7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBQb3NpdGlvbnMgdGhlIGxhcmdlIHZpZGVvLlxuICAgICAqXG4gICAgICogQHBhcmFtIHZpZGVvV2lkdGggdGhlIHN0cmVhbSB2aWRlbyB3aWR0aFxuICAgICAqIEBwYXJhbSB2aWRlb0hlaWdodCB0aGUgc3RyZWFtIHZpZGVvIGhlaWdodFxuICAgICAqL1xuICAgIG15LnBvc2l0aW9uTGFyZ2UgPSBmdW5jdGlvbiAodmlkZW9XaWR0aCwgdmlkZW9IZWlnaHQpIHtcbiAgICAgICAgdmFyIHZpZGVvU3BhY2VXaWR0aCA9ICQoJyN2aWRlb3NwYWNlJykud2lkdGgoKTtcbiAgICAgICAgdmFyIHZpZGVvU3BhY2VIZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG5cbiAgICAgICAgdmFyIHZpZGVvU2l6ZSA9IFZpZGVvTGF5b3V0LmdldFZpZGVvU2l6ZSh2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgICAgICB2YXIgbGFyZ2VWaWRlb1dpZHRoID0gdmlkZW9TaXplWzBdO1xuICAgICAgICB2YXIgbGFyZ2VWaWRlb0hlaWdodCA9IHZpZGVvU2l6ZVsxXTtcblxuICAgICAgICB2YXIgdmlkZW9Qb3NpdGlvbiA9IFZpZGVvTGF5b3V0LmdldFZpZGVvUG9zaXRpb24obGFyZ2VWaWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpO1xuXG4gICAgICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblswXTtcbiAgICAgICAgdmFyIHZlcnRpY2FsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblsxXTtcblxuICAgICAgICBwb3NpdGlvblZpZGVvKCQoJyNsYXJnZVZpZGVvJyksXG4gICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgaG9yaXpvbnRhbEluZGVudCwgdmVydGljYWxJbmRlbnQpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cy9oaWRlcyB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICovXG4gICAgbXkuc2V0TGFyZ2VWaWRlb1Zpc2libGUgPSBmdW5jdGlvbihpc1Zpc2libGUpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcblxuICAgICAgICBpZiAoaXNWaXNpYmxlKSB7XG4gICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcud2F0ZXJtYXJrJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihyZXNvdXJjZUppZCwgdHJ1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgICQoJyNhY3RpdmVTcGVha2VyJykuY3NzKCd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xuICAgICAgICAgICAgJCgnLndhdGVybWFyaycpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihyZXNvdXJjZUppZCwgZmFsc2UpO1xuICAgICAgICAgICAgaWYoZm9jdXNlZFZpZGVvSW5mbykge1xuICAgICAgICAgICAgICAgIHZhciBmb2N1c1Jlc291cmNlSmlkID0gZm9jdXNlZFZpZGVvSW5mby5yZXNvdXJjZUppZDtcbiAgICAgICAgICAgICAgICB2YXIgb2xkQ29udGFpbmVyID0gZ2V0UGFydGljaXBhbnRDb250YWluZXIoZm9jdXNSZXNvdXJjZUppZCk7XG5cbiAgICAgICAgICAgICAgICBpZiAob2xkQ29udGFpbmVyICYmIG9sZENvbnRhaW5lci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIG9sZENvbnRhaW5lci5yZW1vdmVDbGFzcyhcInZpZGVvQ29udGFpbmVyRm9jdXNlZFwiKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IG51bGw7XG4gICAgICAgICAgICAgICAgaWYoZm9jdXNSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgICAgICAgICB4bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UoZm9jdXNSZXNvdXJjZUppZCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgaWYgdGhlIGxhcmdlIHZpZGVvIGlzIGN1cnJlbnRseSB2aXNpYmxlLlxuICAgICAqXG4gICAgICogQHJldHVybiA8dHQ+dHJ1ZTwvdHQ+IGlmIHZpc2libGUsIDx0dD5mYWxzZTwvdHQ+IC0gb3RoZXJ3aXNlXG4gICAgICovXG4gICAgbXkuaXNMYXJnZVZpZGVvVmlzaWJsZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJCgnI2xhcmdlVmlkZW8nKS5pcygnOnZpc2libGUnKTtcbiAgICB9O1xuXG4gICAgbXkuaXNMYXJnZVZpZGVvT25Ub3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBFdGhlcnBhZCA9IHJlcXVpcmUoXCIuLi9ldGhlcnBhZC9FdGhlcnBhZFwiKTtcbiAgICAgICAgdmFyIFByZXppID0gcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpO1xuICAgICAgICByZXR1cm4gIVByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpICYmICFFdGhlcnBhZC5pc1Zpc2libGUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIGlmIGNvbnRhaW5lciBmb3IgcGFydGljaXBhbnQgaWRlbnRpZmllZCBieSBnaXZlbiBwZWVySmlkIGV4aXN0c1xuICAgICAqIGluIHRoZSBkb2N1bWVudCBhbmQgY3JlYXRlcyBpdCBldmVudHVhbGx5LlxuICAgICAqIFxuICAgICAqIEBwYXJhbSBwZWVySmlkIHBlZXIgSmlkIHRvIGNoZWNrLlxuICAgICAqIEBwYXJhbSB1c2VySWQgdXNlciBlbWFpbCBvciBpZCBmb3Igc2V0dGluZyB0aGUgYXZhdGFyXG4gICAgICogXG4gICAgICogQHJldHVybiBSZXR1cm5zIDx0dD50cnVlPC90dD4gaWYgdGhlIHBlZXIgY29udGFpbmVyIGV4aXN0cyxcbiAgICAgKiA8dHQ+ZmFsc2U8L3R0PiAtIG90aGVyd2lzZVxuICAgICAqL1xuICAgIG15LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMgPSBmdW5jdGlvbihwZWVySmlkLCB1c2VySWQpIHtcbiAgICAgICAgQ29udGFjdExpc3QuZW5zdXJlQWRkQ29udGFjdChwZWVySmlkLCB1c2VySWQpO1xuXG4gICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHBlZXJKaWQpO1xuXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG5cbiAgICAgICAgaWYgKCEkKCcjJyArIHZpZGVvU3BhbklkKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBjb250YWluZXIgPVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmFkZFJlbW90ZVZpZGVvQ29udGFpbmVyKHBlZXJKaWQsIHZpZGVvU3BhbklkLCB1c2VySWQpO1xuICAgICAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIocGVlckppZCwgdXNlcklkKTtcbiAgICAgICAgICAgIC8vIFNldCBkZWZhdWx0IGRpc3BsYXkgbmFtZS5cbiAgICAgICAgICAgIHNldERpc3BsYXlOYW1lKHZpZGVvU3BhbklkKTtcblxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbdmlkZW9TcGFuSWRdID1cbiAgICAgICAgICAgICAgICBuZXcgQ29ubmVjdGlvbkluZGljYXRvcihjb250YWluZXIsIHBlZXJKaWQsIFZpZGVvTGF5b3V0KTtcblxuICAgICAgICAgICAgdmFyIG5pY2tmaWVsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgIG5pY2tmaWVsZC5jbGFzc05hbWUgPSBcIm5pY2tcIjtcbiAgICAgICAgICAgIG5pY2tmaWVsZC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShyZXNvdXJjZUppZCkpO1xuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKG5pY2tmaWVsZCk7XG5cbiAgICAgICAgICAgIC8vIEluIGNhc2UgdGhpcyBpcyBub3QgY3VycmVudGx5IGluIHRoZSBsYXN0IG4gd2UgZG9uJ3Qgc2hvdyBpdC5cbiAgICAgICAgICAgIGlmIChsb2NhbExhc3ROQ291bnRcbiAgICAgICAgICAgICAgICAmJiBsb2NhbExhc3ROQ291bnQgPiAwXG4gICAgICAgICAgICAgICAgJiYgJCgnI3JlbW90ZVZpZGVvcz5zcGFuJykubGVuZ3RoID49IGxvY2FsTGFzdE5Db3VudCArIDIpIHtcbiAgICAgICAgICAgICAgICBzaG93UGVlckNvbnRhaW5lcihyZXNvdXJjZUppZCwgJ2hpZGUnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkuYWRkUmVtb3RlVmlkZW9Db250YWluZXIgPSBmdW5jdGlvbihwZWVySmlkLCBzcGFuSWQpIHtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgY29udGFpbmVyLmlkID0gc3BhbklkO1xuICAgICAgICBjb250YWluZXIuY2xhc3NOYW1lID0gJ3ZpZGVvY29udGFpbmVyJztcbiAgICAgICAgdmFyIHJlbW90ZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVtb3RlVmlkZW9zJyk7XG5cbiAgICAgICAgLy8gSWYgdGhlIHBlZXJKaWQgaXMgbnVsbCB0aGVuIHRoaXMgdmlkZW8gc3BhbiBjb3VsZG4ndCBiZSBkaXJlY3RseVxuICAgICAgICAvLyBhc3NvY2lhdGVkIHdpdGggYSBwYXJ0aWNpcGFudCAodGhpcyBjb3VsZCBoYXBwZW4gaW4gdGhlIGNhc2Ugb2YgcHJlemkpLlxuICAgICAgICBpZiAoeG1wcC5pc01vZGVyYXRvcigpICYmIHBlZXJKaWQgIT09IG51bGwpXG4gICAgICAgICAgICBhZGRSZW1vdGVWaWRlb01lbnUocGVlckppZCwgY29udGFpbmVyKTtcblxuICAgICAgICByZW1vdGVzLmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIEF1ZGlvTGV2ZWxzLnVwZGF0ZUF1ZGlvTGV2ZWxDYW52YXMocGVlckppZCwgVmlkZW9MYXlvdXQpO1xuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gYXVkaW8gb3IgdmlkZW8gc3RyZWFtIGVsZW1lbnQuXG4gICAgICovXG4gICAgbXkuY3JlYXRlU3RyZWFtRWxlbWVudCA9IGZ1bmN0aW9uIChzaWQsIHN0cmVhbSkge1xuICAgICAgICB2YXIgaXNWaWRlbyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG5cbiAgICAgICAgdmFyIGVsZW1lbnQgPSBpc1ZpZGVvXG4gICAgICAgICAgICAgICAgICAgICAgICA/IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJylcbiAgICAgICAgICAgICAgICAgICAgICAgIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYXVkaW8nKTtcbiAgICAgICAgdmFyIGlkID0gKGlzVmlkZW8gPyAncmVtb3RlVmlkZW9fJyA6ICdyZW1vdGVBdWRpb18nKVxuICAgICAgICAgICAgICAgICAgICArIHNpZCArICdfJyArIFJUQy5nZXRTdHJlYW1JRChzdHJlYW0pO1xuXG4gICAgICAgIGVsZW1lbnQuaWQgPSBpZDtcbiAgICAgICAgZWxlbWVudC5hdXRvcGxheSA9IHRydWU7XG4gICAgICAgIGVsZW1lbnQub25jb250ZXh0bWVudSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGZhbHNlOyB9O1xuXG4gICAgICAgIHJldHVybiBlbGVtZW50O1xuICAgIH07XG5cbiAgICBteS5hZGRSZW1vdGVTdHJlYW1FbGVtZW50XG4gICAgICAgID0gZnVuY3Rpb24gKGNvbnRhaW5lciwgc2lkLCBzdHJlYW0sIHBlZXJKaWQsIHRoZXNzcmMpIHtcbiAgICAgICAgdmFyIG5ld0VsZW1lbnRJZCA9IG51bGw7XG5cbiAgICAgICAgdmFyIGlzVmlkZW8gPSBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPiAwO1xuXG4gICAgICAgIGlmIChjb250YWluZXIpIHtcbiAgICAgICAgICAgIHZhciBzdHJlYW1FbGVtZW50ID0gVmlkZW9MYXlvdXQuY3JlYXRlU3RyZWFtRWxlbWVudChzaWQsIHN0cmVhbSk7XG4gICAgICAgICAgICBuZXdFbGVtZW50SWQgPSBzdHJlYW1FbGVtZW50LmlkO1xuXG4gICAgICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoc3RyZWFtRWxlbWVudCk7XG5cbiAgICAgICAgICAgIHZhciBzZWwgPSAkKCcjJyArIG5ld0VsZW1lbnRJZCk7XG4gICAgICAgICAgICBzZWwuaGlkZSgpO1xuXG4gICAgICAgICAgICAvLyBJZiB0aGUgY29udGFpbmVyIGlzIGN1cnJlbnRseSB2aXNpYmxlIHdlIGF0dGFjaCB0aGUgc3RyZWFtLlxuICAgICAgICAgICAgaWYgKCFpc1ZpZGVvXG4gICAgICAgICAgICAgICAgfHwgKGNvbnRhaW5lci5vZmZzZXRQYXJlbnQgIT09IG51bGwgJiYgaXNWaWRlbykpIHtcbiAgICAgICAgICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBzaW11bGNhc3QuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0oc3RyZWFtKTtcbiAgICAgICAgICAgICAgICBSVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsLCB2aWRlb1N0cmVhbSk7XG5cbiAgICAgICAgICAgICAgICBpZiAoaXNWaWRlbylcbiAgICAgICAgICAgICAgICAgICAgd2FpdEZvclJlbW90ZVZpZGVvKHNlbCwgdGhlc3NyYywgc3RyZWFtLCBwZWVySmlkKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc3RyZWFtLm9uZW5kZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3N0cmVhbSBlbmRlZCcsIHRoaXMpO1xuXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVtb3ZlUmVtb3RlU3RyZWFtRWxlbWVudChcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLCBpc1ZpZGVvLCBjb250YWluZXIpO1xuXG4gICAgICAgICAgICAgICAgLy8gTk9URShncCkgaXQgc2VlbXMgdGhhdCB1bmRlciBjZXJ0YWluIGNpcmN1bXN0YW5jZXMsIHRoZVxuICAgICAgICAgICAgICAgIC8vIG9uZW5kZWQgZXZlbnQgaXMgbm90IGZpcmVkIGFuZCB0aHVzIHRoZSBjb250YWN0IGxpc3QgaXMgbm90XG4gICAgICAgICAgICAgICAgLy8gdXBkYXRlZC5cbiAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgIC8vIFRoZSBvbmVuZGVkIGV2ZW50IG9mIGEgc3RyZWFtIHNob3VsZCBiZSBmaXJlZCB3aGVuIHRoZSBTU1JDc1xuICAgICAgICAgICAgICAgIC8vIGNvcnJlc3BvbmRpbmcgdG8gdGhhdCBzdHJlYW0gYXJlIHJlbW92ZWQgZnJvbSB0aGUgU0RQOyBidXRcbiAgICAgICAgICAgICAgICAvLyB0aGlzIGRvZXNuJ3Qgc2VlbSB0byBhbHdheXMgYmUgdGhlIGNhc2UsIHJlc3VsdGluZyBpbiBnaG9zdFxuICAgICAgICAgICAgICAgIC8vIGNvbnRhY3RzLlxuICAgICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgICAgLy8gSW4gYW4gYXR0ZW1wdCB0byBmaXggdGhlIGdob3N0IGNvbnRhY3RzIHByb2JsZW0sIEknbSBtb3ZpbmdcbiAgICAgICAgICAgICAgICAvLyB0aGUgcmVtb3ZlQ29udGFjdCgpIG1ldGhvZCBjYWxsIGluIGFwcC5qcywgaW5zaWRlIHRoZVxuICAgICAgICAgICAgICAgIC8vICdtdWMubGVmdCcgZXZlbnQgaGFuZGxlci5cblxuICAgICAgICAgICAgICAgIC8vaWYgKHBlZXJKaWQpXG4gICAgICAgICAgICAgICAgLy8gICAgQ29udGFjdExpc3QucmVtb3ZlQ29udGFjdChwZWVySmlkKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIEFkZCBjbGljayBoYW5kbGVyLlxuICAgICAgICAgICAgY29udGFpbmVyLm9uY2xpY2sgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICAvKlxuICAgICAgICAgICAgICAgICAqIEZJWE1FIEl0IHR1cm5zIG91dCB0aGF0IHZpZGVvVGh1bWIgbWF5IG5vdCBleGlzdCAoaWYgdGhlcmUgaXNcbiAgICAgICAgICAgICAgICAgKiBubyBhY3R1YWwgdmlkZW8pLlxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIHZhciB2aWRlb1RodW1iID0gJCgnIycgKyBjb250YWluZXIuaWQgKyAnPnZpZGVvJykuZ2V0KDApO1xuICAgICAgICAgICAgICAgIGlmICh2aWRlb1RodW1iKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgICAgICAgICAgUlRDLmdldFZpZGVvU3JjKHZpZGVvVGh1bWIpLFxuICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAvLyBBZGQgaG92ZXIgaGFuZGxlclxuICAgICAgICAgICAgJChjb250YWluZXIpLmhvdmVyKFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zaG93RGlzcGxheU5hbWUoY29udGFpbmVyLmlkLCB0cnVlKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdmlkZW9TcmMgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJCgnIycgKyBjb250YWluZXIuaWQgKyAnPnZpZGVvJylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAmJiAkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NyYyA9IFJUQy5nZXRWaWRlb1NyYygkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKS5nZXQoMCkpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlIHZpZGVvIGhhcyBiZWVuIFwicGlubmVkXCIgYnkgdGhlIHVzZXIgd2Ugd2FudCB0b1xuICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZSBkaXNwbGF5IG5hbWUgb24gcGxhY2UuXG4gICAgICAgICAgICAgICAgICAgIGlmICghVmlkZW9MYXlvdXQuaXNMYXJnZVZpZGVvVmlzaWJsZSgpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfHwgdmlkZW9TcmMgIT09IFJUQy5nZXRWaWRlb1NyYygkKCcjbGFyZ2VWaWRlbycpWzBdKSlcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZShjb250YWluZXIuaWQsIGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5ld0VsZW1lbnRJZDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyB0aGUgcmVtb3RlIHN0cmVhbSBlbGVtZW50IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIHN0cmVhbSBhbmRcbiAgICAgKiBwYXJlbnQgY29udGFpbmVyLlxuICAgICAqIFxuICAgICAqIEBwYXJhbSBzdHJlYW0gdGhlIHN0cmVhbVxuICAgICAqIEBwYXJhbSBpc1ZpZGVvIDx0dD50cnVlPC90dD4gaWYgZ2l2ZW4gPHR0PnN0cmVhbTwvdHQ+IGlzIGEgdmlkZW8gb25lLlxuICAgICAqIEBwYXJhbSBjb250YWluZXJcbiAgICAgKi9cbiAgICBteS5yZW1vdmVSZW1vdGVTdHJlYW1FbGVtZW50ID0gZnVuY3Rpb24gKHN0cmVhbSwgaXNWaWRlbywgY29udGFpbmVyKSB7XG4gICAgICAgIGlmICghY29udGFpbmVyKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBzZWxlY3QgPSBudWxsO1xuICAgICAgICB2YXIgcmVtb3ZlZFZpZGVvU3JjID0gbnVsbDtcbiAgICAgICAgaWYgKGlzVmlkZW8pIHtcbiAgICAgICAgICAgIHNlbGVjdCA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpO1xuICAgICAgICAgICAgcmVtb3ZlZFZpZGVvU3JjID0gUlRDLmdldFZpZGVvU3JjKHNlbGVjdC5nZXQoMCkpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIHNlbGVjdCA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz5hdWRpbycpO1xuXG5cbiAgICAgICAgLy8gTWFyayB2aWRlbyBhcyByZW1vdmVkIHRvIGNhbmNlbCB3YWl0aW5nIGxvb3AoaWYgdmlkZW8gaXMgcmVtb3ZlZFxuICAgICAgICAvLyBiZWZvcmUgaGFzIHN0YXJ0ZWQpXG4gICAgICAgIHNlbGVjdC5yZW1vdmVkID0gdHJ1ZTtcbiAgICAgICAgc2VsZWN0LnJlbW92ZSgpO1xuXG4gICAgICAgIHZhciBhdWRpb0NvdW50ID0gJCgnIycgKyBjb250YWluZXIuaWQgKyAnPmF1ZGlvJykubGVuZ3RoO1xuICAgICAgICB2YXIgdmlkZW9Db3VudCA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpLmxlbmd0aDtcblxuICAgICAgICBpZiAoIWF1ZGlvQ291bnQgJiYgIXZpZGVvQ291bnQpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiUmVtb3ZlIHdob2xlIHVzZXJcIiwgY29udGFpbmVyLmlkKTtcbiAgICAgICAgICAgIGlmKFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2NvbnRhaW5lci5pZF0pXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbY29udGFpbmVyLmlkXS5yZW1vdmUoKTtcbiAgICAgICAgICAgIC8vIFJlbW92ZSB3aG9sZSBjb250YWluZXJcbiAgICAgICAgICAgIGNvbnRhaW5lci5yZW1vdmUoKTtcblxuICAgICAgICAgICAgVXRpbC5wbGF5U291bmROb3RpZmljYXRpb24oJ3VzZXJMZWZ0Jyk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocmVtb3ZlZFZpZGVvU3JjKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3ZlZFZpZGVvKHJlbW92ZWRWaWRlb1NyYyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3cvaGlkZSBwZWVyIGNvbnRhaW5lciBmb3IgdGhlIGdpdmVuIHJlc291cmNlSmlkLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHNob3dQZWVyQ29udGFpbmVyKHJlc291cmNlSmlkLCBzdGF0ZSkge1xuICAgICAgICB2YXIgcGVlckNvbnRhaW5lciA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgIGlmICghcGVlckNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICB2YXIgaXNIaWRlID0gc3RhdGUgPT09ICdoaWRlJztcbiAgICAgICAgdmFyIHJlc2l6ZVRodW1ibmFpbHMgPSBmYWxzZTtcblxuICAgICAgICBpZiAoIWlzSGlkZSkge1xuICAgICAgICAgICAgaWYgKCFwZWVyQ29udGFpbmVyLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgcmVzaXplVGh1bWJuYWlscyA9IHRydWU7XG4gICAgICAgICAgICAgICAgcGVlckNvbnRhaW5lci5zaG93KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChzdGF0ZSA9PSAnc2hvdycpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgLy8gcGVlckNvbnRhaW5lci5jc3MoJy13ZWJraXQtZmlsdGVyJywgJycpO1xuICAgICAgICAgICAgICAgIHZhciBqaWQgPSB4bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihqaWQsIGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgLy8gaWYgKHN0YXRlID09ICdhdmF0YXInKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIC8vIHBlZXJDb250YWluZXIuY3NzKCctd2Via2l0LWZpbHRlcicsICdncmF5c2NhbGUoMTAwJSknKTtcbiAgICAgICAgICAgICAgICB2YXIgamlkID0geG1wcC5maW5kSmlkRnJvbVJlc291cmNlKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoamlkLCB0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChwZWVyQ29udGFpbmVyLmlzKCc6dmlzaWJsZScpICYmIGlzSGlkZSlcbiAgICAgICAge1xuICAgICAgICAgICAgcmVzaXplVGh1bWJuYWlscyA9IHRydWU7XG4gICAgICAgICAgICBwZWVyQ29udGFpbmVyLmhpZGUoKTtcbiAgICAgICAgICAgIGlmKFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWRdKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWRdLmhpZGUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXNpemVUaHVtYm5haWxzKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBXZSB3YW50IHRvIGJlIGFibGUgdG8gcGluIGEgcGFydGljaXBhbnQgZnJvbSB0aGUgY29udGFjdCBsaXN0LCBldmVuXG4gICAgICAgIC8vIGlmIGhlJ3Mgbm90IGluIHRoZSBsYXN0TiBzZXQhXG4gICAgICAgIC8vIENvbnRhY3RMaXN0LnNldENsaWNrYWJsZShyZXNvdXJjZUppZCwgIWlzSGlkZSk7XG5cbiAgICB9O1xuXG4gICAgbXkuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgICAgICBpZiAobmFtZSAmJiBuaWNrbmFtZSAhPT0gbmFtZSkge1xuICAgICAgICAgICAgbmlja25hbWUgPSBuYW1lO1xuICAgICAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSA9IG5pY2tuYW1lO1xuICAgICAgICAgICAgeG1wcC5hZGRUb1ByZXNlbmNlKFwiZGlzcGxheU5hbWVcIiwgbmlja25hbWUpO1xuXG4gICAgICAgICAgICBDaGF0LnNldENoYXRDb252ZXJzYXRpb25Nb2RlKHRydWUpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCEkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgICAgIGlmIChuaWNrbmFtZSlcbiAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLnRleHQobmlja25hbWUgKyBcIiAobWUpXCIpO1xuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJylcbiAgICAgICAgICAgICAgICAgICAgLnRleHQoaW50ZXJmYWNlQ29uZmlnLkRFRkFVTFRfTE9DQUxfRElTUExBWV9OQU1FKTtcbiAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJykuc2hvdygpO1xuICAgICAgICB9XG5cbiAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLmhpZGUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MvaGlkZXMgdGhlIGRpc3BsYXkgbmFtZSBvbiB0aGUgcmVtb3RlIHZpZGVvLlxuICAgICAqIEBwYXJhbSB2aWRlb1NwYW5JZCB0aGUgaWRlbnRpZmllciBvZiB0aGUgdmlkZW8gc3BhbiBlbGVtZW50XG4gICAgICogQHBhcmFtIGlzU2hvdyBpbmRpY2F0ZXMgaWYgdGhlIGRpc3BsYXkgbmFtZSBzaG91bGQgYmUgc2hvd24gb3IgaGlkZGVuXG4gICAgICovXG4gICAgbXkuc2hvd0Rpc3BsYXlOYW1lID0gZnVuY3Rpb24odmlkZW9TcGFuSWQsIGlzU2hvdykge1xuICAgICAgICB2YXIgbmFtZVNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLmRpc3BsYXluYW1lJykuZ2V0KDApO1xuICAgICAgICBpZiAoaXNTaG93KSB7XG4gICAgICAgICAgICBpZiAobmFtZVNwYW4gJiYgbmFtZVNwYW4uaW5uZXJIVE1MICYmIG5hbWVTcGFuLmlubmVySFRNTC5sZW5ndGgpIFxuICAgICAgICAgICAgICAgIG5hbWVTcGFuLnNldEF0dHJpYnV0ZShcInN0eWxlXCIsIFwiZGlzcGxheTppbmxpbmUtYmxvY2s7XCIpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKG5hbWVTcGFuKVxuICAgICAgICAgICAgICAgIG5hbWVTcGFuLnNldEF0dHJpYnV0ZShcInN0eWxlXCIsIFwiZGlzcGxheTpub25lO1wiKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB0aGUgcHJlc2VuY2Ugc3RhdHVzIG1lc3NhZ2UgZm9yIHRoZSBnaXZlbiB2aWRlby5cbiAgICAgKi9cbiAgICBteS5zZXRQcmVzZW5jZVN0YXR1cyA9IGZ1bmN0aW9uICh2aWRlb1NwYW5JZCwgc3RhdHVzTXNnKSB7XG5cbiAgICAgICAgaWYgKCEkKCcjJyArIHZpZGVvU3BhbklkKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIC8vIE5vIGNvbnRhaW5lclxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHN0YXR1c1NwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnN0YXR1cycpO1xuICAgICAgICBpZiAoIXN0YXR1c1NwYW4ubGVuZ3RoKSB7XG4gICAgICAgICAgICAvL0FkZCBzdGF0dXMgc3BhblxuICAgICAgICAgICAgc3RhdHVzU3BhbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgIHN0YXR1c1NwYW4uY2xhc3NOYW1lID0gJ3N0YXR1cyc7XG4gICAgICAgICAgICBzdGF0dXNTcGFuLmlkID0gdmlkZW9TcGFuSWQgKyAnX3N0YXR1cyc7XG4gICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkKVswXS5hcHBlbmRDaGlsZChzdGF0dXNTcGFuKTtcblxuICAgICAgICAgICAgc3RhdHVzU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uc3RhdHVzJyk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBEaXNwbGF5IHN0YXR1c1xuICAgICAgICBpZiAoc3RhdHVzTXNnICYmIHN0YXR1c01zZy5sZW5ndGgpIHtcbiAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnX3N0YXR1cycpLnRleHQoc3RhdHVzTXNnKTtcbiAgICAgICAgICAgIHN0YXR1c1NwYW4uZ2V0KDApLnNldEF0dHJpYnV0ZShcInN0eWxlXCIsIFwiZGlzcGxheTppbmxpbmUtYmxvY2s7XCIpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gSGlkZVxuICAgICAgICAgICAgc3RhdHVzU3Bhbi5nZXQoMCkuc2V0QXR0cmlidXRlKFwic3R5bGVcIiwgXCJkaXNwbGF5Om5vbmU7XCIpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgdmlzdWFsIGluZGljYXRvciBmb3IgdGhlIG1vZGVyYXRvciBvZiB0aGUgY29uZmVyZW5jZS5cbiAgICAgKi9cbiAgICBteS5zaG93TW9kZXJhdG9ySW5kaWNhdG9yID0gZnVuY3Rpb24gKCkge1xuXG4gICAgICAgIHZhciBpc01vZGVyYXRvciA9IHhtcHAuaXNNb2RlcmF0b3IoKTtcbiAgICAgICAgaWYgKGlzTW9kZXJhdG9yKSB7XG4gICAgICAgICAgICB2YXIgaW5kaWNhdG9yU3BhbiA9ICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyIC5mb2N1c2luZGljYXRvcicpO1xuXG4gICAgICAgICAgICBpZiAoaW5kaWNhdG9yU3Bhbi5jaGlsZHJlbigpLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBjcmVhdGVNb2RlcmF0b3JJbmRpY2F0b3JFbGVtZW50KGluZGljYXRvclNwYW5bMF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIG1lbWJlcnMgPSB4bXBwLmdldE1lbWJlcnMoKTtcblxuICAgICAgICBPYmplY3Qua2V5cyhtZW1iZXJzKS5mb3JFYWNoKGZ1bmN0aW9uIChqaWQpIHtcblxuICAgICAgICAgICAgaWYgKFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgPT09ICdmb2N1cycpIHtcbiAgICAgICAgICAgICAgICAvLyBTa2lwIHNlcnZlciBzaWRlIGZvY3VzXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZDtcbiAgICAgICAgICAgIHZhciB2aWRlb0NvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHZpZGVvU3BhbklkKTtcblxuICAgICAgICAgICAgaWYgKCF2aWRlb0NvbnRhaW5lcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyBjb250YWluZXIgZm9yIFwiICsgamlkKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBtZW1iZXIgPSBtZW1iZXJzW2ppZF07XG5cbiAgICAgICAgICAgIGlmIChtZW1iZXIucm9sZSA9PT0gJ21vZGVyYXRvcicpIHtcbiAgICAgICAgICAgICAgICAvLyBSZW1vdmUgbWVudSBpZiBwZWVyIGlzIG1vZGVyYXRvclxuICAgICAgICAgICAgICAgIHZhciBtZW51U3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4ucmVtb3RldmlkZW9tZW51Jyk7XG4gICAgICAgICAgICAgICAgaWYgKG1lbnVTcGFuLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICByZW1vdmVSZW1vdGVWaWRlb01lbnUodmlkZW9TcGFuSWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBTaG93IG1vZGVyYXRvciBpbmRpY2F0b3JcbiAgICAgICAgICAgICAgICB2YXIgaW5kaWNhdG9yU3BhblxuICAgICAgICAgICAgICAgICAgICA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnIC5mb2N1c2luZGljYXRvcicpO1xuXG4gICAgICAgICAgICAgICAgaWYgKCFpbmRpY2F0b3JTcGFuIHx8IGluZGljYXRvclNwYW4ubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGluZGljYXRvclNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICAgICAgICAgIGluZGljYXRvclNwYW4uY2xhc3NOYW1lID0gJ2ZvY3VzaW5kaWNhdG9yJztcblxuICAgICAgICAgICAgICAgICAgICB2aWRlb0NvbnRhaW5lci5hcHBlbmRDaGlsZChpbmRpY2F0b3JTcGFuKTtcblxuICAgICAgICAgICAgICAgICAgICBjcmVhdGVNb2RlcmF0b3JJbmRpY2F0b3JFbGVtZW50KGluZGljYXRvclNwYW4pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNNb2RlcmF0b3IpIHtcbiAgICAgICAgICAgICAgICAvLyBXZSBhcmUgbW9kZXJhdG9yLCBidXQgdXNlciBpcyBub3QgLSBhZGQgbWVudVxuICAgICAgICAgICAgICAgIGlmICgkKCcjcmVtb3RlX3BvcHVwbWVudV8nICsgcmVzb3VyY2VKaWQpLmxlbmd0aCA8PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGFkZFJlbW90ZVZpZGVvTWVudShcbiAgICAgICAgICAgICAgICAgICAgICAgIGppZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB2aWRlbyBtdXRlZCBpbmRpY2F0b3Igb3ZlciBzbWFsbCB2aWRlb3MuXG4gICAgICovXG4gICAgbXkuc2hvd1ZpZGVvSW5kaWNhdG9yID0gZnVuY3Rpb24odmlkZW9TcGFuSWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIHZpZGVvTXV0ZWRTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi52aWRlb011dGVkJyk7XG5cbiAgICAgICAgaWYgKGlzTXV0ZWQgPT09ICdmYWxzZScpIHtcbiAgICAgICAgICAgIGlmICh2aWRlb011dGVkU3Bhbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4ucmVtb3ZlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBpZih2aWRlb011dGVkU3Bhbi5sZW5ndGggPT0gMCkge1xuICAgICAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgICAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmNsYXNzTmFtZSA9ICd2aWRlb011dGVkJztcblxuICAgICAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKHZpZGVvTXV0ZWRTcGFuKTtcblxuICAgICAgICAgICAgICAgIHZhciBtdXRlZEluZGljYXRvciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2knKTtcbiAgICAgICAgICAgICAgICBtdXRlZEluZGljYXRvci5jbGFzc05hbWUgPSAnaWNvbi1jYW1lcmEtZGlzYWJsZWQnO1xuICAgICAgICAgICAgICAgIFV0aWwuc2V0VG9vbHRpcChtdXRlZEluZGljYXRvcixcbiAgICAgICAgICAgICAgICAgICAgXCJQYXJ0aWNpcGFudCBoYXM8YnIvPnN0b3BwZWQgdGhlIGNhbWVyYS5cIixcbiAgICAgICAgICAgICAgICAgICAgXCJ0b3BcIik7XG4gICAgICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4uYXBwZW5kQ2hpbGQobXV0ZWRJbmRpY2F0b3IpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVNdXRlUG9zaXRpb24odmlkZW9TcGFuSWQpO1xuXG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkudXBkYXRlTXV0ZVBvc2l0aW9uID0gZnVuY3Rpb24gKHZpZGVvU3BhbklkKSB7XG4gICAgICAgIHZhciBhdWRpb011dGVkU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uYXVkaW9NdXRlZCcpO1xuICAgICAgICB2YXIgY29ubmVjdGlvbkluZGljYXRvciA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPmRpdi5jb25uZWN0aW9uaW5kaWNhdG9yJyk7XG4gICAgICAgIHZhciB2aWRlb011dGVkU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4udmlkZW9NdXRlZCcpO1xuICAgICAgICBpZihjb25uZWN0aW9uSW5kaWNhdG9yLmxlbmd0aCA+IDBcbiAgICAgICAgICAgICYmIGNvbm5lY3Rpb25JbmRpY2F0b3JbMF0uc3R5bGUuZGlzcGxheSAhPSBcIm5vbmVcIikge1xuICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uY3NzKHtyaWdodDogXCIyM3B4XCJ9KTtcbiAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6ICgoYXVkaW9NdXRlZFNwYW4ubGVuZ3RoID4gMD8gMjMgOiAwKSArIDMwKSArIFwicHhcIn0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uY3NzKHtyaWdodDogXCIwcHhcIn0pO1xuICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4uY3NzKHtyaWdodDogKGF1ZGlvTXV0ZWRTcGFuLmxlbmd0aCA+IDA/IDMwIDogMCkgKyBcInB4XCJ9KTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBTaG93cyBhdWRpbyBtdXRlZCBpbmRpY2F0b3Igb3ZlciBzbWFsbCB2aWRlb3MuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGlzTXV0ZWRcbiAgICAgKi9cbiAgICBteS5zaG93QXVkaW9JbmRpY2F0b3IgPSBmdW5jdGlvbih2aWRlb1NwYW5JZCwgaXNNdXRlZCkge1xuICAgICAgICB2YXIgYXVkaW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLmF1ZGlvTXV0ZWQnKTtcblxuICAgICAgICBpZiAoaXNNdXRlZCA9PT0gJ2ZhbHNlJykge1xuICAgICAgICAgICAgaWYgKGF1ZGlvTXV0ZWRTcGFuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICBhdWRpb011dGVkU3Bhbi5wb3BvdmVyKCdoaWRlJyk7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4ucmVtb3ZlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBpZihhdWRpb011dGVkU3Bhbi5sZW5ndGggPT0gMCApIHtcbiAgICAgICAgICAgICAgICBhdWRpb011dGVkU3BhbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgICAgICBhdWRpb011dGVkU3Bhbi5jbGFzc05hbWUgPSAnYXVkaW9NdXRlZCc7XG4gICAgICAgICAgICAgICAgVXRpbC5zZXRUb29sdGlwKGF1ZGlvTXV0ZWRTcGFuLFxuICAgICAgICAgICAgICAgICAgICBcIlBhcnRpY2lwYW50IGlzIG11dGVkXCIsXG4gICAgICAgICAgICAgICAgICAgIFwidG9wXCIpO1xuXG4gICAgICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQoYXVkaW9NdXRlZFNwYW4pO1xuICAgICAgICAgICAgICAgIHZhciBtdXRlZEluZGljYXRvciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2knKTtcbiAgICAgICAgICAgICAgICBtdXRlZEluZGljYXRvci5jbGFzc05hbWUgPSAnaWNvbi1taWMtZGlzYWJsZWQnO1xuICAgICAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLmFwcGVuZENoaWxkKG11dGVkSW5kaWNhdG9yKTtcblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTXV0ZVBvc2l0aW9uKHZpZGVvU3BhbklkKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKlxuICAgICAqIFNob3dzIG9yIGhpZGVzIHRoZSBhdWRpbyBtdXRlZCBpbmRpY2F0b3Igb3ZlciB0aGUgbG9jYWwgdGh1bWJuYWlsIHZpZGVvLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gaXNNdXRlZFxuICAgICAqL1xuICAgIG15LnNob3dMb2NhbEF1ZGlvSW5kaWNhdG9yID0gZnVuY3Rpb24oaXNNdXRlZCkge1xuICAgICAgICBWaWRlb0xheW91dC5zaG93QXVkaW9JbmRpY2F0b3IoJ2xvY2FsVmlkZW9Db250YWluZXInLCBpc011dGVkLnRvU3RyaW5nKCkpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRoZSBsYXJnZSB2aWRlbyBjb250YWluZXIuXG4gICAgICovXG4gICAgbXkucmVzaXplTGFyZ2VWaWRlb0NvbnRhaW5lciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQ2hhdC5yZXNpemVDaGF0KCk7XG4gICAgICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG4gICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IFVJVXRpbC5nZXRBdmFpbGFibGVWaWRlb1dpZHRoKCk7XG5cbiAgICAgICAgaWYgKGF2YWlsYWJsZVdpZHRoIDwgMCB8fCBhdmFpbGFibGVIZWlnaHQgPCAwKSByZXR1cm47XG5cbiAgICAgICAgJCgnI3ZpZGVvc3BhY2UnKS53aWR0aChhdmFpbGFibGVXaWR0aCk7XG4gICAgICAgICQoJyN2aWRlb3NwYWNlJykuaGVpZ2h0KGF2YWlsYWJsZUhlaWdodCk7XG4gICAgICAgICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykud2lkdGgoYXZhaWxhYmxlV2lkdGgpO1xuICAgICAgICAkKCcjbGFyZ2VWaWRlb0NvbnRhaW5lcicpLmhlaWdodChhdmFpbGFibGVIZWlnaHQpO1xuXG4gICAgICAgIHZhciBhdmF0YXJTaXplID0gaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFO1xuICAgICAgICB2YXIgdG9wID0gYXZhaWxhYmxlSGVpZ2h0IC8gMiAtIGF2YXRhclNpemUgLyA0ICogMztcbiAgICAgICAgJCgnI2FjdGl2ZVNwZWFrZXInKS5jc3MoJ3RvcCcsIHRvcCk7XG5cbiAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRodW1ibmFpbHMuXG4gICAgICovXG4gICAgbXkucmVzaXplVGh1bWJuYWlscyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdmlkZW9TcGFjZVdpZHRoID0gJCgnI3JlbW90ZVZpZGVvcycpLndpZHRoKCk7XG5cbiAgICAgICAgdmFyIHRodW1ibmFpbFNpemUgPSBWaWRlb0xheW91dC5jYWxjdWxhdGVUaHVtYm5haWxTaXplKHZpZGVvU3BhY2VXaWR0aCk7XG4gICAgICAgIHZhciB3aWR0aCA9IHRodW1ibmFpbFNpemVbMF07XG4gICAgICAgIHZhciBoZWlnaHQgPSB0aHVtYm5haWxTaXplWzFdO1xuXG4gICAgICAgIC8vIHNpemUgdmlkZW9zIHNvIHRoYXQgd2hpbGUga2VlcGluZyBBUiBhbmQgbWF4IGhlaWdodCwgd2UgaGF2ZSBhXG4gICAgICAgIC8vIG5pY2UgZml0XG4gICAgICAgICQoJyNyZW1vdGVWaWRlb3MnKS5oZWlnaHQoaGVpZ2h0KTtcbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcz5zcGFuJykud2lkdGgod2lkdGgpO1xuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS5oZWlnaHQoaGVpZ2h0KTtcblxuICAgICAgICAkKCcudXNlckF2YXRhcicpLmNzcygnbGVmdCcsICh3aWR0aCAtIGhlaWdodCkgLyAyKTtcblxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwicmVtb3RldmlkZW8ucmVzaXplZFwiLCBbd2lkdGgsIGhlaWdodF0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBkb21pbmFudCBzcGVha2VyIFVJLlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCB0b1xuICAgICAqIGFjdGl2YXRlL2RlYWN0aXZhdGVcbiAgICAgKiBAcGFyYW0gaXNFbmFibGUgaW5kaWNhdGVzIGlmIHRoZSBkb21pbmFudCBzcGVha2VyIHNob3VsZCBiZSBlbmFibGVkIG9yXG4gICAgICogZGlzYWJsZWRcbiAgICAgKi9cbiAgICBteS5lbmFibGVEb21pbmFudFNwZWFrZXIgPSBmdW5jdGlvbihyZXNvdXJjZUppZCwgaXNFbmFibGUpIHtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSBudWxsO1xuICAgICAgICB2YXIgdmlkZW9Db250YWluZXJJZCA9IG51bGw7XG4gICAgICAgIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID09PSB4bXBwLm15UmVzb3VyY2UoKSkge1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb1dyYXBwZXInO1xuICAgICAgICAgICAgdmlkZW9Db250YWluZXJJZCA9ICdsb2NhbFZpZGVvQ29udGFpbmVyJztcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZDtcbiAgICAgICAgICAgIHZpZGVvQ29udGFpbmVySWQgPSB2aWRlb1NwYW5JZDtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBkaXNwbGF5TmFtZSA9IHJlc291cmNlSmlkO1xuICAgICAgICB2YXIgbmFtZVNwYW4gPSAkKCcjJyArIHZpZGVvQ29udGFpbmVySWQgKyAnPnNwYW4uZGlzcGxheW5hbWUnKTtcbiAgICAgICAgaWYgKG5hbWVTcGFuLmxlbmd0aCA+IDApXG4gICAgICAgICAgICBkaXNwbGF5TmFtZSA9IG5hbWVTcGFuLmh0bWwoKTtcblxuICAgICAgICBjb25zb2xlLmxvZyhcIlVJIGVuYWJsZSBkb21pbmFudCBzcGVha2VyXCIsXG4gICAgICAgICAgICBkaXNwbGF5TmFtZSxcbiAgICAgICAgICAgIHJlc291cmNlSmlkLFxuICAgICAgICAgICAgaXNFbmFibGUpO1xuXG4gICAgICAgIHZpZGVvU3BhbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHZpZGVvQ29udGFpbmVySWQpO1xuXG4gICAgICAgIGlmICghdmlkZW9TcGFuKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgdmlkZW8gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz52aWRlbycpO1xuXG4gICAgICAgIGlmICh2aWRlbyAmJiB2aWRlby5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBpZiAoaXNFbmFibGUpIHtcbiAgICAgICAgICAgICAgICB2YXIgaXNMYXJnZVZpZGVvVmlzaWJsZSA9IFZpZGVvTGF5b3V0LmlzTGFyZ2VWaWRlb09uVG9wKCk7XG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd0Rpc3BsYXlOYW1lKHZpZGVvQ29udGFpbmVySWQsIGlzTGFyZ2VWaWRlb1Zpc2libGUpO1xuXG4gICAgICAgICAgICAgICAgaWYgKCF2aWRlb1NwYW4uY2xhc3NMaXN0LmNvbnRhaW5zKFwiZG9taW5hbnRzcGVha2VyXCIpKVxuICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYW4uY2xhc3NMaXN0LmFkZChcImRvbWluYW50c3BlYWtlclwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSh2aWRlb0NvbnRhaW5lcklkLCBmYWxzZSk7XG5cbiAgICAgICAgICAgICAgICBpZiAodmlkZW9TcGFuLmNsYXNzTGlzdC5jb250YWlucyhcImRvbWluYW50c3BlYWtlclwiKSlcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFuLmNsYXNzTGlzdC5yZW1vdmUoXCJkb21pbmFudHNwZWFrZXJcIik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihcbiAgICAgICAgICAgICAgICB4bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2VKaWQpKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGVzIHRoZSB0aHVtYm5haWwgc2l6ZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB2aWRlb1NwYWNlV2lkdGggdGhlIHdpZHRoIG9mIHRoZSB2aWRlbyBzcGFjZVxuICAgICAqL1xuICAgIG15LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUgPSBmdW5jdGlvbiAodmlkZW9TcGFjZVdpZHRoKSB7XG4gICAgICAgIC8vIENhbGN1bGF0ZSB0aGUgYXZhaWxhYmxlIGhlaWdodCwgd2hpY2ggaXMgdGhlIGlubmVyIHdpbmRvdyBoZWlnaHQgbWludXNcbiAgICAgICAvLyAzOXB4IGZvciB0aGUgaGVhZGVyIG1pbnVzIDJweCBmb3IgdGhlIGRlbGltaXRlciBsaW5lcyBvbiB0aGUgdG9wIGFuZFxuICAgICAgIC8vIGJvdHRvbSBvZiB0aGUgbGFyZ2UgdmlkZW8sIG1pbnVzIHRoZSAzNnB4IHNwYWNlIGluc2lkZSB0aGUgcmVtb3RlVmlkZW9zXG4gICAgICAgLy8gY29udGFpbmVyIHVzZWQgZm9yIGhpZ2hsaWdodGluZyBzaGFkb3cuXG4gICAgICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IDEwMDtcblxuICAgICAgICB2YXIgbnVtdmlkcyA9ICQoJyNyZW1vdGVWaWRlb3M+c3Bhbjp2aXNpYmxlJykubGVuZ3RoO1xuICAgICAgICBpZiAobG9jYWxMYXN0TkNvdW50ICYmIGxvY2FsTGFzdE5Db3VudCA+IDApIHtcbiAgICAgICAgICAgIG51bXZpZHMgPSBNYXRoLm1pbihsb2NhbExhc3ROQ291bnQgKyAxLCBudW12aWRzKTtcbiAgICAgICAgfVxuXG4gICAgICAgLy8gUmVtb3ZlIHRoZSAzcHggYm9yZGVycyBhcnJvdW5kIHZpZGVvcyBhbmQgYm9yZGVyIGFyb3VuZCB0aGUgcmVtb3RlXG4gICAgICAgLy8gdmlkZW9zIGFyZWEgYW5kIHRoZSA0IHBpeGVscyBiZXR3ZWVuIHRoZSBsb2NhbCB2aWRlbyBhbmQgdGhlIG90aGVyc1xuICAgICAgIC8vVE9ETzogRmluZCBvdXQgd2hlcmUgdGhlIDQgcGl4ZWxzIGNvbWUgZnJvbSBhbmQgcmVtb3ZlIHRoZW1cbiAgICAgICB2YXIgYXZhaWxhYmxlV2luV2lkdGggPSB2aWRlb1NwYWNlV2lkdGggLSAyICogMyAqIG51bXZpZHMgLSA3MCAtIDQ7XG5cbiAgICAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVXaW5XaWR0aCAvIG51bXZpZHM7XG4gICAgICAgdmFyIGFzcGVjdFJhdGlvID0gMTYuMCAvIDkuMDtcbiAgICAgICB2YXIgbWF4SGVpZ2h0ID0gTWF0aC5taW4oMTYwLCBhdmFpbGFibGVIZWlnaHQpO1xuICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IE1hdGgubWluKG1heEhlaWdodCwgYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyk7XG4gICAgICAgaWYgKGF2YWlsYWJsZUhlaWdodCA8IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW8pIHtcbiAgICAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBNYXRoLmZsb29yKGF2YWlsYWJsZUhlaWdodCAqIGFzcGVjdFJhdGlvKTtcbiAgICAgICB9XG5cbiAgICAgICByZXR1cm4gW2F2YWlsYWJsZVdpZHRoLCBhdmFpbGFibGVIZWlnaHRdO1xuICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIHJlbW90ZSB2aWRlbyBtZW51LlxuICAgICAqXG4gICAgICogQHBhcmFtIGppZCB0aGUgamlkIGluZGljYXRpbmcgdGhlIHZpZGVvIGZvciB3aGljaCB3ZSdyZSBhZGRpbmcgYSBtZW51LlxuICAgICAqIEBwYXJhbSBpc011dGVkIGluZGljYXRlcyB0aGUgY3VycmVudCBtdXRlIHN0YXRlXG4gICAgICovXG4gICAgbXkudXBkYXRlUmVtb3RlVmlkZW9NZW51ID0gZnVuY3Rpb24oamlkLCBpc011dGVkKSB7XG4gICAgICAgIHZhciBtdXRlTWVudUl0ZW1cbiAgICAgICAgICAgID0gJCgnI3JlbW90ZV9wb3B1cG1lbnVfJ1xuICAgICAgICAgICAgICAgICAgICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZClcbiAgICAgICAgICAgICAgICAgICAgKyAnPmxpPmEubXV0ZWxpbmsnKTtcblxuICAgICAgICB2YXIgbXV0ZWRJbmRpY2F0b3IgPSBcIjxpIGNsYXNzPSdpY29uLW1pYy1kaXNhYmxlZCc+PC9pPlwiO1xuXG4gICAgICAgIGlmIChtdXRlTWVudUl0ZW0ubGVuZ3RoKSB7XG4gICAgICAgICAgICB2YXIgbXV0ZUxpbmsgPSBtdXRlTWVudUl0ZW0uZ2V0KDApO1xuXG4gICAgICAgICAgICBpZiAoaXNNdXRlZCA9PT0gJ3RydWUnKSB7XG4gICAgICAgICAgICAgICAgbXV0ZUxpbmsuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgKyAnIE11dGVkJztcbiAgICAgICAgICAgICAgICBtdXRlTGluay5jbGFzc05hbWUgPSAnbXV0ZWxpbmsgZGlzYWJsZWQnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgbXV0ZUxpbmsuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgKyAnIE11dGUnO1xuICAgICAgICAgICAgICAgIG11dGVMaW5rLmNsYXNzTmFtZSA9ICdtdXRlbGluayc7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBkb21pbmFudCBzcGVha2VyIHJlc291cmNlIGppZC5cbiAgICAgKi9cbiAgICBteS5nZXREb21pbmFudFNwZWFrZXJSZXNvdXJjZUppZCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGN1cnJlbnREb21pbmFudFNwZWFrZXI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcmVzb3VyY2UgamlkIHRvIHRoZSBnaXZlbiBwZWVyIGNvbnRhaW5lclxuICAgICAqIERPTSBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHJldHVybiB0aGUgY29ycmVzcG9uZGluZyByZXNvdXJjZSBqaWQgdG8gdGhlIGdpdmVuIHBlZXIgY29udGFpbmVyXG4gICAgICogRE9NIGVsZW1lbnRcbiAgICAgKi9cbiAgICBteS5nZXRQZWVyQ29udGFpbmVyUmVzb3VyY2VKaWQgPSBmdW5jdGlvbiAoY29udGFpbmVyRWxlbWVudCkge1xuICAgICAgICB2YXIgaSA9IGNvbnRhaW5lckVsZW1lbnQuaWQuaW5kZXhPZigncGFydGljaXBhbnRfJyk7XG5cbiAgICAgICAgaWYgKGkgPj0gMClcbiAgICAgICAgICAgIHJldHVybiBjb250YWluZXJFbGVtZW50LmlkLnN1YnN0cmluZyhpICsgMTIpOyBcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT24gY29udGFjdCBsaXN0IGl0ZW0gY2xpY2tlZC5cbiAgICAgKi9cbiAgICAkKENvbnRhY3RMaXN0KS5iaW5kKCdjb250YWN0Y2xpY2tlZCcsIGZ1bmN0aW9uKGV2ZW50LCBqaWQpIHtcbiAgICAgICAgaWYgKCFqaWQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIHZhciB2aWRlb0NvbnRhaW5lciA9ICQoXCIjcGFydGljaXBhbnRfXCIgKyByZXNvdXJjZSk7XG4gICAgICAgIGlmICh2aWRlb0NvbnRhaW5lci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB2YXIgdmlkZW9UaHVtYiA9ICQoJ3ZpZGVvJywgdmlkZW9Db250YWluZXIpLmdldCgwKTtcbiAgICAgICAgICAgIC8vIEl0IGlzIG5vdCBhbHdheXMgdGhlIGNhc2UgdGhhdCBhIHZpZGVvVGh1bWIgZXhpc3RzIChpZiB0aGVyZSBpc1xuICAgICAgICAgICAgLy8gbm8gYWN0dWFsIHZpZGVvKS5cbiAgICAgICAgICAgIGlmICh2aWRlb1RodW1iKSB7XG4gICAgICAgICAgICAgICAgaWYgKHZpZGVvVGh1bWIuc3JjICYmIHZpZGVvVGh1bWIuc3JjICE9ICcnKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gV2UgaGF2ZSBhIHZpZGVvIHNyYywgZ3JlYXQhIExldCdzIHVwZGF0ZSB0aGUgbGFyZ2UgdmlkZW9cbiAgICAgICAgICAgICAgICAgICAgLy8gbm93LlxuXG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9UaHVtYi5zcmMsXG4gICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgd2UgZG9uJ3QgaGF2ZSBhIHZpZGVvIHNyYyBmb3IgamlkLCB0aGVyZSdzIGFic29sdXRlbHlcbiAgICAgICAgICAgICAgICAgICAgLy8gbm8gcG9pbnQgaW4gY2FsbGluZyBoYW5kbGVWaWRlb1RodW1iQ2xpY2tlZDsgUXVpdGVcbiAgICAgICAgICAgICAgICAgICAgLy8gc2ltcGx5LCBpdCB3b24ndCB3b3JrIGJlY2F1c2UgaXQgbmVlZHMgYW4gc3JjIHRvIGF0dGFjaFxuICAgICAgICAgICAgICAgICAgICAvLyB0byB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICAgICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgICAgICAgIC8vIEluc3RlYWQsIHdlIHRyaWdnZXIgdGhlIHBpbm5lZCBlbmRwb2ludCBjaGFuZ2VkIGV2ZW50IHRvXG4gICAgICAgICAgICAgICAgICAgIC8vIGxldCB0aGUgYnJpZGdlIGFkanVzdCBpdHMgbGFzdE4gc2V0IGZvciBteWppZCBhbmQgc3RvcmVcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIHBpbm5lZCB1c2VyIGluIHRoZSBsYXN0TlBpY2t1cEppZCB2YXJpYWJsZSB0byBiZVxuICAgICAgICAgICAgICAgICAgICAvLyBwaWNrZWQgdXAgbGF0ZXIgYnkgdGhlIGxhc3ROIGNoYW5nZWQgZXZlbnQgaGFuZGxlci5cblxuICAgICAgICAgICAgICAgICAgICBsYXN0TlBpY2t1cEppZCA9IGppZDtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcInBpbm5lZGVuZHBvaW50Y2hhbmdlZFwiLCBbamlkXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChqaWQgPT0geG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAgICAgJChcIiNsb2NhbFZpZGVvQ29udGFpbmVyXCIpLmNsaWNrKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIE9uIGF1ZGlvIG11dGVkIGV2ZW50LlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ2F1ZGlvbXV0ZWQubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgLypcbiAgICAgICAgIC8vIEZJWE1FOiBidXQgZm9jdXMgY2FuIG5vdCBtdXRlIGluIHRoaXMgY2FzZSA/IC0gY2hlY2tcbiAgICAgICAgaWYgKGppZCA9PT0geG1wcC5teUppZCgpKSB7XG5cbiAgICAgICAgICAgIC8vIFRoZSBsb2NhbCBtdXRlIGluZGljYXRvciBpcyBjb250cm9sbGVkIGxvY2FsbHlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSovXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChqaWQgPT09IHhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5lbnN1cmVQZWVyQ29udGFpbmVyRXhpc3RzKGppZCk7XG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIG11dGVkQXVkaW9zW2ppZF0gPSBpc011dGVkO1xuXG4gICAgICAgIGlmICh4bXBwLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZVJlbW90ZVZpZGVvTWVudShqaWQsIGlzTXV0ZWQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHZpZGVvU3BhbklkKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd0F1ZGlvSW5kaWNhdG9yKHZpZGVvU3BhbklkLCBpc011dGVkKTtcbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIE9uIHZpZGVvIG11dGVkIGV2ZW50LlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ3ZpZGVvbXV0ZWQubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIHZhbHVlKSB7XG4gICAgICAgIHZhciBpc011dGVkID0gKHZhbHVlID09PSBcInRydWVcIik7XG4gICAgICAgIGlmKCFSVEMubXV0ZVJlbW90ZVZpZGVvU3RyZWFtKGppZCwgaXNNdXRlZCkpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKGppZCwgaXNNdXRlZCk7XG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChqaWQgPT09IHhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5lbnN1cmVQZWVyQ29udGFpbmVyRXhpc3RzKGppZCk7XG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh2aWRlb1NwYW5JZClcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dWaWRlb0luZGljYXRvcih2aWRlb1NwYW5JZCwgdmFsdWUpO1xuICAgIH0pO1xuXG4gICAgLyoqXG4gICAgICogRGlzcGxheSBuYW1lIGNoYW5nZWQuXG4gICAgICovXG4gICAgbXkub25EaXNwbGF5TmFtZUNoYW5nZWQgPVxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoamlkLCBkaXNwbGF5TmFtZSwgc3RhdHVzKSB7XG4gICAgICAgIHZhciBuYW1lID0gbnVsbDtcbiAgICAgICAgaWYgKGppZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInXG4gICAgICAgICAgICB8fCBqaWQgPT09IHhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgbmFtZSA9IG5pY2tuYW1lO1xuICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUoJ2xvY2FsVmlkZW9Db250YWluZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheU5hbWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQpO1xuICAgICAgICAgICAgbmFtZSA9ICQoJyNwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSArIFwiX25hbWVcIikudGV4dCgpO1xuICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUoXG4gICAgICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpLFxuICAgICAgICAgICAgICAgIGRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgICAgIHN0YXR1cyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZihqaWQgPT09ICdsb2NhbFZpZGVvQ29udGFpbmVyJylcbiAgICAgICAgICAgIGppZCA9IHhtcHAubXlKaWQoKTtcbiAgICAgICAgaWYoIW5hbWUgfHwgbmFtZSAhPSBkaXNwbGF5TmFtZSlcbiAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJkaXNwbGF5TmFtZUNoYW5nZVwiLHtqaWQ6IGppZCwgZGlzcGxheW5hbWU6IGRpc3BsYXlOYW1lfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9uIGRvbWluYW50IHNwZWFrZXIgY2hhbmdlZCBldmVudC5cbiAgICAgKi9cbiAgICAkKGRvY3VtZW50KS5iaW5kKCdkb21pbmFudHNwZWFrZXJjaGFuZ2VkJywgZnVuY3Rpb24gKGV2ZW50LCByZXNvdXJjZUppZCkge1xuICAgICAgICAvLyBXZSBpZ25vcmUgbG9jYWwgdXNlciBldmVudHMuXG4gICAgICAgIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID09PSB4bXBwLm15UmVzb3VyY2UoKSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGN1cnJlbnQgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkICE9PSBjdXJyZW50RG9taW5hbnRTcGVha2VyKSB7XG4gICAgICAgICAgICB2YXIgb2xkU3BlYWtlclZpZGVvU3BhbklkID0gXCJwYXJ0aWNpcGFudF9cIiArIGN1cnJlbnREb21pbmFudFNwZWFrZXIsXG4gICAgICAgICAgICAgICAgbmV3U3BlYWtlclZpZGVvU3BhbklkID0gXCJwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkO1xuICAgICAgICAgICAgaWYoJChcIiNcIiArIG9sZFNwZWFrZXJWaWRlb1NwYW5JZCArIFwiPnNwYW4uZGlzcGxheW5hbWVcIikudGV4dCgpID09PVxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5ERUZBVUxUX0RPTUlOQU5UX1NQRUFLRVJfRElTUExBWV9OQU1FKSB7XG4gICAgICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUob2xkU3BlYWtlclZpZGVvU3BhbklkLCBudWxsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmKCQoXCIjXCIgKyBuZXdTcGVha2VyVmlkZW9TcGFuSWQgKyBcIj5zcGFuLmRpc3BsYXluYW1lXCIpLnRleHQoKSA9PT1cbiAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9SRU1PVEVfRElTUExBWV9OQU1FKSB7XG4gICAgICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUobmV3U3BlYWtlclZpZGVvU3BhbklkLFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9ET01JTkFOVF9TUEVBS0VSX0RJU1BMQVlfTkFNRSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjdXJyZW50RG9taW5hbnRTcGVha2VyID0gcmVzb3VyY2VKaWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPYnRhaW4gY29udGFpbmVyIGZvciBuZXcgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgdmFyIGNvbnRhaW5lciAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcbiAgICAgICAgICAgICAgICAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAvLyBMb2NhbCB2aWRlbyB3aWxsIG5vdCBoYXZlIGNvbnRhaW5lciBmb3VuZCwgYnV0IHRoYXQncyBva1xuICAgICAgICAvLyBzaW5jZSB3ZSBkb24ndCB3YW50IHRvIHN3aXRjaCB0byBsb2NhbCB2aWRlby5cbiAgICAgICAgaWYgKGNvbnRhaW5lciAmJiAhZm9jdXNlZFZpZGVvSW5mbylcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIHZpZGVvID0gY29udGFpbmVyLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwidmlkZW9cIik7XG5cbiAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgbGFyZ2UgdmlkZW8gaWYgdGhlIHZpZGVvIHNvdXJjZSBpcyBhbHJlYWR5IGF2YWlsYWJsZSxcbiAgICAgICAgICAgIC8vIG90aGVyd2lzZSB3YWl0IGZvciB0aGUgXCJ2aWRlb2FjdGl2ZS5qaW5nbGVcIiBldmVudC5cbiAgICAgICAgICAgIGlmICh2aWRlby5sZW5ndGggJiYgdmlkZW9bMF0uY3VycmVudFRpbWUgPiAwKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oUlRDLmdldFZpZGVvU3JjKHZpZGVvWzBdKSwgcmVzb3VyY2VKaWQpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICAvKipcbiAgICAgKiBPbiBsYXN0IE4gY2hhbmdlIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGV2ZW50IHRoZSBldmVudCB0aGF0IG5vdGlmaWVkIHVzXG4gICAgICogQHBhcmFtIGxhc3RORW5kcG9pbnRzIHRoZSBsaXN0IG9mIGxhc3QgTiBlbmRwb2ludHNcbiAgICAgKiBAcGFyYW0gZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB0aGUgbGlzdCBjdXJyZW50bHkgZW50ZXJpbmcgbGFzdCBOXG4gICAgICogZW5kcG9pbnRzXG4gICAgICovXG4gICAgJChkb2N1bWVudCkuYmluZCgnbGFzdG5jaGFuZ2VkJywgZnVuY3Rpb24gKCBldmVudCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RORW5kcG9pbnRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kcG9pbnRzRW50ZXJpbmdMYXN0TixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSkge1xuICAgICAgICBpZiAobGFzdE5Db3VudCAhPT0gbGFzdE5FbmRwb2ludHMubGVuZ3RoKVxuICAgICAgICAgICAgbGFzdE5Db3VudCA9IGxhc3RORW5kcG9pbnRzLmxlbmd0aDtcblxuICAgICAgICBsYXN0TkVuZHBvaW50c0NhY2hlID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgLy8gU2F5IEEsIEIsIEMsIEQsIEUsIGFuZCBGIGFyZSBpbiBhIGNvbmZlcmVuY2UgYW5kIExhc3ROID0gMy5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gSWYgTGFzdE4gZHJvcHMgdG8sIHNheSwgMiwgYmVjYXVzZSBvZiBhZGFwdGl2aXR5LCB0aGVuIEUgc2hvdWxkIHNlZVxuICAgICAgICAvLyB0aHVtYm5haWxzIGZvciBBLCBCIGFuZCBDLiBBIGFuZCBCIGFyZSBpbiBFJ3Mgc2VydmVyIHNpZGUgTGFzdE4gc2V0LFxuICAgICAgICAvLyBzbyBFIHNlZXMgdGhlbS4gQyBpcyBvbmx5IGluIEUncyBsb2NhbCBMYXN0TiBzZXQuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIElmIEYgc3RhcnRzIHRhbGtpbmcgYW5kIExhc3ROID0gMywgdGhlbiBFIHNob3VsZCBzZWUgdGh1bWJuYWlscyBmb3JcbiAgICAgICAgLy8gRiwgQSwgQi4gQiBnZXRzIFwiZWplY3RlZFwiIGZyb20gRSdzIHNlcnZlciBzaWRlIExhc3ROIHNldCwgYnV0IGl0XG4gICAgICAgIC8vIGVudGVycyBFJ3MgbG9jYWwgTGFzdE4gZWplY3RpbmcgQy5cblxuICAgICAgICAvLyBJbmNyZWFzZSB0aGUgbG9jYWwgTGFzdE4gc2V0IHNpemUsIGlmIG5lY2Vzc2FyeS5cbiAgICAgICAgaWYgKGxhc3ROQ291bnQgPiBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgIGxvY2FsTGFzdE5Db3VudCA9IGxhc3ROQ291bnQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGxvY2FsIExhc3ROIHNldCBwcmVzZXJ2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGVcbiAgICAgICAgLy8gZW5kcG9pbnRzIGFwcGVhcmVkIGluIHRoZSBMYXN0Ti9sb2NhbCBMYXN0TiBzZXQuXG5cbiAgICAgICAgdmFyIG5leHRMb2NhbExhc3ROU2V0ID0gbGFzdE5FbmRwb2ludHMuc2xpY2UoMCk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbG9jYWxMYXN0TlNldC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKG5leHRMb2NhbExhc3ROU2V0Lmxlbmd0aCA+PSBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbG9jYWxMYXN0TlNldFtpXTtcbiAgICAgICAgICAgIGlmIChuZXh0TG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBuZXh0TG9jYWxMYXN0TlNldC5wdXNoKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxvY2FsTGFzdE5TZXQgPSBuZXh0TG9jYWxMYXN0TlNldDtcblxuICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IGZhbHNlO1xuXG4gICAgICAgIC8vIEhhbmRsZSBMYXN0Ti9sb2NhbCBMYXN0TiBjaGFuZ2VzLlxuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS5lYWNoKGZ1bmN0aW9uKCBpbmRleCwgZWxlbWVudCApIHtcbiAgICAgICAgICAgIHZhciByZXNvdXJjZUppZCA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZChlbGVtZW50KTtcblxuICAgICAgICAgICAgdmFyIGlzUmVjZWl2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA8IDApIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlJlbW92ZSBmcm9tIGxhc3QgTlwiLCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdoaWRlJyk7XG4gICAgICAgICAgICAgICAgaXNSZWNlaXZlZCA9IGZhbHNlO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgICYmICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpLmlzKCc6dmlzaWJsZScpXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA+PSAwKSB7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdhdmF0YXInKTtcbiAgICAgICAgICAgICAgICBpc1JlY2VpdmVkID0gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghaXNSZWNlaXZlZCkge1xuICAgICAgICAgICAgICAgIC8vIHJlc291cmNlSmlkIGhhcyBkcm9wcGVkIG91dCBvZiB0aGUgc2VydmVyIHNpZGUgbGFzdE4gc2V0LCBzb1xuICAgICAgICAgICAgICAgIC8vIGl0IGlzIG5vIGxvbmdlciBiZWluZyByZWNlaXZlZC4gSWYgcmVzb3VyY2VKaWQgd2FzIGJlaW5nXG4gICAgICAgICAgICAgICAgLy8gZGlzcGxheWVkIGluIHRoZSBsYXJnZSB2aWRlbyB3ZSBoYXZlIHRvIHN3aXRjaCB0byBhbm90aGVyXG4gICAgICAgICAgICAgICAgLy8gdXNlci5cbiAgICAgICAgICAgICAgICB2YXIgbGFyZ2VWaWRlb1Jlc291cmNlID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcbiAgICAgICAgICAgICAgICBpZiAoIXVwZGF0ZUxhcmdlVmlkZW8gJiYgcmVzb3VyY2VKaWQgPT09IGxhcmdlVmlkZW9SZXNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICB1cGRhdGVMYXJnZVZpZGVvID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB8fCBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmxlbmd0aCA8IDApXG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgaWYgKGVuZHBvaW50c0VudGVyaW5nTGFzdE4gJiYgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Ti5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmZvckVhY2goZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG5cbiAgICAgICAgICAgICAgICB2YXIgaXNWaXNpYmxlID0gJCgnI3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZCkuaXMoJzp2aXNpYmxlJyk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdzaG93Jyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc1Zpc2libGUpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJBZGQgdG8gbGFzdCBOXCIsIHJlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgICAgICAgICB2YXIgamlkID0geG1wcC5maW5kSmlkRnJvbVJlc291cmNlKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG1lZGlhU3RyZWFtID0gUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgICAgICAgICAgICAgIHZhciBzZWwgPSAkKCcjcGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkICsgJz52aWRlbycpO1xuXG4gICAgICAgICAgICAgICAgICAgIHZhciB2aWRlb1N0cmVhbSA9IHNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhU3RyZWFtLnN0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIFJUQy5hdHRhY2hNZWRpYVN0cmVhbShzZWwsIHZpZGVvU3RyZWFtKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxhc3ROUGlja3VwSmlkID09IG1lZGlhU3RyZWFtLnBlZXJqaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIENsZWFuIHVwIHRoZSBsYXN0TiBwaWNrdXAgamlkLlxuICAgICAgICAgICAgICAgICAgICAgICAgbGFzdE5QaWNrdXBKaWQgPSBudWxsO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBEb24ndCBmaXJlIHRoZSBldmVudHMgYWdhaW4sIHRoZXkndmUgYWxyZWFkeVxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYmVlbiBmaXJlZCBpbiB0aGUgY29udGFjdCBsaXN0IGNsaWNrIGhhbmRsZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5oYW5kbGVWaWRlb1RodW1iQ2xpY2tlZChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkKHNlbCkuYXR0cignc3JjJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQobWVkaWFTdHJlYW0ucGVlcmppZCkpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVMYXJnZVZpZGVvID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgd2FpdEZvclJlbW90ZVZpZGVvKHNlbCwgbWVkaWFTdHJlYW0uc3NyYywgbWVkaWFTdHJlYW0uc3RyZWFtLCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRoZSBlbmRwb2ludCB0aGF0IHdhcyBiZWluZyBzaG93biBpbiB0aGUgbGFyZ2UgdmlkZW8gaGFzIGRyb3BwZWQgb3V0XG4gICAgICAgIC8vIG9mIHRoZSBsYXN0TiBzZXQgYW5kIHRoZXJlIHdhcyBubyBsYXN0TiBwaWNrdXAgamlkLiBXZSBuZWVkIHRvIHVwZGF0ZVxuICAgICAgICAvLyB0aGUgbGFyZ2UgdmlkZW8gbm93LlxuXG4gICAgICAgIGlmICh1cGRhdGVMYXJnZVZpZGVvKSB7XG5cbiAgICAgICAgICAgIHZhciByZXNvdXJjZSwgY29udGFpbmVyLCBzcmM7XG4gICAgICAgICAgICB2YXIgbXlSZXNvdXJjZVxuICAgICAgICAgICAgICAgID0geG1wcC5teVJlc291cmNlKCk7XG5cbiAgICAgICAgICAgIC8vIEZpbmQgb3V0IHdoaWNoIGVuZHBvaW50IHRvIHNob3cgaW4gdGhlIGxhcmdlIHZpZGVvLlxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsYXN0TkVuZHBvaW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHJlc291cmNlID0gbGFzdE5FbmRwb2ludHNbaV07XG4gICAgICAgICAgICAgICAgaWYgKCFyZXNvdXJjZSB8fCByZXNvdXJjZSA9PT0gbXlSZXNvdXJjZSlcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgICAgICBjb250YWluZXIgPSAkKFwiI3BhcnRpY2lwYW50X1wiICsgcmVzb3VyY2UpO1xuICAgICAgICAgICAgICAgIGlmIChjb250YWluZXIubGVuZ3RoID09IDApXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAgICAgc3JjID0gJCgndmlkZW8nLCBjb250YWluZXIpLmF0dHIoJ3NyYycpO1xuICAgICAgICAgICAgICAgIGlmICghc3JjKVxuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgICAgIC8vIHZpZGVvU3JjVG9Tc3JjIG5lZWRzIHRvIGJlIHVwZGF0ZSBmb3IgdGhpcyBjYWxsIHRvIHN1Y2NlZWQuXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTGFyZ2VWaWRlbyhzcmMpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgICQoZG9jdW1lbnQpLmJpbmQoJ3NpbXVsY2FzdGxheWVyc2NoYW5naW5nJywgZnVuY3Rpb24gKGV2ZW50LCBlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICBlbmRwb2ludFNpbXVsY2FzdExheWVycy5mb3JFYWNoKGZ1bmN0aW9uIChlc2wpIHtcblxuICAgICAgICAgICAgdmFyIHJlc291cmNlID0gZXNsLmVuZHBvaW50O1xuXG4gICAgICAgICAgICAvLyBpZiBsYXN0TiBpcyBlbmFibGVkICphbmQqIHRoZSBlbmRwb2ludCBpcyAqbm90KiBpbiB0aGUgbGFzdE4gc2V0LFxuICAgICAgICAgICAgLy8gdGhlbiBpZ25vcmUgdGhlIGV2ZW50ICg9IGRvIG5vdCBwcmVsb2FkIGFueXRoaW5nKS5cbiAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAvLyBUaGUgYnJpZGdlIGNvdWxkIHByb2JhYmx5IHN0b3Agc2VuZGluZyB0aGlzIG1lc3NhZ2UgaWYgaXQncyBmb3JcbiAgICAgICAgICAgIC8vIGFuIGVuZHBvaW50IHRoYXQncyBub3QgaW4gbGFzdE4uXG5cbiAgICAgICAgICAgIGlmIChsYXN0TkNvdW50ICE9IC0xXG4gICAgICAgICAgICAgICAgJiYgKGxhc3ROQ291bnQgPCAxIHx8IGxhc3RORW5kcG9pbnRzQ2FjaGUuaW5kZXhPZihyZXNvdXJjZSkgPT09IC0xKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHByaW1hcnlTU1JDID0gZXNsLnNpbXVsY2FzdExheWVyLnByaW1hcnlTU1JDO1xuXG4gICAgICAgICAgICAvLyBHZXQgc2Vzc2lvbiBhbmQgc3RyZWFtIGZyb20gcHJpbWFyeSBzc3JjLlxuICAgICAgICAgICAgdmFyIHJlcyA9IHNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhwcmltYXJ5U1NSQyk7XG4gICAgICAgICAgICB2YXIgc2lkID0gcmVzLnNpZDtcbiAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtID0gcmVzLnN0cmVhbTtcblxuICAgICAgICAgICAgaWYgKHNpZCAmJiBlbGVjdGVkU3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBzaW11bGNhc3QuZ2V0UmVtb3RlVmlkZW9TdHJlYW1JZEJ5U1NSQyhwcmltYXJ5U1NSQyk7XG5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oW2VzbCwgcHJpbWFyeVNTUkMsIG1zaWQsIHNpZCwgZWxlY3RlZFN0cmVhbV0pO1xuXG4gICAgICAgICAgICAgICAgdmFyIG1zaWRQYXJ0cyA9IG1zaWQuc3BsaXQoJyAnKTtcblxuICAgICAgICAgICAgICAgIHZhciBwcmVsb2FkID0gKFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHNzcmMyamlkW3ByaW1hcnlTU1JDXSkgPT0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocHJlbG9hZCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnUHJlbG9hZGluZyByZW1vdGUgdmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgPSAkKCc8dmlkZW8gYXV0b3BsYXk+PC92aWRlbz4nKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gc3NyY3MgYXJlIHVuaXF1ZSBpbiBhbiBydHAgc2Vzc2lvblxuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZF9zc3JjID0gcHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgICAgICAgICAgUlRDLmF0dGFjaE1lZGlhU3RyZWFtKGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkLCBlbGVjdGVkU3RyZWFtKVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBhIHN0cmVhbSBvciBhIHNlc3Npb24uJywgc2lkLCBlbGVjdGVkU3RyZWFtKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICAvKipcbiAgICAgKiBPbiBzaW11bGNhc3QgbGF5ZXJzIGNoYW5nZWQgZXZlbnQuXG4gICAgICovXG4gICAgJChkb2N1bWVudCkuYmluZCgnc2ltdWxjYXN0bGF5ZXJzY2hhbmdlZCcsIGZ1bmN0aW9uIChldmVudCwgZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpIHtcbiAgICAgICAgZW5kcG9pbnRTaW11bGNhc3RMYXllcnMuZm9yRWFjaChmdW5jdGlvbiAoZXNsKSB7XG5cbiAgICAgICAgICAgIHZhciByZXNvdXJjZSA9IGVzbC5lbmRwb2ludDtcblxuICAgICAgICAgICAgLy8gaWYgbGFzdE4gaXMgZW5hYmxlZCAqYW5kKiB0aGUgZW5kcG9pbnQgaXMgKm5vdCogaW4gdGhlIGxhc3ROIHNldCxcbiAgICAgICAgICAgIC8vIHRoZW4gaWdub3JlIHRoZSBldmVudCAoPSBkbyBub3QgY2hhbmdlIGxhcmdlIHZpZGVvL3RodW1ibmFpbFxuICAgICAgICAgICAgLy8gU1JDcykuXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gTm90ZSB0aGF0IGV2ZW4gaWYgd2UgaWdub3JlIHRoZSBcImNoYW5nZWRcIiBldmVudCBpbiB0aGlzIGV2ZW50XG4gICAgICAgICAgICAvLyBoYW5kbGVyLCB0aGUgYnJpZGdlIG11c3QgY29udGludWUgc2VuZGluZyB0aGVzZSBldmVudHMgYmVjYXVzZVxuICAgICAgICAgICAgLy8gdGhlIHNpbXVsY2FzdCBjb2RlIGluIHNpbXVsY2FzdC5qcyB1c2VzIGl0IHRvIGtub3cgd2hhdCdzIGdvaW5nXG4gICAgICAgICAgICAvLyB0byBiZSBzdHJlYW1lZCBieSB0aGUgYnJpZGdlIHdoZW4vaWYgdGhlIGVuZHBvaW50IGdldHMgYmFjayBpbnRvXG4gICAgICAgICAgICAvLyB0aGUgbGFzdE4gc2V0LlxuXG4gICAgICAgICAgICBpZiAobGFzdE5Db3VudCAhPSAtMVxuICAgICAgICAgICAgICAgICYmIChsYXN0TkNvdW50IDwgMSB8fCBsYXN0TkVuZHBvaW50c0NhY2hlLmluZGV4T2YocmVzb3VyY2UpID09PSAtMSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBwcmltYXJ5U1NSQyA9IGVzbC5zaW11bGNhc3RMYXllci5wcmltYXJ5U1NSQztcblxuICAgICAgICAgICAgLy8gR2V0IHNlc3Npb24gYW5kIHN0cmVhbSBmcm9tIHByaW1hcnkgc3NyYy5cbiAgICAgICAgICAgIHZhciByZXMgPSBzaW11bGNhc3QuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW1CeVNTUkMocHJpbWFyeVNTUkMpO1xuICAgICAgICAgICAgdmFyIHNpZCA9IHJlcy5zaWQ7XG4gICAgICAgICAgICB2YXIgZWxlY3RlZFN0cmVhbSA9IHJlcy5zdHJlYW07XG5cbiAgICAgICAgICAgIGlmIChzaWQgJiYgZWxlY3RlZFN0cmVhbSkge1xuICAgICAgICAgICAgICAgIHZhciBtc2lkID0gc2ltdWxjYXN0LmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMocHJpbWFyeVNTUkMpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdTd2l0Y2hpbmcgc2ltdWxjYXN0IHN1YnN0cmVhbS4nKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oW2VzbCwgcHJpbWFyeVNTUkMsIG1zaWQsIHNpZCwgZWxlY3RlZFN0cmVhbV0pO1xuXG4gICAgICAgICAgICAgICAgdmFyIG1zaWRQYXJ0cyA9IG1zaWQuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsUmVtb3RlVmlkZW8gPSAkKFsnIycsICdyZW1vdGVWaWRlb18nLCBzaWQsICdfJywgbXNpZFBhcnRzWzBdXS5qb2luKCcnKSk7XG5cbiAgICAgICAgICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IChTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChzc3JjMmppZFtwcmltYXJ5U1NSQ10pXG4gICAgICAgICAgICAgICAgICAgID09IGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIHZhciB1cGRhdGVGb2N1c2VkVmlkZW9TcmMgPSAoZm9jdXNlZFZpZGVvSW5mbyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAhPSAnJyAmJlxuICAgICAgICAgICAgICAgICAgICAoUlRDLmdldFZpZGVvU3JjKHNlbFJlbW90ZVZpZGVvWzBdKSA9PSBmb2N1c2VkVmlkZW9JbmZvLnNyYykpO1xuXG4gICAgICAgICAgICAgICAgdmFyIGVsZWN0ZWRTdHJlYW1Vcmw7XG4gICAgICAgICAgICAgICAgaWYgKGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkX3NzcmMgPT0gcHJpbWFyeVNTUkMpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBSVEMuc2V0VmlkZW9TcmMoc2VsUmVtb3RlVmlkZW9bMF0sIFJUQy5nZXRWaWRlb1NyYyhsYXJnZVZpZGVvU3RhdGUucHJlbG9hZFswXSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRcbiAgICAgICAgICAgICAgICAgICAgICAgICYmIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRfc3NyYyA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgUlRDLmF0dGFjaE1lZGlhU3RyZWFtKHNlbFJlbW90ZVZpZGVvLCBlbGVjdGVkU3RyZWFtKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB2YXIgamlkID0gc3NyYzJqaWRbcHJpbWFyeVNTUkNdO1xuICAgICAgICAgICAgICAgIGppZDJTc3JjW2ppZF0gPSBwcmltYXJ5U1NSQztcblxuICAgICAgICAgICAgICAgIGlmICh1cGRhdGVMYXJnZVZpZGVvKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oUlRDLmdldFZpZGVvU3JjKHNlbFJlbW90ZVZpZGVvWzBdKSwgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICh1cGRhdGVGb2N1c2VkVmlkZW9TcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9jdXNlZFZpZGVvSW5mby5zcmMgPSBSVEMuZ2V0VmlkZW9TcmMoc2VsUmVtb3RlVmlkZW9bMF0pO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciB2aWRlb0lkO1xuICAgICAgICAgICAgICAgIGlmKHJlc291cmNlID09IHhtcHAubXlSZXNvdXJjZSgpKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9JZCA9IFwibG9jYWxWaWRlb0NvbnRhaW5lclwiO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2aWRlb0lkID0gXCJwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgY29ubmVjdGlvbkluZGljYXRvciA9IFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW3ZpZGVvSWRdO1xuICAgICAgICAgICAgICAgIGlmKGNvbm5lY3Rpb25JbmRpY2F0b3IpXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25JbmRpY2F0b3IudXBkYXRlUG9wb3ZlckRhdGEoKTtcblxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBhIHN0cmVhbSBvciBhIHNpZC4nLCBzaWQsIGVsZWN0ZWRTdHJlYW0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgbG9jYWwgc3RhdHNcbiAgICAgKiBAcGFyYW0gcGVyY2VudFxuICAgICAqIEBwYXJhbSBvYmplY3RcbiAgICAgKi9cbiAgICBteS51cGRhdGVMb2NhbENvbm5lY3Rpb25TdGF0cyA9IGZ1bmN0aW9uIChwZXJjZW50LCBvYmplY3QpIHtcbiAgICAgICAgdmFyIHJlc29sdXRpb24gPSBudWxsO1xuICAgICAgICBpZihvYmplY3QucmVzb2x1dGlvbiAhPT0gbnVsbClcbiAgICAgICAge1xuICAgICAgICAgICAgcmVzb2x1dGlvbiA9IG9iamVjdC5yZXNvbHV0aW9uO1xuICAgICAgICAgICAgb2JqZWN0LnJlc29sdXRpb24gPSByZXNvbHV0aW9uW3htcHAubXlKaWQoKV07XG4gICAgICAgICAgICBkZWxldGUgcmVzb2x1dGlvblt4bXBwLm15SmlkKCldO1xuICAgICAgICB9XG4gICAgICAgIHVwZGF0ZVN0YXRzSW5kaWNhdG9yKFwibG9jYWxWaWRlb0NvbnRhaW5lclwiLCBwZXJjZW50LCBvYmplY3QpO1xuICAgICAgICBmb3IodmFyIGppZCBpbiByZXNvbHV0aW9uKVxuICAgICAgICB7XG4gICAgICAgICAgICBpZihyZXNvbHV0aW9uW2ppZF0gPT09IG51bGwpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB2YXIgaWQgPSAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0pXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbaWRdLnVwZGF0ZVJlc29sdXRpb24ocmVzb2x1dGlvbltqaWRdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgcmVtb3RlIHN0YXRzLlxuICAgICAqIEBwYXJhbSBqaWQgdGhlIGppZCBhc3NvY2lhdGVkIHdpdGggdGhlIHN0YXRzXG4gICAgICogQHBhcmFtIHBlcmNlbnQgdGhlIGNvbm5lY3Rpb24gcXVhbGl0eSBwZXJjZW50XG4gICAgICogQHBhcmFtIG9iamVjdCB0aGUgc3RhdHMgZGF0YVxuICAgICAqL1xuICAgIG15LnVwZGF0ZUNvbm5lY3Rpb25TdGF0cyA9IGZ1bmN0aW9uIChqaWQsIHBlcmNlbnQsIG9iamVjdCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgIHVwZGF0ZVN0YXRzSW5kaWNhdG9yKHZpZGVvU3BhbklkLCBwZXJjZW50LCBvYmplY3QpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIHRoZSBjb25uZWN0aW9uXG4gICAgICogQHBhcmFtIGppZFxuICAgICAqL1xuICAgIG15LnJlbW92ZUNvbm5lY3Rpb25JbmRpY2F0b3IgPSBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgIGlmKFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKV0pXG4gICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldLnJlbW92ZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyB0aGUgY29ubmVjdGlvbiBpbmRpY2F0b3JcbiAgICAgKiBAcGFyYW0gamlkXG4gICAgICovXG4gICAgbXkuaGlkZUNvbm5lY3Rpb25JbmRpY2F0b3IgPSBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgIGlmKFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKV0pXG4gICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldLmhpZGUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSGlkZXMgYWxsIHRoZSBpbmRpY2F0b3JzXG4gICAgICovXG4gICAgbXkub25TdGF0c1N0b3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvcih2YXIgaW5kaWNhdG9yIGluIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzKVxuICAgICAgICB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpbmRpY2F0b3JdLmhpZGVJbmRpY2F0b3IoKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG59KFZpZGVvTGF5b3V0IHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gVmlkZW9MYXlvdXQ7IiwiLy92YXIgbm91bnMgPSBbXG4vL107XG52YXIgcGx1cmFsTm91bnMgPSBbXG4gICAgXCJBbGllbnNcIiwgXCJBbmltYWxzXCIsIFwiQW50ZWxvcGVzXCIsIFwiQW50c1wiLCBcIkFwZXNcIiwgXCJBcHBsZXNcIiwgXCJCYWJvb25zXCIsIFwiQmFjdGVyaWFcIiwgXCJCYWRnZXJzXCIsIFwiQmFuYW5hc1wiLCBcIkJhdHNcIixcbiAgICBcIkJlYXJzXCIsIFwiQmlyZHNcIiwgXCJCb25vYm9zXCIsIFwiQnJpZGVzXCIsIFwiQnVnc1wiLCBcIkJ1bGxzXCIsIFwiQnV0dGVyZmxpZXNcIiwgXCJDaGVldGFoc1wiLFxuICAgIFwiQ2hlcnJpZXNcIiwgXCJDaGlja2VuXCIsIFwiQ2hpbGRyZW5cIiwgXCJDaGltcHNcIiwgXCJDbG93bnNcIiwgXCJDb3dzXCIsIFwiQ3JlYXR1cmVzXCIsIFwiRGlub3NhdXJzXCIsIFwiRG9nc1wiLCBcIkRvbHBoaW5zXCIsXG4gICAgXCJEb25rZXlzXCIsIFwiRHJhZ29uc1wiLCBcIkR1Y2tzXCIsIFwiRHdhcmZzXCIsIFwiRWFnbGVzXCIsIFwiRWxlcGhhbnRzXCIsIFwiRWx2ZXNcIiwgXCJGQUlMXCIsIFwiRmF0aGVyc1wiLFxuICAgIFwiRmlzaFwiLCBcIkZsb3dlcnNcIiwgXCJGcm9nc1wiLCBcIkZydWl0XCIsIFwiRnVuZ2lcIiwgXCJHYWxheGllc1wiLCBcIkdlZXNlXCIsIFwiR29hdHNcIixcbiAgICBcIkdvcmlsbGFzXCIsIFwiSGVkZ2Vob2dzXCIsIFwiSGlwcG9zXCIsIFwiSG9yc2VzXCIsIFwiSHVudGVyc1wiLCBcIkluc2VjdHNcIiwgXCJLaWRzXCIsIFwiS25pZ2h0c1wiLFxuICAgIFwiTGVtb25zXCIsIFwiTGVtdXJzXCIsIFwiTGVvcGFyZHNcIiwgXCJMaWZlRm9ybXNcIiwgXCJMaW9uc1wiLCBcIkxpemFyZHNcIiwgXCJNaWNlXCIsIFwiTW9ua2V5c1wiLCBcIk1vbnN0ZXJzXCIsXG4gICAgXCJNdXNocm9vbXNcIiwgXCJPY3RvcG9kZXNcIiwgXCJPcmFuZ2VzXCIsIFwiT3Jhbmd1dGFuc1wiLCBcIk9yZ2FuaXNtc1wiLCBcIlBhbnRzXCIsIFwiUGFycm90c1wiLCBcIlBlbmd1aW5zXCIsXG4gICAgXCJQZW9wbGVcIiwgXCJQaWdlb25zXCIsIFwiUGlnc1wiLCBcIlBpbmVhcHBsZXNcIiwgXCJQbGFudHNcIiwgXCJQb3RhdG9lc1wiLCBcIlByaWVzdHNcIiwgXCJSYXRzXCIsIFwiUmVwdGlsZXNcIiwgXCJSZXB0aWxpYW5zXCIsXG4gICAgXCJSaGlub3NcIiwgXCJTZWFndWxsc1wiLCBcIlNoZWVwXCIsIFwiU2libGluZ3NcIiwgXCJTbmFrZXNcIiwgXCJTcGFnaGV0dGlcIiwgXCJTcGlkZXJzXCIsIFwiU3F1aWRcIiwgXCJTcXVpcnJlbHNcIixcbiAgICBcIlN0YXJzXCIsIFwiU3R1ZGVudHNcIiwgXCJUZWFjaGVyc1wiLCBcIlRpZ2Vyc1wiLCBcIlRvbWF0b2VzXCIsIFwiVHJlZXNcIiwgXCJWYW1waXJlc1wiLCBcIlZlZ2V0YWJsZXNcIiwgXCJWaXJ1c2VzXCIsIFwiVnVsY2Fuc1wiLFxuICAgIFwiV2FyZXdvbHZlc1wiLCBcIldlYXNlbHNcIiwgXCJXaGFsZXNcIiwgXCJXaXRjaGVzXCIsIFwiV2l6YXJkc1wiLCBcIldvbHZlc1wiLCBcIldvcmtlcnNcIiwgXCJXb3Jtc1wiLCBcIlplYnJhc1wiXG5dO1xuLy92YXIgcGxhY2VzID0gW1xuLy9cIlB1YlwiLCBcIlVuaXZlcnNpdHlcIiwgXCJBaXJwb3J0XCIsIFwiTGlicmFyeVwiLCBcIk1hbGxcIiwgXCJUaGVhdGVyXCIsIFwiU3RhZGl1bVwiLCBcIk9mZmljZVwiLCBcIlNob3dcIiwgXCJHYWxsb3dzXCIsIFwiQmVhY2hcIixcbi8vIFwiQ2VtZXRlcnlcIiwgXCJIb3NwaXRhbFwiLCBcIlJlY2VwdGlvblwiLCBcIlJlc3RhdXJhbnRcIiwgXCJCYXJcIiwgXCJDaHVyY2hcIiwgXCJIb3VzZVwiLCBcIlNjaG9vbFwiLCBcIlNxdWFyZVwiLCBcIlZpbGxhZ2VcIixcbi8vIFwiQ2luZW1hXCIsIFwiTW92aWVzXCIsIFwiUGFydHlcIiwgXCJSZXN0cm9vbVwiLCBcIkVuZFwiLCBcIkphaWxcIiwgXCJQb3N0T2ZmaWNlXCIsIFwiU3RhdGlvblwiLCBcIkNpcmN1c1wiLCBcIkdhdGVzXCIsIFwiRW50cmFuY2VcIixcbi8vIFwiQnJpZGdlXCJcbi8vXTtcbnZhciB2ZXJicyA9IFtcbiAgICBcIkFiYW5kb25cIiwgXCJBZGFwdFwiLCBcIkFkdmVydGlzZVwiLCBcIkFuc3dlclwiLCBcIkFudGljaXBhdGVcIiwgXCJBcHByZWNpYXRlXCIsXG4gICAgXCJBcHByb2FjaFwiLCBcIkFyZ3VlXCIsIFwiQXNrXCIsIFwiQml0ZVwiLCBcIkJsb3Nzb21cIiwgXCJCbHVzaFwiLCBcIkJyZWF0aGVcIiwgXCJCcmVlZFwiLCBcIkJyaWJlXCIsIFwiQnVyblwiLCBcIkNhbGN1bGF0ZVwiLFxuICAgIFwiQ2xlYW5cIiwgXCJDb2RlXCIsIFwiQ29tbXVuaWNhdGVcIiwgXCJDb21wdXRlXCIsIFwiQ29uZmVzc1wiLCBcIkNvbmZpc2NhdGVcIiwgXCJDb25qdWdhdGVcIiwgXCJDb25qdXJlXCIsIFwiQ29uc3VtZVwiLFxuICAgIFwiQ29udGVtcGxhdGVcIiwgXCJDcmF3bFwiLCBcIkRhbmNlXCIsIFwiRGVsZWdhdGVcIiwgXCJEZXZvdXJcIiwgXCJEZXZlbG9wXCIsIFwiRGlmZmVyXCIsIFwiRGlzY3Vzc1wiLFxuICAgIFwiRGlzc29sdmVcIiwgXCJEcmlua1wiLCBcIkVhdFwiLCBcIkVsYWJvcmF0ZVwiLCBcIkVtYW5jaXBhdGVcIiwgXCJFc3RpbWF0ZVwiLCBcIkV4cGlyZVwiLCBcIkV4dGluZ3Vpc2hcIixcbiAgICBcIkV4dHJhY3RcIiwgXCJGQUlMXCIsIFwiRmFjaWxpdGF0ZVwiLCBcIkZhbGxcIiwgXCJGZWVkXCIsIFwiRmluaXNoXCIsIFwiRmxvc3NcIiwgXCJGbHlcIiwgXCJGb2xsb3dcIiwgXCJGcmFnbWVudFwiLCBcIkZyZWV6ZVwiLFxuICAgIFwiR2F0aGVyXCIsIFwiR2xvd1wiLCBcIkdyb3dcIiwgXCJIZXhcIiwgXCJIaWRlXCIsIFwiSHVnXCIsIFwiSHVycnlcIiwgXCJJbXByb3ZlXCIsIFwiSW50ZXJzZWN0XCIsIFwiSW52ZXN0aWdhdGVcIiwgXCJKaW54XCIsXG4gICAgXCJKb2tlXCIsIFwiSnViaWxhdGVcIiwgXCJLaXNzXCIsIFwiTGF1Z2hcIiwgXCJNYW5hZ2VcIiwgXCJNZWV0XCIsIFwiTWVyZ2VcIiwgXCJNb3ZlXCIsIFwiT2JqZWN0XCIsIFwiT2JzZXJ2ZVwiLCBcIk9mZmVyXCIsXG4gICAgXCJQYWludFwiLCBcIlBhcnRpY2lwYXRlXCIsIFwiUGFydHlcIiwgXCJQZXJmb3JtXCIsIFwiUGxhblwiLCBcIlB1cnN1ZVwiLCBcIlBpZXJjZVwiLCBcIlBsYXlcIiwgXCJQb3N0cG9uZVwiLCBcIlByYXlcIiwgXCJQcm9jbGFpbVwiLFxuICAgIFwiUXVlc3Rpb25cIiwgXCJSZWFkXCIsIFwiUmVja29uXCIsIFwiUmVqb2ljZVwiLCBcIlJlcHJlc2VudFwiLCBcIlJlc2l6ZVwiLCBcIlJoeW1lXCIsIFwiU2NyZWFtXCIsIFwiU2VhcmNoXCIsIFwiU2VsZWN0XCIsIFwiU2hhcmVcIiwgXCJTaG9vdFwiLFxuICAgIFwiU2hvdXRcIiwgXCJTaWduYWxcIiwgXCJTaW5nXCIsIFwiU2thdGVcIiwgXCJTbGVlcFwiLCBcIlNtaWxlXCIsIFwiU21va2VcIiwgXCJTb2x2ZVwiLCBcIlNwZWxsXCIsIFwiU3RlZXJcIiwgXCJTdGlua1wiLFxuICAgIFwiU3Vic3RpdHV0ZVwiLCBcIlN3aW1cIiwgXCJUYXN0ZVwiLCBcIlRlYWNoXCIsIFwiVGVybWluYXRlXCIsIFwiVGhpbmtcIiwgXCJUeXBlXCIsIFwiVW5pdGVcIiwgXCJWYW5pc2hcIiwgXCJXb3JzaGlwXCJcbl07XG52YXIgYWR2ZXJicyA9IFtcbiAgICBcIkFic2VudGx5XCIsIFwiQWNjdXJhdGVseVwiLCBcIkFjY3VzaW5nbHlcIiwgXCJBZG9yYWJseVwiLCBcIkFsbFRoZVRpbWVcIiwgXCJBbG9uZVwiLCBcIkFsd2F5c1wiLCBcIkFtYXppbmdseVwiLCBcIkFuZ3JpbHlcIixcbiAgICBcIkFueGlvdXNseVwiLCBcIkFueXdoZXJlXCIsIFwiQXBwYWxsaW5nbHlcIiwgXCJBcHBhcmVudGx5XCIsIFwiQXJ0aWN1bGF0ZWx5XCIsIFwiQXN0b25pc2hpbmdseVwiLCBcIkJhZGx5XCIsIFwiQmFyZWx5XCIsXG4gICAgXCJCZWF1dGlmdWxseVwiLCBcIkJsaW5kbHlcIiwgXCJCcmF2ZWx5XCIsIFwiQnJpZ2h0bHlcIiwgXCJCcmlza2x5XCIsIFwiQnJ1dGFsbHlcIiwgXCJDYWxtbHlcIiwgXCJDYXJlZnVsbHlcIiwgXCJDYXN1YWxseVwiLFxuICAgIFwiQ2F1dGlvdXNseVwiLCBcIkNsZXZlcmx5XCIsIFwiQ29uc3RhbnRseVwiLCBcIkNvcnJlY3RseVwiLCBcIkNyYXppbHlcIiwgXCJDdXJpb3VzbHlcIiwgXCJDeW5pY2FsbHlcIiwgXCJEYWlseVwiLFxuICAgIFwiRGFuZ2Vyb3VzbHlcIiwgXCJEZWxpYmVyYXRlbHlcIiwgXCJEZWxpY2F0ZWx5XCIsIFwiRGVzcGVyYXRlbHlcIiwgXCJEaXNjcmVldGx5XCIsIFwiRWFnZXJseVwiLCBcIkVhc2lseVwiLCBcIkV1cGhvcmljbHlcIixcbiAgICBcIkV2ZW5seVwiLCBcIkV2ZXJ5d2hlcmVcIiwgXCJFeGFjdGx5XCIsIFwiRXhwZWN0YW50bHlcIiwgXCJFeHRlbnNpdmVseVwiLCBcIkZBSUxcIiwgXCJGZXJvY2lvdXNseVwiLCBcIkZpZXJjZWx5XCIsIFwiRmluZWx5XCIsXG4gICAgXCJGbGF0bHlcIiwgXCJGcmVxdWVudGx5XCIsIFwiRnJpZ2h0ZW5pbmdseVwiLCBcIkdlbnRseVwiLCBcIkdsb3Jpb3VzbHlcIiwgXCJHcmltbHlcIiwgXCJHdWlsdGlseVwiLCBcIkhhcHBpbHlcIixcbiAgICBcIkhhcmRcIiwgXCJIYXN0aWx5XCIsIFwiSGVyb2ljYWxseVwiLCBcIkhpZ2hcIiwgXCJIaWdobHlcIiwgXCJIb3VybHlcIiwgXCJIdW1ibHlcIiwgXCJIeXN0ZXJpY2FsbHlcIiwgXCJJbW1lbnNlbHlcIixcbiAgICBcIkltcGFydGlhbGx5XCIsIFwiSW1wb2xpdGVseVwiLCBcIkluZGlmZmVyZW50bHlcIiwgXCJJbnRlbnNlbHlcIiwgXCJKZWFsb3VzbHlcIiwgXCJKb3ZpYWxseVwiLCBcIktpbmRseVwiLCBcIkxhemlseVwiLFxuICAgIFwiTGlnaHRseVwiLCBcIkxvdWRseVwiLCBcIkxvdmluZ2x5XCIsIFwiTG95YWxseVwiLCBcIk1hZ25pZmljZW50bHlcIiwgXCJNYWxldm9sZW50bHlcIiwgXCJNZXJyaWx5XCIsIFwiTWlnaHRpbHlcIiwgXCJNaXNlcmFibHlcIixcbiAgICBcIk15c3RlcmlvdXNseVwiLCBcIk5PVFwiLCBcIk5lcnZvdXNseVwiLCBcIk5pY2VseVwiLCBcIk5vd2hlcmVcIiwgXCJPYmplY3RpdmVseVwiLCBcIk9ibm94aW91c2x5XCIsIFwiT2JzZXNzaXZlbHlcIixcbiAgICBcIk9idmlvdXNseVwiLCBcIk9mdGVuXCIsIFwiUGFpbmZ1bGx5XCIsIFwiUGF0aWVudGx5XCIsIFwiUGxheWZ1bGx5XCIsIFwiUG9saXRlbHlcIiwgXCJQb29ybHlcIiwgXCJQcmVjaXNlbHlcIiwgXCJQcm9tcHRseVwiLFxuICAgIFwiUXVpY2tseVwiLCBcIlF1aWV0bHlcIiwgXCJSYW5kb21seVwiLCBcIlJhcGlkbHlcIiwgXCJSYXJlbHlcIiwgXCJSZWNrbGVzc2x5XCIsIFwiUmVndWxhcmx5XCIsIFwiUmVtb3JzZWZ1bGx5XCIsIFwiUmVzcG9uc2libHlcIixcbiAgICBcIlJ1ZGVseVwiLCBcIlJ1dGhsZXNzbHlcIiwgXCJTYWRseVwiLCBcIlNjb3JuZnVsbHlcIiwgXCJTZWFtbGVzc2x5XCIsIFwiU2VsZG9tXCIsIFwiU2VsZmlzaGx5XCIsIFwiU2VyaW91c2x5XCIsIFwiU2hha2lseVwiLFxuICAgIFwiU2hhcnBseVwiLCBcIlNpZGV3YXlzXCIsIFwiU2lsZW50bHlcIiwgXCJTbGVlcGlseVwiLCBcIlNsaWdodGx5XCIsIFwiU2xvd2x5XCIsIFwiU2x5bHlcIiwgXCJTbW9vdGhseVwiLCBcIlNvZnRseVwiLCBcIlNvbGVtbmx5XCIsIFwiU3RlYWRpbHlcIiwgXCJTdGVybmx5XCIsIFwiU3RyYW5nZWx5XCIsIFwiU3Ryb25nbHlcIiwgXCJTdHVubmluZ2x5XCIsIFwiU3VyZWx5XCIsIFwiVGVuZGVybHlcIiwgXCJUaG91Z2h0ZnVsbHlcIixcbiAgICBcIlRpZ2h0bHlcIiwgXCJVbmVhc2lseVwiLCBcIlZhbmlzaGluZ2x5XCIsIFwiVmlvbGVudGx5XCIsIFwiV2FybWx5XCIsIFwiV2Vha2x5XCIsIFwiV2VhcmlseVwiLCBcIldlZWtseVwiLCBcIldlaXJkbHlcIiwgXCJXZWxsXCIsXG4gICAgXCJXZWxsXCIsIFwiV2lja2VkbHlcIiwgXCJXaWxkbHlcIiwgXCJXaXNlbHlcIiwgXCJXb25kZXJmdWxseVwiLCBcIlllYXJseVwiXG5dO1xudmFyIGFkamVjdGl2ZXMgPSBbXG4gICAgXCJBYm9taW5hYmxlXCIsIFwiQWNjdXJhdGVcIiwgXCJBZG9yYWJsZVwiLCBcIkFsbFwiLCBcIkFsbGVnZWRcIiwgXCJBbmNpZW50XCIsIFwiQW5ncnlcIiwgXCJBbmdyeVwiLCBcIkFueGlvdXNcIiwgXCJBcHBhbGxpbmdcIixcbiAgICBcIkFwcGFyZW50XCIsIFwiQXN0b25pc2hpbmdcIiwgXCJBdHRyYWN0aXZlXCIsIFwiQXdlc29tZVwiLCBcIkJhYnlcIiwgXCJCYWRcIiwgXCJCZWF1dGlmdWxcIiwgXCJCZW5pZ25cIiwgXCJCaWdcIiwgXCJCaXR0ZXJcIixcbiAgICBcIkJsaW5kXCIsIFwiQmx1ZVwiLCBcIkJvbGRcIiwgXCJCcmF2ZVwiLCBcIkJyaWdodFwiLCBcIkJyaXNrXCIsIFwiQ2FsbVwiLCBcIkNhbW91ZmxhZ2VkXCIsIFwiQ2FzdWFsXCIsIFwiQ2F1dGlvdXNcIixcbiAgICBcIkNob3BweVwiLCBcIkNob3NlblwiLCBcIkNsZXZlclwiLCBcIkNvbGRcIiwgXCJDb29sXCIsIFwiQ3Jhd2x5XCIsIFwiQ3JhenlcIiwgXCJDcmVlcHlcIiwgXCJDcnVlbFwiLCBcIkN1cmlvdXNcIiwgXCJDeW5pY2FsXCIsXG4gICAgXCJEYW5nZXJvdXNcIiwgXCJEYXJrXCIsIFwiRGVsaWNhdGVcIiwgXCJEZXNwZXJhdGVcIiwgXCJEaWZmaWN1bHRcIiwgXCJEaXNjcmVldFwiLCBcIkRpc2d1aXNlZFwiLCBcIkRpenp5XCIsXG4gICAgXCJEdW1iXCIsIFwiRWFnZXJcIiwgXCJFYXN5XCIsIFwiRWRneVwiLCBcIkVsZWN0cmljXCIsIFwiRWxlZ2FudFwiLCBcIkVtYW5jaXBhdGVkXCIsIFwiRW5vcm1vdXNcIiwgXCJFdXBob3JpY1wiLCBcIkV2aWxcIixcbiAgICBcIkZBSUxcIiwgXCJGYXN0XCIsIFwiRmVyb2Npb3VzXCIsIFwiRmllcmNlXCIsIFwiRmluZVwiLCBcIkZsYXdlZFwiLCBcIkZseWluZ1wiLCBcIkZvb2xpc2hcIiwgXCJGb3h5XCIsXG4gICAgXCJGcmVlemluZ1wiLCBcIkZ1bm55XCIsIFwiRnVyaW91c1wiLCBcIkdlbnRsZVwiLCBcIkdsb3Jpb3VzXCIsIFwiR29sZGVuXCIsIFwiR29vZFwiLCBcIkdyZWVuXCIsIFwiR3JlZW5cIiwgXCJHdWlsdHlcIixcbiAgICBcIkhhaXJ5XCIsIFwiSGFwcHlcIiwgXCJIYXJkXCIsIFwiSGFzdHlcIiwgXCJIYXp5XCIsIFwiSGVyb2ljXCIsIFwiSG9zdGlsZVwiLCBcIkhvdFwiLCBcIkh1bWJsZVwiLCBcIkh1bW9uZ291c1wiLFxuICAgIFwiSHVtb3JvdXNcIiwgXCJIeXN0ZXJpY2FsXCIsIFwiSWRlYWxpc3RpY1wiLCBcIklnbm9yYW50XCIsIFwiSW1tZW5zZVwiLCBcIkltcGFydGlhbFwiLCBcIkltcG9saXRlXCIsIFwiSW5kaWZmZXJlbnRcIixcbiAgICBcIkluZnVyaWF0ZWRcIiwgXCJJbnNpZ2h0ZnVsXCIsIFwiSW50ZW5zZVwiLCBcIkludGVyZXN0aW5nXCIsIFwiSW50aW1pZGF0ZWRcIiwgXCJJbnRyaWd1aW5nXCIsIFwiSmVhbG91c1wiLCBcIkpvbGx5XCIsIFwiSm92aWFsXCIsXG4gICAgXCJKdW1weVwiLCBcIktpbmRcIiwgXCJMYXVnaGluZ1wiLCBcIkxhenlcIiwgXCJMaXF1aWRcIiwgXCJMb25lbHlcIiwgXCJMb25naW5nXCIsIFwiTG91ZFwiLCBcIkxvdmluZ1wiLCBcIkxveWFsXCIsIFwiTWFjYWJyZVwiLCBcIk1hZFwiLFxuICAgIFwiTWFnaWNhbFwiLCBcIk1hZ25pZmljZW50XCIsIFwiTWFsZXZvbGVudFwiLCBcIk1lZGlldmFsXCIsIFwiTWVtb3JhYmxlXCIsIFwiTWVyZVwiLCBcIk1lcnJ5XCIsIFwiTWlnaHR5XCIsXG4gICAgXCJNaXNjaGlldm91c1wiLCBcIk1pc2VyYWJsZVwiLCBcIk1vZGlmaWVkXCIsIFwiTW9vZHlcIiwgXCJNb3N0XCIsIFwiTXlzdGVyaW91c1wiLCBcIk15c3RpY2FsXCIsIFwiTmVlZHlcIixcbiAgICBcIk5lcnZvdXNcIiwgXCJOaWNlXCIsIFwiT2JqZWN0aXZlXCIsIFwiT2Jub3hpb3VzXCIsIFwiT2JzZXNzaXZlXCIsIFwiT2J2aW91c1wiLCBcIk9waW5pb25hdGVkXCIsIFwiT3JhbmdlXCIsXG4gICAgXCJQYWluZnVsXCIsIFwiUGFzc2lvbmF0ZVwiLCBcIlBlcmZlY3RcIiwgXCJQaW5rXCIsIFwiUGxheWZ1bFwiLCBcIlBvaXNvbm91c1wiLCBcIlBvbGl0ZVwiLCBcIlBvb3JcIiwgXCJQb3B1bGFyXCIsIFwiUG93ZXJmdWxcIixcbiAgICBcIlByZWNpc2VcIiwgXCJQcmVzZXJ2ZWRcIiwgXCJQcmV0dHlcIiwgXCJQdXJwbGVcIiwgXCJRdWlja1wiLCBcIlF1aWV0XCIsIFwiUmFuZG9tXCIsIFwiUmFwaWRcIiwgXCJSYXJlXCIsIFwiUmVhbFwiLFxuICAgIFwiUmVhc3N1cmluZ1wiLCBcIlJlY2tsZXNzXCIsIFwiUmVkXCIsIFwiUmVndWxhclwiLCBcIlJlbW9yc2VmdWxcIiwgXCJSZXNwb25zaWJsZVwiLCBcIlJpY2hcIiwgXCJSdWRlXCIsIFwiUnV0aGxlc3NcIixcbiAgICBcIlNhZFwiLCBcIlNjYXJlZFwiLCBcIlNjYXJ5XCIsIFwiU2Nvcm5mdWxcIiwgXCJTY3JlYW1pbmdcIiwgXCJTZWxmaXNoXCIsIFwiU2VyaW91c1wiLCBcIlNoYWR5XCIsIFwiU2hha3lcIiwgXCJTaGFycFwiLFxuICAgIFwiU2hpbnlcIiwgXCJTaHlcIiwgXCJTaW1wbGVcIiwgXCJTbGVlcHlcIiwgXCJTbG93XCIsIFwiU2x5XCIsIFwiU21hbGxcIiwgXCJTbWFydFwiLCBcIlNtZWxseVwiLCBcIlNtaWxpbmdcIiwgXCJTbW9vdGhcIixcbiAgICBcIlNtdWdcIiwgXCJTb2JlclwiLCBcIlNvZnRcIiwgXCJTb2xlbW5cIiwgXCJTcXVhcmVcIiwgXCJTcXVhcmVcIiwgXCJTdGVhZHlcIiwgXCJTdHJhbmdlXCIsIFwiU3Ryb25nXCIsXG4gICAgXCJTdHVubmluZ1wiLCBcIlN1YmplY3RpdmVcIiwgXCJTdWNjZXNzZnVsXCIsIFwiU3VybHlcIiwgXCJTd2VldFwiLCBcIlRhY3RmdWxcIiwgXCJUZW5zZVwiLFxuICAgIFwiVGhvdWdodGZ1bFwiLCBcIlRpZ2h0XCIsIFwiVGlueVwiLCBcIlRvbGVyYW50XCIsIFwiVW5lYXN5XCIsIFwiVW5pcXVlXCIsIFwiVW5zZWVuXCIsIFwiV2FybVwiLCBcIldlYWtcIixcbiAgICBcIldlaXJkXCIsIFwiV2VsbENvb2tlZFwiLCBcIldpbGRcIiwgXCJXaXNlXCIsIFwiV2l0dHlcIiwgXCJXb25kZXJmdWxcIiwgXCJXb3JyaWVkXCIsIFwiWWVsbG93XCIsIFwiWW91bmdcIixcbiAgICBcIlplYWxvdXNcIlxuICAgIF07XG4vL3ZhciBwcm9ub3VucyA9IFtcbi8vXTtcbi8vdmFyIGNvbmp1bmN0aW9ucyA9IFtcbi8vXCJBbmRcIiwgXCJPclwiLCBcIkZvclwiLCBcIkFib3ZlXCIsIFwiQmVmb3JlXCIsIFwiQWdhaW5zdFwiLCBcIkJldHdlZW5cIlxuLy9dO1xuXG4vKlxuICogTWFwcyBhIHN0cmluZyAoY2F0ZWdvcnkgbmFtZSkgdG8gdGhlIGFycmF5IG9mIHdvcmRzIGZyb20gdGhhdCBjYXRlZ29yeS5cbiAqL1xudmFyIENBVEVHT1JJRVMgPVxue1xuICAgIC8vXCJfTk9VTl9cIjogbm91bnMsXG4gICAgXCJfUExVUkFMTk9VTl9cIjogcGx1cmFsTm91bnMsXG4gICAgLy9cIl9QTEFDRV9cIjogcGxhY2VzLFxuICAgIFwiX1ZFUkJfXCI6IHZlcmJzLFxuICAgIFwiX0FEVkVSQl9cIjogYWR2ZXJicyxcbiAgICBcIl9BREpFQ1RJVkVfXCI6IGFkamVjdGl2ZXNcbiAgICAvL1wiX1BST05PVU5fXCI6IHByb25vdW5zLFxuICAgIC8vXCJfQ09OSlVOQ1RJT05fXCI6IGNvbmp1bmN0aW9ucyxcbn07XG5cbnZhciBQQVRURVJOUyA9IFtcbiAgICBcIl9BREpFQ1RJVkVfX1BMVVJBTE5PVU5fX1ZFUkJfX0FEVkVSQl9cIlxuXG4gICAgLy8gQmVhdXRpZnVsRnVuZ2lPclNwYWdoZXR0aVxuICAgIC8vXCJfQURKRUNUSVZFX19QTFVSQUxOT1VOX19DT05KVU5DVElPTl9fUExVUkFMTk9VTl9cIixcblxuICAgIC8vIEFtYXppbmdseVNjYXJ5VG95XG4gICAgLy9cIl9BRFZFUkJfX0FESkVDVElWRV9fTk9VTl9cIixcblxuICAgIC8vIE5laXRoZXJUcmFzaE5vclJpZmxlXG4gICAgLy9cIk5laXRoZXJfTk9VTl9Ob3JfTk9VTl9cIixcbiAgICAvL1wiRWl0aGVyX05PVU5fT3JfTk9VTl9cIixcblxuICAgIC8vIEVpdGhlckNvcHVsYXRlT3JJbnZlc3RpZ2F0ZVxuICAgIC8vXCJFaXRoZXJfVkVSQl9Pcl9WRVJCX1wiLFxuICAgIC8vXCJOZWl0aGVyX1ZFUkJfTm9yX1ZFUkJfXCIsXG5cbiAgICAvL1wiVGhlX0FESkVDVElWRV9fQURKRUNUSVZFX19OT1VOX1wiLFxuICAgIC8vXCJUaGVfQURWRVJCX19BREpFQ1RJVkVfX05PVU5fXCIsXG4gICAgLy9cIlRoZV9BRFZFUkJfX0FESkVDVElWRV9fTk9VTl9zXCIsXG4gICAgLy9cIlRoZV9BRFZFUkJfX0FESkVDVElWRV9fUExVUkFMTk9VTl9fVkVSQl9cIixcblxuICAgIC8vIFdvbHZlc0NvbXB1dGVCYWRseVxuICAgIC8vXCJfUExVUkFMTk9VTl9fVkVSQl9fQURWRVJCX1wiLFxuXG4gICAgLy8gVW5pdGVGYWNpbGl0YXRlQW5kTWVyZ2VcbiAgICAvL1wiX1ZFUkJfX1ZFUkJfQW5kX1ZFUkJfXCIsXG5cbiAgICAvL05hc3R5V2l0Y2hlc0F0VGhlUHViXG4gICAgLy9cIl9BREpFQ1RJVkVfX1BMVVJBTE5PVU5fQXRUaGVfUExBQ0VfXCIsXG5dO1xuXG5cbi8qXG4gKiBSZXR1cm5zIGEgcmFuZG9tIGVsZW1lbnQgZnJvbSB0aGUgYXJyYXkgJ2FycidcbiAqL1xuZnVuY3Rpb24gcmFuZG9tRWxlbWVudChhcnIpXG57XG4gICAgcmV0dXJuIGFycltNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhcnIubGVuZ3RoKV07XG59XG5cbi8qXG4gKiBSZXR1cm5zIHRydWUgaWYgdGhlIHN0cmluZyAncycgY29udGFpbnMgb25lIG9mIHRoZVxuICogdGVtcGxhdGUgc3RyaW5ncy5cbiAqL1xuZnVuY3Rpb24gaGFzVGVtcGxhdGUocylcbntcbiAgICBmb3IgKHZhciB0ZW1wbGF0ZSBpbiBDQVRFR09SSUVTKXtcbiAgICAgICAgaWYgKHMuaW5kZXhPZih0ZW1wbGF0ZSkgPj0gMCl7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgbmV3IHJvb20gbmFtZS5cbiAqL1xudmFyIFJvb21OYW1lR2VuZXJhdG9yID0ge1xuICAgIGdlbmVyYXRlUm9vbVdpdGhvdXRTZXBhcmF0b3I6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIC8vIE5vdGUgdGhhdCBpZiBtb3JlIHRoYW4gb25lIHBhdHRlcm4gaXMgYXZhaWxhYmxlLCB0aGUgY2hvaWNlIG9mICduYW1lJyB3b24ndCBiZSByYW5kb20gKG5hbWVzIGZyb20gcGF0dGVybnNcbiAgICAgICAgLy8gd2l0aCBmZXdlciBvcHRpb25zIHdpbGwgaGF2ZSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgYmVpbmcgY2hvc2VuIHRoYXQgbmFtZXMgZnJvbSBwYXR0ZXJucyB3aXRoIG1vcmUgb3B0aW9ucykuXG4gICAgICAgIHZhciBuYW1lID0gcmFuZG9tRWxlbWVudChQQVRURVJOUyk7XG4gICAgICAgIHZhciB3b3JkO1xuICAgICAgICB3aGlsZSAoaGFzVGVtcGxhdGUobmFtZSkpe1xuICAgICAgICAgICAgZm9yICh2YXIgdGVtcGxhdGUgaW4gQ0FURUdPUklFUyl7XG4gICAgICAgICAgICAgICAgd29yZCA9IHJhbmRvbUVsZW1lbnQoQ0FURUdPUklFU1t0ZW1wbGF0ZV0pO1xuICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lLnJlcGxhY2UodGVtcGxhdGUsIHdvcmQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5hbWU7XG4gICAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFJvb21OYW1lR2VuZXJhdG9yO1xuIiwidmFyIGFuaW1hdGVUaW1lb3V0LCB1cGRhdGVUaW1lb3V0O1xuXG52YXIgUm9vbU5hbWVHZW5lcmF0b3IgPSByZXF1aXJlKFwiLi9Sb29tbmFtZUdlbmVyYXRvclwiKTtcblxuZnVuY3Rpb24gZW50ZXJfcm9vbSgpXG57XG4gICAgdmFyIHZhbCA9ICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS52YWwoKTtcbiAgICBpZighdmFsKSB7XG4gICAgICAgIHZhbCA9ICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5hdHRyKFwicm9vbV9uYW1lXCIpO1xuICAgIH1cbiAgICBpZiAodmFsKSB7XG4gICAgICAgIHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSA9IFwiL1wiICsgdmFsO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gYW5pbWF0ZSh3b3JkKSB7XG4gICAgdmFyIGN1cnJlbnRWYWwgPSAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikuYXR0cihcInBsYWNlaG9sZGVyXCIpO1xuICAgICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5hdHRyKFwicGxhY2Vob2xkZXJcIiwgY3VycmVudFZhbCArIHdvcmQuc3Vic3RyKDAsIDEpKTtcbiAgICBhbmltYXRlVGltZW91dCA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgIGFuaW1hdGUod29yZC5zdWJzdHJpbmcoMSwgd29yZC5sZW5ndGgpKVxuICAgIH0sIDcwKTtcbn1cblxuZnVuY3Rpb24gdXBkYXRlX3Jvb21uYW1lKClcbntcbiAgICB2YXIgd29yZCA9IFJvb21OYW1lR2VuZXJhdG9yLmdlbmVyYXRlUm9vbVdpdGhvdXRTZXBhcmF0b3IoKTtcbiAgICAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikuYXR0cihcInJvb21fbmFtZVwiLCB3b3JkKTtcbiAgICAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikuYXR0cihcInBsYWNlaG9sZGVyXCIsIFwiXCIpO1xuICAgIGNsZWFyVGltZW91dChhbmltYXRlVGltZW91dCk7XG4gICAgYW5pbWF0ZSh3b3JkKTtcbiAgICB1cGRhdGVUaW1lb3V0ID0gc2V0VGltZW91dCh1cGRhdGVfcm9vbW5hbWUsIDEwMDAwKTtcbn1cblxuXG5mdW5jdGlvbiBzZXR1cFdlbGNvbWVQYWdlKClcbntcbiAgICAkKFwiI3ZpZGVvY29uZmVyZW5jZV9wYWdlXCIpLmhpZGUoKTtcbiAgICAkKFwiI2RvbWFpbl9uYW1lXCIpLnRleHQoXG4gICAgICAgICAgICB3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wgKyBcIi8vXCIgKyB3aW5kb3cubG9jYXRpb24uaG9zdCArIFwiL1wiKTtcbiAgICAkKFwic3BhbltuYW1lPSdhcHBOYW1lJ11cIikudGV4dChpbnRlcmZhY2VDb25maWcuQVBQX05BTUUpO1xuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0pJVFNJX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgbGVmdFdhdGVybWFya0RpdlxuICAgICAgICAgICAgPSAkKFwiI3dlbGNvbWVfcGFnZV9oZWFkZXIgZGl2W2NsYXNzPSd3YXRlcm1hcmsgbGVmdHdhdGVybWFyayddXCIpO1xuICAgICAgICBpZihsZWZ0V2F0ZXJtYXJrRGl2ICYmIGxlZnRXYXRlcm1hcmtEaXYubGVuZ3RoID4gMClcbiAgICAgICAge1xuICAgICAgICAgICAgbGVmdFdhdGVybWFya0Rpdi5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICAgICAgICAgIGxlZnRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5KSVRTSV9XQVRFUk1BUktfTElOSztcbiAgICAgICAgfVxuXG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0JSQU5EX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgcmlnaHRXYXRlcm1hcmtEaXZcbiAgICAgICAgICAgID0gJChcIiN3ZWxjb21lX3BhZ2VfaGVhZGVyIGRpdltjbGFzcz0nd2F0ZXJtYXJrIHJpZ2h0d2F0ZXJtYXJrJ11cIik7XG4gICAgICAgIGlmKHJpZ2h0V2F0ZXJtYXJrRGl2ICYmIHJpZ2h0V2F0ZXJtYXJrRGl2Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmNzcyh7ZGlzcGxheTogJ2Jsb2NrJ30pO1xuICAgICAgICAgICAgcmlnaHRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5CUkFORF9XQVRFUk1BUktfTElOSztcbiAgICAgICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmdldCgwKS5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2VcbiAgICAgICAgICAgICAgICA9IFwidXJsKGltYWdlcy9yaWdodHdhdGVybWFyay5wbmcpXCI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLlNIT1dfUE9XRVJFRF9CWSkge1xuICAgICAgICAkKFwiI3dlbGNvbWVfcGFnZV9oZWFkZXI+YVtjbGFzcz0ncG93ZXJlZGJ5J11cIilcbiAgICAgICAgICAgIC5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICB9XG5cbiAgICAkKFwiI2VudGVyX3Jvb21fYnV0dG9uXCIpLmNsaWNrKGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIGVudGVyX3Jvb20oKTtcbiAgICB9KTtcblxuICAgICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5rZXlkb3duKGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBpZiAoZXZlbnQua2V5Q29kZSA9PT0gMTMgLyogZW50ZXIgKi8pIHtcbiAgICAgICAgICAgIGVudGVyX3Jvb20oKTtcbiAgICAgICAgfVxuICAgIH0pO1xuXG4gICAgaWYgKCEoaW50ZXJmYWNlQ29uZmlnLkdFTkVSQVRFX1JPT01OQU1FU19PTl9XRUxDT01FX1BBR0UgPT09IGZhbHNlKSl7XG4gICAgICAgIHZhciB1cGRhdGVUaW1lb3V0O1xuICAgICAgICB2YXIgYW5pbWF0ZVRpbWVvdXQ7XG4gICAgICAgICQoXCIjcmVsb2FkX3Jvb21uYW1lXCIpLmNsaWNrKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh1cGRhdGVUaW1lb3V0KTtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChhbmltYXRlVGltZW91dCk7XG4gICAgICAgICAgICB1cGRhdGVfcm9vbW5hbWUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgICQoXCIjcmVsb2FkX3Jvb21uYW1lXCIpLnNob3coKTtcblxuXG4gICAgICAgIHVwZGF0ZV9yb29tbmFtZSgpO1xuICAgIH1cblxuICAgICQoXCIjZGlzYWJsZV93ZWxjb21lXCIpLmNsaWNrKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkXG4gICAgICAgICAgICA9ICQoXCIjZGlzYWJsZV93ZWxjb21lXCIpLmlzKFwiOmNoZWNrZWRcIik7XG4gICAgfSk7XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBzZXR1cFdlbGNvbWVQYWdlOyJdfQ==
diff --git a/libs/modules/connectionquality.bundle.js b/libs/modules/connectionquality.bundle.js
index 50b92a66d..ee43400f6 100644
--- a/libs/modules/connectionquality.bundle.js
+++ b/libs/modules/connectionquality.bundle.js
@@ -30,8 +30,7 @@ function startSendingStats() {
* Sends statistics to other participants
*/
function sendStats() {
- connection.emuc.addConnectionInfoToPresence(convertToMUCStats(stats));
- connection.emuc.sendPresence();
+ xmpp.addToPresence("connectionQuality", convertToMUCStats(stats));
}
/**
@@ -119,4 +118,5 @@ var ConnectionQuality = {
module.exports = ConnectionQuality;
},{}]},{},[1])(1)
-});
\ No newline at end of file
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL2Nvbm5lY3Rpb25xdWFsaXR5L2Nvbm5lY3Rpb25xdWFsaXR5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoqXG4gKiBsb2NhbCBzdGF0c1xuICogQHR5cGUge3t9fVxuICovXG52YXIgc3RhdHMgPSB7fTtcblxuLyoqXG4gKiByZW1vdGUgc3RhdHNcbiAqIEB0eXBlIHt7fX1cbiAqL1xudmFyIHJlbW90ZVN0YXRzID0ge307XG5cbi8qKlxuICogSW50ZXJ2YWwgZm9yIHNlbmRpbmcgc3RhdGlzdGljcyB0byBvdGhlciBwYXJ0aWNpcGFudHNcbiAqIEB0eXBlIHtudWxsfVxuICovXG52YXIgc2VuZEludGVydmFsSWQgPSBudWxsO1xuXG5cbi8qKlxuICogU3RhcnQgc3RhdGlzdGljcyBzZW5kaW5nLlxuICovXG5mdW5jdGlvbiBzdGFydFNlbmRpbmdTdGF0cygpIHtcbiAgICBzZW5kU3RhdHMoKTtcbiAgICBzZW5kSW50ZXJ2YWxJZCA9IHNldEludGVydmFsKHNlbmRTdGF0cywgMTAwMDApO1xufVxuXG4vKipcbiAqIFNlbmRzIHN0YXRpc3RpY3MgdG8gb3RoZXIgcGFydGljaXBhbnRzXG4gKi9cbmZ1bmN0aW9uIHNlbmRTdGF0cygpIHtcbiAgICB4bXBwLmFkZFRvUHJlc2VuY2UoXCJjb25uZWN0aW9uUXVhbGl0eVwiLCBjb252ZXJ0VG9NVUNTdGF0cyhzdGF0cykpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIHN0YXRpc3RpY3MgdG8gZm9ybWF0IGZvciBzZW5kaW5nIHRocm91Z2ggWE1QUFxuICogQHBhcmFtIHN0YXRzIHRoZSBzdGF0aXN0aWNzXG4gKiBAcmV0dXJucyB7e2JpdHJhdGVfZG9ud2xvYWQ6ICosIGJpdHJhdGVfdXBscG9hZDogKiwgcGFja2V0TG9zc190b3RhbDogKiwgcGFja2V0TG9zc19kb3dubG9hZDogKiwgcGFja2V0TG9zc191cGxvYWQ6ICp9fVxuICovXG5mdW5jdGlvbiBjb252ZXJ0VG9NVUNTdGF0cyhzdGF0cykge1xuICAgIHJldHVybiB7XG4gICAgICAgIFwiYml0cmF0ZV9kb3dubG9hZFwiOiBzdGF0cy5iaXRyYXRlLmRvd25sb2FkLFxuICAgICAgICBcImJpdHJhdGVfdXBsb2FkXCI6IHN0YXRzLmJpdHJhdGUudXBsb2FkLFxuICAgICAgICBcInBhY2tldExvc3NfdG90YWxcIjogc3RhdHMucGFja2V0TG9zcy50b3RhbCxcbiAgICAgICAgXCJwYWNrZXRMb3NzX2Rvd25sb2FkXCI6IHN0YXRzLnBhY2tldExvc3MuZG93bmxvYWQsXG4gICAgICAgIFwicGFja2V0TG9zc191cGxvYWRcIjogc3RhdHMucGFja2V0TG9zcy51cGxvYWRcbiAgICB9O1xufVxuXG4vKipcbiAqIENvbnZlcnRzIHN0YXRpdGlzdGljcyB0byBmb3JtYXQgdXNlZCBieSBWaWRlb0xheW91dFxuICogQHBhcmFtIHN0YXRzXG4gKiBAcmV0dXJucyB7e2JpdHJhdGU6IHtkb3dubG9hZDogKiwgdXBsb2FkOiAqfSwgcGFja2V0TG9zczoge3RvdGFsOiAqLCBkb3dubG9hZDogKiwgdXBsb2FkOiAqfX19XG4gKi9cbmZ1bmN0aW9uIHBhcnNlTVVDU3RhdHMoc3RhdHMpIHtcbiAgICByZXR1cm4ge1xuICAgICAgICBiaXRyYXRlOiB7XG4gICAgICAgICAgICBkb3dubG9hZDogc3RhdHMuYml0cmF0ZV9kb3dubG9hZCxcbiAgICAgICAgICAgIHVwbG9hZDogc3RhdHMuYml0cmF0ZV91cGxvYWRcbiAgICAgICAgfSxcbiAgICAgICAgcGFja2V0TG9zczoge1xuICAgICAgICAgICAgdG90YWw6IHN0YXRzLnBhY2tldExvc3NfdG90YWwsXG4gICAgICAgICAgICBkb3dubG9hZDogc3RhdHMucGFja2V0TG9zc19kb3dubG9hZCxcbiAgICAgICAgICAgIHVwbG9hZDogc3RhdHMucGFja2V0TG9zc191cGxvYWRcbiAgICAgICAgfVxuICAgIH07XG59XG5cblxudmFyIENvbm5lY3Rpb25RdWFsaXR5ID0ge1xuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGxvY2FsIHN0YXRpc3RpY3NcbiAgICAgKiBAcGFyYW0gZGF0YSBuZXcgc3RhdGlzdGljc1xuICAgICAqL1xuICAgIHVwZGF0ZUxvY2FsU3RhdHM6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIHN0YXRzID0gZGF0YTtcbiAgICAgICAgVUkudXBkYXRlTG9jYWxDb25uZWN0aW9uU3RhdHMoMTAwIC0gc3RhdHMucGFja2V0TG9zcy50b3RhbCwgc3RhdHMpO1xuICAgICAgICBpZiAoc2VuZEludGVydmFsSWQgPT0gbnVsbCkge1xuICAgICAgICAgICAgc3RhcnRTZW5kaW5nU3RhdHMoKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHJlbW90ZSBzdGF0aXN0aWNzXG4gICAgICogQHBhcmFtIGppZCB0aGUgamlkIGFzc29jaWF0ZWQgd2l0aCB0aGUgc3RhdGlzdGljc1xuICAgICAqIEBwYXJhbSBkYXRhIHRoZSBzdGF0aXN0aWNzXG4gICAgICovXG4gICAgdXBkYXRlUmVtb3RlU3RhdHM6IGZ1bmN0aW9uIChqaWQsIGRhdGEpIHtcbiAgICAgICAgaWYgKGRhdGEgPT0gbnVsbCB8fCBkYXRhLnBhY2tldExvc3NfdG90YWwgPT0gbnVsbCkge1xuICAgICAgICAgICAgVUkudXBkYXRlQ29ubmVjdGlvblN0YXRzKGppZCwgbnVsbCwgbnVsbCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgcmVtb3RlU3RhdHNbamlkXSA9IHBhcnNlTVVDU3RhdHMoZGF0YSk7XG5cbiAgICAgICAgVUkudXBkYXRlQ29ubmVjdGlvblN0YXRzKGppZCwgMTAwIC0gZGF0YS5wYWNrZXRMb3NzX3RvdGFsLCByZW1vdGVTdGF0c1tqaWRdKTtcblxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBTdG9wcyBzdGF0aXN0aWNzIHNlbmRpbmcuXG4gICAgICovXG4gICAgc3RvcFNlbmRpbmdTdGF0czogZnVuY3Rpb24gKCkge1xuICAgICAgICBjbGVhckludGVydmFsKHNlbmRJbnRlcnZhbElkKTtcbiAgICAgICAgc2VuZEludGVydmFsSWQgPSBudWxsO1xuICAgICAgICAvL25vdGlmeSBVSSBhYm91dCBzdG9wcGluZyBzdGF0aXN0aWNzIGdhdGhlcmluZ1xuICAgICAgICBVSS5vblN0YXRzU3RvcCgpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBsb2NhbCBzdGF0aXN0aWNzLlxuICAgICAqL1xuICAgIGdldFN0YXRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzdGF0cztcbiAgICB9XG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29ubmVjdGlvblF1YWxpdHk7Il19
diff --git a/libs/modules/desktopsharing.bundle.js b/libs/modules/desktopsharing.bundle.js
index 3ce0d817c..dc8e493b5 100644
--- a/libs/modules/desktopsharing.bundle.js
+++ b/libs/modules/desktopsharing.bundle.js
@@ -1,5 +1,5 @@
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.desktopsharing=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
+ var ice = SDPUtil.iceparams(this.localSDP.media[mid], this.localSDP.session);
+ ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
+ cand.c('content', {creator: this.initiator == this.me ? 'initiator' : 'responder',
+ name: (cands[0].sdpMid? cands[0].sdpMid : mline.media)
+ }).c('transport', ice);
+ for (var i = 0; i < cands.length; i++) {
+ cand.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
+ }
+ // add fingerprint
+ if (SDPUtil.find_line(this.localSDP.media[mid], 'a=fingerprint:', this.localSDP.session)) {
+ var tmp = SDPUtil.parse_fingerprint(SDPUtil.find_line(this.localSDP.media[mid], 'a=fingerprint:', this.localSDP.session));
+ tmp.required = true;
+ cand.c(
+ 'fingerprint',
+ {xmlns: 'urn:xmpp:jingle:apps:dtls:0'})
+ .t(tmp.fingerprint);
+ delete tmp.fingerprint;
+ cand.attrs(tmp);
+ cand.up();
+ }
+ cand.up(); // transport
+ cand.up(); // content
+ }
+ }
+ // might merge last-candidate notification into this, but it is called alot later. See webrtc issue #2340
+ //console.log('was this the last candidate', this.lasticecandidate);
+ this.connection.sendIQ(cand,
+ function () {
+ var ack = {};
+ ack.source = 'transportinfo';
+ $(document).trigger('ack.jingle', [this.sid, ack]);
+ },
+ function (stanza) {
+ var error = ($(stanza).find('error').length) ? {
+ code: $(stanza).find('error').attr('code'),
+ reason: $(stanza).find('error :first')[0].tagName,
+ }:{};
+ error.source = 'transportinfo';
+ JingleSession.onJingleError(this.sid, error);
+ },
+ 10000);
+};
+
+
+JingleSession.prototype.sendOffer = function () {
+ //console.log('sendOffer...');
+ var self = this;
+ this.peerconnection.createOffer(function (sdp) {
+ self.createdOffer(sdp);
+ },
+ function (e) {
+ console.error('createOffer failed', e);
+ },
+ this.media_constraints
+ );
+};
+
+JingleSession.prototype.createdOffer = function (sdp) {
+ //console.log('createdOffer', sdp);
+ var self = this;
+ this.localSDP = new SDP(sdp.sdp);
+ //this.localSDP.mangle();
+ var sendJingle = function () {
+ var init = $iq({to: this.peerjid,
+ type: 'set'})
+ .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
+ action: 'session-initiate',
+ initiator: this.initiator,
+ sid: this.sid});
+ self.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
+ self.connection.sendIQ(init,
+ function () {
+ var ack = {};
+ ack.source = 'offer';
+ $(document).trigger('ack.jingle', [self.sid, ack]);
+ },
+ function (stanza) {
+ self.state = 'error';
+ self.peerconnection.close();
+ var error = ($(stanza).find('error').length) ? {
+ code: $(stanza).find('error').attr('code'),
+ reason: $(stanza).find('error :first')[0].tagName,
+ }:{};
+ error.source = 'offer';
+ JingleSession.onJingleError(self.sid, error);
+ },
+ 10000);
+ }
+ sdp.sdp = this.localSDP.raw;
+ this.peerconnection.setLocalDescription(sdp,
+ function () {
+ if(self.usetrickle)
+ {
+ sendJingle();
+ }
+ self.setLocalDescription();
+ //console.log('setLocalDescription success');
+ },
+ function (e) {
+ console.error('setLocalDescription failed', e);
+ }
+ );
+ var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
+ for (var i = 0; i < cands.length; i++) {
+ var cand = SDPUtil.parse_icecandidate(cands[i]);
+ if (cand.type == 'srflx') {
+ this.hadstuncandidate = true;
+ } else if (cand.type == 'relay') {
+ this.hadturncandidate = true;
+ }
+ }
+};
+
+JingleSession.prototype.setRemoteDescription = function (elem, desctype) {
+ //console.log('setting remote description... ', desctype);
+ this.remoteSDP = new SDP('');
+ this.remoteSDP.fromJingle(elem);
+ if (this.peerconnection.remoteDescription !== null) {
+ console.log('setRemoteDescription when remote description is not null, should be pranswer', this.peerconnection.remoteDescription);
+ if (this.peerconnection.remoteDescription.type == 'pranswer') {
+ var pranswer = new SDP(this.peerconnection.remoteDescription.sdp);
+ for (var i = 0; i < pranswer.media.length; i++) {
+ // make sure we have ice ufrag and pwd
+ if (!SDPUtil.find_line(this.remoteSDP.media[i], 'a=ice-ufrag:', this.remoteSDP.session)) {
+ if (SDPUtil.find_line(pranswer.media[i], 'a=ice-ufrag:', pranswer.session)) {
+ this.remoteSDP.media[i] += SDPUtil.find_line(pranswer.media[i], 'a=ice-ufrag:', pranswer.session) + '\r\n';
+ } else {
+ console.warn('no ice ufrag?');
+ }
+ if (SDPUtil.find_line(pranswer.media[i], 'a=ice-pwd:', pranswer.session)) {
+ this.remoteSDP.media[i] += SDPUtil.find_line(pranswer.media[i], 'a=ice-pwd:', pranswer.session) + '\r\n';
+ } else {
+ console.warn('no ice pwd?');
+ }
+ }
+ // copy over candidates
+ var lines = SDPUtil.find_lines(pranswer.media[i], 'a=candidate:');
+ for (var j = 0; j < lines.length; j++) {
+ this.remoteSDP.media[i] += lines[j] + '\r\n';
+ }
+ }
+ this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
+ }
+ }
+ var remotedesc = new RTCSessionDescription({type: desctype, sdp: this.remoteSDP.raw});
+
+ this.peerconnection.setRemoteDescription(remotedesc,
+ function () {
+ //console.log('setRemoteDescription success');
+ },
+ function (e) {
+ console.error('setRemoteDescription error', e);
+ JingleSession.onJingleFatalError(self, e);
+ }
+ );
+};
+
+JingleSession.prototype.addIceCandidate = function (elem) {
+ var self = this;
+ if (this.peerconnection.signalingState == 'closed') {
+ return;
+ }
+ if (!this.peerconnection.remoteDescription && this.peerconnection.signalingState == 'have-local-offer') {
+ console.log('trickle ice candidate arriving before session accept...');
+ // create a PRANSWER for setRemoteDescription
+ if (!this.remoteSDP) {
+ var cobbled = 'v=0\r\n' +
+ 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
+ 's=-\r\n' +
+ 't=0 0\r\n';
+ // first, take some things from the local description
+ for (var i = 0; i < this.localSDP.media.length; i++) {
+ cobbled += SDPUtil.find_line(this.localSDP.media[i], 'm=') + '\r\n';
+ cobbled += SDPUtil.find_lines(this.localSDP.media[i], 'a=rtpmap:').join('\r\n') + '\r\n';
+ if (SDPUtil.find_line(this.localSDP.media[i], 'a=mid:')) {
+ cobbled += SDPUtil.find_line(this.localSDP.media[i], 'a=mid:') + '\r\n';
+ }
+ cobbled += 'a=inactive\r\n';
+ }
+ this.remoteSDP = new SDP(cobbled);
+ }
+ // then add things like ice and dtls from remote candidate
+ elem.each(function () {
+ for (var i = 0; i < self.remoteSDP.media.length; i++) {
+ if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
+ self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
+ if (!SDPUtil.find_line(self.remoteSDP.media[i], 'a=ice-ufrag:')) {
+ var tmp = $(this).find('transport');
+ self.remoteSDP.media[i] += 'a=ice-ufrag:' + tmp.attr('ufrag') + '\r\n';
+ self.remoteSDP.media[i] += 'a=ice-pwd:' + tmp.attr('pwd') + '\r\n';
+ tmp = $(this).find('transport>fingerprint');
+ if (tmp.length) {
+ self.remoteSDP.media[i] += 'a=fingerprint:' + tmp.attr('hash') + ' ' + tmp.text() + '\r\n';
+ } else {
+ console.log('no dtls fingerprint (webrtc issue #1718?)');
+ self.remoteSDP.media[i] += 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:BAADBAADBAADBAADBAADBAADBAADBAADBAADBAAD\r\n';
+ }
+ break;
+ }
+ }
+ }
+ });
+ this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
+
+ // we need a complete SDP with ice-ufrag/ice-pwd in all parts
+ // this makes the assumption that the PRANSWER is constructed such that the ice-ufrag is in all mediaparts
+ // but it could be in the session part as well. since the code above constructs this sdp this can't happen however
+ var iscomplete = this.remoteSDP.media.filter(function (mediapart) {
+ return SDPUtil.find_line(mediapart, 'a=ice-ufrag:');
+ }).length == this.remoteSDP.media.length;
+
+ if (iscomplete) {
+ console.log('setting pranswer');
+ try {
+ this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'pranswer', sdp: this.remoteSDP.raw }),
+ function() {
+ },
+ function(e) {
+ console.log('setRemoteDescription pranswer failed', e.toString());
+ });
+ } catch (e) {
+ console.error('setting pranswer failed', e);
+ }
+ } else {
+ //console.log('not yet setting pranswer');
+ }
+ }
+ // operate on each content element
+ elem.each(function () {
+ // would love to deactivate this, but firefox still requires it
+ var idx = -1;
+ var i;
+ for (i = 0; i < self.remoteSDP.media.length; i++) {
+ if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
+ self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1) { // fall back to localdescription
+ for (i = 0; i < self.localSDP.media.length; i++) {
+ if (SDPUtil.find_line(self.localSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
+ self.localSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
+ idx = i;
+ break;
+ }
+ }
+ }
+ var name = $(this).attr('name');
+ // TODO: check ice-pwd and ice-ufrag?
+ $(this).find('transport>candidate').each(function () {
+ var line, candidate;
+ line = SDPUtil.candidateFromJingle(this);
+ candidate = new RTCIceCandidate({sdpMLineIndex: idx,
+ sdpMid: name,
+ candidate: line});
+ try {
+ self.peerconnection.addIceCandidate(candidate);
+ } catch (e) {
+ console.error('addIceCandidate failed', e.toString(), line);
+ }
+ });
+ });
+};
+
+JingleSession.prototype.sendAnswer = function (provisional) {
+ //console.log('createAnswer', provisional);
+ var self = this;
+ this.peerconnection.createAnswer(
+ function (sdp) {
+ self.createdAnswer(sdp, provisional);
+ },
+ function (e) {
+ console.error('createAnswer failed', e);
+ },
+ this.media_constraints
+ );
+};
+
+JingleSession.prototype.createdAnswer = function (sdp, provisional) {
+ //console.log('createAnswer callback');
+ var self = this;
+ this.localSDP = new SDP(sdp.sdp);
+ //this.localSDP.mangle();
+ this.usepranswer = provisional === true;
+ if (this.usetrickle) {
+ if (this.usepranswer) {
+ sdp.type = 'pranswer';
+ for (var i = 0; i < this.localSDP.media.length; i++) {
+ this.localSDP.media[i] = this.localSDP.media[i].replace('a=sendrecv\r\n', 'a=inactive\r\n');
+ }
+ this.localSDP.raw = this.localSDP.session + '\r\n' + this.localSDP.media.join('');
+ }
+ }
+ var self = this;
+ var sendJingle = function (ssrcs) {
+
+ var accept = $iq({to: self.peerjid,
+ type: 'set'})
+ .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
+ action: 'session-accept',
+ initiator: self.initiator,
+ responder: self.responder,
+ sid: self.sid });
+ var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
+ var publicLocalSDP = new SDP(publicLocalDesc.sdp);
+ publicLocalSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
+ self.connection.sendIQ(accept,
+ function () {
+ var ack = {};
+ ack.source = 'answer';
+ $(document).trigger('ack.jingle', [self.sid, ack]);
+ },
+ function (stanza) {
+ var error = ($(stanza).find('error').length) ? {
+ code: $(stanza).find('error').attr('code'),
+ reason: $(stanza).find('error :first')[0].tagName,
+ }:{};
+ error.source = 'answer';
+ JingleSession.onJingleError(self.sid, error);
+ },
+ 10000);
+ }
+ sdp.sdp = this.localSDP.raw;
+ this.peerconnection.setLocalDescription(sdp,
+ function () {
+
+ //console.log('setLocalDescription success');
+ if (self.usetrickle && !self.usepranswer) {
+ sendJingle();
+ }
+ self.setLocalDescription();
+ },
+ function (e) {
+ console.error('setLocalDescription failed', e);
+ }
+ );
+ var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
+ for (var j = 0; j < cands.length; j++) {
+ var cand = SDPUtil.parse_icecandidate(cands[j]);
+ if (cand.type == 'srflx') {
+ this.hadstuncandidate = true;
+ } else if (cand.type == 'relay') {
+ this.hadturncandidate = true;
+ }
+ }
+};
+
+JingleSession.prototype.sendTerminate = function (reason, text) {
+ var self = this,
+ term = $iq({to: this.peerjid,
+ type: 'set'})
+ .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
+ action: 'session-terminate',
+ initiator: this.initiator,
+ sid: this.sid})
+ .c('reason')
+ .c(reason || 'success');
+
+ if (text) {
+ term.up().c('text').t(text);
+ }
+
+ this.connection.sendIQ(term,
+ function () {
+ self.peerconnection.close();
+ self.peerconnection = null;
+ self.terminate();
+ var ack = {};
+ ack.source = 'terminate';
+ $(document).trigger('ack.jingle', [self.sid, ack]);
+ },
+ function (stanza) {
+ var error = ($(stanza).find('error').length) ? {
+ code: $(stanza).find('error').attr('code'),
+ reason: $(stanza).find('error :first')[0].tagName,
+ }:{};
+ $(document).trigger('ack.jingle', [self.sid, error]);
+ },
+ 10000);
+ if (this.statsinterval !== null) {
+ window.clearInterval(this.statsinterval);
+ this.statsinterval = null;
+ }
+};
+
+JingleSession.prototype.addSource = function (elem, fromJid) {
+
+ var self = this;
+ // FIXME: dirty waiting
+ if (!this.peerconnection.localDescription)
+ {
+ console.warn("addSource - localDescription not ready yet")
+ setTimeout(function()
+ {
+ self.addSource(elem, fromJid);
+ },
+ 200
+ );
+ return;
+ }
+
+ console.log('addssrc', new Date().getTime());
+ console.log('ice', this.peerconnection.iceConnectionState);
+ var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
+ var mySdp = new SDP(this.peerconnection.localDescription.sdp);
+
+ $(elem).each(function (idx, content) {
+ var name = $(content).attr('name');
+ var lines = '';
+ tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
+ var semantics = this.getAttribute('semantics');
+ var ssrcs = $(this).find('>source').map(function () {
+ return this.getAttribute('ssrc');
+ }).get();
+
+ if (ssrcs.length != 0) {
+ lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
+ }
+ });
+ tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
+ tmp.each(function () {
+ var ssrc = $(this).attr('ssrc');
+ if(mySdp.containsSSRC(ssrc)){
+ /**
+ * This happens when multiple participants change their streams at the same time and
+ * ColibriFocus.modifySources have to wait for stable state. In the meantime multiple
+ * addssrc are scheduled for update IQ. See
+ */
+ console.warn("Got add stream request for my own ssrc: "+ssrc);
+ return;
+ }
+ $(this).find('>parameter').each(function () {
+ lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name');
+ if ($(this).attr('value') && $(this).attr('value').length)
+ lines += ':' + $(this).attr('value');
+ lines += '\r\n';
+ });
+ });
+ sdp.media.forEach(function(media, idx) {
+ if (!SDPUtil.find_line(media, 'a=mid:' + name))
+ return;
+ sdp.media[idx] += lines;
+ if (!self.addssrc[idx]) self.addssrc[idx] = '';
+ self.addssrc[idx] += lines;
+ });
+ sdp.raw = sdp.session + sdp.media.join('');
+ });
+ this.modifySources();
+};
+
+JingleSession.prototype.removeSource = function (elem, fromJid) {
+
+ var self = this;
+ // FIXME: dirty waiting
+ if (!this.peerconnection.localDescription)
+ {
+ console.warn("removeSource - localDescription not ready yet")
+ setTimeout(function()
+ {
+ self.removeSource(elem, fromJid);
+ },
+ 200
+ );
+ return;
+ }
+
+ console.log('removessrc', new Date().getTime());
+ console.log('ice', this.peerconnection.iceConnectionState);
+ var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
+ var mySdp = new SDP(this.peerconnection.localDescription.sdp);
+
+ $(elem).each(function (idx, content) {
+ var name = $(content).attr('name');
+ var lines = '';
+ tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
+ var semantics = this.getAttribute('semantics');
+ var ssrcs = $(this).find('>source').map(function () {
+ return this.getAttribute('ssrc');
+ }).get();
+
+ if (ssrcs.length != 0) {
+ lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
+ }
+ });
+ tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
+ tmp.each(function () {
+ var ssrc = $(this).attr('ssrc');
+ // This should never happen, but can be useful for bug detection
+ if(mySdp.containsSSRC(ssrc)){
+ console.error("Got remove stream request for my own ssrc: "+ssrc);
+ return;
+ }
+ $(this).find('>parameter').each(function () {
+ lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name');
+ if ($(this).attr('value') && $(this).attr('value').length)
+ lines += ':' + $(this).attr('value');
+ lines += '\r\n';
+ });
+ });
+ sdp.media.forEach(function(media, idx) {
+ if (!SDPUtil.find_line(media, 'a=mid:' + name))
+ return;
+ sdp.media[idx] += lines;
+ if (!self.removessrc[idx]) self.removessrc[idx] = '';
+ self.removessrc[idx] += lines;
+ });
+ sdp.raw = sdp.session + sdp.media.join('');
+ });
+ this.modifySources();
+};
+
+JingleSession.prototype.modifySources = function (successCallback) {
+ var self = this;
+ if (this.peerconnection.signalingState == 'closed') return;
+ if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){
+ // There is nothing to do since scheduled job might have been executed by another succeeding call
+ this.setLocalDescription();
+ if(successCallback){
+ successCallback();
+ }
+ return;
+ }
+
+ // FIXME: this is a big hack
+ // https://code.google.com/p/webrtc/issues/detail?id=2688
+ // ^ has been fixed.
+ if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) {
+ console.warn('modifySources not yet', this.peerconnection.signalingState, this.peerconnection.iceConnectionState);
+ this.wait = true;
+ window.setTimeout(function() { self.modifySources(successCallback); }, 250);
+ return;
+ }
+ if (this.wait) {
+ window.setTimeout(function() { self.modifySources(successCallback); }, 2500);
+ this.wait = false;
+ return;
+ }
+
+ // Reset switch streams flag
+ this.switchstreams = false;
+
+ var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
+
+ // add sources
+ this.addssrc.forEach(function(lines, idx) {
+ sdp.media[idx] += lines;
+ });
+ this.addssrc = [];
+
+ // remove sources
+ this.removessrc.forEach(function(lines, idx) {
+ lines = lines.split('\r\n');
+ lines.pop(); // remove empty last element;
+ lines.forEach(function(line) {
+ sdp.media[idx] = sdp.media[idx].replace(line + '\r\n', '');
+ });
+ });
+ this.removessrc = [];
+
+ // FIXME:
+ // this was a hack for the situation when only one peer exists
+ // in the conference.
+ // check if still required and remove
+ if (sdp.media[0])
+ sdp.media[0] = sdp.media[0].replace('a=recvonly', 'a=sendrecv');
+ if (sdp.media[1])
+ sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
+
+ sdp.raw = sdp.session + sdp.media.join('');
+ this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}),
+ function() {
+
+ if(self.signalingState == 'closed') {
+ console.error("createAnswer attempt on closed state");
+ return;
+ }
+
+ self.peerconnection.createAnswer(
+ function(modifiedAnswer) {
+ // change video direction, see https://github.com/jitsi/jitmeet/issues/41
+ if (self.pendingop !== null) {
+ var sdp = new SDP(modifiedAnswer.sdp);
+ if (sdp.media.length > 1) {
+ switch(self.pendingop) {
+ case 'mute':
+ sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
+ break;
+ case 'unmute':
+ sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
+ break;
+ }
+ sdp.raw = sdp.session + sdp.media.join('');
+ modifiedAnswer.sdp = sdp.raw;
+ }
+ self.pendingop = null;
+ }
+
+ // FIXME: pushing down an answer while ice connection state
+ // is still checking is bad...
+ //console.log(self.peerconnection.iceConnectionState);
+
+ // trying to work around another chrome bug
+ //modifiedAnswer.sdp = modifiedAnswer.sdp.replace(/a=setup:active/g, 'a=setup:actpass');
+ self.peerconnection.setLocalDescription(modifiedAnswer,
+ function() {
+ //console.log('modified setLocalDescription ok');
+ self.setLocalDescription();
+ if(successCallback){
+ successCallback();
+ }
+ },
+ function(error) {
+ console.error('modified setLocalDescription failed', error);
+ }
+ );
+ },
+ function(error) {
+ console.error('modified answer failed', error);
+ }
+ );
+ },
+ function(error) {
+ console.error('modify failed', error);
+ }
+ );
+};
+
+/**
+ * Switches video streams.
+ * @param new_stream new stream that will be used as video of this session.
+ * @param oldStream old video stream of this session.
+ * @param success_callback callback executed after successful stream switch.
+ */
+JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
+
+ var self = this;
+
+ // Remember SDP to figure out added/removed SSRCs
+ var oldSdp = null;
+ if(self.peerconnection) {
+ if(self.peerconnection.localDescription) {
+ oldSdp = new SDP(self.peerconnection.localDescription.sdp);
+ }
+ self.peerconnection.removeStream(oldStream, true);
+ self.peerconnection.addStream(new_stream);
+ }
+
+ RTC.switchVideoStreams(new_stream, oldStream);
+
+ // Conference is not active
+ if(!oldSdp || !self.peerconnection) {
+ success_callback();
+ return;
+ }
+
+ self.switchstreams = true;
+ self.modifySources(function() {
+ console.log('modify sources done');
+
+ success_callback();
+
+ var newSdp = new SDP(self.peerconnection.localDescription.sdp);
+ console.log("SDPs", oldSdp, newSdp);
+ self.notifyMySSRCUpdate(oldSdp, newSdp);
+ });
+};
+
+/**
+ * Figures out added/removed ssrcs and send update IQs.
+ * @param old_sdp SDP object for old description.
+ * @param new_sdp SDP object for new description.
+ */
+JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
+
+ if (!(this.peerconnection.signalingState == 'stable' &&
+ this.peerconnection.iceConnectionState == 'connected')){
+ console.log("Too early to send updates");
+ return;
+ }
+
+ // send source-remove IQ.
+ sdpDiffer = new SDPDiffer(new_sdp, old_sdp);
+ var remove = $iq({to: this.peerjid, type: 'set'})
+ .c('jingle', {
+ xmlns: 'urn:xmpp:jingle:1',
+ action: 'source-remove',
+ initiator: this.initiator,
+ sid: this.sid
+ }
+ );
+ var removed = sdpDiffer.toJingle(remove);
+ if (removed) {
+ this.connection.sendIQ(remove,
+ function (res) {
+ console.info('got remove result', res);
+ },
+ function (err) {
+ console.error('got remove error', err);
+ }
+ );
+ } else {
+ console.log('removal not necessary');
+ }
+
+ // send source-add IQ.
+ var sdpDiffer = new SDPDiffer(old_sdp, new_sdp);
+ var add = $iq({to: this.peerjid, type: 'set'})
+ .c('jingle', {
+ xmlns: 'urn:xmpp:jingle:1',
+ action: 'source-add',
+ initiator: this.initiator,
+ sid: this.sid
+ }
+ );
+ var added = sdpDiffer.toJingle(add);
+ if (added) {
+ this.connection.sendIQ(add,
+ function (res) {
+ console.info('got add result', res);
+ },
+ function (err) {
+ console.error('got add error', err);
+ }
+ );
+ } else {
+ console.log('addition not necessary');
+ }
+};
+
+/**
+ * Determines whether the (local) video is mute i.e. all video tracks are
+ * disabled.
+ *
+ * @return true if the (local) video is mute i.e. all video tracks are
+ * disabled; otherwise, false
+ */
+JingleSession.prototype.isVideoMute = function () {
+ var tracks = RTC.localVideo.getVideoTracks();
+ var mute = true;
+
+ for (var i = 0; i < tracks.length; ++i) {
+ if (tracks[i].enabled) {
+ mute = false;
+ break;
+ }
+ }
+ return mute;
+};
+
+/**
+ * Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
+ *
+ * @param mute true to mute the (local) video i.e. to disable all video
+ * tracks; otherwise, false
+ * @param callback a function to be invoked with mute after all video
+ * tracks have been enabled/disabled. The function may, optionally, return
+ * another function which is to be invoked after the whole mute/unmute operation
+ * has completed successfully.
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision made by the application logic)
+ */
+JingleSession.prototype.setVideoMute = function (mute, callback, options) {
+ var byUser;
+
+ if (options) {
+ byUser = options.byUser;
+ if (typeof byUser === 'undefined') {
+ byUser = true;
+ }
+ } else {
+ byUser = true;
+ }
+ // The user's command to mute the (local) video takes precedence over any
+ // automatic decision made by the application logic.
+ if (byUser) {
+ this.videoMuteByUser = mute;
+ } else if (this.videoMuteByUser) {
+ return;
+ }
+
+ var self = this;
+ var localCallback = function (mute) {
+ self.connection.emuc.addVideoInfoToPresence(mute);
+ self.connection.emuc.sendPresence();
+ return callback(mute)
+ };
+
+ if (mute == RTC.localVideo.isMuted())
+ {
+ // Even if no change occurs, the specified callback is to be executed.
+ // The specified callback may, optionally, return a successCallback
+ // which is to be executed as well.
+ var successCallback = localCallback(mute);
+
+ if (successCallback) {
+ successCallback();
+ }
+ } else {
+ RTC.localVideo.setMute(!mute);
+
+ this.hardMuteVideo(mute);
+
+ this.modifySources(localCallback(mute));
+ }
+};
+
+// SDP-based mute by going recvonly/sendrecv
+// FIXME: should probably black out the screen as well
+JingleSession.prototype.toggleVideoMute = function (callback) {
+ this.service.setVideoMute(RTC.localVideo.isMuted(), callback);
+};
+
+JingleSession.prototype.hardMuteVideo = function (muted) {
+ this.pendingop = muted ? 'mute' : 'unmute';
+};
+
+JingleSession.prototype.sendMute = function (muted, content) {
+ var info = $iq({to: this.peerjid,
+ type: 'set'})
+ .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
+ action: 'session-info',
+ initiator: this.initiator,
+ sid: this.sid });
+ info.c(muted ? 'mute' : 'unmute', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'});
+ info.attrs({'creator': this.me == this.initiator ? 'creator' : 'responder'});
+ if (content) {
+ info.attrs({'name': content});
+ }
+ this.connection.send(info);
+};
+
+JingleSession.prototype.sendRinging = function () {
+ var info = $iq({to: this.peerjid,
+ type: 'set'})
+ .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
+ action: 'session-info',
+ initiator: this.initiator,
+ sid: this.sid });
+ info.c('ringing', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'});
+ this.connection.send(info);
+};
+
+JingleSession.prototype.getStats = function (interval) {
+ var self = this;
+ var recv = {audio: 0, video: 0};
+ var lost = {audio: 0, video: 0};
+ var lastrecv = {audio: 0, video: 0};
+ var lastlost = {audio: 0, video: 0};
+ var loss = {audio: 0, video: 0};
+ var delta = {audio: 0, video: 0};
+ this.statsinterval = window.setInterval(function () {
+ if (self && self.peerconnection && self.peerconnection.getStats) {
+ self.peerconnection.getStats(function (stats) {
+ var results = stats.result();
+ // TODO: there are so much statistics you can get from this..
+ for (var i = 0; i < results.length; ++i) {
+ if (results[i].type == 'ssrc') {
+ var packetsrecv = results[i].stat('packetsReceived');
+ var packetslost = results[i].stat('packetsLost');
+ if (packetsrecv && packetslost) {
+ packetsrecv = parseInt(packetsrecv, 10);
+ packetslost = parseInt(packetslost, 10);
+
+ if (results[i].stat('googFrameRateReceived')) {
+ lastlost.video = lost.video;
+ lastrecv.video = recv.video;
+ recv.video = packetsrecv;
+ lost.video = packetslost;
+ } else {
+ lastlost.audio = lost.audio;
+ lastrecv.audio = recv.audio;
+ recv.audio = packetsrecv;
+ lost.audio = packetslost;
+ }
+ }
+ }
+ }
+ delta.audio = recv.audio - lastrecv.audio;
+ delta.video = recv.video - lastrecv.video;
+ loss.audio = (delta.audio > 0) ? Math.ceil(100 * (lost.audio - lastlost.audio) / delta.audio) : 0;
+ loss.video = (delta.video > 0) ? Math.ceil(100 * (lost.video - lastlost.video) / delta.video) : 0;
+ $(document).trigger('packetloss.jingle', [self.sid, loss]);
+ });
+ }
+ }, interval || 3000);
+ return this.statsinterval;
+};
+
+JingleSession.onJingleError = function (session, error)
+{
+ console.error("Jingle error", error);
+}
+
+JingleSession.onJingleFatalError = function (session, error)
+{
+ this.service.sessionTerminated = true;
+ connection.emuc.doLeave();
+ UI.messageHandler.showError( "Sorry",
+ "Internal application error[setRemoteDescription]");
+}
+
+JingleSession.prototype.setLocalDescription = function () {
+ // put our ssrcs into presence so other clients can identify our stream
+ var newssrcs = [];
+ var media = simulcast.parseMedia(this.peerconnection.localDescription);
+ media.forEach(function (media) {
+
+ if(Object.keys(media.sources).length > 0) {
+ // TODO(gp) maybe exclude FID streams?
+ Object.keys(media.sources).forEach(function (ssrc) {
+ newssrcs.push({
+ 'ssrc': ssrc,
+ 'type': media.type,
+ 'direction': media.direction
+ });
+ });
+ }
+ else if(this.localStreamsSSRC && this.localStreamsSSRC[media.type])
+ {
+ newssrcs.push({
+ 'ssrc': this.localStreamsSSRC[media.type],
+ 'type': media.type,
+ 'direction': media.direction
+ });
+ }
+
+ });
+
+ console.log('new ssrcs', newssrcs);
+
+ // Have to clear presence map to get rid of removed streams
+ this.connection.emuc.clearPresenceMedia();
+
+ if (newssrcs.length > 0) {
+ for (var i = 1; i <= newssrcs.length; i ++) {
+ // Change video type to screen
+ if (newssrcs[i-1].type === 'video' && desktopsharing.isUsingScreenStream()) {
+ newssrcs[i-1].type = 'screen';
+ }
+ this.connection.emuc.addMediaToPresence(i,
+ newssrcs[i-1].type, newssrcs[i-1].ssrc, newssrcs[i-1].direction);
+ }
+
+ this.connection.emuc.sendPresence();
+ }
+}
+
+// an attempt to work around https://github.com/jitsi/jitmeet/issues/32
+function sendKeyframe(pc) {
+ console.log('sendkeyframe', pc.iceConnectionState);
+ if (pc.iceConnectionState !== 'connected') return; // safe...
+ pc.setRemoteDescription(
+ pc.remoteDescription,
+ function () {
+ pc.createAnswer(
+ function (modifiedAnswer) {
+ pc.setLocalDescription(
+ modifiedAnswer,
+ function () {
+ // noop
+ },
+ function (error) {
+ console.log('triggerKeyframe setLocalDescription failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe createAnswer failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe setRemoteDescription failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+}
+
+
+JingleSession.prototype.remoteStreamAdded = function (data) {
+ var self = this;
+ var thessrc;
+
+ // look up an associated JID for a stream id
+ if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
+ // look only at a=ssrc: and _not_ at a=ssrc-group: lines
+
+ var ssrclines
+ = SDPUtil.find_lines(this.peerconnection.remoteDescription.sdp, 'a=ssrc:');
+ ssrclines = ssrclines.filter(function (line) {
+ // NOTE(gp) previously we filtered on the mslabel, but that property
+ // is not always present.
+ // return line.indexOf('mslabel:' + data.stream.label) !== -1;
+
+ return ((line.indexOf('msid:' + data.stream.id) !== -1));
+ });
+ if (ssrclines.length) {
+ thessrc = ssrclines[0].substring(7).split(' ')[0];
+
+ // We signal our streams (through Jingle to the focus) before we set
+ // our presence (through which peers associate remote streams to
+ // jids). So, it might arrive that a remote stream is added but
+ // ssrc2jid is not yet updated and thus data.peerjid cannot be
+ // successfully set. Here we wait for up to a second for the
+ // presence to arrive.
+
+ if (!ssrc2jid[thessrc]) {
+ // TODO(gp) limit wait duration to 1 sec.
+ setTimeout(function(d) {
+ return function() {
+ self.remoteStreamAdded(d);
+ }
+ }(data), 250);
+ return;
+ }
+
+ // ok to overwrite the one from focus? might save work in colibri.js
+ console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
+ if (ssrc2jid[thessrc]) {
+ data.peerjid = ssrc2jid[thessrc];
+ }
+ }
+ }
+
+ //TODO: this code should be removed when firefox implement multistream support
+ if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
+ {
+ if((notReceivedSSRCs.length == 0) ||
+ !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
+ {
+ // TODO(gp) limit wait duration to 1 sec.
+ setTimeout(function(d) {
+ return function() {
+ self.remoteStreamAdded(d);
+ }
+ }(data), 250);
+ return;
+ }
+
+ thessrc = notReceivedSSRCs.pop();
+ if (ssrc2jid[thessrc]) {
+ data.peerjid = ssrc2jid[thessrc];
+ }
+ }
+
+ RTC.createRemoteStream(data, this.sid, thessrc);
+
+ var isVideo = data.stream.getVideoTracks().length > 0;
+ // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
+ if (isVideo &&
+ data.peerjid && this.peerjid === data.peerjid &&
+ data.stream.getVideoTracks().length === 0 &&
+ RTC.localVideo.getTracks().length > 0) {
+ window.setTimeout(function () {
+ sendKeyframe(self.peerconnection);
+ }, 3000);
+ }
+}
+
+module.exports = JingleSession;
+},{"./SDP":2,"./SDPDiffer":3,"./SDPUtil":4,"./TraceablePeerConnection":5}],2:[function(require,module,exports){
+/* jshint -W117 */
+var SDPUtil = require("./SDPUtil");
+
+// SDP STUFF
+function SDP(sdp) {
+ this.media = sdp.split('\r\nm=');
+ for (var i = 1; i < this.media.length; i++) {
+ this.media[i] = 'm=' + this.media[i];
+ if (i != this.media.length - 1) {
+ this.media[i] += '\r\n';
+ }
+ }
+ this.session = this.media.shift() + '\r\n';
+ this.raw = this.session + this.media.join('');
+}
+/**
+ * Returns map of MediaChannel mapped per channel idx.
+ */
+SDP.prototype.getMediaSsrcMap = function() {
+ var self = this;
+ var media_ssrcs = {};
+ var tmp;
+ for (var mediaindex = 0; mediaindex < self.media.length; mediaindex++) {
+ tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:');
+ var mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:'));
+ var media = {
+ mediaindex: mediaindex,
+ mid: mid,
+ ssrcs: {},
+ ssrcGroups: []
+ };
+ media_ssrcs[mediaindex] = media;
+ tmp.forEach(function (line) {
+ var linessrc = line.substring(7).split(' ')[0];
+ // allocate new ChannelSsrc
+ if(!media.ssrcs[linessrc]) {
+ media.ssrcs[linessrc] = {
+ ssrc: linessrc,
+ lines: []
+ };
+ }
+ media.ssrcs[linessrc].lines.push(line);
+ });
+ tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc-group:');
+ tmp.forEach(function(line){
+ var semantics = line.substr(0, idx).substr(13);
+ var ssrcs = line.substr(14 + semantics.length).split(' ');
+ if (ssrcs.length != 0) {
+ media.ssrcGroups.push({
+ semantics: semantics,
+ ssrcs: ssrcs
+ });
+ }
+ });
+ }
+ return media_ssrcs;
+};
+/**
+ * Returns true if this SDP contains given SSRC.
+ * @param ssrc the ssrc to check.
+ * @returns {boolean} true if this SDP contains given SSRC.
+ */
+SDP.prototype.containsSSRC = function(ssrc) {
+ var medias = this.getMediaSsrcMap();
+ var contains = false;
+ Object.keys(medias).forEach(function(mediaindex){
+ var media = medias[mediaindex];
+ //console.log("Check", channel, ssrc);
+ if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){
+ contains = true;
+ }
+ });
+ return contains;
+};
+
+
+// remove iSAC and CN from SDP
+SDP.prototype.mangle = function () {
+ var i, j, mline, lines, rtpmap, newdesc;
+ for (i = 0; i < this.media.length; i++) {
+ lines = this.media[i].split('\r\n');
+ lines.pop(); // remove empty last element
+ mline = SDPUtil.parse_mline(lines.shift());
+ if (mline.media != 'audio')
+ continue;
+ newdesc = '';
+ mline.fmt.length = 0;
+ for (j = 0; j < lines.length; j++) {
+ if (lines[j].substr(0, 9) == 'a=rtpmap:') {
+ rtpmap = SDPUtil.parse_rtpmap(lines[j]);
+ if (rtpmap.name == 'CN' || rtpmap.name == 'ISAC')
+ continue;
+ mline.fmt.push(rtpmap.id);
+ newdesc += lines[j] + '\r\n';
+ } else {
+ newdesc += lines[j] + '\r\n';
+ }
+ }
+ this.media[i] = SDPUtil.build_mline(mline) + '\r\n';
+ this.media[i] += newdesc;
+ }
+ this.raw = this.session + this.media.join('');
+};
+
+// remove lines matching prefix from session section
+SDP.prototype.removeSessionLines = function(prefix) {
+ var self = this;
+ var lines = SDPUtil.find_lines(this.session, prefix);
+ lines.forEach(function(line) {
+ self.session = self.session.replace(line + '\r\n', '');
+ });
+ this.raw = this.session + this.media.join('');
+ return lines;
+}
+// remove lines matching prefix from a media section specified by mediaindex
+// TODO: non-numeric mediaindex could match mid
+SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
+ var self = this;
+ var lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
+ lines.forEach(function(line) {
+ self.media[mediaindex] = self.media[mediaindex].replace(line + '\r\n', '');
+ });
+ this.raw = this.session + this.media.join('');
+ return lines;
+}
+
+// add content's to a jingle element
+SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
+// console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]);
+ var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
+ var self = this;
+ // new bundle plan
+ if (SDPUtil.find_line(this.session, 'a=group:')) {
+ lines = SDPUtil.find_lines(this.session, 'a=group:');
+ for (i = 0; i < lines.length; i++) {
+ tmp = lines[i].split(' ');
+ var semantics = tmp.shift().substr(8);
+ elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics:semantics});
+ for (j = 0; j < tmp.length; j++) {
+ elem.c('content', {name: tmp[j]}).up();
+ }
+ elem.up();
+ }
+ }
+ for (i = 0; i < this.media.length; i++) {
+ mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
+ if (!(mline.media === 'audio' ||
+ mline.media === 'video' ||
+ mline.media === 'application'))
+ {
+ continue;
+ }
+ if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
+ ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
+ } else {
+ if(ssrcs && ssrcs[mline.media])
+ {
+ ssrc = ssrcs[mline.media];
+ }
+ else
+ ssrc = false;
+ }
+
+ elem.c('content', {creator: thecreator, name: mline.media});
+ if (SDPUtil.find_line(this.media[i], 'a=mid:')) {
+ // prefer identifier from a=mid if present
+ var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:'));
+ elem.attrs({ name: mid });
+ }
+
+ if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
+ {
+ elem.c('description',
+ {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
+ media: mline.media });
+ if (ssrc) {
+ elem.attrs({ssrc: ssrc});
+ }
+ for (j = 0; j < mline.fmt.length; j++) {
+ rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]);
+ elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
+ // put any 'a=fmtp:' + mline.fmt[j] lines into
+ if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) {
+ tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]));
+ for (k = 0; k < tmp.length; k++) {
+ elem.c('parameter', tmp[k]).up();
+ }
+ }
+ this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
+
+ elem.up();
+ }
+ if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) {
+ elem.c('encryption', {required: 1});
+ var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
+ crypto.forEach(function(line) {
+ elem.c('crypto', SDPUtil.parse_crypto(line)).up();
+ });
+ elem.up(); // end of encryption
+ }
+
+ if (ssrc) {
+ // new style mapping
+ elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ // FIXME: group by ssrc and support multiple different ssrcs
+ var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
+ if(ssrclines.length > 0) {
+ ssrclines.forEach(function (line) {
+ idx = line.indexOf(' ');
+ var linessrc = line.substr(0, idx).substr(7);
+ if (linessrc != ssrc) {
+ elem.up();
+ ssrc = linessrc;
+ elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ }
+ var kv = line.substr(idx + 1);
+ elem.c('parameter');
+ if (kv.indexOf(':') == -1) {
+ elem.attrs({ name: kv });
+ } else {
+ elem.attrs({ name: kv.split(':', 2)[0] });
+ elem.attrs({ value: kv.split(':', 2)[1] });
+ }
+ elem.up();
+ });
+ elem.up();
+ }
+ else
+ {
+ elem.up();
+ elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ elem.c('parameter');
+ elem.attrs({name: "cname", value:Math.random().toString(36).substring(7)});
+ elem.up();
+ var msid = null;
+ if(mline.media == "audio")
+ {
+ msid = RTC.localAudio.getId();
+ }
+ else
+ {
+ msid = RTC.localVideo.getId();
+ }
+ if(msid != null)
+ {
+ msid = msid.replace(/[\{,\}]/g,"");
+ elem.c('parameter');
+ elem.attrs({name: "msid", value:msid});
+ elem.up();
+ elem.c('parameter');
+ elem.attrs({name: "mslabel", value:msid});
+ elem.up();
+ elem.c('parameter');
+ elem.attrs({name: "label", value:msid});
+ elem.up();
+ elem.up();
+ }
+
+
+ }
+
+ // XEP-0339 handle ssrc-group attributes
+ var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
+ ssrc_group_lines.forEach(function(line) {
+ idx = line.indexOf(' ');
+ var semantics = line.substr(0, idx).substr(13);
+ var ssrcs = line.substr(14 + semantics.length).split(' ');
+ if (ssrcs.length != 0) {
+ elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ ssrcs.forEach(function(ssrc) {
+ elem.c('source', { ssrc: ssrc })
+ .up();
+ });
+ elem.up();
+ }
+ });
+ }
+
+ if (SDPUtil.find_line(this.media[i], 'a=rtcp-mux')) {
+ elem.c('rtcp-mux').up();
+ }
+
+ // XEP-0293 -- map a=rtcp-fb:*
+ this.RtcpFbToJingle(i, elem, '*');
+
+ // XEP-0294
+ if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
+ lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
+ for (j = 0; j < lines.length; j++) {
+ tmp = SDPUtil.parse_extmap(lines[j]);
+ elem.c('rtp-hdrext', { xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',
+ uri: tmp.uri,
+ id: tmp.value });
+ if (tmp.hasOwnProperty('direction')) {
+ switch (tmp.direction) {
+ case 'sendonly':
+ elem.attrs({senders: 'responder'});
+ break;
+ case 'recvonly':
+ elem.attrs({senders: 'initiator'});
+ break;
+ case 'sendrecv':
+ elem.attrs({senders: 'both'});
+ break;
+ case 'inactive':
+ elem.attrs({senders: 'none'});
+ break;
+ }
+ }
+ // TODO: handle params
+ elem.up();
+ }
+ }
+ elem.up(); // end of description
+ }
+
+ // map ice-ufrag/pwd, dtls fingerprint, candidates
+ this.TransportToJingle(i, elem);
+
+ if (SDPUtil.find_line(this.media[i], 'a=sendrecv', this.session)) {
+ elem.attrs({senders: 'both'});
+ } else if (SDPUtil.find_line(this.media[i], 'a=sendonly', this.session)) {
+ elem.attrs({senders: 'initiator'});
+ } else if (SDPUtil.find_line(this.media[i], 'a=recvonly', this.session)) {
+ elem.attrs({senders: 'responder'});
+ } else if (SDPUtil.find_line(this.media[i], 'a=inactive', this.session)) {
+ elem.attrs({senders: 'none'});
+ }
+ if (mline.port == '0') {
+ // estos hack to reject an m-line
+ elem.attrs({senders: 'rejected'});
+ }
+ elem.up(); // end of content
+ }
+ elem.up();
+ return elem;
+};
+
+SDP.prototype.TransportToJingle = function (mediaindex, elem) {
+ var i = mediaindex;
+ var tmp;
+ var self = this;
+ elem.c('transport');
+
+ // XEP-0343 DTLS/SCTP
+ if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
+ {
+ var sctpmap = SDPUtil.find_line(
+ this.media[i], 'a=sctpmap:', self.session);
+ if (sctpmap)
+ {
+ var sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
+ elem.c('sctpmap',
+ {
+ xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
+ number: sctpAttrs[0], /* SCTP port */
+ protocol: sctpAttrs[1], /* protocol */
+ });
+ // Optional stream count attribute
+ if (sctpAttrs.length > 2)
+ elem.attrs({ streams: sctpAttrs[2]});
+ elem.up();
+ }
+ }
+ // XEP-0320
+ var fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
+ fingerprints.forEach(function(line) {
+ tmp = SDPUtil.parse_fingerprint(line);
+ tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
+ elem.c('fingerprint').t(tmp.fingerprint);
+ delete tmp.fingerprint;
+ line = SDPUtil.find_line(self.media[mediaindex], 'a=setup:', self.session);
+ if (line) {
+ tmp.setup = line.substr(8);
+ }
+ elem.attrs(tmp);
+ elem.up(); // end of fingerprint
+ });
+ tmp = SDPUtil.iceparams(this.media[mediaindex], this.session);
+ if (tmp) {
+ tmp.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
+ elem.attrs(tmp);
+ // XEP-0176
+ if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
+ var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
+ lines.forEach(function (line) {
+ elem.c('candidate', SDPUtil.candidateToJingle(line)).up();
+ });
+ }
+ }
+ elem.up(); // end of transport
+}
+
+SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
+ var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=rtcp-fb:' + payloadtype);
+ lines.forEach(function (line) {
+ var tmp = SDPUtil.parse_rtcpfb(line);
+ if (tmp.type == 'trr-int') {
+ elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
+ elem.up();
+ } else {
+ elem.c('rtcp-fb', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', type: tmp.type});
+ if (tmp.params.length > 0) {
+ elem.attrs({'subtype': tmp.params[0]});
+ }
+ elem.up();
+ }
+ });
+};
+
+SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
+ var media = '';
+ var tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
+ if (tmp.length) {
+ media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
+ if (tmp.attr('value')) {
+ media += tmp.attr('value');
+ } else {
+ media += '0';
+ }
+ media += '\r\n';
+ }
+ tmp = elem.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
+ tmp.each(function () {
+ media += 'a=rtcp-fb:' + payloadtype + ' ' + $(this).attr('type');
+ if ($(this).attr('subtype')) {
+ media += ' ' + $(this).attr('subtype');
+ }
+ media += '\r\n';
+ });
+ return media;
+};
+
+// construct an SDP from a jingle stanza
+SDP.prototype.fromJingle = function (jingle) {
+ var self = this;
+ this.raw = 'v=0\r\n' +
+ 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
+ 's=-\r\n' +
+ 't=0 0\r\n';
+ // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
+ if ($(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').length) {
+ $(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').each(function (idx, group) {
+ var contents = $(group).find('>content').map(function (idx, content) {
+ return content.getAttribute('name');
+ }).get();
+ if (contents.length > 0) {
+ self.raw += 'a=group:' + (group.getAttribute('semantics') || group.getAttribute('type')) + ' ' + contents.join(' ') + '\r\n';
+ }
+ });
+ }
+
+ this.session = this.raw;
+ jingle.find('>content').each(function () {
+ var m = self.jingle2media($(this));
+ self.media.push(m);
+ });
+
+ // reconstruct msid-semantic -- apparently not necessary
+ /*
+ var msid = SDPUtil.parse_ssrc(this.raw);
+ if (msid.hasOwnProperty('mslabel')) {
+ this.session += "a=msid-semantic: WMS " + msid.mslabel + "\r\n";
+ }
+ */
+
+ this.raw = this.session + this.media.join('');
+};
+
+// translate a jingle content element into an an SDP media part
+SDP.prototype.jingle2media = function (content) {
+ var media = '',
+ desc = content.find('description'),
+ ssrc = desc.attr('ssrc'),
+ self = this,
+ tmp;
+ var sctp = content.find(
+ '>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
+
+ tmp = { media: desc.attr('media') };
+ tmp.port = '1';
+ if (content.attr('senders') == 'rejected') {
+ // estos hack to reject an m-line.
+ tmp.port = '0';
+ }
+ if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
+ if (sctp.length)
+ tmp.proto = 'DTLS/SCTP';
+ else
+ tmp.proto = 'RTP/SAVPF';
+ } else {
+ tmp.proto = 'RTP/AVPF';
+ }
+ if (!sctp.length)
+ {
+ tmp.fmt = desc.find('payload-type').map(
+ function () { return this.getAttribute('id'); }).get();
+ media += SDPUtil.build_mline(tmp) + '\r\n';
+ }
+ else
+ {
+ media += 'm=application 1 DTLS/SCTP ' + sctp.attr('number') + '\r\n';
+ media += 'a=sctpmap:' + sctp.attr('number') +
+ ' ' + sctp.attr('protocol');
+
+ var streamCount = sctp.attr('streams');
+ if (streamCount)
+ media += ' ' + streamCount + '\r\n';
+ else
+ media += '\r\n';
+ }
+
+ media += 'c=IN IP4 0.0.0.0\r\n';
+ if (!sctp.length)
+ media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
+ tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
+ if (tmp.length) {
+ if (tmp.attr('ufrag')) {
+ media += SDPUtil.build_iceufrag(tmp.attr('ufrag')) + '\r\n';
+ }
+ if (tmp.attr('pwd')) {
+ media += SDPUtil.build_icepwd(tmp.attr('pwd')) + '\r\n';
+ }
+ tmp.find('>fingerprint').each(function () {
+ // FIXME: check namespace at some point
+ media += 'a=fingerprint:' + this.getAttribute('hash');
+ media += ' ' + $(this).text();
+ media += '\r\n';
+ if (this.getAttribute('setup')) {
+ media += 'a=setup:' + this.getAttribute('setup') + '\r\n';
+ }
+ });
+ }
+ switch (content.attr('senders')) {
+ case 'initiator':
+ media += 'a=sendonly\r\n';
+ break;
+ case 'responder':
+ media += 'a=recvonly\r\n';
+ break;
+ case 'none':
+ media += 'a=inactive\r\n';
+ break;
+ case 'both':
+ media += 'a=sendrecv\r\n';
+ break;
+ }
+ media += 'a=mid:' + content.attr('name') + '\r\n';
+
+ //
+ // see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec though
+ // and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html
+ if (desc.find('rtcp-mux').length) {
+ media += 'a=rtcp-mux\r\n';
+ }
+
+ if (desc.find('encryption').length) {
+ desc.find('encryption>crypto').each(function () {
+ media += 'a=crypto:' + this.getAttribute('tag');
+ media += ' ' + this.getAttribute('crypto-suite');
+ media += ' ' + this.getAttribute('key-params');
+ if (this.getAttribute('session-params')) {
+ media += ' ' + this.getAttribute('session-params');
+ }
+ media += '\r\n';
+ });
+ }
+ desc.find('payload-type').each(function () {
+ media += SDPUtil.build_rtpmap(this) + '\r\n';
+ if ($(this).find('>parameter').length) {
+ media += 'a=fmtp:' + this.getAttribute('id') + ' ';
+ media += $(this).find('parameter').map(function () { return (this.getAttribute('name') ? (this.getAttribute('name') + '=') : '') + this.getAttribute('value'); }).get().join('; ');
+ media += '\r\n';
+ }
+ // xep-0293
+ media += self.RtcpFbFromJingle($(this), this.getAttribute('id'));
+ });
+
+ // xep-0293
+ media += self.RtcpFbFromJingle(desc, '*');
+
+ // xep-0294
+ tmp = desc.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]');
+ tmp.each(function () {
+ media += 'a=extmap:' + this.getAttribute('id') + ' ' + this.getAttribute('uri') + '\r\n';
+ });
+
+ content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function () {
+ media += SDPUtil.candidateFromJingle(this);
+ });
+
+ // XEP-0339 handle ssrc-group attributes
+ tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
+ var semantics = this.getAttribute('semantics');
+ var ssrcs = $(this).find('>source').map(function() {
+ return this.getAttribute('ssrc');
+ }).get();
+
+ if (ssrcs.length != 0) {
+ media += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
+ }
+ });
+
+ tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
+ tmp.each(function () {
+ var ssrc = this.getAttribute('ssrc');
+ $(this).find('>parameter').each(function () {
+ media += 'a=ssrc:' + ssrc + ' ' + this.getAttribute('name');
+ if (this.getAttribute('value') && this.getAttribute('value').length)
+ media += ':' + this.getAttribute('value');
+ media += '\r\n';
+ });
+ });
+
+ return media;
+};
+
+
+module.exports = SDP;
+
+
+},{"./SDPUtil":4}],3:[function(require,module,exports){
+function SDPDiffer(mySDP, otherSDP) {
+ this.mySDP = mySDP;
+ this.otherSDP = otherSDP;
+}
+
+/**
+ * Returns map of MediaChannel that contains only media not contained in otherSdp. Mapped by channel idx.
+ * @param otherSdp the other SDP to check ssrc with.
+ */
+SDPDiffer.prototype.getNewMedia = function() {
+
+ // this could be useful in Array.prototype.
+ function arrayEquals(array) {
+ // if the other array is a falsy value, return
+ if (!array)
+ return false;
+
+ // compare lengths - can save a lot of time
+ if (this.length != array.length)
+ return false;
+
+ for (var i = 0, l=this.length; i < l; i++) {
+ // Check if we have nested arrays
+ if (this[i] instanceof Array && array[i] instanceof Array) {
+ // recurse into the nested arrays
+ if (!this[i].equals(array[i]))
+ return false;
+ }
+ else if (this[i] != array[i]) {
+ // Warning - two different object instances will never be equal: {x:20} != {x:20}
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var myMedias = this.mySDP.getMediaSsrcMap();
+ var othersMedias = this.otherSDP.getMediaSsrcMap();
+ var newMedia = {};
+ Object.keys(othersMedias).forEach(function(othersMediaIdx) {
+ var myMedia = myMedias[othersMediaIdx];
+ var othersMedia = othersMedias[othersMediaIdx];
+ if(!myMedia && othersMedia) {
+ // Add whole channel
+ newMedia[othersMediaIdx] = othersMedia;
+ return;
+ }
+ // Look for new ssrcs accross the channel
+ Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
+ if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
+ // Allocate channel if we've found ssrc that doesn't exist in our channel
+ if(!newMedia[othersMediaIdx]){
+ newMedia[othersMediaIdx] = {
+ mediaindex: othersMedia.mediaindex,
+ mid: othersMedia.mid,
+ ssrcs: {},
+ ssrcGroups: []
+ };
+ }
+ newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
+ }
+ });
+
+ // Look for new ssrc groups across the channels
+ othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
+
+ // try to match the other ssrc-group with an ssrc-group of ours
+ var matched = false;
+ for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
+ var mySsrcGroup = myMedia.ssrcGroups[i];
+ if (otherSsrcGroup.semantics == mySsrcGroup.semantics
+ && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
+
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ // Allocate channel if we've found an ssrc-group that doesn't
+ // exist in our channel
+
+ if(!newMedia[othersMediaIdx]){
+ newMedia[othersMediaIdx] = {
+ mediaindex: othersMedia.mediaindex,
+ mid: othersMedia.mid,
+ ssrcs: {},
+ ssrcGroups: []
+ };
+ }
+ newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
+ }
+ });
+ });
+ return newMedia;
+};
+
+/**
+ * Sends SSRC update IQ.
+ * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
+ * @param sid session identifier that will be put into the IQ.
+ * @param initiator initiator identifier.
+ * @param toJid destination Jid
+ * @param isAdd indicates if this is remove or add operation.
+ */
+SDPDiffer.prototype.toJingle = function(modify) {
+ var sdpMediaSsrcs = this.getNewMedia();
+ var self = this;
+
+ // FIXME: only announce video ssrcs since we mix audio and dont need
+ // the audio ssrcs therefore
+ var modified = false;
+ Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
+ modified = true;
+ var media = sdpMediaSsrcs[mediaindex];
+ modify.c('content', {name: media.mid});
+
+ modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
+ // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
+ // generate sources from lines
+ Object.keys(media.ssrcs).forEach(function(ssrcNum) {
+ var mediaSsrc = media.ssrcs[ssrcNum];
+ modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ modify.attrs({ssrc: mediaSsrc.ssrc});
+ // iterate over ssrc lines
+ mediaSsrc.lines.forEach(function (line) {
+ var idx = line.indexOf(' ');
+ var kv = line.substr(idx + 1);
+ modify.c('parameter');
+ if (kv.indexOf(':') == -1) {
+ modify.attrs({ name: kv });
+ } else {
+ modify.attrs({ name: kv.split(':', 2)[0] });
+ modify.attrs({ value: kv.split(':', 2)[1] });
+ }
+ modify.up(); // end of parameter
+ });
+ modify.up(); // end of source
+ });
+
+ // generate source groups from lines
+ media.ssrcGroups.forEach(function(ssrcGroup) {
+ if (ssrcGroup.ssrcs.length != 0) {
+
+ modify.c('ssrc-group', {
+ semantics: ssrcGroup.semantics,
+ xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
+ });
+
+ ssrcGroup.ssrcs.forEach(function (ssrc) {
+ modify.c('source', { ssrc: ssrc })
+ .up(); // end of source
+ });
+ modify.up(); // end of ssrc-group
+ }
+ });
+
+ modify.up(); // end of description
+ modify.up(); // end of content
+ });
+
+ return modified;
+};
+
+module.exports = SDPDiffer;
+},{}],4:[function(require,module,exports){
+SDPUtil = {
+ iceparams: function (mediadesc, sessiondesc) {
+ var data = null;
+ if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
+ SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
+ data = {
+ ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
+ pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
+ };
+ }
+ return data;
+ },
+ parse_iceufrag: function (line) {
+ return line.substring(12);
+ },
+ build_iceufrag: function (frag) {
+ return 'a=ice-ufrag:' + frag;
+ },
+ parse_icepwd: function (line) {
+ return line.substring(10);
+ },
+ build_icepwd: function (pwd) {
+ return 'a=ice-pwd:' + pwd;
+ },
+ parse_mid: function (line) {
+ return line.substring(6);
+ },
+ parse_mline: function (line) {
+ var parts = line.substring(2).split(' '),
+ data = {};
+ data.media = parts.shift();
+ data.port = parts.shift();
+ data.proto = parts.shift();
+ if (parts[parts.length - 1] === '') { // trailing whitespace
+ parts.pop();
+ }
+ data.fmt = parts;
+ return data;
+ },
+ build_mline: function (mline) {
+ return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
+ },
+ parse_rtpmap: function (line) {
+ var parts = line.substring(9).split(' '),
+ data = {};
+ data.id = parts.shift();
+ parts = parts[0].split('/');
+ data.name = parts.shift();
+ data.clockrate = parts.shift();
+ data.channels = parts.length ? parts.shift() : '1';
+ return data;
+ },
+ /**
+ * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
+ * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
+ * @returns [SCTP port number, protocol, streams]
+ */
+ parse_sctpmap: function (line)
+ {
+ var parts = line.substring(10).split(' ');
+ var sctpPort = parts[0];
+ var protocol = parts[1];
+ // Stream count is optional
+ var streamCount = parts.length > 2 ? parts[2] : null;
+ return [sctpPort, protocol, streamCount];// SCTP port
+ },
+ build_rtpmap: function (el) {
+ var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
+ if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
+ line += '/' + el.getAttribute('channels');
+ }
+ return line;
+ },
+ parse_crypto: function (line) {
+ var parts = line.substring(9).split(' '),
+ data = {};
+ data.tag = parts.shift();
+ data['crypto-suite'] = parts.shift();
+ data['key-params'] = parts.shift();
+ if (parts.length) {
+ data['session-params'] = parts.join(' ');
+ }
+ return data;
+ },
+ parse_fingerprint: function (line) { // RFC 4572
+ var parts = line.substring(14).split(' '),
+ data = {};
+ data.hash = parts.shift();
+ data.fingerprint = parts.shift();
+ // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
+ return data;
+ },
+ parse_fmtp: function (line) {
+ var parts = line.split(' '),
+ i, key, value,
+ data = [];
+ parts.shift();
+ parts = parts.join(' ').split(';');
+ for (i = 0; i < parts.length; i++) {
+ key = parts[i].split('=')[0];
+ while (key.length && key[0] == ' ') {
+ key = key.substring(1);
+ }
+ value = parts[i].split('=')[1];
+ if (key && value) {
+ data.push({name: key, value: value});
+ } else if (key) {
+ // rfc 4733 (DTMF) style stuff
+ data.push({name: '', value: key});
+ }
+ }
+ return data;
+ },
+ parse_icecandidate: function (line) {
+ var candidate = {},
+ elems = line.split(' ');
+ candidate.foundation = elems[0].substring(12);
+ candidate.component = elems[1];
+ candidate.protocol = elems[2].toLowerCase();
+ candidate.priority = elems[3];
+ candidate.ip = elems[4];
+ candidate.port = elems[5];
+ // elems[6] => "typ"
+ candidate.type = elems[7];
+ candidate.generation = 0; // default value, may be overwritten below
+ for (var i = 8; i < elems.length; i += 2) {
+ switch (elems[i]) {
+ case 'raddr':
+ candidate['rel-addr'] = elems[i + 1];
+ break;
+ case 'rport':
+ candidate['rel-port'] = elems[i + 1];
+ break;
+ case 'generation':
+ candidate.generation = elems[i + 1];
+ break;
+ case 'tcptype':
+ candidate.tcptype = elems[i + 1];
+ break;
+ default: // TODO
+ console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
+ }
+ }
+ candidate.network = '1';
+ candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
+ return candidate;
+ },
+ build_icecandidate: function (cand) {
+ var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
+ line += ' ';
+ switch (cand.type) {
+ case 'srflx':
+ case 'prflx':
+ case 'relay':
+ if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
+ line += 'raddr';
+ line += ' ';
+ line += cand['rel-addr'];
+ line += ' ';
+ line += 'rport';
+ line += ' ';
+ line += cand['rel-port'];
+ line += ' ';
+ }
+ break;
+ }
+ if (cand.hasOwnAttribute('tcptype')) {
+ line += 'tcptype';
+ line += ' ';
+ line += cand.tcptype;
+ line += ' ';
+ }
+ line += 'generation';
+ line += ' ';
+ line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
+ return line;
+ },
+ parse_ssrc: function (desc) {
+ // proprietary mapping of a=ssrc lines
+ // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
+ // and parse according to that
+ var lines = desc.split('\r\n'),
+ data = {};
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, 7) == 'a=ssrc:') {
+ var idx = lines[i].indexOf(' ');
+ data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
+ }
+ }
+ return data;
+ },
+ parse_rtcpfb: function (line) {
+ var parts = line.substr(10).split(' ');
+ var data = {};
+ data.pt = parts.shift();
+ data.type = parts.shift();
+ data.params = parts;
+ return data;
+ },
+ parse_extmap: function (line) {
+ var parts = line.substr(9).split(' ');
+ var data = {};
+ data.value = parts.shift();
+ if (data.value.indexOf('/') != -1) {
+ data.direction = data.value.substr(data.value.indexOf('/') + 1);
+ data.value = data.value.substr(0, data.value.indexOf('/'));
+ } else {
+ data.direction = 'both';
+ }
+ data.uri = parts.shift();
+ data.params = parts;
+ return data;
+ },
+ find_line: function (haystack, needle, sessionpart) {
+ var lines = haystack.split('\r\n');
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, needle.length) == needle) {
+ return lines[i];
+ }
+ }
+ if (!sessionpart) {
+ return false;
+ }
+ // search session part
+ lines = sessionpart.split('\r\n');
+ for (var j = 0; j < lines.length; j++) {
+ if (lines[j].substring(0, needle.length) == needle) {
+ return lines[j];
+ }
+ }
+ return false;
+ },
+ find_lines: function (haystack, needle, sessionpart) {
+ var lines = haystack.split('\r\n'),
+ needles = [];
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, needle.length) == needle)
+ needles.push(lines[i]);
+ }
+ if (needles.length || !sessionpart) {
+ return needles;
+ }
+ // search session part
+ lines = sessionpart.split('\r\n');
+ for (var j = 0; j < lines.length; j++) {
+ if (lines[j].substring(0, needle.length) == needle) {
+ needles.push(lines[j]);
+ }
+ }
+ return needles;
+ },
+ candidateToJingle: function (line) {
+ // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
+ //
+ if (line.indexOf('candidate:') === 0) {
+ line = 'a=' + line;
+ } else if (line.substring(0, 12) != 'a=candidate:') {
+ console.log('parseCandidate called with a line that is not a candidate line');
+ console.log(line);
+ return null;
+ }
+ if (line.substring(line.length - 2) == '\r\n') // chomp it
+ line = line.substring(0, line.length - 2);
+ var candidate = {},
+ elems = line.split(' '),
+ i;
+ if (elems[6] != 'typ') {
+ console.log('did not find typ in the right place');
+ console.log(line);
+ return null;
+ }
+ candidate.foundation = elems[0].substring(12);
+ candidate.component = elems[1];
+ candidate.protocol = elems[2].toLowerCase();
+ candidate.priority = elems[3];
+ candidate.ip = elems[4];
+ candidate.port = elems[5];
+ // elems[6] => "typ"
+ candidate.type = elems[7];
+
+ candidate.generation = '0'; // default, may be overwritten below
+ for (i = 8; i < elems.length; i += 2) {
+ switch (elems[i]) {
+ case 'raddr':
+ candidate['rel-addr'] = elems[i + 1];
+ break;
+ case 'rport':
+ candidate['rel-port'] = elems[i + 1];
+ break;
+ case 'generation':
+ candidate.generation = elems[i + 1];
+ break;
+ case 'tcptype':
+ candidate.tcptype = elems[i + 1];
+ break;
+ default: // TODO
+ console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
+ }
+ }
+ candidate.network = '1';
+ candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
+ return candidate;
+ },
+ candidateFromJingle: function (cand) {
+ var line = 'a=candidate:';
+ line += cand.getAttribute('foundation');
+ line += ' ';
+ line += cand.getAttribute('component');
+ line += ' ';
+ line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
+ line += ' ';
+ line += cand.getAttribute('priority');
+ line += ' ';
+ line += cand.getAttribute('ip');
+ line += ' ';
+ line += cand.getAttribute('port');
+ line += ' ';
+ line += 'typ';
+ line += ' ' + cand.getAttribute('type');
+ line += ' ';
+ switch (cand.getAttribute('type')) {
+ case 'srflx':
+ case 'prflx':
+ case 'relay':
+ if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
+ line += 'raddr';
+ line += ' ';
+ line += cand.getAttribute('rel-addr');
+ line += ' ';
+ line += 'rport';
+ line += ' ';
+ line += cand.getAttribute('rel-port');
+ line += ' ';
+ }
+ break;
+ }
+ if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
+ line += 'tcptype';
+ line += ' ';
+ line += cand.getAttribute('tcptype');
+ line += ' ';
+ }
+ line += 'generation';
+ line += ' ';
+ line += cand.getAttribute('generation') || '0';
+ return line + '\r\n';
+ }
+};
+module.exports = SDPUtil;
+},{}],5:[function(require,module,exports){
+function TraceablePeerConnection(ice_config, constraints) {
+ var self = this;
+ var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
+ this.peerconnection = new RTCPeerconnection(ice_config, constraints);
+ this.updateLog = [];
+ this.stats = {};
+ this.statsinterval = null;
+ this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
+
+ // override as desired
+ this.trace = function (what, info) {
+ //console.warn('WTRACE', what, info);
+ self.updateLog.push({
+ time: new Date(),
+ type: what,
+ value: info || ""
+ });
+ };
+ this.onicecandidate = null;
+ this.peerconnection.onicecandidate = function (event) {
+ self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
+ if (self.onicecandidate !== null) {
+ self.onicecandidate(event);
+ }
+ };
+ this.onaddstream = null;
+ this.peerconnection.onaddstream = function (event) {
+ self.trace('onaddstream', event.stream.id);
+ if (self.onaddstream !== null) {
+ self.onaddstream(event);
+ }
+ };
+ this.onremovestream = null;
+ this.peerconnection.onremovestream = function (event) {
+ self.trace('onremovestream', event.stream.id);
+ if (self.onremovestream !== null) {
+ self.onremovestream(event);
+ }
+ };
+ this.onsignalingstatechange = null;
+ this.peerconnection.onsignalingstatechange = function (event) {
+ self.trace('onsignalingstatechange', self.signalingState);
+ if (self.onsignalingstatechange !== null) {
+ self.onsignalingstatechange(event);
+ }
+ };
+ this.oniceconnectionstatechange = null;
+ this.peerconnection.oniceconnectionstatechange = function (event) {
+ self.trace('oniceconnectionstatechange', self.iceConnectionState);
+ if (self.oniceconnectionstatechange !== null) {
+ self.oniceconnectionstatechange(event);
+ }
+ };
+ this.onnegotiationneeded = null;
+ this.peerconnection.onnegotiationneeded = function (event) {
+ self.trace('onnegotiationneeded');
+ if (self.onnegotiationneeded !== null) {
+ self.onnegotiationneeded(event);
+ }
+ };
+ self.ondatachannel = null;
+ this.peerconnection.ondatachannel = function (event) {
+ self.trace('ondatachannel', event);
+ if (self.ondatachannel !== null) {
+ self.ondatachannel(event);
+ }
+ };
+ if (!navigator.mozGetUserMedia && this.maxstats) {
+ this.statsinterval = window.setInterval(function() {
+ self.peerconnection.getStats(function(stats) {
+ var results = stats.result();
+ for (var i = 0; i < results.length; ++i) {
+ //console.log(results[i].type, results[i].id, results[i].names())
+ var now = new Date();
+ results[i].names().forEach(function (name) {
+ var id = results[i].id + '-' + name;
+ if (!self.stats[id]) {
+ self.stats[id] = {
+ startTime: now,
+ endTime: now,
+ values: [],
+ times: []
+ };
+ }
+ self.stats[id].values.push(results[i].stat(name));
+ self.stats[id].times.push(now.getTime());
+ if (self.stats[id].values.length > self.maxstats) {
+ self.stats[id].values.shift();
+ self.stats[id].times.shift();
+ }
+ self.stats[id].endTime = now;
+ });
+ }
+ });
+
+ }, 1000);
+ }
+};
+
+dumpSDP = function(description) {
+ return 'type: ' + description.type + '\r\n' + description.sdp;
+}
+
+if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
+ TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
+ TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
+ TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
+ var publicLocalDescription = simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
+ return publicLocalDescription;
+ });
+ TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
+ var publicRemoteDescription = simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
+ return publicRemoteDescription;
+ });
+}
+
+TraceablePeerConnection.prototype.addStream = function (stream) {
+ this.trace('addStream', stream.id);
+ simulcast.resetSender();
+ try
+ {
+ this.peerconnection.addStream(stream);
+ }
+ catch (e)
+ {
+ console.error(e);
+ return;
+ }
+};
+
+TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
+ this.trace('removeStream', stream.id);
+ simulcast.resetSender();
+ if(stopStreams) {
+ stream.getAudioTracks().forEach(function (track) {
+ track.stop();
+ });
+ stream.getVideoTracks().forEach(function (track) {
+ track.stop();
+ });
+ }
+ this.peerconnection.removeStream(stream);
+};
+
+TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
+ this.trace('createDataChannel', label, opts);
+ return this.peerconnection.createDataChannel(label, opts);
+};
+
+TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
+ var self = this;
+ description = simulcast.transformLocalDescription(description);
+ this.trace('setLocalDescription', dumpSDP(description));
+ this.peerconnection.setLocalDescription(description,
+ function () {
+ self.trace('setLocalDescriptionOnSuccess');
+ successCallback();
+ },
+ function (err) {
+ self.trace('setLocalDescriptionOnFailure', err);
+ failureCallback(err);
+ }
+ );
+ /*
+ if (this.statsinterval === null && this.maxstats > 0) {
+ // start gathering stats
+ }
+ */
+};
+
+TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
+ var self = this;
+ description = simulcast.transformRemoteDescription(description);
+ this.trace('setRemoteDescription', dumpSDP(description));
+ this.peerconnection.setRemoteDescription(description,
+ function () {
+ self.trace('setRemoteDescriptionOnSuccess');
+ successCallback();
+ },
+ function (err) {
+ self.trace('setRemoteDescriptionOnFailure', err);
+ failureCallback(err);
+ }
+ );
+ /*
+ if (this.statsinterval === null && this.maxstats > 0) {
+ // start gathering stats
+ }
+ */
+};
+
+TraceablePeerConnection.prototype.close = function () {
+ this.trace('stop');
+ if (this.statsinterval !== null) {
+ window.clearInterval(this.statsinterval);
+ this.statsinterval = null;
+ }
+ this.peerconnection.close();
+};
+
+TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
+ var self = this;
+ this.trace('createOffer', JSON.stringify(constraints, null, ' '));
+ this.peerconnection.createOffer(
+ function (offer) {
+ self.trace('createOfferOnSuccess', dumpSDP(offer));
+ successCallback(offer);
+ },
+ function(err) {
+ self.trace('createOfferOnFailure', err);
+ failureCallback(err);
+ },
+ constraints
+ );
+};
+
+TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
+ var self = this;
+ this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
+ this.peerconnection.createAnswer(
+ function (answer) {
+ answer = simulcast.transformAnswer(answer);
+ self.trace('createAnswerOnSuccess', dumpSDP(answer));
+ successCallback(answer);
+ },
+ function(err) {
+ self.trace('createAnswerOnFailure', err);
+ failureCallback(err);
+ },
+ constraints
+ );
+};
+
+TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
+ var self = this;
+ this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
+ this.peerconnection.addIceCandidate(candidate);
+ /* maybe later
+ this.peerconnection.addIceCandidate(candidate,
+ function () {
+ self.trace('addIceCandidateOnSuccess');
+ successCallback();
+ },
+ function (err) {
+ self.trace('addIceCandidateOnFailure', err);
+ failureCallback(err);
+ }
+ );
+ */
+};
+
+TraceablePeerConnection.prototype.getStats = function(callback, errback) {
+ if (navigator.mozGetUserMedia) {
+ // ignore for now...
+ if(!errback)
+ errback = function () {
+
+ }
+ this.peerconnection.getStats(null,callback,errback);
+ } else {
+ this.peerconnection.getStats(callback);
+ }
+};
+
+module.exports = TraceablePeerConnection;
+
+
+},{}],6:[function(require,module,exports){
+/* global $, $iq, config, connection, UI, messageHandler,
+ roomName, sessionTerminated, Strophe, Util */
+/**
+ * Contains logic responsible for enabling/disabling functionality available
+ * only to moderator users.
+ */
+var connection = null;
+var focusUserJid;
+var getNextTimeout = Util.createExpBackoffTimer(1000);
+var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
+// External authentication stuff
+var externalAuthEnabled = false;
+// Sip gateway can be enabled by configuring Jigasi host in config.js or
+// it will be enabled automatically if focus detects the component through
+// service discovery.
+var sipGatewayEnabled = config.hosts.call_control !== undefined;
+
+var Moderator = {
+ isModerator: function () {
+ return connection && connection.emuc.isModerator();
+ },
+
+ isPeerModerator: function (peerJid) {
+ return connection &&
+ connection.emuc.getMemberRole(peerJid) === 'moderator';
+ },
+
+ isExternalAuthEnabled: function () {
+ return externalAuthEnabled;
+ },
+
+ isSipGatewayEnabled: function () {
+ return sipGatewayEnabled;
+ },
+
+ setConnection: function (con) {
+ connection = con;
+ },
+
+ init: function (xmpp) {
+ this.xmppService = xmpp;
+ this.onLocalRoleChange = function (from, member, pres) {
+ UI.onModeratorStatusChanged(Moderator.isModerator());
+ };
+ },
+
+ onMucLeft: function (jid) {
+ console.info("Someone left is it focus ? " + jid);
+ var resource = Strophe.getResourceFromJid(jid);
+ if (resource === 'focus' && !this.xmppService.sessionTerminated) {
+ console.info(
+ "Focus has left the room - leaving conference");
+ //hangUp();
+ // We'd rather reload to have everything re-initialized
+ // FIXME: show some message before reload
+ location.reload();
+ }
+ },
+
+ setFocusUserJid: function (focusJid) {
+ if (!focusUserJid) {
+ focusUserJid = focusJid;
+ console.info("Focus jid set to: " + focusUserJid);
+ }
+ },
+
+ getFocusUserJid: function () {
+ return focusUserJid;
+ },
+
+ getFocusComponent: function () {
+ // Get focus component address
+ var focusComponent = config.hosts.focus;
+ // If not specified use default: 'focus.domain'
+ if (!focusComponent) {
+ focusComponent = 'focus.' + config.hosts.domain;
+ }
+ return focusComponent;
+ },
+
+ createConferenceIq: function (roomName) {
+ // Generate create conference IQ
+ var elem = $iq({to: Moderator.getFocusComponent(), type: 'set'});
+ elem.c('conference', {
+ xmlns: 'http://jitsi.org/protocol/focus',
+ room: roomName
+ });
+ if (config.hosts.bridge !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'bridge', value: config.hosts.bridge})
+ .up();
+ }
+ // Tell the focus we have Jigasi configured
+ if (config.hosts.call_control !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'call_control', value: config.hosts.call_control})
+ .up();
+ }
+ if (config.channelLastN !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'channelLastN', value: config.channelLastN})
+ .up();
+ }
+ if (config.adaptiveLastN !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'adaptiveLastN', value: config.adaptiveLastN})
+ .up();
+ }
+ if (config.adaptiveSimulcast !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'adaptiveSimulcast', value: config.adaptiveSimulcast})
+ .up();
+ }
+ if (config.openSctp !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'openSctp', value: config.openSctp})
+ .up();
+ }
+ if (config.enableFirefoxSupport !== undefined) {
+ elem.c(
+ 'property',
+ { name: 'enableFirefoxHacks',
+ value: config.enableFirefoxSupport})
+ .up();
+ }
+ elem.up();
+ return elem;
+ },
+
+ parseConfigOptions: function (resultIq) {
+
+ Moderator.setFocusUserJid(
+ $(resultIq).find('conference').attr('focusjid'));
+
+ var extAuthParam
+ = $(resultIq).find('>conference>property[name=\'externalAuth\']');
+ if (extAuthParam.length) {
+ externalAuthEnabled = extAuthParam.attr('value') === 'true';
+ }
+
+ console.info("External authentication enabled: " + externalAuthEnabled);
+
+ // Check if focus has auto-detected Jigasi component(this will be also
+ // included if we have passed our host from the config)
+ if ($(resultIq).find(
+ '>conference>property[name=\'sipGatewayEnabled\']').length) {
+ sipGatewayEnabled = true;
+ }
+
+ console.info("Sip gateway enabled: " + sipGatewayEnabled);
+ },
+
+ // FIXME: we need to show the fact that we're waiting for the focus
+ // to the user(or that focus is not available)
+ allocateConferenceFocus: function (roomName, callback) {
+ // Try to use focus user JID from the config
+ Moderator.setFocusUserJid(config.focusUserJid);
+ // Send create conference IQ
+ var iq = Moderator.createConferenceIq(roomName);
+ connection.sendIQ(
+ iq,
+ function (result) {
+ if ('true' === $(result).find('conference').attr('ready')) {
+ // Reset both timers
+ getNextTimeout(true);
+ getNextErrorTimeout(true);
+ // Setup config options
+ Moderator.parseConfigOptions(result);
+ // Exec callback
+ callback();
+ } else {
+ var waitMs = getNextTimeout();
+ console.info("Waiting for the focus... " + waitMs);
+ // Reset error timeout
+ getNextErrorTimeout(true);
+ window.setTimeout(
+ function () {
+ Moderator.allocateConferenceFocus(
+ roomName, callback);
+ }, waitMs);
+ }
+ },
+ function (error) {
+ // Not authorized to create new room
+ if ($(error).find('>error>not-authorized').length) {
+ console.warn("Unauthorized to start the conference");
+ UI.onAuthenticationRequired(function () {
+ Moderator.allocateConferenceFocus(roomName, callback);
+ });
+ return;
+ }
+ var waitMs = getNextErrorTimeout();
+ console.error("Focus error, retry after " + waitMs, error);
+ // Show message
+ UI.messageHandler.notify(
+ 'Conference focus', 'disconnected',
+ Moderator.getFocusComponent() +
+ ' not available - retry in ' +
+ (waitMs / 1000) + ' sec');
+ // Reset response timeout
+ getNextTimeout(true);
+ window.setTimeout(
+ function () {
+ Moderator.allocateConferenceFocus(roomName, callback);
+ }, waitMs);
+ }
+ );
+ },
+
+ getAuthUrl: function (roomName, urlCallback) {
+ var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
+ iq.c('auth-url', {
+ xmlns: 'http://jitsi.org/protocol/focus',
+ room: roomName
+ });
+ connection.sendIQ(
+ iq,
+ function (result) {
+ var url = $(result).find('auth-url').attr('url');
+ if (url) {
+ console.info("Got auth url: " + url);
+ urlCallback(url);
+ } else {
+ console.error(
+ "Failed to get auth url fro mthe focus", result);
+ }
+ },
+ function (error) {
+ console.error("Get auth url error", error);
+ }
+ );
+ }
+};
+
+module.exports = Moderator;
+
+
+
+
+},{}],7:[function(require,module,exports){
+/* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
+ Toolbar, Util */
+var Moderator = require("./moderator");
+
+
+var recordingToken = null;
+var recordingEnabled;
+
+/**
+ * Whether to use a jirecon component for recording, or use the videobridge
+ * through COLIBRI.
+ */
+var useJirecon = (typeof config.hosts.jirecon != "undefined");
+
+/**
+ * The ID of the jirecon recording session. Jirecon generates it when we
+ * initially start recording, and it needs to be used in subsequent requests
+ * to jirecon.
+ */
+var jireconRid = null;
+
+function setRecordingToken(token) {
+ recordingToken = token;
+}
+
+function setRecording(state, token, callback) {
+ if (useJirecon){
+ this.setRecordingJirecon(state, token, callback);
+ } else {
+ this.setRecordingColibri(state, token, callback);
+ }
+}
+
+function setRecordingJirecon(state, token, callback) {
+ if (state == recordingEnabled){
+ return;
+ }
+
+ var iq = $iq({to: config.hosts.jirecon, type: 'set'})
+ .c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
+ action: state ? 'start' : 'stop',
+ mucjid: connection.emuc.roomjid});
+ if (!state){
+ iq.attrs({rid: jireconRid});
+ }
+
+ console.log('Start recording');
+
+ connection.sendIQ(
+ iq,
+ function (result) {
+ // TODO wait for an IQ with the real status, since this is
+ // provisional?
+ jireconRid = $(result).find('recording').attr('rid');
+ console.log('Recording ' + (state ? 'started' : 'stopped') +
+ '(jirecon)' + result);
+ recordingEnabled = state;
+ if (!state){
+ jireconRid = null;
+ }
+
+ callback(state);
+ },
+ function (error) {
+ console.log('Failed to start recording, error: ', error);
+ callback(recordingEnabled);
+ });
+}
+
+// Sends a COLIBRI message which enables or disables (according to 'state')
+// the recording on the bridge. Waits for the result IQ and calls 'callback'
+// with the new recording state, according to the IQ.
+function setRecordingColibri(state, token, callback) {
+ var elem = $iq({to: focusMucJid, type: 'set'});
+ elem.c('conference', {
+ xmlns: 'http://jitsi.org/protocol/colibri'
+ });
+ elem.c('recording', {state: state, token: token});
+
+ connection.sendIQ(elem,
+ function (result) {
+ console.log('Set recording "', state, '". Result:', result);
+ var recordingElem = $(result).find('>conference>recording');
+ var newState = ('true' === recordingElem.attr('state'));
+
+ recordingEnabled = newState;
+ callback(newState);
+ },
+ function (error) {
+ console.warn(error);
+ callback(recordingEnabled);
+ }
+ );
+}
+
+var Recording = {
+ toggleRecording: function (tokenEmptyCallback,
+ startingCallback, startedCallback) {
+ if (!Moderator.isModerator()) {
+ console.log(
+ 'non-focus, or conference not yet organized:' +
+ ' not enabling recording');
+ return;
+ }
+
+ // Jirecon does not (currently) support a token.
+ if (!recordingToken && !useJirecon) {
+ tokenEmptyCallback(function (value) {
+ setRecordingToken(value);
+ this.toggleRecording();
+ });
+
+ return;
+ }
+
+ var oldState = recordingEnabled;
+ startingCallback(!oldState);
+ setRecording(!oldState,
+ recordingToken,
+ function (state) {
+ console.log("New recording state: ", state);
+ if (state === oldState) {
+ // FIXME: new focus:
+ // this will not work when moderator changes
+ // during active session. Then it will assume that
+ // recording status has changed to true, but it might have
+ // been already true(and we only received actual status from
+ // the focus).
+ //
+ // SO we start with status null, so that it is initialized
+ // here and will fail only after second click, so if invalid
+ // token was used we have to press the button twice before
+ // current status will be fetched and token will be reset.
+ //
+ // Reliable way would be to return authentication error.
+ // Or status update when moderator connects.
+ // Or we have to stop recording session when current
+ // moderator leaves the room.
+
+ // Failed to change, reset the token because it might
+ // have been wrong
+ setRecordingToken(null);
+ }
+ startedCallback(state);
+
+ }
+ );
+ }
+
+}
+
+module.exports = Recording;
+},{"./moderator":6}],8:[function(require,module,exports){
+/* jshint -W117 */
+/* a simple MUC connection plugin
+ * can only handle a single MUC room
+ */
+
+var bridgeIsDown = false;
+
+var Moderator = require("./moderator");
+
+module.exports = function(XMPP, eventEmitter) {
+ Strophe.addConnectionPlugin('emuc', {
+ connection: null,
+ roomjid: null,
+ myroomjid: null,
+ members: {},
+ list_members: [], // so we can elect a new focus
+ presMap: {},
+ preziMap: {},
+ joined: false,
+ isOwner: false,
+ role: null,
+ init: function (conn) {
+ this.connection = conn;
+ },
+ initPresenceMap: function (myroomjid) {
+ this.presMap['to'] = myroomjid;
+ this.presMap['xns'] = 'http://jabber.org/protocol/muc';
+ },
+ doJoin: function (jid, password) {
+ this.myroomjid = jid;
+
+ console.info("Joined MUC as " + this.myroomjid);
+
+ this.initPresenceMap(this.myroomjid);
+
+ if (!this.roomjid) {
+ this.roomjid = Strophe.getBareJidFromJid(jid);
+ // add handlers (just once)
+ this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
+ }
+ if (password !== undefined) {
+ this.presMap['password'] = password;
+ }
+ this.sendPresence();
+ },
+ doLeave: function () {
+ console.log("do leave", this.myroomjid);
+ var pres = $pres({to: this.myroomjid, type: 'unavailable' });
+ this.presMap.length = 0;
+ this.connection.send(pres);
+ },
+ createNonAnonymousRoom: function () {
+ // http://xmpp.org/extensions/xep-0045.html#createroom-reserved
+
+ var getForm = $iq({type: 'get', to: this.roomjid})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
+ .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+
+ this.connection.sendIQ(getForm, function (form) {
+
+ if (!$(form).find(
+ '>query>x[xmlns="jabber:x:data"]' +
+ '>field[var="muc#roomconfig_whois"]').length) {
+
+ console.error('non-anonymous rooms not supported');
+ return;
+ }
+
+ var formSubmit = $iq({to: this.roomjid, type: 'set'})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
+
+ formSubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+
+ formSubmit.c('field', {'var': 'FORM_TYPE'})
+ .c('value')
+ .t('http://jabber.org/protocol/muc#roomconfig').up().up();
+
+ formSubmit.c('field', {'var': 'muc#roomconfig_whois'})
+ .c('value').t('anyone').up().up();
+
+ this.connection.sendIQ(formSubmit);
+
+ }, function (error) {
+ console.error("Error getting room configuration form");
+ });
+ },
+ onPresence: function (pres) {
+ var from = pres.getAttribute('from');
+
+ // What is this for? A workaround for something?
+ if (pres.getAttribute('type')) {
+ return true;
+ }
+
+ // Parse etherpad tag.
+ var etherpad = $(pres).find('>etherpad');
+ if (etherpad.length) {
+ if (config.etherpad_base && !Moderator.isModerator()) {
+ UI.initEtherpad(etherpad.text());
+ }
+ }
+
+ // Parse prezi tag.
+ var presentation = $(pres).find('>prezi');
+ if (presentation.length) {
+ var url = presentation.attr('url');
+ var current = presentation.find('>current').text();
+
+ console.log('presentation info received from', from, url);
+
+ if (this.preziMap[from] == null) {
+ this.preziMap[from] = url;
+
+ $(document).trigger('presentationadded.muc', [from, url, current]);
+ }
+ else {
+ $(document).trigger('gotoslide.muc', [from, url, current]);
+ }
+ }
+ else if (this.preziMap[from] != null) {
+ var url = this.preziMap[from];
+ delete this.preziMap[from];
+ $(document).trigger('presentationremoved.muc', [from, url]);
+ }
+
+ // Parse audio info tag.
+ var audioMuted = $(pres).find('>audiomuted');
+ if (audioMuted.length) {
+ $(document).trigger('audiomuted.muc', [from, audioMuted.text()]);
+ }
+
+ // Parse video info tag.
+ var videoMuted = $(pres).find('>videomuted');
+ if (videoMuted.length) {
+ $(document).trigger('videomuted.muc', [from, videoMuted.text()]);
+ }
+
+ var stats = $(pres).find('>stats');
+ if (stats.length) {
+ var statsObj = {};
+ Strophe.forEachChild(stats[0], "stat", function (el) {
+ statsObj[el.getAttribute("name")] = el.getAttribute("value");
+ });
+ connectionquality.updateRemoteStats(from, statsObj);
+ }
+
+ // Parse status.
+ if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
+ this.isOwner = true;
+ this.createNonAnonymousRoom();
+ }
+
+ // Parse roles.
+ var member = {};
+ member.show = $(pres).find('>show').text();
+ member.status = $(pres).find('>status').text();
+ var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
+ member.affiliation = tmp.attr('affiliation');
+ member.role = tmp.attr('role');
+
+ // Focus recognition
+ member.jid = tmp.attr('jid');
+ member.isFocus = false;
+ if (member.jid
+ && member.jid.indexOf(Moderator.getFocusUserJid() + "/") == 0) {
+ member.isFocus = true;
+ }
+
+ var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
+ member.displayName = (nicktag.length > 0 ? nicktag.html() : null);
+
+ if (from == this.myroomjid) {
+ if (member.affiliation == 'owner') this.isOwner = true;
+ if (this.role !== member.role) {
+ this.role = member.role;
+ if (Moderator.onLocalRoleChange)
+ Moderator.onLocalRoleChange(from, member, pres);
+ UI.onLocalRoleChange(from, member, pres);
+ }
+ if (!this.joined) {
+ this.joined = true;
+ eventEmitter.emit(XMPPEvents.MUC_JOINED, from, member);
+ this.list_members.push(from);
+ }
+ } else if (this.members[from] === undefined) {
+ // new participant
+ this.members[from] = member;
+ this.list_members.push(from);
+ console.log('entered', from, member);
+ if (member.isFocus) {
+ focusMucJid = from;
+ console.info("Ignore focus: " + from + ", real JID: " + member.jid);
+ }
+ else {
+ var id = $(pres).find('>userID').text();
+ var email = $(pres).find('>email');
+ if (email.length > 0) {
+ id = email.text();
+ }
+ UI.onMucEntered(from, id, member.displayName);
+ API.triggerEvent("participantJoined", {jid: from});
+ }
+ } else {
+ // Presence update for existing participant
+ // Watch role change:
+ if (this.members[from].role != member.role) {
+ this.members[from].role = member.role;
+ UI.onMucRoleChanged(member.role, member.displayName);
+ }
+ }
+
+ // Always trigger presence to update bindings
+ $(document).trigger('presence.muc', [from, member, pres]);
+ this.parsePresence(from, member, pres);
+
+ // Trigger status message update
+ if (member.status) {
+ UI.onMucPresenceStatus(from, member);
+ }
+
+ return true;
+ },
+ onPresenceUnavailable: function (pres) {
+ var from = pres.getAttribute('from');
+ // Status code 110 indicates that this notification is "self-presence".
+ if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
+ delete this.members[from];
+ this.list_members.splice(this.list_members.indexOf(from), 1);
+ this.onParticipantLeft(from);
+ }
+ // If the status code is 110 this means we're leaving and we would like
+ // to remove everyone else from our view, so we trigger the event.
+ else if (this.list_members.length > 1) {
+ for (var i = 0; i < this.list_members.length; i++) {
+ var member = this.list_members[i];
+ delete this.members[i];
+ this.list_members.splice(i, 1);
+ this.onParticipantLeft(member);
+ }
+ }
+ if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length) {
+ $(document).trigger('kicked.muc', [from]);
+ if (this.myroomjid === from) {
+ XMPP.disposeConference(false);
+ eventEmitter.emit(XMPPEvents.KICKED);
+ }
+ }
+ return true;
+ },
+ onPresenceError: function (pres) {
+ var from = pres.getAttribute('from');
+ if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
+ console.log('on password required', from);
+ var self = this;
+ UI.onPasswordReqiured(function (value) {
+ self.doJoin(from, value);
+ });
+ } else if ($(pres).find(
+ '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
+ var toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
+ if (toDomain === config.hosts.anonymousdomain) {
+ // we are connected with anonymous domain and only non anonymous users can create rooms
+ // we must authorize the user
+ XMPP.promptLogin();
+ } else {
+ console.warn('onPresError ', pres);
+ UI.messageHandler.openReportDialog(null,
+ 'Oops! Something went wrong and we couldn`t connect to the conference.',
+ pres);
+ }
+ } else {
+ console.warn('onPresError ', pres);
+ UI.messageHandler.openReportDialog(null,
+ 'Oops! Something went wrong and we couldn`t connect to the conference.',
+ pres);
+ }
+ return true;
+ },
+ sendMessage: function (body, nickname) {
+ var msg = $msg({to: this.roomjid, type: 'groupchat'});
+ msg.c('body', body).up();
+ if (nickname) {
+ msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
+ }
+ this.connection.send(msg);
+ API.triggerEvent("outgoingMessage", {"message": body});
+ },
+ setSubject: function (subject) {
+ var msg = $msg({to: this.roomjid, type: 'groupchat'});
+ msg.c('subject', subject);
+ this.connection.send(msg);
+ console.log("topic changed to " + subject);
+ },
+ onMessage: function (msg) {
+ // FIXME: this is a hack. but jingle on muc makes nickchanges hard
+ var from = msg.getAttribute('from');
+ var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(from);
+
+ var txt = $(msg).find('>body').text();
+ var type = msg.getAttribute("type");
+ if (type == "error") {
+ UI.chatAddError($(msg).find('>text').text(), txt);
+ return true;
+ }
+
+ var subject = $(msg).find('>subject');
+ if (subject.length) {
+ var subjectText = subject.text();
+ if (subjectText || subjectText == "") {
+ UI.chatSetSubject(subjectText);
+ console.log("Subject is changed to " + subjectText);
+ }
+ }
+
+
+ if (txt) {
+ console.log('chat', nick, txt);
+ UI.updateChatConversation(from, nick, txt);
+ if (from != this.myroomjid)
+ API.triggerEvent("incomingMessage",
+ {"from": from, "nick": nick, "message": txt});
+ }
+ return true;
+ },
+ lockRoom: function (key, onSuccess, onError, onNotSupported) {
+ //http://xmpp.org/extensions/xep-0045.html#roomconfig
+ var ob = this;
+ this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
+ function (res) {
+ if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
+ var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
+ formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+ formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
+ formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
+ // Fixes a bug in prosody 0.9.+ https://code.google.com/p/lxmppd/issues/detail?id=373
+ formsubmit.c('field', {'var': 'muc#roomconfig_whois'}).c('value').t('anyone').up().up();
+ // FIXME: is muc#roomconfig_passwordprotectedroom required?
+ this.connection.sendIQ(formsubmit,
+ onSuccess,
+ onError);
+ } else {
+ onNotSupported();
+ }
+ }, onError);
+ },
+ kick: function (jid) {
+ var kickIQ = $iq({to: this.roomjid, type: 'set'})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#admin'})
+ .c('item', {nick: Strophe.getResourceFromJid(jid), role: 'none'})
+ .c('reason').t('You have been kicked.').up().up().up();
+
+ this.connection.sendIQ(
+ kickIQ,
+ function (result) {
+ console.log('Kick participant with jid: ', jid, result);
+ },
+ function (error) {
+ console.log('Kick participant error: ', error);
+ });
+ },
+ sendPresence: function () {
+ var pres = $pres({to: this.presMap['to'] });
+ pres.c('x', {xmlns: this.presMap['xns']});
+
+ if (this.presMap['password']) {
+ pres.c('password').t(this.presMap['password']).up();
+ }
+
+ pres.up();
+
+ // Send XEP-0115 'c' stanza that contains our capabilities info
+ if (this.connection.caps) {
+ this.connection.caps.node = config.clientNode;
+ pres.c('c', this.connection.caps.generateCapsAttrs()).up();
+ }
+
+ pres.c('user-agent', {xmlns: 'http://jitsi.org/jitmeet/user-agent'})
+ .t(navigator.userAgent).up();
+
+ if (this.presMap['bridgeIsDown']) {
+ pres.c('bridgeIsDown').up();
+ }
+
+ if (this.presMap['email']) {
+ pres.c('email').t(this.presMap['email']).up();
+ }
+
+ if (this.presMap['userId']) {
+ pres.c('userId').t(this.presMap['userId']).up();
+ }
+
+ if (this.presMap['displayName']) {
+ // XEP-0172
+ pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'})
+ .t(this.presMap['displayName']).up();
+ }
+
+ if (this.presMap['audions']) {
+ pres.c('audiomuted', {xmlns: this.presMap['audions']})
+ .t(this.presMap['audiomuted']).up();
+ }
+
+ if (this.presMap['videons']) {
+ pres.c('videomuted', {xmlns: this.presMap['videons']})
+ .t(this.presMap['videomuted']).up();
+ }
+
+ if (this.presMap['statsns']) {
+ var stats = pres.c('stats', {xmlns: this.presMap['statsns']});
+ for (var stat in this.presMap["stats"])
+ if (this.presMap["stats"][stat] != null)
+ stats.c("stat", {name: stat, value: this.presMap["stats"][stat]}).up();
+ pres.up();
+ }
+
+ if (this.presMap['prezins']) {
+ pres.c('prezi',
+ {xmlns: this.presMap['prezins'],
+ 'url': this.presMap['preziurl']})
+ .c('current').t(this.presMap['prezicurrent']).up().up();
+ }
+
+ if (this.presMap['etherpadns']) {
+ pres.c('etherpad', {xmlns: this.presMap['etherpadns']})
+ .t(this.presMap['etherpadname']).up();
+ }
+
+ if (this.presMap['medians']) {
+ pres.c('media', {xmlns: this.presMap['medians']});
+ var sourceNumber = 0;
+ Object.keys(this.presMap).forEach(function (key) {
+ if (key.indexOf('source') >= 0) {
+ sourceNumber++;
+ }
+ });
+ if (sourceNumber > 0)
+ for (var i = 1; i <= sourceNumber / 3; i++) {
+ pres.c('source',
+ {type: this.presMap['source' + i + '_type'],
+ ssrc: this.presMap['source' + i + '_ssrc'],
+ direction: this.presMap['source' + i + '_direction']
+ || 'sendrecv' }
+ ).up();
+ }
+ }
+
+ pres.up();
+// console.debug(pres.toString());
+ this.connection.send(pres);
+ },
+ addDisplayNameToPresence: function (displayName) {
+ this.presMap['displayName'] = displayName;
+ },
+ addMediaToPresence: function (sourceNumber, mtype, ssrcs, direction) {
+ if (!this.presMap['medians'])
+ this.presMap['medians'] = 'http://estos.de/ns/mjs';
+
+ this.presMap['source' + sourceNumber + '_type'] = mtype;
+ this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
+ this.presMap['source' + sourceNumber + '_direction'] = direction;
+ },
+ clearPresenceMedia: function () {
+ var self = this;
+ Object.keys(this.presMap).forEach(function (key) {
+ if (key.indexOf('source') != -1) {
+ delete self.presMap[key];
+ }
+ });
+ },
+ addPreziToPresence: function (url, currentSlide) {
+ this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
+ this.presMap['preziurl'] = url;
+ this.presMap['prezicurrent'] = currentSlide;
+ },
+ removePreziFromPresence: function () {
+ delete this.presMap['prezins'];
+ delete this.presMap['preziurl'];
+ delete this.presMap['prezicurrent'];
+ },
+ addCurrentSlideToPresence: function (currentSlide) {
+ this.presMap['prezicurrent'] = currentSlide;
+ },
+ getPrezi: function (roomjid) {
+ return this.preziMap[roomjid];
+ },
+ addEtherpadToPresence: function (etherpadName) {
+ this.presMap['etherpadns'] = 'http://jitsi.org/jitmeet/etherpad';
+ this.presMap['etherpadname'] = etherpadName;
+ },
+ addAudioInfoToPresence: function (isMuted) {
+ this.presMap['audions'] = 'http://jitsi.org/jitmeet/audio';
+ this.presMap['audiomuted'] = isMuted.toString();
+ },
+ addVideoInfoToPresence: function (isMuted) {
+ this.presMap['videons'] = 'http://jitsi.org/jitmeet/video';
+ this.presMap['videomuted'] = isMuted.toString();
+ },
+ addConnectionInfoToPresence: function (stats) {
+ this.presMap['statsns'] = 'http://jitsi.org/jitmeet/stats';
+ this.presMap['stats'] = stats;
+ },
+ findJidFromResource: function (resourceJid) {
+ if (resourceJid &&
+ resourceJid === Strophe.getResourceFromJid(this.myroomjid)) {
+ return this.myroomjid;
+ }
+ var peerJid = null;
+ Object.keys(this.members).some(function (jid) {
+ peerJid = jid;
+ return Strophe.getResourceFromJid(jid) === resourceJid;
+ });
+ return peerJid;
+ },
+ addBridgeIsDownToPresence: function () {
+ this.presMap['bridgeIsDown'] = true;
+ },
+ addEmailToPresence: function (email) {
+ this.presMap['email'] = email;
+ },
+ addUserIdToPresence: function (userId) {
+ this.presMap['userId'] = userId;
+ },
+ isModerator: function () {
+ return this.role === 'moderator';
+ },
+ getMemberRole: function (peerJid) {
+ if (this.members[peerJid]) {
+ return this.members[peerJid].role;
+ }
+ return null;
+ },
+ onParticipantLeft: function (jid) {
+ UI.onMucLeft(jid);
+
+ API.triggerEvent("participantLeft", {jid: jid});
+
+ delete jid2Ssrc[jid];
+
+ this.connection.jingle.terminateByJid(jid);
+
+ if (this.getPrezi(jid)) {
+ $(document).trigger('presentationremoved.muc',
+ [jid, this.getPrezi(jid)]);
+ }
+
+ Moderator.onMucLeft(jid);
+ },
+ parsePresence: function (from, memeber, pres) {
+ if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
+ bridgeIsDown = true;
+ eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);
+ }
+
+ if(memeber.isFocus)
+ return;
+
+ // Remove old ssrcs coming from the jid
+ Object.keys(ssrc2jid).forEach(function (ssrc) {
+ if (ssrc2jid[ssrc] == jid) {
+ delete ssrc2jid[ssrc];
+ delete ssrc2videoType[ssrc];
+ }
+ });
+
+ var changedStreams = [];
+ $(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
+ //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
+ var ssrcV = ssrc.getAttribute('ssrc');
+ ssrc2jid[ssrcV] = from;
+ notReceivedSSRCs.push(ssrcV);
+
+ var type = ssrc.getAttribute('type');
+ ssrc2videoType[ssrcV] = type;
+
+ var direction = ssrc.getAttribute('direction');
+
+ changedStreams.push({type: type, direction: direction});
+
+ });
+
+ eventEmitter.emit(XMPPEvents.CHANGED_STREAMS, from, changedStreams);
+
+ var displayName = !config.displayJids
+ ? memeber.displayName : Strophe.getResourceFromJid(from);
+
+ if (displayName && displayName.length > 0)
+ {
+// $(document).trigger('displaynamechanged',
+// [jid, displayName]);
+ eventEmitter.emit(XMPPEvents.DISPLAY_NAME_CHANGED, from, displayName);
+ }
+
+
+ var id = $(pres).find('>userID').text();
+ var email = $(pres).find('>email');
+ if(email.length > 0) {
+ id = email.text();
+ }
+
+ eventEmitter.emit(XMPPEvents.USER_ID_CHANGED, from, id);
+ }
+ });
+};
+
+
+},{"./moderator":6}],9:[function(require,module,exports){
+/* jshint -W117 */
+
+var JingleSession = require("./JingleSession");
+
+function CallIncomingJingle(sid, connection) {
+ var sess = connection.jingle.sessions[sid];
+
+ // TODO: do we check activecall == null?
+ activecall = sess;
+
+ statistics.onConferenceCreated(sess);
+ RTC.onConferenceCreated(sess);
+
+ // TODO: check affiliation and/or role
+ console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
+ sess.usedrip = true; // not-so-naive trickle ice
+ sess.sendAnswer();
+ sess.accept();
+
+};
+
+module.exports = function(XMPP)
+{
+ Strophe.addConnectionPlugin('jingle', {
+ connection: null,
+ sessions: {},
+ jid2session: {},
+ ice_config: {iceServers: []},
+ pc_constraints: {},
+ media_constraints: {
+ mandatory: {
+ 'OfferToReceiveAudio': true,
+ 'OfferToReceiveVideo': true
+ }
+ // MozDontOfferDataChannel: true when this is firefox
+ },
+ init: function (conn) {
+ this.connection = conn;
+ if (this.connection.disco) {
+ // http://xmpp.org/extensions/xep-0167.html#support
+ // http://xmpp.org/extensions/xep-0176.html#support
+ this.connection.disco.addFeature('urn:xmpp:jingle:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');
+
+
+ // this is dealt with by SDP O/A so we don't need to annouce this
+ //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
+ //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
+ if (config.useRtcpMux) {
+ this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
+ }
+ if (config.useBundle) {
+ this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
+ }
+ //this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
+ }
+ this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
+ },
+ onJingle: function (iq) {
+ var sid = $(iq).find('jingle').attr('sid');
+ var action = $(iq).find('jingle').attr('action');
+ var fromJid = iq.getAttribute('from');
+ // send ack first
+ var ack = $iq({type: 'result',
+ to: fromJid,
+ id: iq.getAttribute('id')
+ });
+ console.log('on jingle ' + action + ' from ' + fromJid, iq);
+ var sess = this.sessions[sid];
+ if ('session-initiate' != action) {
+ if (sess === null) {
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
+ .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
+ this.connection.send(ack);
+ return true;
+ }
+ // compare from to sess.peerjid (bare jid comparison for later compat with message-mode)
+ // local jid is not checked
+ if (Strophe.getBareJidFromJid(fromJid) != Strophe.getBareJidFromJid(sess.peerjid)) {
+ console.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
+ .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
+ this.connection.send(ack);
+ return true;
+ }
+ } else if (sess !== undefined) {
+ // existing session with same session id
+ // this might be out-of-order if the sess.peerjid is the same as from
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
+ console.warn('duplicate session id', sid);
+ this.connection.send(ack);
+ return true;
+ }
+ // FIXME: check for a defined action
+ this.connection.send(ack);
+ // see http://xmpp.org/extensions/xep-0166.html#concepts-session
+ switch (action) {
+ case 'session-initiate':
+ sess = new JingleSession(
+ $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
+ this.connection, XMPP);
+ // configure session
+
+ sess.media_constraints = this.media_constraints;
+ sess.pc_constraints = this.pc_constraints;
+ sess.ice_config = this.ice_config;
+
+ sess.initiate(fromJid, false);
+ // FIXME: setRemoteDescription should only be done when this call is to be accepted
+ sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
+
+ this.sessions[sess.sid] = sess;
+ this.jid2session[sess.peerjid] = sess;
+
+ // the callback should either
+ // .sendAnswer and .accept
+ // or .sendTerminate -- not necessarily synchronus
+ CallIncomingJingle(sess.sid, this.connection);
+ break;
+ case 'session-accept':
+ sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
+ sess.accept();
+ $(document).trigger('callaccepted.jingle', [sess.sid]);
+ break;
+ case 'session-terminate':
+ // If this is not the focus sending the terminate, we have
+ // nothing more to do here.
+ if (Object.keys(this.sessions).length < 1
+ || !(this.sessions[Object.keys(this.sessions)[0]]
+ instanceof JingleSession))
+ {
+ break;
+ }
+ console.log('terminating...', sess.sid);
+ sess.terminate();
+ this.terminate(sess.sid);
+ if ($(iq).find('>jingle>reason').length) {
+ $(document).trigger('callterminated.jingle', [
+ sess.sid,
+ sess.peerjid,
+ $(iq).find('>jingle>reason>:first')[0].tagName,
+ $(iq).find('>jingle>reason>text').text()
+ ]);
+ } else {
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, sess.peerjid]);
+ }
+ break;
+ case 'transport-info':
+ sess.addIceCandidate($(iq).find('>jingle>content'));
+ break;
+ case 'session-info':
+ var affected;
+ if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ $(document).trigger('ringing.jingle', [sess.sid]);
+ } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
+ $(document).trigger('mute.jingle', [sess.sid, affected]);
+ } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
+ $(document).trigger('unmute.jingle', [sess.sid, affected]);
+ }
+ break;
+ case 'addsource': // FIXME: proprietary, un-jingleish
+ case 'source-add': // FIXME: proprietary
+ sess.addSource($(iq).find('>jingle>content'), fromJid);
+ break;
+ case 'removesource': // FIXME: proprietary, un-jingleish
+ case 'source-remove': // FIXME: proprietary
+ sess.removeSource($(iq).find('>jingle>content'), fromJid);
+ break;
+ default:
+ console.warn('jingle action not implemented', action);
+ break;
+ }
+ return true;
+ },
+ initiate: function (peerjid, myjid) { // initiate a new jinglesession to peerjid
+ var sess = new JingleSession(myjid || this.connection.jid,
+ Math.random().toString(36).substr(2, 12), // random string
+ this.connection, XMPP);
+ // configure session
+
+ sess.media_constraints = this.media_constraints;
+ sess.pc_constraints = this.pc_constraints;
+ sess.ice_config = this.ice_config;
+
+ sess.initiate(peerjid, true);
+ this.sessions[sess.sid] = sess;
+ this.jid2session[sess.peerjid] = sess;
+ sess.sendOffer();
+ return sess;
+ },
+ terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
+ if (sid === null || sid === undefined) {
+ for (sid in this.sessions) {
+ if (this.sessions[sid].state != 'ended') {
+ this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
+ this.sessions[sid].terminate();
+ }
+ delete this.jid2session[this.sessions[sid].peerjid];
+ delete this.sessions[sid];
+ }
+ } else if (this.sessions.hasOwnProperty(sid)) {
+ if (this.sessions[sid].state != 'ended') {
+ this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
+ this.sessions[sid].terminate();
+ }
+ delete this.jid2session[this.sessions[sid].peerjid];
+ delete this.sessions[sid];
+ }
+ },
+ // Used to terminate a session when an unavailable presence is received.
+ terminateByJid: function (jid) {
+ if (this.jid2session.hasOwnProperty(jid)) {
+ var sess = this.jid2session[jid];
+ if (sess) {
+ sess.terminate();
+ console.log('peer went away silently', jid);
+ delete this.sessions[sess.sid];
+ delete this.jid2session[jid];
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, jid], 'gone');
+ }
+ }
+ },
+ terminateRemoteByJid: function (jid, reason) {
+ if (this.jid2session.hasOwnProperty(jid)) {
+ var sess = this.jid2session[jid];
+ if (sess) {
+ sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
+ sess.terminate();
+ console.log('terminate peer with jid', sess.sid, jid);
+ delete this.sessions[sess.sid];
+ delete this.jid2session[jid];
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, jid, 'kicked']);
+ }
+ }
+ },
+ getStunAndTurnCredentials: function () {
+ // get stun and turn configuration from server via xep-0215
+ // uses time-limited credentials as described in
+ // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
+ //
+ // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
+ // for a prosody module which implements this
+ //
+ // currently, this doesn't work with updateIce and therefore credentials with a long
+ // validity have to be fetched before creating the peerconnection
+ // TODO: implement refresh via updateIce as described in
+ // https://code.google.com/p/webrtc/issues/detail?id=1650
+ var self = this;
+ this.connection.sendIQ(
+ $iq({type: 'get', to: this.connection.domain})
+ .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
+ function (res) {
+ var iceservers = [];
+ $(res).find('>services>service').each(function (idx, el) {
+ el = $(el);
+ var dict = {};
+ var type = el.attr('type');
+ switch (type) {
+ case 'stun':
+ dict.url = 'stun:' + el.attr('host');
+ if (el.attr('port')) {
+ dict.url += ':' + el.attr('port');
+ }
+ iceservers.push(dict);
+ break;
+ case 'turn':
+ case 'turns':
+ dict.url = type + ':';
+ if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
+ if (navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10) < 28) {
+ dict.url += el.attr('username') + '@';
+ } else {
+ dict.username = el.attr('username'); // only works in M28
+ }
+ }
+ dict.url += el.attr('host');
+ if (el.attr('port') && el.attr('port') != '3478') {
+ dict.url += ':' + el.attr('port');
+ }
+ if (el.attr('transport') && el.attr('transport') != 'udp') {
+ dict.url += '?transport=' + el.attr('transport');
+ }
+ if (el.attr('password')) {
+ dict.credential = el.attr('password');
+ }
+ iceservers.push(dict);
+ break;
+ }
+ });
+ self.ice_config.iceServers = iceservers;
+ },
+ function (err) {
+ console.warn('getting turn credentials failed', err);
+ console.warn('is mod_turncredentials or similar installed?');
+ }
+ );
+ // implement push?
+ },
+
+ /**
+ * Populates the log data
+ */
+ populateData: function () {
+ var data = {};
+ Object.keys(this.sessions).forEach(function (sid) {
+ var session = this.sessions[sid];
+ if (session.peerconnection && session.peerconnection.updateLog) {
+ // FIXME: should probably be a .dump call
+ data["jingle_" + session.sid] = {
+ updateLog: session.peerconnection.updateLog,
+ stats: session.peerconnection.stats,
+ url: window.location.href
+ };
+ }
+ });
+ return data;
+ }
+ });
+};
+
+
+},{"./JingleSession":1}],10:[function(require,module,exports){
+/* global Strophe */
+module.exports = function () {
+
+ Strophe.addConnectionPlugin('logger', {
+ // logs raw stanzas and makes them available for download as JSON
+ connection: null,
+ log: [],
+ init: function (conn) {
+ this.connection = conn;
+ this.connection.rawInput = this.log_incoming.bind(this);
+ this.connection.rawOutput = this.log_outgoing.bind(this);
+ },
+ log_incoming: function (stanza) {
+ this.log.push([new Date().getTime(), 'incoming', stanza]);
+ },
+ log_outgoing: function (stanza) {
+ this.log.push([new Date().getTime(), 'outgoing', stanza]);
+ }
+ });
+};
+},{}],11:[function(require,module,exports){
+/* global $, $iq, config, connection, focusMucJid, forceMuted,
+ setAudioMuted, Strophe */
+/**
+ * Moderate connection plugin.
+ */
+module.exports = function (XMPP) {
+ Strophe.addConnectionPlugin('moderate', {
+ connection: null,
+ init: function (conn) {
+ this.connection = conn;
+
+ this.connection.addHandler(this.onMute.bind(this),
+ 'http://jitsi.org/jitmeet/audio',
+ 'iq',
+ 'set',
+ null,
+ null);
+ },
+ setMute: function (jid, mute) {
+ console.info("set mute", mute);
+ var iqToFocus = $iq({to: focusMucJid, type: 'set'})
+ .c('mute', {
+ xmlns: 'http://jitsi.org/jitmeet/audio',
+ jid: jid
+ })
+ .t(mute.toString())
+ .up();
+
+ this.connection.sendIQ(
+ iqToFocus,
+ function (result) {
+ console.log('set mute', result);
+ },
+ function (error) {
+ console.log('set mute error', error);
+ });
+ },
+ onMute: function (iq) {
+ var from = iq.getAttribute('from');
+ if (from !== focusMucJid) {
+ console.warn("Ignored mute from non focus peer");
+ return false;
+ }
+ var mute = $(iq).find('mute');
+ if (mute.length) {
+ var doMuteAudio = mute.text() === "true";
+ UI.setAudioMuted(doMuteAudio);
+ XMPP.forceMuted = doMuteAudio;
+ }
+ return true;
+ },
+ eject: function (jid) {
+ // We're not the focus, so can't terminate
+ //connection.jingle.terminateRemoteByJid(jid, 'kick');
+ this.connection.emuc.kick(jid);
+ }
+ });
+}
+},{}],12:[function(require,module,exports){
+/* jshint -W117 */
+module.exports = function() {
+ Strophe.addConnectionPlugin('rayo',
+ {
+ RAYO_XMLNS: 'urn:xmpp:rayo:1',
+ connection: null,
+ init: function (conn) {
+ this.connection = conn;
+ if (this.connection.disco) {
+ this.connection.disco.addFeature('urn:xmpp:rayo:client:1');
+ }
+
+ this.connection.addHandler(
+ this.onRayo.bind(this), this.RAYO_XMLNS, 'iq', 'set', null, null);
+ },
+ onRayo: function (iq) {
+ console.info("Rayo IQ", iq);
+ },
+ dial: function (to, from, roomName, roomPass) {
+ var self = this;
+ var req = $iq(
+ {
+ type: 'set',
+ to: focusMucJid
+ }
+ );
+ req.c('dial',
+ {
+ xmlns: this.RAYO_XMLNS,
+ to: to,
+ from: from
+ });
+ req.c('header',
+ {
+ name: 'JvbRoomName',
+ value: roomName
+ }).up();
+
+ if (roomPass !== null && roomPass.length) {
+
+ req.c('header',
+ {
+ name: 'JvbRoomPassword',
+ value: roomPass
+ }).up();
+ }
+
+ this.connection.sendIQ(
+ req,
+ function (result) {
+ console.info('Dial result ', result);
+
+ var resource = $(result).find('ref').attr('uri');
+ this.call_resource = resource.substr('xmpp:'.length);
+ console.info(
+ "Received call resource: " + this.call_resource);
+ },
+ function (error) {
+ console.info('Dial error ', error);
+ }
+ );
+ },
+ hang_up: function () {
+ if (!this.call_resource) {
+ console.warn("No call in progress");
+ return;
+ }
+
+ var self = this;
+ var req = $iq(
+ {
+ type: 'set',
+ to: this.call_resource
+ }
+ );
+ req.c('hangup',
+ {
+ xmlns: this.RAYO_XMLNS
+ });
+
+ this.connection.sendIQ(
+ req,
+ function (result) {
+ console.info('Hangup result ', result);
+ self.call_resource = null;
+ },
+ function (error) {
+ console.info('Hangup error ', error);
+ self.call_resource = null;
+ }
+ );
+ }
+ }
+ );
+};
+
+},{}],13:[function(require,module,exports){
+/**
+ * Strophe logger implementation. Logs from level WARN and above.
+ */
+module.exports = function () {
+
+ Strophe.log = function (level, msg) {
+ switch (level) {
+ case Strophe.LogLevel.WARN:
+ console.warn("Strophe: " + msg);
+ break;
+ case Strophe.LogLevel.ERROR:
+ case Strophe.LogLevel.FATAL:
+ console.error("Strophe: " + msg);
+ break;
+ }
+ };
+
+ Strophe.getStatusString = function (status) {
+ switch (status) {
+ case Strophe.Status.ERROR:
+ return "ERROR";
+ case Strophe.Status.CONNECTING:
+ return "CONNECTING";
+ case Strophe.Status.CONNFAIL:
+ return "CONNFAIL";
+ case Strophe.Status.AUTHENTICATING:
+ return "AUTHENTICATING";
+ case Strophe.Status.AUTHFAIL:
+ return "AUTHFAIL";
+ case Strophe.Status.CONNECTED:
+ return "CONNECTED";
+ case Strophe.Status.DISCONNECTED:
+ return "DISCONNECTED";
+ case Strophe.Status.DISCONNECTING:
+ return "DISCONNECTING";
+ case Strophe.Status.ATTACHED:
+ return "ATTACHED";
+ default:
+ return "unknown";
+ }
+ };
+};
+
+},{}],14:[function(require,module,exports){
+var Moderator = require("./moderator");
+var EventEmitter = require("events");
+var Recording = require("./recording");
+var SDP = require("./SDP");
+
+var eventEmitter = new EventEmitter();
+var connection = null;
+var authenticatedUser = false;
+var activecall = null;
+
+function connect(jid, password, uiCredentials) {
+ var bosh
+ = uiCredentials.bosh || config.bosh || '/http-bind';
+ connection = new Strophe.Connection(bosh);
+ Moderator.setConnection(connection);
+
+ var settings = UI.getSettings();
+ var email = settings.email;
+ var displayName = settings.displayName;
+ if(email) {
+ connection.emuc.addEmailToPresence(email);
+ } else {
+ connection.emuc.addUserIdToPresence(settings.uid);
+ }
+ if(displayName) {
+ connection.emuc.addDisplayNameToPresence(displayName);
+ }
+
+ if (connection.disco) {
+ // for chrome, add multistream cap
+ }
+ connection.jingle.pc_constraints = RTC.getPCConstraints();
+ if (config.useIPv6) {
+ // https://code.google.com/p/webrtc/issues/detail?id=2828
+ if (!connection.jingle.pc_constraints.optional)
+ connection.jingle.pc_constraints.optional = [];
+ connection.jingle.pc_constraints.optional.push({googIPv6: true});
+ }
+
+ if(!password)
+ password = uiCredentials.password;
+
+ var anonymousConnectionFailed = false;
+ connection.connect(jid, password, function (status, msg) {
+ console.log('Strophe status changed to',
+ Strophe.getStatusString(status));
+ if (status === Strophe.Status.CONNECTED) {
+ if (config.useStunTurn) {
+ connection.jingle.getStunAndTurnCredentials();
+ }
+ UI.disableConnect();
+
+ console.info("My Jabber ID: " + connection.jid);
+
+ if(password)
+ authenticatedUser = true;
+ maybeDoJoin();
+ } else if (status === Strophe.Status.CONNFAIL) {
+ if(msg === 'x-strophe-bad-non-anon-jid') {
+ anonymousConnectionFailed = true;
+ }
+ } else if (status === Strophe.Status.DISCONNECTED) {
+ if(anonymousConnectionFailed) {
+ // prompt user for username and password
+ XMPP.promptLogin();
+ }
+ } else if (status === Strophe.Status.AUTHFAIL) {
+ // wrong password or username, prompt user
+ XMPP.promptLogin();
+
+ }
+ });
+}
+
+
+
+function maybeDoJoin() {
+ if (connection && connection.connected &&
+ Strophe.getResourceFromJid(connection.jid)
+ && (RTC.localAudio || RTC.localVideo)) {
+ // .connected is true while connecting?
+ doJoin();
+ }
+}
+
+function doJoin() {
+ var roomName = UI.generateRoomName();
+
+ Moderator.allocateConferenceFocus(
+ roomName, UI.checkForNicknameAndJoin);
+}
+
+function initStrophePlugins()
+{
+ require("./strophe.emuc")(XMPP, eventEmitter);
+ require("./strophe.jingle")();
+ require("./strophe.moderate")(XMPP);
+ require("./strophe.util")();
+ require("./strophe.rayo")();
+ require("./strophe.logger")();
+}
+
+function registerListeners() {
+ RTC.addStreamListener(maybeDoJoin,
+ StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
+}
+
+function setupEvents() {
+ $(window).bind('beforeunload', function () {
+ if (connection && connection.connected) {
+ // ensure signout
+ $.ajax({
+ type: 'POST',
+ url: config.bosh,
+ async: false,
+ cache: false,
+ contentType: 'application/xml',
+ data: "" +
+ "" +
+ "",
+ success: function (data) {
+ console.log('signed out');
+ console.log(data);
+ },
+ error: function (XMLHttpRequest, textStatus, errorThrown) {
+ console.log('signout error',
+ textStatus + ' (' + errorThrown + ')');
+ }
+ });
+ }
+ XMPP.disposeConference(true);
+ });
+}
+
+var XMPP = {
+ sessionTerminated: false,
+ /**
+ * Remembers if we were muted by the focus.
+ * @type {boolean}
+ */
+ forceMuted: false,
+ start: function (uiCredentials) {
+ setupEvents();
+ initStrophePlugins();
+ registerListeners();
+ Moderator.init();
+ var jid = uiCredentials.jid ||
+ config.hosts.anonymousdomain ||
+ config.hosts.domain ||
+ window.location.hostname;
+ connect(jid, null, uiCredentials);
+ },
+ promptLogin: function () {
+ UI.showLoginPopup(connect);
+ },
+ joinRooom: function(roomName, useNicks, nick)
+ {
+ var roomjid;
+ roomjid = roomName;
+
+ if (useNicks) {
+ if (nick) {
+ roomjid += '/' + nick;
+ } else {
+ roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
+ }
+ } else {
+
+ var tmpJid = Strophe.getNodeFromJid(connection.jid);
+
+ if(!authenticatedUser)
+ tmpJid = tmpJid.substr(0, 8);
+
+ roomjid += '/' + tmpJid;
+ }
+ connection.emuc.doJoin(roomjid);
+ },
+ myJid: function () {
+ if(!connection)
+ return null;
+ return connection.emuc.myroomjid;
+ },
+ myResource: function () {
+ if(!connection || ! connection.emuc.myroomjid)
+ return null;
+ return Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ },
+ disposeConference: function (onUnload) {
+ eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
+ var handler = activecall;
+ if (handler && handler.peerconnection) {
+ // FIXME: probably removing streams is not required and close() should
+ // be enough
+ if (RTC.localAudio) {
+ handler.peerconnection.removeStream(RTC.localAudio.getOriginalStream(), onUnload);
+ }
+ if (RTC.localVideo) {
+ handler.peerconnection.removeStream(RTC.localVideo.getOriginalStream(), onUnload);
+ }
+ handler.peerconnection.close();
+ }
+ activecall = null;
+ if(!onUnload)
+ {
+ this.sessionTerminated = true;
+ connection.emuc.doLeave();
+ }
+ },
+ addListener: function(type, listener)
+ {
+ eventEmitter.on(type, listener);
+ },
+ removeListener: function (type, listener) {
+ eventEmitter.removeListener(type, listener);
+ },
+ allocateConferenceFocus: function(roomName, callback) {
+ Moderator.allocateConferenceFocus(roomName, callback);
+ },
+ isModerator: function () {
+ return Moderator.isModerator();
+ },
+ isSipGatewayEnabled: function () {
+ return Moderator.isSipGatewayEnabled();
+ },
+ isExternalAuthEnabled: function () {
+ return Moderator.isExternalAuthEnabled();
+ },
+ switchStreams: function (stream, oldStream, callback) {
+ if (activecall) {
+ // FIXME: will block switchInProgress on true value in case of exception
+ activecall.switchStreams(stream, oldStream, callback);
+ } else {
+ // We are done immediately
+ console.error("No conference handler");
+ UI.messageHandler.showError('Error',
+ 'Unable to switch video stream.');
+ callback();
+ }
+ },
+ setVideoMute: function (mute, callback, options) {
+ if(activecall && connection && RTC.localVideo)
+ {
+ activecall.setVideoMute(mute, callback, options);
+ }
+ },
+ setAudioMute: function (mute, callback) {
+ if (!(connection && RTC.localAudio)) {
+ return false;
+ }
+
+
+ if (this.forceMuted && !mute) {
+ console.info("Asking focus for unmute");
+ connection.moderate.setMute(connection.emuc.myroomjid, mute);
+ // FIXME: wait for result before resetting muted status
+ this.forceMuted = false;
+ }
+
+ if (mute == RTC.localAudio.isMuted()) {
+ // Nothing to do
+ return true;
+ }
+
+ // It is not clear what is the right way to handle multiple tracks.
+ // So at least make sure that they are all muted or all unmuted and
+ // that we send presence just once.
+ RTC.localAudio.mute();
+ // isMuted is the opposite of audioEnabled
+ connection.emuc.addAudioInfoToPresence(mute);
+ connection.emuc.sendPresence();
+ callback();
+ return true;
+ },
+ // Really mute video, i.e. dont even send black frames
+ muteVideo: function (pc, unmute) {
+ // FIXME: this probably needs another of those lovely state safeguards...
+ // which checks for iceconn == connected and sigstate == stable
+ pc.setRemoteDescription(pc.remoteDescription,
+ function () {
+ pc.createAnswer(
+ function (answer) {
+ var sdp = new SDP(answer.sdp);
+ if (sdp.media.length > 1) {
+ if (unmute)
+ sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
+ else
+ sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
+ sdp.raw = sdp.session + sdp.media.join('');
+ answer.sdp = sdp.raw;
+ }
+ pc.setLocalDescription(answer,
+ function () {
+ console.log('mute SLD ok');
+ },
+ function (error) {
+ console.log('mute SLD error');
+ UI.messageHandler.showError('Error',
+ 'Oops! Something went wrong and we failed to ' +
+ 'mute! (SLD Failure)');
+ }
+ );
+ },
+ function (error) {
+ console.log(error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('muteVideo SRD error');
+ UI.messageHandler.showError('Error',
+ 'Oops! Something went wrong and we failed to stop video!' +
+ '(SRD Failure)');
+
+ }
+ );
+ },
+ toggleRecording: function (tokenEmptyCallback,
+ startingCallback, startedCallback) {
+ Recording.toggleRecording(tokenEmptyCallback,
+ startingCallback, startedCallback);
+ },
+ addToPresence: function (name, value, dontSend) {
+ switch (name)
+ {
+ case "displayName":
+ connection.emuc.addDisplayNameToPresence(value);
+ break;
+ case "etherpad":
+ connection.emuc.addEtherpadToPresence(value);
+ break;
+ case "prezi":
+ connection.emuc.addPreziToPresence(value, 0);
+ break;
+ case "preziSlide":
+ connection.emuc.addCurrentSlideToPresence(value);
+ break;
+ case "connectionQuality":
+ connection.emuc.addConnectionInfoToPresence(value);
+ break;
+ case "email":
+ connection.emuc.addEmailToPresence(value);
+ default :
+ console.log("Unknown tag for presence.");
+ return;
+ }
+ if(!dontSend)
+ connection.emuc.sendPresence();
+ },
+ sendLogs: function (content) {
+ // XEP-0337-ish
+ var message = $msg({to: focusMucJid, type: 'normal'});
+ message.c('log', { xmlns: 'urn:xmpp:eventlog',
+ id: 'PeerConnectionStats'});
+ message.c('message').t(content).up();
+ if (deflate) {
+ message.c('tag', {name: "deflated", value: "true"}).up();
+ }
+ message.up();
+
+ connection.send(message);
+ },
+ populateData: function () {
+ var data = {};
+ if (connection.jingle) {
+ data = connection.jingle.populateData();
+ }
+ return data;
+ },
+ getLogger: function () {
+ if(connection.logger)
+ return connection.logger.log;
+ return null;
+ },
+ getPrezi: function () {
+ return connection.emuc.getPrezi(this.myJid());
+ },
+ removePreziFromPresence: function () {
+ connection.emuc.removePreziFromPresence();
+ connection.emuc.sendPresence();
+ },
+ sendChatMessage: function (message, nickname) {
+ connection.emuc.sendMessage(message, nickname);
+ },
+ setSubject: function (topic) {
+ connection.emuc.setSubject(topic);
+ },
+ lockRoom: function (key, onSuccess, onError, onNotSupported) {
+ connection.emuc.lockRoom(key, onSuccess, onError, onNotSupported);
+ },
+ dial: function (to, from, roomName,roomPass) {
+ connection.rayo.dial(to, from, roomName,roomPass);
+ },
+ setMute: function (jid, mute) {
+ connection.moderate.setMute(jid, mute);
+ },
+ eject: function (jid) {
+ connection.moderate.eject(jid);
+ },
+ findJidFromResource: function (resource) {
+ connection.emuc.findJidFromResource(resource);
+ },
+ getMembers: function () {
+ return connection.emuc.members;
+ }
+
+};
+
+module.exports = XMPP;
+},{"./SDP":2,"./moderator":6,"./recording":7,"./strophe.emuc":8,"./strophe.jingle":9,"./strophe.logger":10,"./strophe.moderate":11,"./strophe.rayo":12,"./strophe.util":13,"events":15}],15:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+function EventEmitter() {
+ this._events = this._events || {};
+ this._maxListeners = this._maxListeners || undefined;
+}
+module.exports = EventEmitter;
+
+// Backwards-compat with node 0.10.x
+EventEmitter.EventEmitter = EventEmitter;
+
+EventEmitter.prototype._events = undefined;
+EventEmitter.prototype._maxListeners = undefined;
+
+// By default EventEmitters will print a warning if more than 10 listeners are
+// added to it. This is a useful default which helps finding memory leaks.
+EventEmitter.defaultMaxListeners = 10;
+
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+EventEmitter.prototype.setMaxListeners = function(n) {
+ if (!isNumber(n) || n < 0 || isNaN(n))
+ throw TypeError('n must be a positive number');
+ this._maxListeners = n;
+ return this;
+};
+
+EventEmitter.prototype.emit = function(type) {
+ var er, handler, len, args, i, listeners;
+
+ if (!this._events)
+ this._events = {};
+
+ // If there is no 'error' event listener then throw.
+ if (type === 'error') {
+ if (!this._events.error ||
+ (isObject(this._events.error) && !this._events.error.length)) {
+ er = arguments[1];
+ if (er instanceof Error) {
+ throw er; // Unhandled 'error' event
+ } else {
+ throw TypeError('Uncaught, unspecified "error" event.');
+ }
+ return false;
+ }
+ }
+
+ handler = this._events[type];
+
+ if (isUndefined(handler))
+ return false;
+
+ if (isFunction(handler)) {
+ switch (arguments.length) {
+ // fast cases
+ case 1:
+ handler.call(this);
+ break;
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
+ // slower
+ default:
+ len = arguments.length;
+ args = new Array(len - 1);
+ for (i = 1; i < len; i++)
+ args[i - 1] = arguments[i];
+ handler.apply(this, args);
+ }
+ } else if (isObject(handler)) {
+ len = arguments.length;
+ args = new Array(len - 1);
+ for (i = 1; i < len; i++)
+ args[i - 1] = arguments[i];
+
+ listeners = handler.slice();
+ len = listeners.length;
+ for (i = 0; i < len; i++)
+ listeners[i].apply(this, args);
+ }
+
+ return true;
+};
+
+EventEmitter.prototype.addListener = function(type, listener) {
+ var m;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events)
+ this._events = {};
+
+ // To avoid recursion in the case that type === "newListener"! Before
+ // adding it to the listeners, first emit "newListener".
+ if (this._events.newListener)
+ this.emit('newListener', type,
+ isFunction(listener.listener) ?
+ listener.listener : listener);
+
+ if (!this._events[type])
+ // Optimize the case of one listener. Don't need the extra array object.
+ this._events[type] = listener;
+ else if (isObject(this._events[type]))
+ // If we've already got an array, just append.
+ this._events[type].push(listener);
+ else
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+
+ // Check for listener leak
+ if (isObject(this._events[type]) && !this._events[type].warned) {
+ var m;
+ if (!isUndefined(this._maxListeners)) {
+ m = this._maxListeners;
+ } else {
+ m = EventEmitter.defaultMaxListeners;
+ }
+
+ if (m && m > 0 && this._events[type].length > m) {
+ this._events[type].warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ this._events[type].length);
+ if (typeof console.trace === 'function') {
+ // not supported in IE 10
+ console.trace();
+ }
+ }
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.once = function(type, listener) {
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ var fired = false;
+
+ function g() {
+ this.removeListener(type, g);
+
+ if (!fired) {
+ fired = true;
+ listener.apply(this, arguments);
+ }
+ }
+
+ g.listener = listener;
+ this.on(type, g);
+
+ return this;
+};
+
+// emits a 'removeListener' event iff the listener was removed
+EventEmitter.prototype.removeListener = function(type, listener) {
+ var list, position, length, i;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events || !this._events[type])
+ return this;
+
+ list = this._events[type];
+ length = list.length;
+ position = -1;
+
+ if (list === listener ||
+ (isFunction(list.listener) && list.listener === listener)) {
+ delete this._events[type];
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+
+ } else if (isObject(list)) {
+ for (i = length; i-- > 0;) {
+ if (list[i] === listener ||
+ (list[i].listener && list[i].listener === listener)) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position < 0)
+ return this;
+
+ if (list.length === 1) {
+ list.length = 0;
+ delete this._events[type];
+ } else {
+ list.splice(position, 1);
+ }
+
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.removeAllListeners = function(type) {
+ var key, listeners;
+
+ if (!this._events)
+ return this;
+
+ // not listening for removeListener, no need to emit
+ if (!this._events.removeListener) {
+ if (arguments.length === 0)
+ this._events = {};
+ else if (this._events[type])
+ delete this._events[type];
+ return this;
+ }
+
+ // emit removeListener for all listeners on all events
+ if (arguments.length === 0) {
+ for (key in this._events) {
+ if (key === 'removeListener') continue;
+ this.removeAllListeners(key);
+ }
+ this.removeAllListeners('removeListener');
+ this._events = {};
+ return this;
+ }
+
+ listeners = this._events[type];
+
+ if (isFunction(listeners)) {
+ this.removeListener(type, listeners);
+ } else {
+ // LIFO order
+ while (listeners.length)
+ this.removeListener(type, listeners[listeners.length - 1]);
+ }
+ delete this._events[type];
+
+ return this;
+};
+
+EventEmitter.prototype.listeners = function(type) {
+ var ret;
+ if (!this._events || !this._events[type])
+ ret = [];
+ else if (isFunction(this._events[type]))
+ ret = [this._events[type]];
+ else
+ ret = this._events[type].slice();
+ return ret;
+};
+
+EventEmitter.listenerCount = function(emitter, type) {
+ var ret;
+ if (!emitter._events || !emitter._events[type])
+ ret = 0;
+ else if (isFunction(emitter._events[type]))
+ ret = 1;
+ else
+ ret = emitter._events[type].length;
+ return ret;
+};
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+
+},{}]},{},[14])(14)
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvSmluZ2xlU2Vzc2lvbi5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9TRFAuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvU0RQRGlmZmVyLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL1NEUFV0aWwuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24uanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvbW9kZXJhdG9yLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL3JlY29yZGluZy5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9zdHJvcGhlLmVtdWMuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5qaW5nbGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5sb2dnZXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5tb2RlcmF0ZS5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9zdHJvcGhlLnJheW8uanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS51dGlsLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL3htcHAuanMiLCIvdXNyL2xvY2FsL2xpYi9ub2RlX21vZHVsZXMvYnJvd3NlcmlmeS9ub2RlX21vZHVsZXMvZXZlbnRzL2V2ZW50cy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1MkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1bUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNVZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxUUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwUEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2SkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOVVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoganNoaW50IC1XMTE3ICovXG52YXIgVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24gPSByZXF1aXJlKFwiLi9UcmFjZWFibGVQZWVyQ29ubmVjdGlvblwiKTtcbnZhciBTRFBEaWZmZXIgPSByZXF1aXJlKFwiLi9TRFBEaWZmZXJcIik7XG52YXIgU0RQVXRpbCA9IHJlcXVpcmUoXCIuL1NEUFV0aWxcIik7XG52YXIgU0RQID0gcmVxdWlyZShcIi4vU0RQXCIpO1xuXG4vLyBKaW5nbGUgc3R1ZmZcbmZ1bmN0aW9uIEppbmdsZVNlc3Npb24obWUsIHNpZCwgY29ubmVjdGlvbiwgc2VydmljZSkge1xuICAgIHRoaXMubWUgPSBtZTtcbiAgICB0aGlzLnNpZCA9IHNpZDtcbiAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xuICAgIHRoaXMuaW5pdGlhdG9yID0gbnVsbDtcbiAgICB0aGlzLnJlc3BvbmRlciA9IG51bGw7XG4gICAgdGhpcy5pc0luaXRpYXRvciA9IG51bGw7XG4gICAgdGhpcy5wZWVyamlkID0gbnVsbDtcbiAgICB0aGlzLnN0YXRlID0gbnVsbDtcbiAgICB0aGlzLmxvY2FsU0RQID0gbnVsbDtcbiAgICB0aGlzLnJlbW90ZVNEUCA9IG51bGw7XG4gICAgdGhpcy5yZWxheWVkU3RyZWFtcyA9IFtdO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gbnVsbDtcbiAgICB0aGlzLnN0b3BUaW1lID0gbnVsbDtcbiAgICB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzID0gbnVsbDtcbiAgICB0aGlzLnBjX2NvbnN0cmFpbnRzID0gbnVsbDtcbiAgICB0aGlzLmljZV9jb25maWcgPSB7fTtcbiAgICB0aGlzLmRyaXBfY29udGFpbmVyID0gW107XG4gICAgdGhpcy5zZXJ2aWNlID0gc2VydmljZTtcblxuICAgIHRoaXMudXNldHJpY2tsZSA9IHRydWU7XG4gICAgdGhpcy51c2VwcmFuc3dlciA9IGZhbHNlOyAvLyBlYXJseSB0cmFuc3BvcnQgd2FybXVwIC0tIG1pbmQgeW91LCB0aGlzIG1pZ2h0IGZhaWwuIGRlcGVuZHMgb24gd2VicnRjIGlzc3VlIDE3MThcbiAgICB0aGlzLnVzZWRyaXAgPSBmYWxzZTsgLy8gZHJpcHBpbmcgaXMgc2VuZGluZyB0cmlja2xlIGNhbmRpZGF0ZXMgbm90IG9uZS1ieS1vbmVcblxuICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMubGFzdGljZWNhbmRpZGF0ZSA9IGZhbHNlO1xuXG4gICAgdGhpcy5zdGF0c2ludGVydmFsID0gbnVsbDtcblxuICAgIHRoaXMucmVhc29uID0gbnVsbDtcblxuICAgIHRoaXMuYWRkc3NyYyA9IFtdO1xuICAgIHRoaXMucmVtb3Zlc3NyYyA9IFtdO1xuICAgIHRoaXMucGVuZGluZ29wID0gbnVsbDtcbiAgICB0aGlzLnN3aXRjaHN0cmVhbXMgPSBmYWxzZTtcblxuICAgIHRoaXMud2FpdCA9IHRydWU7XG4gICAgdGhpcy5sb2NhbFN0cmVhbXNTU1JDID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBpbmRpY2F0b3Igd2hpY2ggZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSAobG9jYWwpIHZpZGVvIGhhcyBiZWVuIG11dGVkXG4gICAgICogaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgaW4gY29udHJhc3QgdG8gYW4gYXV0b21hdGljIGRlY2lzaW9uIG1hZGVcbiAgICAgKiBieSB0aGUgYXBwbGljYXRpb24gbG9naWMuXG4gICAgICovXG4gICAgdGhpcy52aWRlb011dGVCeVVzZXIgPSBmYWxzZTtcbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuaW5pdGlhdGUgPSBmdW5jdGlvbiAocGVlcmppZCwgaXNJbml0aWF0b3IpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09IG51bGwpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignYXR0ZW1wdCB0byBpbml0aWF0ZSBvbiBzZXNzaW9uICcgKyB0aGlzLnNpZCArXG4gICAgICAgICAgICAnaW4gc3RhdGUgJyArIHRoaXMuc3RhdGUpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuaXNJbml0aWF0b3IgPSBpc0luaXRpYXRvcjtcbiAgICB0aGlzLnN0YXRlID0gJ3BlbmRpbmcnO1xuICAgIHRoaXMuaW5pdGlhdG9yID0gaXNJbml0aWF0b3IgPyB0aGlzLm1lIDogcGVlcmppZDtcbiAgICB0aGlzLnJlc3BvbmRlciA9ICFpc0luaXRpYXRvciA/IHRoaXMubWUgOiBwZWVyamlkO1xuICAgIHRoaXMucGVlcmppZCA9IHBlZXJqaWQ7XG4gICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0aWNlY2FuZGlkYXRlID0gZmFsc2U7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uXG4gICAgICAgID0gbmV3IFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5pY2VfY29uZmlnLFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cyApO1xuXG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmljZWNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoZXZlbnQuY2FuZGlkYXRlKTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25hZGRzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJSRU1PVEUgU1RSRUFNIEFEREVEOiBcIiArIGV2ZW50LnN0cmVhbSArIFwiIC0gXCIgKyBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGV2ZW50KTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25yZW1vdmVzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgLy8gUmVtb3ZlIHRoZSBzdHJlYW0gZnJvbSByZW1vdGVTdHJlYW1zXG4gICAgICAgIC8vIEZJWE1FOiByZW1vdGVzdHJlYW1yZW1vdmVkLmppbmdsZSBub3QgZGVmaW5lZCBhbnl3aGVyZSh1bnVzZWQpXG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3JlbW90ZXN0cmVhbXJlbW92ZWQuamluZ2xlJywgW2V2ZW50LCBzZWxmLnNpZF0pO1xuICAgIH07XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGlmICghKHNlbGYgJiYgc2VsZi5wZWVyY29ubmVjdGlvbikpIHJldHVybjtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgaWYgKCEoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uKSkgcmV0dXJuO1xuICAgICAgICBzd2l0Y2ggKHNlbGYucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgICAgICBjYXNlICdjb25uZWN0ZWQnOlxuICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRUaW1lID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Rpc2Nvbm5lY3RlZCc6XG4gICAgICAgICAgICAgICAgdGhpcy5zdG9wVGltZSA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgb25JY2VDb25uZWN0aW9uU3RhdGVDaGFuZ2Uoc2VsZi5zaWQsIHNlbGYpO1xuICAgIH07XG4gICAgLy8gYWRkIGFueSBsb2NhbCBhbmQgcmVsYXllZCBzdHJlYW1cbiAgICBSVEMubG9jYWxTdHJlYW1zLmZvckVhY2goZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpKTtcbiAgICB9KTtcbiAgICB0aGlzLnJlbGF5ZWRTdHJlYW1zLmZvckVhY2goZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKHN0cmVhbSk7XG4gICAgfSk7XG59O1xuXG5mdW5jdGlvbiBvbkljZUNvbm5lY3Rpb25TdGF0ZUNoYW5nZShzaWQsIHNlc3Npb24pIHtcbiAgICBzd2l0Y2ggKHNlc3Npb24ucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIGNhc2UgJ2NoZWNraW5nJzpcbiAgICAgICAgICAgIHNlc3Npb24udGltZUNoZWNraW5nID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTtcbiAgICAgICAgICAgIHNlc3Npb24uZmlyc3Rjb25uZWN0ID0gdHJ1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdjb21wbGV0ZWQnOiAvLyBvbiBjYWxsZXIgc2lkZVxuICAgICAgICBjYXNlICdjb25uZWN0ZWQnOlxuICAgICAgICAgICAgaWYgKHNlc3Npb24uZmlyc3Rjb25uZWN0KSB7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5maXJzdGNvbm5lY3QgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB2YXIgbWV0YWRhdGEgPSB7fTtcbiAgICAgICAgICAgICAgICBtZXRhZGF0YS5zZXR1cFRpbWVcbiAgICAgICAgICAgICAgICAgICAgPSAobmV3IERhdGUoKSkuZ2V0VGltZSgpIC0gc2Vzc2lvbi50aW1lQ2hlY2tpbmc7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKHJlcyAmJiByZXMucmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXMucmVzdWx0KCkuZm9yRWFjaChmdW5jdGlvbiAocmVwb3J0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHJlcG9ydC50eXBlID09ICdnb29nQ2FuZGlkYXRlUGFpcicgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3J0LnN0YXQoJ2dvb2dBY3RpdmVDb25uZWN0aW9uJykgPT0gJ3RydWUnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLmxvY2FsQ2FuZGlkYXRlVHlwZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSByZXBvcnQuc3RhdCgnZ29vZ0xvY2FsQ2FuZGlkYXRlVHlwZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YS5yZW1vdGVDYW5kaWRhdGVUeXBlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IHJlcG9ydC5zdGF0KCdnb29nUmVtb3RlQ2FuZGlkYXRlVHlwZScpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvZyBwYWlyIGFzIHdlbGwgc28gd2UgY2FuIGdldCBuaWNlIHBpZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaGFydHNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEuY2FuZGlkYXRlUGFpclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSByZXBvcnQuc3RhdCgnZ29vZ0xvY2FsQ2FuZGlkYXRlVHlwZScpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnOycgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9ydC5zdGF0KCdnb29nUmVtb3RlQ2FuZGlkYXRlVHlwZScpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXBvcnQuc3RhdCgnZ29vZ1JlbW90ZUFkZHJlc3MnKS5pbmRleE9mKCdbJykgPT09IDApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLmlwdjYgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWNjZXB0ID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnN0YXRlID0gJ2FjdGl2ZSc7XG5cbiAgICB2YXIgcHJhbnN3ZXIgPSB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb247XG4gICAgaWYgKCFwcmFuc3dlciB8fCBwcmFuc3dlci50eXBlICE9ICdwcmFuc3dlcicpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zb2xlLmxvZygnZ29pbmcgZnJvbSBwcmFuc3dlciB0byBhbnN3ZXInKTtcbiAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgIC8vIHJlbW92ZSBjYW5kaWRhdGVzIGFscmVhZHkgc2VudCBmcm9tIHNlc3Npb24tYWNjZXB0XG4gICAgICAgIHZhciBsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyhwcmFuc3dlci5zZHAsICdhPWNhbmRpZGF0ZTonKTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcHJhbnN3ZXIuc2RwID0gcHJhbnN3ZXIuc2RwLnJlcGxhY2UobGluZXNbaV0gKyAnXFxyXFxuJywgJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHdoaWxlIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5zZHAsICdhPWluYWN0aXZlJykpIHtcbiAgICAgICAgLy8gRklYTUU6IGNoYW5nZSBhbnkgaW5hY3RpdmUgdG8gc2VuZHJlY3Ygb3Igd2hhdGV2ZXIgdGhleSB3ZXJlIG9yaWdpbmFsbHlcbiAgICAgICAgcHJhbnN3ZXIuc2RwID0gcHJhbnN3ZXIuc2RwLnJlcGxhY2UoJ2E9aW5hY3RpdmUnLCAnYT1zZW5kcmVjdicpO1xuICAgIH1cbiAgICBwcmFuc3dlciA9IHNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihwcmFuc3dlcik7XG4gICAgdmFyIHByc2RwID0gbmV3IFNEUChwcmFuc3dlci5zZHApO1xuICAgIHZhciBhY2NlcHQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3Nlc3Npb24tYWNjZXB0JyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICByZXNwb25kZXI6IHRoaXMucmVzcG9uZGVyLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZCB9KTtcbiAgICBwcnNkcC50b0ppbmdsZShhY2NlcHQsIHRoaXMuaW5pdGlhdG9yID09IHRoaXMubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLCB0aGlzLmxvY2FsU3RyZWFtc1NTUkMpO1xuICAgIHZhciBzZHAgPSB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwO1xuICAgIHdoaWxlIChTRFBVdGlsLmZpbmRfbGluZShzZHAsICdhPWluYWN0aXZlJykpIHtcbiAgICAgICAgLy8gRklYTUU6IGNoYW5nZSBhbnkgaW5hY3RpdmUgdG8gc2VuZHJlY3Ygb3Igd2hhdGV2ZXIgdGhleSB3ZXJlIG9yaWdpbmFsbHlcbiAgICAgICAgc2RwID0gc2RwLnJlcGxhY2UoJ2E9aW5hY3RpdmUnLCAnYT1zZW5kcmVjdicpO1xuICAgIH1cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRMb2NhbERlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdhbnN3ZXInLCBzZHA6IHNkcH0pLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuXG4gICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lXG4gICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUudGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbikge1xuICAgIHRoaXMuc3RhdGUgPSAnZW5kZWQnO1xuICAgIHRoaXMucmVhc29uID0gcmVhc29uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWN0aXZlID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLnN0YXRlID09ICdhY3RpdmUnO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChjYW5kaWRhdGUpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKGNhbmRpZGF0ZSAmJiAhdGhpcy5sYXN0aWNlY2FuZGlkYXRlKSB7XG4gICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW2NhbmRpZGF0ZS5zZHBNTGluZUluZGV4XSwgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKTtcbiAgICAgICAgdmFyIGpjYW5kID0gU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShjYW5kaWRhdGUuY2FuZGlkYXRlKTtcbiAgICAgICAgaWYgKCEoaWNlICYmIGpjYW5kKSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignZmFpbGVkIHRvIGdldCBpY2UgJiYgamNhbmQnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcblxuICAgICAgICBpZiAoamNhbmQudHlwZSA9PT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChqY2FuZC50eXBlID09PSAncmVsYXknKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMudXNldHJpY2tsZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMudXNlZHJpcCkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRyaXBfY29udGFpbmVyLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBzdGFydCAyMG1zIGNhbGxvdXRcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuZHJpcF9jb250YWluZXIubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGVzKHNlbGYuZHJpcF9jb250YWluZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5kcmlwX2NvbnRhaW5lciA9IFtdO1xuICAgICAgICAgICAgICAgICAgICB9LCAyMCk7XG5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5kcmlwX2NvbnRhaW5lci5wdXNoKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoW2NhbmRpZGF0ZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy9jb25zb2xlLmxvZygnc2VuZEljZUNhbmRpZGF0ZTogbGFzdCBjYW5kaWRhdGUuJyk7XG4gICAgICAgIGlmICghdGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzaG91bGQgc2VuZCBmdWxsIG9mZmVyIG5vdy4uLicpO1xuICAgICAgICAgICAgdmFyIGluaXQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbjogdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnR5cGUgPT0gJ29mZmVyJyA/ICdzZXNzaW9uLWluaXRpYXRlJyA6ICdzZXNzaW9uLWFjY2VwdCcsXG4gICAgICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgICAgIHNpZDogdGhpcy5zaWR9KTtcbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgdmFyIHNlbmRKaW5nbGUgPSBmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIGlmKCFzc3JjKVxuICAgICAgICAgICAgICAgICAgICBzc3JjID0ge307XG4gICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC50b0ppbmdsZShpbml0LCBzZWxmLmluaXRpYXRvciA9PSBzZWxmLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJywgc3NyYyk7XG4gICAgICAgICAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShpbml0LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXNzaW9uIGluaXRpYXRlIGFjaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAnb2ZmZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbmRKaW5nbGUoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxhc3RpY2VjYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICBjb25zb2xlLmxvZygnSGF2ZSB3ZSBlbmNvdW50ZXJlZCBhbnkgc3JmbHggY2FuZGlkYXRlcz8gJyArIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdIYXZlIHdlIGVuY291bnRlcmVkIGFueSByZWxheSBjYW5kaWRhdGVzPyAnICsgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKTtcblxuICAgICAgICBpZiAoISh0aGlzLmhhZHN0dW5jYW5kaWRhdGUgfHwgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKSAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlICE9ICdjbG9zZWQnKSB7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdub3N0dW5jYW5kaWRhdGVzLmppbmdsZScsIFt0aGlzLnNpZF0pO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZXMgPSBmdW5jdGlvbiAoY2FuZGlkYXRlcykge1xuICAgIGNvbnNvbGUubG9nKCdzZW5kSWNlQ2FuZGlkYXRlcycsIGNhbmRpZGF0ZXMpO1xuICAgIHZhciBjYW5kID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICd0cmFuc3BvcnQtaW5mbycsXG4gICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pO1xuICAgIGZvciAodmFyIG1pZCA9IDA7IG1pZCA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBtaWQrKykge1xuICAgICAgICB2YXIgY2FuZHMgPSBjYW5kaWRhdGVzLmZpbHRlcihmdW5jdGlvbiAoZWwpIHsgcmV0dXJuIGVsLnNkcE1MaW5lSW5kZXggPT0gbWlkOyB9KTtcbiAgICAgICAgdmFyIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0uc3BsaXQoJ1xcclxcbicpWzBdKTtcbiAgICAgICAgaWYgKGNhbmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sIHRoaXMubG9jYWxTRFAuc2Vzc2lvbik7XG4gICAgICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgICAgIGNhbmQuYygnY29udGVudCcsIHtjcmVhdG9yOiB0aGlzLmluaXRpYXRvciA9PSB0aGlzLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJyxcbiAgICAgICAgICAgICAgICBuYW1lOiAoY2FuZHNbMF0uc2RwTWlkPyBjYW5kc1swXS5zZHBNaWQgOiBtbGluZS5tZWRpYSlcbiAgICAgICAgICAgIH0pLmMoJ3RyYW5zcG9ydCcsIGljZSk7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhbmRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgY2FuZC5jKCdjYW5kaWRhdGUnLCBTRFBVdGlsLmNhbmRpZGF0ZVRvSmluZ2xlKGNhbmRzW2ldLmNhbmRpZGF0ZSkpLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBhZGQgZmluZ2VycHJpbnRcbiAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICB2YXIgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpO1xuICAgICAgICAgICAgICAgIHRtcC5yZXF1aXJlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgY2FuZC5jKFxuICAgICAgICAgICAgICAgICAgICAnZmluZ2VycHJpbnQnLFxuICAgICAgICAgICAgICAgICAgICB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpkdGxzOjAnfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodG1wLmZpbmdlcnByaW50KTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdG1wLmZpbmdlcnByaW50O1xuICAgICAgICAgICAgICAgIGNhbmQuYXR0cnModG1wKTtcbiAgICAgICAgICAgICAgICBjYW5kLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYW5kLnVwKCk7IC8vIHRyYW5zcG9ydFxuICAgICAgICAgICAgY2FuZC51cCgpOyAvLyBjb250ZW50XG4gICAgICAgIH1cbiAgICB9XG4gICAgLy8gbWlnaHQgbWVyZ2UgbGFzdC1jYW5kaWRhdGUgbm90aWZpY2F0aW9uIGludG8gdGhpcywgYnV0IGl0IGlzIGNhbGxlZCBhbG90IGxhdGVyLiBTZWUgd2VicnRjIGlzc3VlICMyMzQwXG4gICAgLy9jb25zb2xlLmxvZygnd2FzIHRoaXMgdGhlIGxhc3QgY2FuZGlkYXRlJywgdGhpcy5sYXN0aWNlY2FuZGlkYXRlKTtcbiAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKGNhbmQsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgIGFjay5zb3VyY2UgPSAndHJhbnNwb3J0aW5mbyc7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3RoaXMuc2lkLCBhY2tdKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgIHJlYXNvbjogJChzdGFuemEpLmZpbmQoJ2Vycm9yIDpmaXJzdCcpWzBdLnRhZ05hbWUsXG4gICAgICAgICAgICB9Ont9O1xuICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ3RyYW5zcG9ydGluZm8nO1xuICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHRoaXMuc2lkLCBlcnJvcik7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbn07XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZE9mZmVyID0gZnVuY3Rpb24gKCkge1xuICAgIC8vY29uc29sZS5sb2coJ3NlbmRPZmZlci4uLicpO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZU9mZmVyKGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZE9mZmVyKHNkcCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdjcmVhdGVPZmZlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkT2ZmZXIgPSBmdW5jdGlvbiAoc2RwKSB7XG4gICAgLy9jb25zb2xlLmxvZygnY3JlYXRlZE9mZmVyJywgc2RwKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHZhciBzZW5kSmluZ2xlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW5pdCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbml0aWF0ZScsXG4gICAgICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICBzaWQ6IHRoaXMuc2lkfSk7XG4gICAgICAgIHNlbGYubG9jYWxTRFAudG9KaW5nbGUoaW5pdCwgdGhpcy5pbml0aWF0b3IgPT0gdGhpcy5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHRoaXMubG9jYWxTdHJlYW1zU1NSQyk7XG4gICAgICAgIHNlbGYuY29ubmVjdGlvbi5zZW5kSVEoaW5pdCxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB2YXIgYWNrID0ge307XG4gICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ29mZmVyJztcbiAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAxMDAwMCk7XG4gICAgfVxuICAgIHNkcC5zZHAgPSB0aGlzLmxvY2FsU0RQLnJhdztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24oc2RwLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBpZihzZWxmLnVzZXRyaWNrbGUpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xuICAgIHZhciBjYW5kcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLmxvY2FsU0RQLnJhdywgJ2E9Y2FuZGlkYXRlOicpO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FuZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIGNhbmQgPSBTRFBVdGlsLnBhcnNlX2ljZWNhbmRpZGF0ZShjYW5kc1tpXSk7XG4gICAgICAgIGlmIChjYW5kLnR5cGUgPT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChjYW5kLnR5cGUgPT0gJ3JlbGF5Jykge1xuICAgICAgICAgICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGVsZW0sIGRlc2N0eXBlKSB7XG4gICAgLy9jb25zb2xlLmxvZygnc2V0dGluZyByZW1vdGUgZGVzY3JpcHRpb24uLi4gJywgZGVzY3R5cGUpO1xuICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUCgnJyk7XG4gICAgdGhpcy5yZW1vdGVTRFAuZnJvbUppbmdsZShlbGVtKTtcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAhPT0gbnVsbCkge1xuICAgICAgICBjb25zb2xlLmxvZygnc2V0UmVtb3RlRGVzY3JpcHRpb24gd2hlbiByZW1vdGUgZGVzY3JpcHRpb24gaXMgbm90IG51bGwsIHNob3VsZCBiZSBwcmFuc3dlcicsIHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24pO1xuICAgICAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi50eXBlID09ICdwcmFuc3dlcicpIHtcbiAgICAgICAgICAgIHZhciBwcmFuc3dlciA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwcmFuc3dlci5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSB3ZSBoYXZlIGljZSB1ZnJhZyBhbmQgcHdkXG4gICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZSh0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHRoaXMucmVtb3RlU0RQLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSArPSBTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSB1ZnJhZz8nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUocHJhbnN3ZXIubWVkaWFbaV0sICdhPWljZS1wd2Q6JywgcHJhbnN3ZXIuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLm1lZGlhW2ldLCAnYT1pY2UtcHdkOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSBwd2Q/Jyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gY29weSBvdmVyIGNhbmRpZGF0ZXNcbiAgICAgICAgICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXMocHJhbnN3ZXIubWVkaWFbaV0sICdhPWNhbmRpZGF0ZTonKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciByZW1vdGVkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogZGVzY3R5cGUsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3fSk7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKHJlbW90ZWRlc2MsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldFJlbW90ZURlc2NyaXB0aW9uIGVycm9yJywgZSk7XG4gICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvcihzZWxmLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoZWxlbSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnY2xvc2VkJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdoYXZlLWxvY2FsLW9mZmVyJykge1xuICAgICAgICBjb25zb2xlLmxvZygndHJpY2tsZSBpY2UgY2FuZGlkYXRlIGFycml2aW5nIGJlZm9yZSBzZXNzaW9uIGFjY2VwdC4uLicpO1xuICAgICAgICAvLyBjcmVhdGUgYSBQUkFOU1dFUiBmb3Igc2V0UmVtb3RlRGVzY3JpcHRpb25cbiAgICAgICAgaWYgKCF0aGlzLnJlbW90ZVNEUCkge1xuICAgICAgICAgICAgdmFyIGNvYmJsZWQgPSAndj0wXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgICAgICAgICAncz0tXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ3Q9MCAwXFxyXFxuJztcbiAgICAgICAgICAgIC8vIGZpcnN0LCB0YWtlIHNvbWUgdGhpbmdzIGZyb20gdGhlIGxvY2FsIGRlc2NyaXB0aW9uXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdtPScpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgY29iYmxlZCArPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9cnRwbWFwOicpLmpvaW4oJ1xcclxcbicpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSkge1xuICAgICAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb2JibGVkICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUChjb2JibGVkKTtcbiAgICAgICAgfVxuICAgICAgICAvLyB0aGVuIGFkZCB0aGluZ3MgbGlrZSBpY2UgYW5kIGR0bHMgZnJvbSByZW1vdGUgY2FuZGlkYXRlXG4gICAgICAgIGVsZW0uZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNlbGYucmVtb3RlU0RQLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS11ZnJhZzonICsgdG1wLmF0dHIoJ3VmcmFnJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS1wd2Q6JyArIHRtcC5hdHRyKCdwd2QnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+ZmluZ2VycHJpbnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRtcC5hdHRyKCdoYXNoJykgKyAnICcgKyB0bXAudGV4dCgpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdubyBkdGxzIGZpbmdlcnByaW50ICh3ZWJydGMgaXNzdWUgIzE3MTg/KScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWNyeXB0bzoxIEFFU19DTV8xMjhfSE1BQ19TSEExXzgwIGlubGluZTpCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuXG4gICAgICAgIC8vIHdlIG5lZWQgYSBjb21wbGV0ZSBTRFAgd2l0aCBpY2UtdWZyYWcvaWNlLXB3ZCBpbiBhbGwgcGFydHNcbiAgICAgICAgLy8gdGhpcyBtYWtlcyB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBQUkFOU1dFUiBpcyBjb25zdHJ1Y3RlZCBzdWNoIHRoYXQgdGhlIGljZS11ZnJhZyBpcyBpbiBhbGwgbWVkaWFwYXJ0c1xuICAgICAgICAvLyBidXQgaXQgY291bGQgYmUgaW4gdGhlIHNlc3Npb24gcGFydCBhcyB3ZWxsLiBzaW5jZSB0aGUgY29kZSBhYm92ZSBjb25zdHJ1Y3RzIHRoaXMgc2RwIHRoaXMgY2FuJ3QgaGFwcGVuIGhvd2V2ZXJcbiAgICAgICAgdmFyIGlzY29tcGxldGUgPSB0aGlzLnJlbW90ZVNEUC5tZWRpYS5maWx0ZXIoZnVuY3Rpb24gKG1lZGlhcGFydCkge1xuICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWwuZmluZF9saW5lKG1lZGlhcGFydCwgJ2E9aWNlLXVmcmFnOicpO1xuICAgICAgICB9KS5sZW5ndGggPT0gdGhpcy5yZW1vdGVTRFAubWVkaWEubGVuZ3RoO1xuXG4gICAgICAgIGlmIChpc2NvbXBsZXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0dGluZyBwcmFuc3dlcicpO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdwcmFuc3dlcicsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3IH0pLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHByYW5zd2VyIGZhaWxlZCcsIGUudG9TdHJpbmcoKSk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldHRpbmcgcHJhbnN3ZXIgZmFpbGVkJywgZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdub3QgeWV0IHNldHRpbmcgcHJhbnN3ZXInKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBvcGVyYXRlIG9uIGVhY2ggY29udGVudCBlbGVtZW50XG4gICAgZWxlbS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gd291bGQgbG92ZSB0byBkZWFjdGl2YXRlIHRoaXMsIGJ1dCBmaXJlZm94IHN0aWxsIHJlcXVpcmVzIGl0XG4gICAgICAgIHZhciBpZHggPSAtMTtcbiAgICAgICAgdmFyIGk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBzZWxmLnJlbW90ZVNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLmluZGV4T2YoJ209JyArICQodGhpcykuYXR0cignbmFtZScpKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGlkeCA9IGk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlkeCA9PSAtMSkgeyAvLyBmYWxsIGJhY2sgdG8gbG9jYWxkZXNjcmlwdGlvblxuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHNlbGYubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUoc2VsZi5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9bWlkOicgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgfHxcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWR4ID0gaTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHZhciBuYW1lID0gJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgIC8vIFRPRE86IGNoZWNrIGljZS1wd2QgYW5kIGljZS11ZnJhZz9cbiAgICAgICAgJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+Y2FuZGlkYXRlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbGluZSwgY2FuZGlkYXRlO1xuICAgICAgICAgICAgbGluZSA9IFNEUFV0aWwuY2FuZGlkYXRlRnJvbUppbmdsZSh0aGlzKTtcbiAgICAgICAgICAgIGNhbmRpZGF0ZSA9IG5ldyBSVENJY2VDYW5kaWRhdGUoe3NkcE1MaW5lSW5kZXg6IGlkeCxcbiAgICAgICAgICAgICAgICBzZHBNaWQ6IG5hbWUsXG4gICAgICAgICAgICAgICAgY2FuZGlkYXRlOiBsaW5lfSk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignYWRkSWNlQ2FuZGlkYXRlIGZhaWxlZCcsIGUudG9TdHJpbmcoKSwgbGluZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEFuc3dlciA9IGZ1bmN0aW9uIChwcm92aXNpb25hbCkge1xuICAgIC8vY29uc29sZS5sb2coJ2NyZWF0ZUFuc3dlcicsIHByb3Zpc2lvbmFsKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgIGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZEFuc3dlcihzZHAsIHByb3Zpc2lvbmFsKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2NyZWF0ZUFuc3dlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkQW5zd2VyID0gZnVuY3Rpb24gKHNkcCwgcHJvdmlzaW9uYWwpIHtcbiAgICAvL2NvbnNvbGUubG9nKCdjcmVhdGVBbnN3ZXIgY2FsbGJhY2snKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHRoaXMudXNlcHJhbnN3ZXIgPSBwcm92aXNpb25hbCA9PT0gdHJ1ZTtcbiAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgIGlmICh0aGlzLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICBzZHAudHlwZSA9ICdwcmFuc3dlcic7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvY2FsU0RQLm1lZGlhW2ldID0gdGhpcy5sb2NhbFNEUC5tZWRpYVtpXS5yZXBsYWNlKCdhPXNlbmRyZWN2XFxyXFxuJywgJ2E9aW5hY3RpdmVcXHJcXG4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAucmF3ID0gdGhpcy5sb2NhbFNEUC5zZXNzaW9uICsgJ1xcclxcbicgKyB0aGlzLmxvY2FsU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgc2VuZEppbmdsZSA9IGZ1bmN0aW9uIChzc3Jjcykge1xuXG4gICAgICAgICAgICAgICAgdmFyIGFjY2VwdCA9ICRpcSh7dG86IHNlbGYucGVlcmppZCxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1hY2NlcHQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5pdGlhdG9yOiBzZWxmLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbmRlcjogc2VsZi5yZXNwb25kZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBzaWQ6IHNlbGYuc2lkIH0pO1xuICAgICAgICAgICAgICAgIHZhciBwdWJsaWNMb2NhbERlc2MgPSBzaW11bGNhc3QucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24oc2RwKTtcbiAgICAgICAgICAgICAgICB2YXIgcHVibGljTG9jYWxTRFAgPSBuZXcgU0RQKHB1YmxpY0xvY2FsRGVzYy5zZHApO1xuICAgICAgICAgICAgICAgIHB1YmxpY0xvY2FsU0RQLnRvSmluZ2xlKGFjY2VwdCwgc2VsZi5pbml0aWF0b3IgPT0gc2VsZi5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHNzcmNzKTtcbiAgICAgICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICB9XG4gICAgc2RwLnNkcCA9IHRoaXMubG9jYWxTRFAucmF3O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihzZHAsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcblxuICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnc2V0TG9jYWxEZXNjcmlwdGlvbiBzdWNjZXNzJyk7XG4gICAgICAgICAgICBpZiAoc2VsZi51c2V0cmlja2xlICYmICFzZWxmLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGUpO1xuICAgICAgICB9XG4gICAgKTtcbiAgICB2YXIgY2FuZHMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5yYXcsICdhPWNhbmRpZGF0ZTonKTtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IGNhbmRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHZhciBjYW5kID0gU0RQVXRpbC5wYXJzZV9pY2VjYW5kaWRhdGUoY2FuZHNbal0pO1xuICAgICAgICBpZiAoY2FuZC50eXBlID09ICdzcmZseCcpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAoY2FuZC50eXBlID09ICdyZWxheScpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kVGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbiwgdGV4dCkge1xuICAgIHZhciBzZWxmID0gdGhpcyxcbiAgICAgICAgdGVybSA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi10ZXJtaW5hdGUnLFxuICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pXG4gICAgICAgICAgICAuYygncmVhc29uJylcbiAgICAgICAgICAgIC5jKHJlYXNvbiB8fCAnc3VjY2VzcycpO1xuXG4gICAgaWYgKHRleHQpIHtcbiAgICAgICAgdGVybS51cCgpLmMoJ3RleHQnKS50KHRleHQpO1xuICAgIH1cblxuICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEodGVybSxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbiA9IG51bGw7XG4gICAgICAgICAgICBzZWxmLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICd0ZXJtaW5hdGUnO1xuICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGVycm9yXSk7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWRkU291cmNlID0gZnVuY3Rpb24gKGVsZW0sIGZyb21KaWQpIHtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBGSVhNRTogZGlydHkgd2FpdGluZ1xuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uKVxuICAgIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiYWRkU291cmNlIC0gbG9jYWxEZXNjcmlwdGlvbiBub3QgcmVhZHkgeWV0XCIpXG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNlbGYuYWRkU291cmNlKGVsZW0sIGZyb21KaWQpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIDIwMFxuICAgICAgICApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS5sb2coJ2FkZHNzcmMnLCBuZXcgRGF0ZSgpLmdldFRpbWUoKSk7XG4gICAgY29uc29sZS5sb2coJ2ljZScsIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICB2YXIgc2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG4gICAgdmFyIG15U2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcblxuICAgICQoZWxlbSkuZWFjaChmdW5jdGlvbiAoaWR4LCBjb250ZW50KSB7XG4gICAgICAgIHZhciBuYW1lID0gJChjb250ZW50KS5hdHRyKCduYW1lJyk7XG4gICAgICAgIHZhciBsaW5lcyA9ICcnO1xuICAgICAgICB0bXAgPSAkKGNvbnRlbnQpLmZpbmQoJ3NzcmMtZ3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJykuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgICAgICB2YXIgc3NyY3MgPSAkKHRoaXMpLmZpbmQoJz5zb3VyY2UnKS5tYXAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG5cbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdG1wID0gJChjb250ZW50KS5maW5kKCdzb3VyY2VbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJyk7IC8vIGNhbiBoYW5kbGUgYm90aCA+c291cmNlIGFuZCA+ZGVzY3JpcHRpb24+c291cmNlXG4gICAgICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBzc3JjID0gJCh0aGlzKS5hdHRyKCdzc3JjJyk7XG4gICAgICAgICAgICBpZihteVNkcC5jb250YWluc1NTUkMoc3NyYykpe1xuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIFRoaXMgaGFwcGVucyB3aGVuIG11bHRpcGxlIHBhcnRpY2lwYW50cyBjaGFuZ2UgdGhlaXIgc3RyZWFtcyBhdCB0aGUgc2FtZSB0aW1lIGFuZFxuICAgICAgICAgICAgICAgICAqIENvbGlicmlGb2N1cy5tb2RpZnlTb3VyY2VzIGhhdmUgdG8gd2FpdCBmb3Igc3RhYmxlIHN0YXRlLiBJbiB0aGUgbWVhbnRpbWUgbXVsdGlwbGVcbiAgICAgICAgICAgICAgICAgKiBhZGRzc3JjIGFyZSBzY2hlZHVsZWQgZm9yIHVwZGF0ZSBJUS4gU2VlXG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiR290IGFkZCBzdHJlYW0gcmVxdWVzdCBmb3IgbXkgb3duIHNzcmM6IFwiK3NzcmMpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgICQodGhpcykuZmluZCgnPnBhcmFtZXRlcicpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmM6JyArIHNzcmMgKyAnICcgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICBpZiAoJCh0aGlzKS5hdHRyKCd2YWx1ZScpICYmICQodGhpcykuYXR0cigndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgICAgIGxpbmVzICs9ICc6JyArICQodGhpcykuYXR0cigndmFsdWUnKTtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnXFxyXFxuJztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgc2RwLm1lZGlhLmZvckVhY2goZnVuY3Rpb24obWVkaWEsIGlkeCkge1xuICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShtZWRpYSwgJ2E9bWlkOicgKyBuYW1lKSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICAgICAgICAgIGlmICghc2VsZi5hZGRzc3JjW2lkeF0pIHNlbGYuYWRkc3NyY1tpZHhdID0gJyc7XG4gICAgICAgICAgICBzZWxmLmFkZHNzcmNbaWR4XSArPSBsaW5lcztcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICB9KTtcbiAgICB0aGlzLm1vZGlmeVNvdXJjZXMoKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnJlbW92ZVNvdXJjZSA9IGZ1bmN0aW9uIChlbGVtLCBmcm9tSmlkKSB7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gRklYTUU6IGRpcnR5IHdhaXRpbmdcbiAgICBpZiAoIXRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbilcbiAgICB7XG4gICAgICAgIGNvbnNvbGUud2FybihcInJlbW92ZVNvdXJjZSAtIGxvY2FsRGVzY3JpcHRpb24gbm90IHJlYWR5IHlldFwiKVxuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzZWxmLnJlbW92ZVNvdXJjZShlbGVtLCBmcm9tSmlkKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAyMDBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdyZW1vdmVzc3JjJywgbmV3IERhdGUoKS5nZXRUaW1lKCkpO1xuICAgIGNvbnNvbGUubG9nKCdpY2UnLCB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgdmFyIHNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgIHZhciBteVNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG5cbiAgICAkKGVsZW0pLmVhY2goZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICB2YXIgbmFtZSA9ICQoY29udGVudCkuYXR0cignbmFtZScpO1xuICAgICAgICB2YXIgbGluZXMgPSAnJztcbiAgICAgICAgdG1wID0gJChjb250ZW50KS5maW5kKCdzc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NlbWFudGljcycpO1xuICAgICAgICAgICAgdmFyIHNzcmNzID0gJCh0aGlzKS5maW5kKCc+c291cmNlJykubWFwKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgIH0pLmdldCgpO1xuXG4gICAgICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjLWdyb3VwOicgKyBzZW1hbnRpY3MgKyAnICcgKyBzc3Jjcy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHRtcCA9ICQoY29udGVudCkuZmluZCgnc291cmNlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpOyAvLyBjYW4gaGFuZGxlIGJvdGggPnNvdXJjZSBhbmQgPmRlc2NyaXB0aW9uPnNvdXJjZVxuICAgICAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc3NyYyA9ICQodGhpcykuYXR0cignc3NyYycpO1xuICAgICAgICAgICAgLy8gVGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuLCBidXQgY2FuIGJlIHVzZWZ1bCBmb3IgYnVnIGRldGVjdGlvblxuICAgICAgICAgICAgaWYobXlTZHAuY29udGFpbnNTU1JDKHNzcmMpKXtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiR290IHJlbW92ZSBzdHJlYW0gcmVxdWVzdCBmb3IgbXkgb3duIHNzcmM6IFwiK3NzcmMpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgICQodGhpcykuZmluZCgnPnBhcmFtZXRlcicpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmM6JyArIHNzcmMgKyAnICcgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICBpZiAoJCh0aGlzKS5hdHRyKCd2YWx1ZScpICYmICQodGhpcykuYXR0cigndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgICAgIGxpbmVzICs9ICc6JyArICQodGhpcykuYXR0cigndmFsdWUnKTtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnXFxyXFxuJztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgc2RwLm1lZGlhLmZvckVhY2goZnVuY3Rpb24obWVkaWEsIGlkeCkge1xuICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShtZWRpYSwgJ2E9bWlkOicgKyBuYW1lKSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICAgICAgICAgIGlmICghc2VsZi5yZW1vdmVzc3JjW2lkeF0pIHNlbGYucmVtb3Zlc3NyY1tpZHhdID0gJyc7XG4gICAgICAgICAgICBzZWxmLnJlbW92ZXNzcmNbaWR4XSArPSBsaW5lcztcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICB9KTtcbiAgICB0aGlzLm1vZGlmeVNvdXJjZXMoKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLm1vZGlmeVNvdXJjZXMgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmICh0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdjbG9zZWQnKSByZXR1cm47XG4gICAgaWYgKCEodGhpcy5hZGRzc3JjLmxlbmd0aCB8fCB0aGlzLnJlbW92ZXNzcmMubGVuZ3RoIHx8IHRoaXMucGVuZGluZ29wICE9PSBudWxsIHx8IHRoaXMuc3dpdGNoc3RyZWFtcykpe1xuICAgICAgICAvLyBUaGVyZSBpcyBub3RoaW5nIHRvIGRvIHNpbmNlIHNjaGVkdWxlZCBqb2IgbWlnaHQgaGF2ZSBiZWVuIGV4ZWN1dGVkIGJ5IGFub3RoZXIgc3VjY2VlZGluZyBjYWxsXG4gICAgICAgIHRoaXMuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuICAgICAgICBpZihzdWNjZXNzQ2FsbGJhY2spe1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEZJWE1FOiB0aGlzIGlzIGEgYmlnIGhhY2tcbiAgICAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTI2ODhcbiAgICAvLyBeIGhhcyBiZWVuIGZpeGVkLlxuICAgIGlmICghKHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgPT0gJ3N0YWJsZScgJiYgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUgPT0gJ2Nvbm5lY3RlZCcpKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignbW9kaWZ5U291cmNlcyBub3QgeWV0JywgdGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSwgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpO1xuICAgICAgICB0aGlzLndhaXQgPSB0cnVlO1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHsgc2VsZi5tb2RpZnlTb3VyY2VzKHN1Y2Nlc3NDYWxsYmFjayk7IH0sIDI1MCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMud2FpdCkge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHsgc2VsZi5tb2RpZnlTb3VyY2VzKHN1Y2Nlc3NDYWxsYmFjayk7IH0sIDI1MDApO1xuICAgICAgICB0aGlzLndhaXQgPSBmYWxzZTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFJlc2V0IHN3aXRjaCBzdHJlYW1zIGZsYWdcbiAgICB0aGlzLnN3aXRjaHN0cmVhbXMgPSBmYWxzZTtcblxuICAgIHZhciBzZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcblxuICAgIC8vIGFkZCBzb3VyY2VzXG4gICAgdGhpcy5hZGRzc3JjLmZvckVhY2goZnVuY3Rpb24obGluZXMsIGlkeCkge1xuICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICB9KTtcbiAgICB0aGlzLmFkZHNzcmMgPSBbXTtcblxuICAgIC8vIHJlbW92ZSBzb3VyY2VzXG4gICAgdGhpcy5yZW1vdmVzc3JjLmZvckVhY2goZnVuY3Rpb24obGluZXMsIGlkeCkge1xuICAgICAgICBsaW5lcyA9IGxpbmVzLnNwbGl0KCdcXHJcXG4nKTtcbiAgICAgICAgbGluZXMucG9wKCk7IC8vIHJlbW92ZSBlbXB0eSBsYXN0IGVsZW1lbnQ7XG4gICAgICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgc2RwLm1lZGlhW2lkeF0gPSBzZHAubWVkaWFbaWR4XS5yZXBsYWNlKGxpbmUgKyAnXFxyXFxuJywgJycpO1xuICAgICAgICB9KTtcbiAgICB9KTtcbiAgICB0aGlzLnJlbW92ZXNzcmMgPSBbXTtcblxuICAgIC8vIEZJWE1FOlxuICAgIC8vIHRoaXMgd2FzIGEgaGFjayBmb3IgdGhlIHNpdHVhdGlvbiB3aGVuIG9ubHkgb25lIHBlZXIgZXhpc3RzXG4gICAgLy8gaW4gdGhlIGNvbmZlcmVuY2UuXG4gICAgLy8gY2hlY2sgaWYgc3RpbGwgcmVxdWlyZWQgYW5kIHJlbW92ZVxuICAgIGlmIChzZHAubWVkaWFbMF0pXG4gICAgICAgIHNkcC5tZWRpYVswXSA9IHNkcC5tZWRpYVswXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICBpZiAoc2RwLm1lZGlhWzFdKVxuICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG5cbiAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRSZW1vdGVEZXNjcmlwdGlvbihuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHt0eXBlOiAnb2ZmZXInLCBzZHA6IHNkcC5yYXd9KSxcbiAgICAgICAgZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgICAgIGlmKHNlbGYuc2lnbmFsaW5nU3RhdGUgPT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiY3JlYXRlQW5zd2VyIGF0dGVtcHQgb24gY2xvc2VkIHN0YXRlXCIpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24obW9kaWZpZWRBbnN3ZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gY2hhbmdlIHZpZGVvIGRpcmVjdGlvbiwgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qaXRzaS9qaXRtZWV0L2lzc3Vlcy80MVxuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5wZW5kaW5nb3AgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBzZHAgPSBuZXcgU0RQKG1vZGlmaWVkQW5zd2VyLnNkcCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2RwLm1lZGlhLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2goc2VsZi5wZW5kaW5nb3ApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnbXV0ZSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1zZW5kcmVjdicsICdhPXJlY3Zvbmx5Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAndW5tdXRlJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kaWZpZWRBbnN3ZXIuc2RwID0gc2RwLnJhdztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucGVuZGluZ29wID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBwdXNoaW5nIGRvd24gYW4gYW5zd2VyIHdoaWxlIGljZSBjb25uZWN0aW9uIHN0YXRlXG4gICAgICAgICAgICAgICAgICAgIC8vIGlzIHN0aWxsIGNoZWNraW5nIGlzIGJhZC4uLlxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKHNlbGYucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyB0cnlpbmcgdG8gd29yayBhcm91bmQgYW5vdGhlciBjaHJvbWUgYnVnXG4gICAgICAgICAgICAgICAgICAgIC8vbW9kaWZpZWRBbnN3ZXIuc2RwID0gbW9kaWZpZWRBbnN3ZXIuc2RwLnJlcGxhY2UoL2E9c2V0dXA6YWN0aXZlL2csICdhPXNldHVwOmFjdHBhc3MnKTtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5zZXRMb2NhbERlc2NyaXB0aW9uKG1vZGlmaWVkQW5zd2VyLFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnbW9kaWZpZWQgc2V0TG9jYWxEZXNjcmlwdGlvbiBvaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHN1Y2Nlc3NDYWxsYmFjayl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ21vZGlmaWVkIHNldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24oZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbW9kaWZpZWQgYW5zd2VyIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignbW9kaWZ5IGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG4vKipcbiAqIFN3aXRjaGVzIHZpZGVvIHN0cmVhbXMuXG4gKiBAcGFyYW0gbmV3X3N0cmVhbSBuZXcgc3RyZWFtIHRoYXQgd2lsbCBiZSB1c2VkIGFzIHZpZGVvIG9mIHRoaXMgc2Vzc2lvbi5cbiAqIEBwYXJhbSBvbGRTdHJlYW0gb2xkIHZpZGVvIHN0cmVhbSBvZiB0aGlzIHNlc3Npb24uXG4gKiBAcGFyYW0gc3VjY2Vzc19jYWxsYmFjayBjYWxsYmFjayBleGVjdXRlZCBhZnRlciBzdWNjZXNzZnVsIHN0cmVhbSBzd2l0Y2guXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnN3aXRjaFN0cmVhbXMgPSBmdW5jdGlvbiAobmV3X3N0cmVhbSwgb2xkU3RyZWFtLCBzdWNjZXNzX2NhbGxiYWNrKSB7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBSZW1lbWJlciBTRFAgdG8gZmlndXJlIG91dCBhZGRlZC9yZW1vdmVkIFNTUkNzXG4gICAgdmFyIG9sZFNkcCA9IG51bGw7XG4gICAgaWYoc2VsZi5wZWVyY29ubmVjdGlvbikge1xuICAgICAgICBpZihzZWxmLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pIHtcbiAgICAgICAgICAgIG9sZFNkcCA9IG5ldyBTRFAoc2VsZi5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgIH1cbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0ob2xkU3RyZWFtLCB0cnVlKTtcbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRTdHJlYW0obmV3X3N0cmVhbSk7XG4gICAgfVxuXG4gICAgUlRDLnN3aXRjaFZpZGVvU3RyZWFtcyhuZXdfc3RyZWFtLCBvbGRTdHJlYW0pO1xuXG4gICAgLy8gQ29uZmVyZW5jZSBpcyBub3QgYWN0aXZlXG4gICAgaWYoIW9sZFNkcCB8fCAhc2VsZi5wZWVyY29ubmVjdGlvbikge1xuICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBzZWxmLnN3aXRjaHN0cmVhbXMgPSB0cnVlO1xuICAgIHNlbGYubW9kaWZ5U291cmNlcyhmdW5jdGlvbigpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ21vZGlmeSBzb3VyY2VzIGRvbmUnKTtcblxuICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKCk7XG5cbiAgICAgICAgdmFyIG5ld1NkcCA9IG5ldyBTRFAoc2VsZi5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiU0RQc1wiLCBvbGRTZHAsIG5ld1NkcCk7XG4gICAgICAgIHNlbGYubm90aWZ5TXlTU1JDVXBkYXRlKG9sZFNkcCwgbmV3U2RwKTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogRmlndXJlcyBvdXQgYWRkZWQvcmVtb3ZlZCBzc3JjcyBhbmQgc2VuZCB1cGRhdGUgSVFzLlxuICogQHBhcmFtIG9sZF9zZHAgU0RQIG9iamVjdCBmb3Igb2xkIGRlc2NyaXB0aW9uLlxuICogQHBhcmFtIG5ld19zZHAgU0RQIG9iamVjdCBmb3IgbmV3IGRlc2NyaXB0aW9uLlxuICovXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5ub3RpZnlNeVNTUkNVcGRhdGUgPSBmdW5jdGlvbiAob2xkX3NkcCwgbmV3X3NkcCkge1xuXG4gICAgaWYgKCEodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnc3RhYmxlJyAmJlxuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSA9PSAnY29ubmVjdGVkJykpe1xuICAgICAgICBjb25zb2xlLmxvZyhcIlRvbyBlYXJseSB0byBzZW5kIHVwZGF0ZXNcIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzZW5kIHNvdXJjZS1yZW1vdmUgSVEuXG4gICAgc2RwRGlmZmVyID0gbmV3IFNEUERpZmZlcihuZXdfc2RwLCBvbGRfc2RwKTtcbiAgICB2YXIgcmVtb3ZlID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7XG4gICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3NvdXJjZS1yZW1vdmUnLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWRcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIHJlbW92ZWQgPSBzZHBEaWZmZXIudG9KaW5nbGUocmVtb3ZlKTtcbiAgICBpZiAocmVtb3ZlZCkge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKHJlbW92ZSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ2dvdCByZW1vdmUgcmVzdWx0JywgcmVzKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignZ290IHJlbW92ZSBlcnJvcicsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coJ3JlbW92YWwgbm90IG5lY2Vzc2FyeScpO1xuICAgIH1cblxuICAgIC8vIHNlbmQgc291cmNlLWFkZCBJUS5cbiAgICB2YXIgc2RwRGlmZmVyID0gbmV3IFNEUERpZmZlcihvbGRfc2RwLCBuZXdfc2RwKTtcbiAgICB2YXIgYWRkID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7XG4gICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3NvdXJjZS1hZGQnLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWRcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIGFkZGVkID0gc2RwRGlmZmVyLnRvSmluZ2xlKGFkZCk7XG4gICAgaWYgKGFkZGVkKSB7XG4gICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoYWRkLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnZ290IGFkZCByZXN1bHQnLCByZXMpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdnb3QgYWRkIGVycm9yJywgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmxvZygnYWRkaXRpb24gbm90IG5lY2Vzc2FyeScpO1xuICAgIH1cbn07XG5cbi8qKlxuICogRGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSAobG9jYWwpIHZpZGVvIGlzIG11dGUgaS5lLiBhbGwgdmlkZW8gdHJhY2tzIGFyZVxuICogZGlzYWJsZWQuXG4gKlxuICogQHJldHVybiA8dHQ+dHJ1ZTwvdHQ+IGlmIHRoZSAobG9jYWwpIHZpZGVvIGlzIG11dGUgaS5lLiBhbGwgdmlkZW8gdHJhY2tzIGFyZVxuICogZGlzYWJsZWQ7IG90aGVyd2lzZSwgPHR0PmZhbHNlPC90dD5cbiAqL1xuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuaXNWaWRlb011dGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHRyYWNrcyA9IFJUQy5sb2NhbFZpZGVvLmdldFZpZGVvVHJhY2tzKCk7XG4gICAgdmFyIG11dGUgPSB0cnVlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgaWYgKHRyYWNrc1tpXS5lbmFibGVkKSB7XG4gICAgICAgICAgICBtdXRlID0gZmFsc2U7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbXV0ZTtcbn07XG5cbi8qKlxuICogTXV0ZXMvdW5tdXRlcyB0aGUgKGxvY2FsKSB2aWRlbyBpLmUuIGVuYWJsZXMvZGlzYWJsZXMgYWxsIHZpZGVvIHRyYWNrcy5cbiAqXG4gKiBAcGFyYW0gbXV0ZSA8dHQ+dHJ1ZTwvdHQ+IHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gaS5lLiB0byBkaXNhYmxlIGFsbCB2aWRlb1xuICogdHJhY2tzOyBvdGhlcndpc2UsIDx0dD5mYWxzZTwvdHQ+XG4gKiBAcGFyYW0gY2FsbGJhY2sgYSBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHdpdGggPHR0Pm11dGU8L3R0PiBhZnRlciBhbGwgdmlkZW9cbiAqIHRyYWNrcyBoYXZlIGJlZW4gZW5hYmxlZC9kaXNhYmxlZC4gVGhlIGZ1bmN0aW9uIG1heSwgb3B0aW9uYWxseSwgcmV0dXJuXG4gKiBhbm90aGVyIGZ1bmN0aW9uIHdoaWNoIGlzIHRvIGJlIGludm9rZWQgYWZ0ZXIgdGhlIHdob2xlIG11dGUvdW5tdXRlIG9wZXJhdGlvblxuICogaGFzIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHkuXG4gKiBAcGFyYW0gb3B0aW9ucyBhbiBvYmplY3Qgd2hpY2ggc3BlY2lmaWVzIG9wdGlvbmFsIGFyZ3VtZW50cyBzdWNoIGFzIHRoZVxuICogPHR0PmJvb2xlYW48L3R0PiBrZXkgPHR0PmJ5VXNlcjwvdHQ+IHdpdGggZGVmYXVsdCB2YWx1ZSA8dHQ+dHJ1ZTwvdHQ+IHdoaWNoXG4gKiBzcGVjaWZpZXMgd2hldGhlciB0aGUgbWV0aG9kIHdhcyBpbml0aWF0ZWQgaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgKGluXG4gKiBjb250cmFzdCB0byBhbiBhdXRvbWF0aWMgZGVjaXNpb24gbWFkZSBieSB0aGUgYXBwbGljYXRpb24gbG9naWMpXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFZpZGVvTXV0ZSA9IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgIHZhciBieVVzZXI7XG5cbiAgICBpZiAob3B0aW9ucykge1xuICAgICAgICBieVVzZXIgPSBvcHRpb25zLmJ5VXNlcjtcbiAgICAgICAgaWYgKHR5cGVvZiBieVVzZXIgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBieVVzZXIgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgYnlVc2VyID0gdHJ1ZTtcbiAgICB9XG4gICAgLy8gVGhlIHVzZXIncyBjb21tYW5kIHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gdGFrZXMgcHJlY2VkZW5jZSBvdmVyIGFueVxuICAgIC8vIGF1dG9tYXRpYyBkZWNpc2lvbiBtYWRlIGJ5IHRoZSBhcHBsaWNhdGlvbiBsb2dpYy5cbiAgICBpZiAoYnlVc2VyKSB7XG4gICAgICAgIHRoaXMudmlkZW9NdXRlQnlVc2VyID0gbXV0ZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMudmlkZW9NdXRlQnlVc2VyKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGxvY2FsQ2FsbGJhY2sgPSBmdW5jdGlvbiAobXV0ZSkge1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5hZGRWaWRlb0luZm9Ub1ByZXNlbmNlKG11dGUpO1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG11dGUpXG4gICAgfTtcblxuICAgIGlmIChtdXRlID09IFJUQy5sb2NhbFZpZGVvLmlzTXV0ZWQoKSlcbiAgICB7XG4gICAgICAgIC8vIEV2ZW4gaWYgbm8gY2hhbmdlIG9jY3VycywgdGhlIHNwZWNpZmllZCBjYWxsYmFjayBpcyB0byBiZSBleGVjdXRlZC5cbiAgICAgICAgLy8gVGhlIHNwZWNpZmllZCBjYWxsYmFjayBtYXksIG9wdGlvbmFsbHksIHJldHVybiBhIHN1Y2Nlc3NDYWxsYmFja1xuICAgICAgICAvLyB3aGljaCBpcyB0byBiZSBleGVjdXRlZCBhcyB3ZWxsLlxuICAgICAgICB2YXIgc3VjY2Vzc0NhbGxiYWNrID0gbG9jYWxDYWxsYmFjayhtdXRlKTtcblxuICAgICAgICBpZiAoc3VjY2Vzc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIFJUQy5sb2NhbFZpZGVvLnNldE11dGUoIW11dGUpO1xuXG4gICAgICAgIHRoaXMuaGFyZE11dGVWaWRlbyhtdXRlKTtcblxuICAgICAgICB0aGlzLm1vZGlmeVNvdXJjZXMobG9jYWxDYWxsYmFjayhtdXRlKSk7XG4gICAgfVxufTtcblxuLy8gU0RQLWJhc2VkIG11dGUgYnkgZ29pbmcgcmVjdm9ubHkvc2VuZHJlY3Zcbi8vIEZJWE1FOiBzaG91bGQgcHJvYmFibHkgYmxhY2sgb3V0IHRoZSBzY3JlZW4gYXMgd2VsbFxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUudG9nZ2xlVmlkZW9NdXRlID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdGhpcy5zZXJ2aWNlLnNldFZpZGVvTXV0ZShSVEMubG9jYWxWaWRlby5pc011dGVkKCksIGNhbGxiYWNrKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmhhcmRNdXRlVmlkZW8gPSBmdW5jdGlvbiAobXV0ZWQpIHtcbiAgICB0aGlzLnBlbmRpbmdvcCA9IG11dGVkID8gJ211dGUnIDogJ3VubXV0ZSc7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kTXV0ZSA9IGZ1bmN0aW9uIChtdXRlZCwgY29udGVudCkge1xuICAgIHZhciBpbmZvID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLFxuICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWluZm8nLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWQgfSk7XG4gICAgaW5mby5jKG11dGVkID8gJ211dGUnIDogJ3VubXV0ZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgaW5mby5hdHRycyh7J2NyZWF0b3InOiB0aGlzLm1lID09IHRoaXMuaW5pdGlhdG9yID8gJ2NyZWF0b3InIDogJ3Jlc3BvbmRlcid9KTtcbiAgICBpZiAoY29udGVudCkge1xuICAgICAgICBpbmZvLmF0dHJzKHsnbmFtZSc6IGNvbnRlbnR9KTtcbiAgICB9XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kUmluZ2luZyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5mbyA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbmZvJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkIH0pO1xuICAgIGluZm8uYygncmluZ2luZycsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uIChpbnRlcnZhbCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgcmVjdiA9IHthdWRpbzogMCwgdmlkZW86IDB9O1xuICAgIHZhciBsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3RyZWN2ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3Rsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxvc3MgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB2YXIgZGVsdGEgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uICYmIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24gKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAvLyBUT0RPOiB0aGVyZSBhcmUgc28gbXVjaCBzdGF0aXN0aWNzIHlvdSBjYW4gZ2V0IGZyb20gdGhpcy4uXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnR5cGUgPT0gJ3NzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFja2V0c3JlY3YgPSByZXN1bHRzW2ldLnN0YXQoJ3BhY2tldHNSZWNlaXZlZCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHBhY2tldHNsb3N0ID0gcmVzdWx0c1tpXS5zdGF0KCdwYWNrZXRzTG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhY2tldHNyZWN2ICYmIHBhY2tldHNsb3N0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0c3JlY3YgPSBwYXJzZUludChwYWNrZXRzcmVjdiwgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2tldHNsb3N0ID0gcGFyc2VJbnQocGFja2V0c2xvc3QsIDEwKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnN0YXQoJ2dvb2dGcmFtZVJhdGVSZWNlaXZlZCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3Rsb3N0LnZpZGVvID0gbG9zdC52aWRlbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdHJlY3YudmlkZW8gPSByZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWN2LnZpZGVvID0gcGFja2V0c3JlY3Y7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvc3QudmlkZW8gPSBwYWNrZXRzbG9zdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bG9zdC5hdWRpbyA9IGxvc3QuYXVkaW87XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RyZWN2LmF1ZGlvID0gcmVjdi5hdWRpbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdi5hdWRpbyA9IHBhY2tldHNyZWN2O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3N0LmF1ZGlvID0gcGFja2V0c2xvc3Q7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGRlbHRhLmF1ZGlvID0gcmVjdi5hdWRpbyAtIGxhc3RyZWN2LmF1ZGlvO1xuICAgICAgICAgICAgICAgIGRlbHRhLnZpZGVvID0gcmVjdi52aWRlbyAtIGxhc3RyZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgIGxvc3MuYXVkaW8gPSAoZGVsdGEuYXVkaW8gPiAwKSA/IE1hdGguY2VpbCgxMDAgKiAobG9zdC5hdWRpbyAtIGxhc3Rsb3N0LmF1ZGlvKSAvIGRlbHRhLmF1ZGlvKSA6IDA7XG4gICAgICAgICAgICAgICAgbG9zcy52aWRlbyA9IChkZWx0YS52aWRlbyA+IDApID8gTWF0aC5jZWlsKDEwMCAqIChsb3N0LnZpZGVvIC0gbGFzdGxvc3QudmlkZW8pIC8gZGVsdGEudmlkZW8pIDogMDtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwYWNrZXRsb3NzLmppbmdsZScsIFtzZWxmLnNpZCwgbG9zc10pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LCBpbnRlcnZhbCB8fCAzMDAwKTtcbiAgICByZXR1cm4gdGhpcy5zdGF0c2ludGVydmFsO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yID0gZnVuY3Rpb24gKHNlc3Npb24sIGVycm9yKVxue1xuICAgIGNvbnNvbGUuZXJyb3IoXCJKaW5nbGUgZXJyb3JcIiwgZXJyb3IpO1xufVxuXG5KaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvciA9IGZ1bmN0aW9uIChzZXNzaW9uLCBlcnJvcilcbntcbiAgICB0aGlzLnNlcnZpY2Uuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgIGNvbm5lY3Rpb24uZW11Yy5kb0xlYXZlKCk7XG4gICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCAgXCJTb3JyeVwiLFxuICAgICAgICBcIkludGVybmFsIGFwcGxpY2F0aW9uIGVycm9yW3NldFJlbW90ZURlc2NyaXB0aW9uXVwiKTtcbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBwdXQgb3VyIHNzcmNzIGludG8gcHJlc2VuY2Ugc28gb3RoZXIgY2xpZW50cyBjYW4gaWRlbnRpZnkgb3VyIHN0cmVhbVxuICAgIHZhciBuZXdzc3JjcyA9IFtdO1xuICAgIHZhciBtZWRpYSA9IHNpbXVsY2FzdC5wYXJzZU1lZGlhKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbik7XG4gICAgbWVkaWEuZm9yRWFjaChmdW5jdGlvbiAobWVkaWEpIHtcblxuICAgICAgICBpZihPYmplY3Qua2V5cyhtZWRpYS5zb3VyY2VzKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBtYXliZSBleGNsdWRlIEZJRCBzdHJlYW1zP1xuICAgICAgICAgICAgT2JqZWN0LmtleXMobWVkaWEuc291cmNlcykuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIG5ld3NzcmNzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAnc3NyYyc6IHNzcmMsXG4gICAgICAgICAgICAgICAgICAgICd0eXBlJzogbWVkaWEudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgJ2RpcmVjdGlvbic6IG1lZGlhLmRpcmVjdGlvblxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZih0aGlzLmxvY2FsU3RyZWFtc1NTUkMgJiYgdGhpcy5sb2NhbFN0cmVhbXNTU1JDW21lZGlhLnR5cGVdKVxuICAgICAgICB7XG4gICAgICAgICAgICBuZXdzc3Jjcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAnc3NyYyc6IHRoaXMubG9jYWxTdHJlYW1zU1NSQ1ttZWRpYS50eXBlXSxcbiAgICAgICAgICAgICAgICAndHlwZSc6IG1lZGlhLnR5cGUsXG4gICAgICAgICAgICAgICAgJ2RpcmVjdGlvbic6IG1lZGlhLmRpcmVjdGlvblxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coJ25ldyBzc3JjcycsIG5ld3NzcmNzKTtcblxuICAgIC8vIEhhdmUgdG8gY2xlYXIgcHJlc2VuY2UgbWFwIHRvIGdldCByaWQgb2YgcmVtb3ZlZCBzdHJlYW1zXG4gICAgdGhpcy5jb25uZWN0aW9uLmVtdWMuY2xlYXJQcmVzZW5jZU1lZGlhKCk7XG5cbiAgICBpZiAobmV3c3NyY3MubGVuZ3RoID4gMCkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8PSBuZXdzc3Jjcy5sZW5ndGg7IGkgKyspIHtcbiAgICAgICAgICAgIC8vIENoYW5nZSB2aWRlbyB0eXBlIHRvIHNjcmVlblxuICAgICAgICAgICAgaWYgKG5ld3NzcmNzW2ktMV0udHlwZSA9PT0gJ3ZpZGVvJyAmJiBkZXNrdG9wc2hhcmluZy5pc1VzaW5nU2NyZWVuU3RyZWFtKCkpIHtcbiAgICAgICAgICAgICAgICBuZXdzc3Jjc1tpLTFdLnR5cGUgPSAnc2NyZWVuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmFkZE1lZGlhVG9QcmVzZW5jZShpLFxuICAgICAgICAgICAgICAgIG5ld3NzcmNzW2ktMV0udHlwZSwgbmV3c3NyY3NbaS0xXS5zc3JjLCBuZXdzc3Jjc1tpLTFdLmRpcmVjdGlvbik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICB9XG59XG5cbi8vIGFuIGF0dGVtcHQgdG8gd29yayBhcm91bmQgaHR0cHM6Ly9naXRodWIuY29tL2ppdHNpL2ppdG1lZXQvaXNzdWVzLzMyXG5mdW5jdGlvbiBzZW5kS2V5ZnJhbWUocGMpIHtcbiAgICBjb25zb2xlLmxvZygnc2VuZGtleWZyYW1lJywgcGMuaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICBpZiAocGMuaWNlQ29ubmVjdGlvblN0YXRlICE9PSAnY29ubmVjdGVkJykgcmV0dXJuOyAvLyBzYWZlLi4uXG4gICAgcGMuc2V0UmVtb3RlRGVzY3JpcHRpb24oXG4gICAgICAgIHBjLnJlbW90ZURlc2NyaXB0aW9uLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBwYy5jcmVhdGVBbnN3ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKG1vZGlmaWVkQW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgIHBjLnNldExvY2FsRGVzY3JpcHRpb24oXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RpZmllZEFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBub29wXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndHJpZ2dlcktleWZyYW1lIGNyZWF0ZUFuc3dlciBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIFVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcigpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBzZXRSZW1vdGVEZXNjcmlwdGlvbiBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUucmVtb3RlU3RyZWFtQWRkZWQgPSBmdW5jdGlvbiAoZGF0YSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgdGhlc3NyYztcblxuICAgIC8vIGxvb2sgdXAgYW4gYXNzb2NpYXRlZCBKSUQgZm9yIGEgc3RyZWFtIGlkXG4gICAgaWYgKGRhdGEuc3RyZWFtLmlkICYmIGRhdGEuc3RyZWFtLmlkLmluZGV4T2YoJ21peGVkbXNsYWJlbCcpID09PSAtMSkge1xuICAgICAgICAvLyBsb29rIG9ubHkgYXQgYT1zc3JjOiBhbmQgX25vdF8gYXQgYT1zc3JjLWdyb3VwOiBsaW5lc1xuXG4gICAgICAgIHZhciBzc3JjbGluZXNcbiAgICAgICAgICAgID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwLCAnYT1zc3JjOicpO1xuICAgICAgICBzc3JjbGluZXMgPSBzc3JjbGluZXMuZmlsdGVyKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAvLyBOT1RFKGdwKSBwcmV2aW91c2x5IHdlIGZpbHRlcmVkIG9uIHRoZSBtc2xhYmVsLCBidXQgdGhhdCBwcm9wZXJ0eVxuICAgICAgICAgICAgLy8gaXMgbm90IGFsd2F5cyBwcmVzZW50LlxuICAgICAgICAgICAgLy8gcmV0dXJuIGxpbmUuaW5kZXhPZignbXNsYWJlbDonICsgZGF0YS5zdHJlYW0ubGFiZWwpICE9PSAtMTtcblxuICAgICAgICAgICAgcmV0dXJuICgobGluZS5pbmRleE9mKCdtc2lkOicgKyBkYXRhLnN0cmVhbS5pZCkgIT09IC0xKSk7XG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoc3NyY2xpbmVzLmxlbmd0aCkge1xuICAgICAgICAgICAgdGhlc3NyYyA9IHNzcmNsaW5lc1swXS5zdWJzdHJpbmcoNykuc3BsaXQoJyAnKVswXTtcblxuICAgICAgICAgICAgLy8gV2Ugc2lnbmFsIG91ciBzdHJlYW1zICh0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpIGJlZm9yZSB3ZSBzZXRcbiAgICAgICAgICAgIC8vIG91ciBwcmVzZW5jZSAodGhyb3VnaCB3aGljaCBwZWVycyBhc3NvY2lhdGUgcmVtb3RlIHN0cmVhbXMgdG9cbiAgICAgICAgICAgIC8vIGppZHMpLiBTbywgaXQgbWlnaHQgYXJyaXZlIHRoYXQgYSByZW1vdGUgc3RyZWFtIGlzIGFkZGVkIGJ1dFxuICAgICAgICAgICAgLy8gc3NyYzJqaWQgaXMgbm90IHlldCB1cGRhdGVkIGFuZCB0aHVzIGRhdGEucGVlcmppZCBjYW5ub3QgYmVcbiAgICAgICAgICAgIC8vIHN1Y2Nlc3NmdWxseSBzZXQuIEhlcmUgd2Ugd2FpdCBmb3IgdXAgdG8gYSBzZWNvbmQgZm9yIHRoZVxuICAgICAgICAgICAgLy8gcHJlc2VuY2UgdG8gYXJyaXZlLlxuXG4gICAgICAgICAgICBpZiAoIXNzcmMyamlkW3RoZXNzcmNdKSB7XG4gICAgICAgICAgICAgICAgLy8gVE9ETyhncCkgbGltaXQgd2FpdCBkdXJhdGlvbiB0byAxIHNlYy5cbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKGQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1BZGRlZChkKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0oZGF0YSksIDI1MCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBvayB0byBvdmVyd3JpdGUgdGhlIG9uZSBmcm9tIGZvY3VzPyBtaWdodCBzYXZlIHdvcmsgaW4gY29saWJyaS5qc1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ2Fzc29jaWF0ZWQgamlkJywgc3NyYzJqaWRbdGhlc3NyY10sIGRhdGEucGVlcmppZCk7XG4gICAgICAgICAgICBpZiAoc3NyYzJqaWRbdGhlc3NyY10pIHtcbiAgICAgICAgICAgICAgICBkYXRhLnBlZXJqaWQgPSBzc3JjMmppZFt0aGVzc3JjXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vVE9ETzogdGhpcyBjb2RlIHNob3VsZCBiZSByZW1vdmVkIHdoZW4gZmlyZWZveCBpbXBsZW1lbnQgbXVsdGlzdHJlYW0gc3VwcG9ydFxuICAgIGlmKFJUQy5nZXRCcm93c2VyVHlwZSgpID09IFJUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0ZJUkVGT1gpXG4gICAge1xuICAgICAgICBpZigobm90UmVjZWl2ZWRTU1JDcy5sZW5ndGggPT0gMCkgfHxcbiAgICAgICAgICAgICFzc3JjMmppZFtub3RSZWNlaXZlZFNTUkNzW25vdFJlY2VpdmVkU1NSQ3MubGVuZ3RoIC0gMV1dKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBsaW1pdCB3YWl0IGR1cmF0aW9uIHRvIDEgc2VjLlxuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbihkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0oZGF0YSksIDI1MCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGVzc3JjID0gbm90UmVjZWl2ZWRTU1JDcy5wb3AoKTtcbiAgICAgICAgaWYgKHNzcmMyamlkW3RoZXNzcmNdKSB7XG4gICAgICAgICAgICBkYXRhLnBlZXJqaWQgPSBzc3JjMmppZFt0aGVzc3JjXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIFJUQy5jcmVhdGVSZW1vdGVTdHJlYW0oZGF0YSwgdGhpcy5zaWQsIHRoZXNzcmMpO1xuXG4gICAgdmFyIGlzVmlkZW8gPSBkYXRhLnN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG4gICAgLy8gYW4gYXR0ZW1wdCB0byB3b3JrIGFyb3VuZCBodHRwczovL2dpdGh1Yi5jb20vaml0c2kvaml0bWVldC9pc3N1ZXMvMzJcbiAgICBpZiAoaXNWaWRlbyAmJlxuICAgICAgICBkYXRhLnBlZXJqaWQgJiYgdGhpcy5wZWVyamlkID09PSBkYXRhLnBlZXJqaWQgJiZcbiAgICAgICAgZGF0YS5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPT09IDAgJiZcbiAgICAgICAgUlRDLmxvY2FsVmlkZW8uZ2V0VHJhY2tzKCkubGVuZ3RoID4gMCkge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZW5kS2V5ZnJhbWUoc2VsZi5wZWVyY29ubmVjdGlvbik7XG4gICAgICAgIH0sIDMwMDApO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBKaW5nbGVTZXNzaW9uOyIsIi8qIGpzaGludCAtVzExNyAqL1xudmFyIFNEUFV0aWwgPSByZXF1aXJlKFwiLi9TRFBVdGlsXCIpO1xuXG4vLyBTRFAgU1RVRkZcbmZ1bmN0aW9uIFNEUChzZHApIHtcbiAgICB0aGlzLm1lZGlhID0gc2RwLnNwbGl0KCdcXHJcXG5tPScpO1xuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgdGhpcy5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICB0aGlzLm1lZGlhW2ldID0gJ209JyArIHRoaXMubWVkaWFbaV07XG4gICAgICAgIGlmIChpICE9IHRoaXMubWVkaWEubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgdGhpcy5tZWRpYVtpXSArPSAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnNlc3Npb24gPSB0aGlzLm1lZGlhLnNoaWZ0KCkgKyAnXFxyXFxuJztcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59XG4vKipcbiAqIFJldHVybnMgbWFwIG9mIE1lZGlhQ2hhbm5lbCBtYXBwZWQgcGVyIGNoYW5uZWwgaWR4LlxuICovXG5TRFAucHJvdG90eXBlLmdldE1lZGlhU3NyY01hcCA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbWVkaWFfc3NyY3MgPSB7fTtcbiAgICB2YXIgdG1wO1xuICAgIGZvciAodmFyIG1lZGlhaW5kZXggPSAwOyBtZWRpYWluZGV4IDwgc2VsZi5tZWRpYS5sZW5ndGg7IG1lZGlhaW5kZXgrKykge1xuICAgICAgICB0bXAgPSBTRFBVdGlsLmZpbmRfbGluZXMoc2VsZi5tZWRpYVttZWRpYWluZGV4XSwgJ2E9c3NyYzonKTtcbiAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPW1pZDonKSk7XG4gICAgICAgIHZhciBtZWRpYSA9IHtcbiAgICAgICAgICAgIG1lZGlhaW5kZXg6IG1lZGlhaW5kZXgsXG4gICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgIH07XG4gICAgICAgIG1lZGlhX3NzcmNzW21lZGlhaW5kZXhdID0gbWVkaWE7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICB2YXIgbGluZXNzcmMgPSBsaW5lLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdO1xuICAgICAgICAgICAgLy8gYWxsb2NhdGUgbmV3IENoYW5uZWxTc3JjXG4gICAgICAgICAgICBpZighbWVkaWEuc3NyY3NbbGluZXNzcmNdKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEuc3NyY3NbbGluZXNzcmNdID0ge1xuICAgICAgICAgICAgICAgICAgICBzc3JjOiBsaW5lc3NyYyxcbiAgICAgICAgICAgICAgICAgICAgbGluZXM6IFtdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG1lZGlhLnNzcmNzW2xpbmVzc3JjXS5saW5lcy5wdXNoKGxpbmUpO1xuICAgICAgICB9KTtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5maW5kX2xpbmVzKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPXNzcmMtZ3JvdXA6Jyk7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpe1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDEzKTtcbiAgICAgICAgICAgIHZhciBzc3JjcyA9IGxpbmUuc3Vic3RyKDE0ICsgc2VtYW50aWNzLmxlbmd0aCkuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIG1lZGlhLnNzcmNHcm91cHMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHNlbWFudGljczogc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICBzc3Jjczogc3NyY3NcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBtZWRpYV9zc3Jjcztcbn07XG4vKipcbiAqIFJldHVybnMgPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICogQHBhcmFtIHNzcmMgdGhlIHNzcmMgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICovXG5TRFAucHJvdG90eXBlLmNvbnRhaW5zU1NSQyA9IGZ1bmN0aW9uKHNzcmMpIHtcbiAgICB2YXIgbWVkaWFzID0gdGhpcy5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgY29udGFpbnMgPSBmYWxzZTtcbiAgICBPYmplY3Qua2V5cyhtZWRpYXMpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIHZhciBtZWRpYSA9IG1lZGlhc1ttZWRpYWluZGV4XTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIkNoZWNrXCIsIGNoYW5uZWwsIHNzcmMpO1xuICAgICAgICBpZihPYmplY3Qua2V5cyhtZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSAhPSAtMSl7XG4gICAgICAgICAgICBjb250YWlucyA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gY29udGFpbnM7XG59O1xuXG5cbi8vIHJlbW92ZSBpU0FDIGFuZCBDTiBmcm9tIFNEUFxuU0RQLnByb3RvdHlwZS5tYW5nbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGksIGosIG1saW5lLCBsaW5lcywgcnRwbWFwLCBuZXdkZXNjO1xuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGxpbmVzID0gdGhpcy5tZWRpYVtpXS5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGxpbmVzLnBvcCgpOyAvLyByZW1vdmUgZW1wdHkgbGFzdCBlbGVtZW50XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZShsaW5lcy5zaGlmdCgpKTtcbiAgICAgICAgaWYgKG1saW5lLm1lZGlhICE9ICdhdWRpbycpXG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgbmV3ZGVzYyA9ICcnO1xuICAgICAgICBtbGluZS5mbXQubGVuZ3RoID0gMDtcbiAgICAgICAgZm9yIChqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyKDAsIDkpID09ICdhPXJ0cG1hcDonKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5wYXJzZV9ydHBtYXAobGluZXNbal0pO1xuICAgICAgICAgICAgICAgIGlmIChydHBtYXAubmFtZSA9PSAnQ04nIHx8IHJ0cG1hcC5uYW1lID09ICdJU0FDJylcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgbWxpbmUuZm10LnB1c2gocnRwbWFwLmlkKTtcbiAgICAgICAgICAgICAgICBuZXdkZXNjICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG5ld2Rlc2MgKz0gbGluZXNbal0gKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLm1lZGlhW2ldID0gU0RQVXRpbC5idWlsZF9tbGluZShtbGluZSkgKyAnXFxyXFxuJztcbiAgICAgICAgdGhpcy5tZWRpYVtpXSArPSBuZXdkZXNjO1xuICAgIH1cbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59O1xuXG4vLyByZW1vdmUgbGluZXMgbWF0Y2hpbmcgcHJlZml4IGZyb20gc2Vzc2lvbiBzZWN0aW9uXG5TRFAucHJvdG90eXBlLnJlbW92ZVNlc3Npb25MaW5lcyA9IGZ1bmN0aW9uKHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCBwcmVmaXgpO1xuICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICBzZWxmLnNlc3Npb24gPSBzZWxmLnNlc3Npb24ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuLy8gcmVtb3ZlIGxpbmVzIG1hdGNoaW5nIHByZWZpeCBmcm9tIGEgbWVkaWEgc2VjdGlvbiBzcGVjaWZpZWQgYnkgbWVkaWFpbmRleFxuLy8gVE9ETzogbm9uLW51bWVyaWMgbWVkaWFpbmRleCBjb3VsZCBtYXRjaCBtaWRcblNEUC5wcm90b3R5cGUucmVtb3ZlTWVkaWFMaW5lcyA9IGZ1bmN0aW9uKG1lZGlhaW5kZXgsIHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgcHJlZml4KTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgc2VsZi5tZWRpYVttZWRpYWluZGV4XSA9IHNlbGYubWVkaWFbbWVkaWFpbmRleF0ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuXG4vLyBhZGQgY29udGVudCdzIHRvIGEgamluZ2xlIGVsZW1lbnRcblNEUC5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgdGhlY3JlYXRvciwgc3NyY3MpIHtcbi8vICAgIGNvbnNvbGUubG9nKFwiU1NSQ1wiICsgc3NyY3NbXCJhdWRpb1wiXSArIFwiIC0gXCIgKyBzc3Jjc1tcInZpZGVvXCJdKTtcbiAgICB2YXIgaSwgaiwgaywgbWxpbmUsIHNzcmMsIHJ0cG1hcCwgdG1wLCBsaW5lLCBsaW5lcztcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gbmV3IGJ1bmRsZSBwbGFuXG4gICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMuc2Vzc2lvbiwgJ2E9Z3JvdXA6JykpIHtcbiAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCAnYT1ncm91cDonKTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB0bXAgPSBsaW5lc1tpXS5zcGxpdCgnICcpO1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IHRtcC5zaGlmdCgpLnN1YnN0cig4KTtcbiAgICAgICAgICAgIGVsZW0uYygnZ3JvdXAnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowJywgc2VtYW50aWNzOnNlbWFudGljc30pO1xuICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IHRtcC5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY29udGVudCcsIHtuYW1lOiB0bXBbal19KS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLm1lZGlhW2ldLnNwbGl0KCdcXHJcXG4nKVswXSk7XG4gICAgICAgIGlmICghKG1saW5lLm1lZGlhID09PSAnYXVkaW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAndmlkZW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAnYXBwbGljYXRpb24nKSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNzcmM6JykpIHtcbiAgICAgICAgICAgIHNzcmMgPSBTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdOyAvLyB0YWtlIHRoZSBmaXJzdFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYoc3NyY3MgJiYgc3NyY3NbbWxpbmUubWVkaWFdKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNzcmMgPSBzc3Jjc1ttbGluZS5tZWRpYV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgc3NyYyA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgZWxlbS5jKCdjb250ZW50Jywge2NyZWF0b3I6IHRoZWNyZWF0b3IsIG5hbWU6IG1saW5lLm1lZGlhfSk7XG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1taWQ6JykpIHtcbiAgICAgICAgICAgIC8vIHByZWZlciBpZGVudGlmaWVyIGZyb20gYT1taWQgaWYgcHJlc2VudFxuICAgICAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPW1pZDonKSk7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHsgbmFtZTogbWlkIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0cG1hcDonKS5sZW5ndGgpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGVsZW0uYygnZGVzY3JpcHRpb24nLFxuICAgICAgICAgICAgICAgIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDoxJyxcbiAgICAgICAgICAgICAgICAgICAgbWVkaWE6IG1saW5lLm1lZGlhIH0pO1xuICAgICAgICAgICAgaWYgKHNzcmMpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzc3JjOiBzc3JjfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGogPSAwOyBqIDwgbWxpbmUuZm10Lmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9cnRwbWFwOicgKyBtbGluZS5mbXRbal0pO1xuICAgICAgICAgICAgICAgIGVsZW0uYygncGF5bG9hZC10eXBlJywgU0RQVXRpbC5wYXJzZV9ydHBtYXAocnRwbWFwKSk7XG4gICAgICAgICAgICAgICAgLy8gcHV0IGFueSAnYT1mbXRwOicgKyBtbGluZS5mbXRbal0gbGluZXMgaW50byA8cGFyYW0gbmFtZT1mb28gdmFsdWU9YmFyLz5cbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSkge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2ZtdHAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSk7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCB0bXAubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJywgdG1wW2tdKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuUnRjcEZiVG9KaW5nbGUoaSwgZWxlbSwgbWxpbmUuZm10W2pdKTsgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYlxuXG4gICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWNyeXB0bzonLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5jKCdlbmNyeXB0aW9uJywge3JlcXVpcmVkOiAxfSk7XG4gICAgICAgICAgICAgICAgdmFyIGNyeXB0byA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1jcnlwdG86JywgdGhpcy5zZXNzaW9uKTtcbiAgICAgICAgICAgICAgICBjcnlwdG8uZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnY3J5cHRvJywgU0RQVXRpbC5wYXJzZV9jcnlwdG8obGluZSkpLnVwKCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZW5jcnlwdGlvblxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoc3NyYykge1xuICAgICAgICAgICAgICAgIC8vIG5ldyBzdHlsZSBtYXBwaW5nXG4gICAgICAgICAgICAgICAgZWxlbS5jKCdzb3VyY2UnLCB7IHNzcmM6IHNzcmMsIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGdyb3VwIGJ5IHNzcmMgYW5kIHN1cHBvcnQgbXVsdGlwbGUgZGlmZmVyZW50IHNzcmNzXG4gICAgICAgICAgICAgICAgdmFyIHNzcmNsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpO1xuICAgICAgICAgICAgICAgIGlmKHNzcmNsaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNzcmNsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZHggPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsaW5lc3NyYyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGxpbmVzc3JjICE9IHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYyA9IGxpbmVzc3JjO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGt2LmluZGV4T2YoJzonKSA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeyBuYW1lOiBrdiB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IG5hbWU6IGt2LnNwbGl0KCc6JywgMilbMF0gfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHZhbHVlOiBrdi5zcGxpdCgnOicsIDIpWzFdIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcImNuYW1lXCIsIHZhbHVlOk1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZyg3KX0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBtc2lkID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgaWYobWxpbmUubWVkaWEgPT0gXCJhdWRpb1wiKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gUlRDLmxvY2FsQXVkaW8uZ2V0SWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1zaWQgPSBSVEMubG9jYWxWaWRlby5nZXRJZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmKG1zaWQgIT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IG1zaWQucmVwbGFjZSgvW1xceyxcXH1dL2csXCJcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7bmFtZTogXCJtc2lkXCIsIHZhbHVlOm1zaWR9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcIm1zbGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe25hbWU6IFwibGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFhFUC0wMzM5IGhhbmRsZSBzc3JjLWdyb3VwIGF0dHJpYnV0ZXNcbiAgICAgICAgICAgICAgICB2YXIgc3NyY19ncm91cF9saW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjLWdyb3VwOicpO1xuICAgICAgICAgICAgICAgIHNzcmNfZ3JvdXBfbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGlkeCA9IGxpbmUuaW5kZXhPZignICcpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gbGluZS5zdWJzdHIoMCwgaWR4KS5zdWJzdHIoMTMpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc3NyY3MgPSBsaW5lLnN1YnN0cigxNCArIHNlbWFudGljcy5sZW5ndGgpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdzc3JjLWdyb3VwJywgeyBzZW1hbnRpY3M6IHNlbWFudGljcywgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0Y3AtbXV4JykpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmMoJ3J0Y3AtbXV4JykudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYjoqXG4gICAgICAgICAgICB0aGlzLlJ0Y3BGYlRvSmluZ2xlKGksIGVsZW0sICcqJyk7XG5cbiAgICAgICAgICAgIC8vIFhFUC0wMjk0XG4gICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpO1xuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBsaW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2V4dG1hcChsaW5lc1tqXSk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygncnRwLWhkcmV4dCcsIHsgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogdG1wLnVyaSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGlkOiB0bXAudmFsdWUgfSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0bXAuaGFzT3duUHJvcGVydHkoJ2RpcmVjdGlvbicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHRtcC5kaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzZW5kb25seSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdyZXNwb25kZXInfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3JlY3Zvbmx5JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2luaXRpYXRvcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnc2VuZHJlY3YnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnYm90aCd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnaW5hY3RpdmUnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnbm9uZSd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy8gVE9ETzogaGFuZGxlIHBhcmFtc1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZGVzY3JpcHRpb25cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG1hcCBpY2UtdWZyYWcvcHdkLCBkdGxzIGZpbmdlcnByaW50LCBjYW5kaWRhdGVzXG4gICAgICAgIHRoaXMuVHJhbnNwb3J0VG9KaW5nbGUoaSwgZWxlbSk7XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNlbmRyZWN2JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2JvdGgnfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9c2VuZG9ubHknLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnaW5pdGlhdG9yJ30pO1xuICAgICAgICB9IGVsc2UgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJlY3Zvbmx5JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ3Jlc3BvbmRlcid9KTtcbiAgICAgICAgfSBlbHNlIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1pbmFjdGl2ZScsIHRoaXMuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdub25lJ30pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtbGluZS5wb3J0ID09ICcwJykge1xuICAgICAgICAgICAgLy8gZXN0b3MgaGFjayB0byByZWplY3QgYW4gbS1saW5lXG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAncmVqZWN0ZWQnfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH1cbiAgICBlbGVtLnVwKCk7XG4gICAgcmV0dXJuIGVsZW07XG59O1xuXG5TRFAucHJvdG90eXBlLlRyYW5zcG9ydFRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0pIHtcbiAgICB2YXIgaSA9IG1lZGlhaW5kZXg7XG4gICAgdmFyIHRtcDtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgZWxlbS5jKCd0cmFuc3BvcnQnKTtcblxuICAgIC8vIFhFUC0wMzQzIERUTFMvU0NUUFxuICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zY3RwbWFwOicpLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHZhciBzY3RwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUoXG4gICAgICAgICAgICB0aGlzLm1lZGlhW2ldLCAnYT1zY3RwbWFwOicsIHNlbGYuc2Vzc2lvbik7XG4gICAgICAgIGlmIChzY3RwbWFwKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgc2N0cEF0dHJzID0gU0RQVXRpbC5wYXJzZV9zY3RwbWFwKHNjdHBtYXApO1xuICAgICAgICAgICAgZWxlbS5jKCdzY3RwbWFwJyxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6ZHRscy1zY3RwOjEnLFxuICAgICAgICAgICAgICAgICAgICBudW1iZXI6IHNjdHBBdHRyc1swXSwgLyogU0NUUCBwb3J0ICovXG4gICAgICAgICAgICAgICAgICAgIHByb3RvY29sOiBzY3RwQXR0cnNbMV0sIC8qIHByb3RvY29sICovXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBPcHRpb25hbCBzdHJlYW0gY291bnQgYXR0cmlidXRlXG4gICAgICAgICAgICBpZiAoc2N0cEF0dHJzLmxlbmd0aCA+IDIpXG4gICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHN0cmVhbXM6IHNjdHBBdHRyc1syXX0pO1xuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8vIFhFUC0wMzIwXG4gICAgdmFyIGZpbmdlcnByaW50cyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1maW5nZXJwcmludDonLCB0aGlzLnNlc3Npb24pO1xuICAgIGZpbmdlcnByaW50cy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChsaW5lKTtcbiAgICAgICAgdG1wLnhtbG5zID0gJ3Vybjp4bXBwOmppbmdsZTphcHBzOmR0bHM6MCc7XG4gICAgICAgIGVsZW0uYygnZmluZ2VycHJpbnQnKS50KHRtcC5maW5nZXJwcmludCk7XG4gICAgICAgIGRlbGV0ZSB0bXAuZmluZ2VycHJpbnQ7XG4gICAgICAgIGxpbmUgPSBTRFBVdGlsLmZpbmRfbGluZShzZWxmLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zZXR1cDonLCBzZWxmLnNlc3Npb24pO1xuICAgICAgICBpZiAobGluZSkge1xuICAgICAgICAgICAgdG1wLnNldHVwID0gbGluZS5zdWJzdHIoOCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICBlbGVtLnVwKCk7IC8vIGVuZCBvZiBmaW5nZXJwcmludFxuICAgIH0pO1xuICAgIHRtcCA9IFNEUFV0aWwuaWNlcGFyYW1zKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sIHRoaXMuc2Vzc2lvbik7XG4gICAgaWYgKHRtcCkge1xuICAgICAgICB0bXAueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICAvLyBYRVAtMDE3NlxuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVttZWRpYWluZGV4XSwgJ2E9Y2FuZGlkYXRlOicsIHRoaXMuc2Vzc2lvbikpIHsgLy8gYWRkIGFueSBhPWNhbmRpZGF0ZSBsaW5lc1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPWNhbmRpZGF0ZTonLCB0aGlzLnNlc3Npb24pO1xuICAgICAgICAgICAgbGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY2FuZGlkYXRlJywgU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShsaW5lKSkudXAoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsZW0udXAoKTsgLy8gZW5kIG9mIHRyYW5zcG9ydFxufVxuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYlRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0sIHBheWxvYWR0eXBlKSB7IC8vIFhFUC0wMjkzXG4gICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlKTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciB0bXAgPSBTRFBVdGlsLnBhcnNlX3J0Y3BmYihsaW5lKTtcbiAgICAgICAgaWYgKHRtcC50eXBlID09ICd0cnItaW50Jykge1xuICAgICAgICAgICAgZWxlbS5jKCdydGNwLWZiLXRyci1pbnQnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRjcC1mYjowJywgdmFsdWU6IHRtcC5wYXJhbXNbMF19KTtcbiAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGVsZW0uYygncnRjcC1mYicsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnLCB0eXBlOiB0bXAudHlwZX0pO1xuICAgICAgICAgICAgaWYgKHRtcC5wYXJhbXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeydzdWJ0eXBlJzogdG1wLnBhcmFtc1swXX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfSk7XG59O1xuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYkZyb21KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgcGF5bG9hZHR5cGUpIHsgLy8gWEVQLTAyOTNcbiAgICB2YXIgbWVkaWEgPSAnJztcbiAgICB2YXIgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYi10cnItaW50W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MFwiXScpO1xuICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArICcqJyArICcgJyArICd0cnItaW50JyArICcgJztcbiAgICAgICAgaWYgKHRtcC5hdHRyKCd2YWx1ZScpKSB7XG4gICAgICAgICAgICBtZWRpYSArPSB0bXAuYXR0cigndmFsdWUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICcwJztcbiAgICAgICAgfVxuICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG4gICAgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYlt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlICsgJyAnICsgJCh0aGlzKS5hdHRyKCd0eXBlJyk7XG4gICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ3N1YnR5cGUnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgJCh0aGlzKS5hdHRyKCdzdWJ0eXBlJyk7XG4gICAgICAgIH1cbiAgICAgICAgbWVkaWEgKz0gJ1xcclxcbic7XG4gICAgfSk7XG4gICAgcmV0dXJuIG1lZGlhO1xufTtcblxuLy8gY29uc3RydWN0IGFuIFNEUCBmcm9tIGEgamluZ2xlIHN0YW56YVxuU0RQLnByb3RvdHlwZS5mcm9tSmluZ2xlID0gZnVuY3Rpb24gKGppbmdsZSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnJhdyA9ICd2PTBcXHJcXG4nICtcbiAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgJ3M9LVxcclxcbicgK1xuICAgICAgICAndD0wIDBcXHJcXG4nO1xuICAgIC8vIGh0dHA6Ly90b29scy5pZXRmLm9yZy9odG1sL2RyYWZ0LWlldGYtbW11c2ljLXNkcC1idW5kbGUtbmVnb3RpYXRpb24tMDQjc2VjdGlvbi04XG4gICAgaWYgKCQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykuZWFjaChmdW5jdGlvbiAoaWR4LCBncm91cCkge1xuICAgICAgICAgICAgdmFyIGNvbnRlbnRzID0gJChncm91cCkuZmluZCgnPmNvbnRlbnQnKS5tYXAoZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBjb250ZW50LmdldEF0dHJpYnV0ZSgnbmFtZScpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICBpZiAoY29udGVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHNlbGYucmF3ICs9ICdhPWdyb3VwOicgKyAoZ3JvdXAuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKSB8fCBncm91cC5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSkgKyAnICcgKyBjb250ZW50cy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5zZXNzaW9uID0gdGhpcy5yYXc7XG4gICAgamluZ2xlLmZpbmQoJz5jb250ZW50JykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBtID0gc2VsZi5qaW5nbGUybWVkaWEoJCh0aGlzKSk7XG4gICAgICAgIHNlbGYubWVkaWEucHVzaChtKTtcbiAgICB9KTtcblxuICAgIC8vIHJlY29uc3RydWN0IG1zaWQtc2VtYW50aWMgLS0gYXBwYXJlbnRseSBub3QgbmVjZXNzYXJ5XG4gICAgLypcbiAgICAgdmFyIG1zaWQgPSBTRFBVdGlsLnBhcnNlX3NzcmModGhpcy5yYXcpO1xuICAgICBpZiAobXNpZC5oYXNPd25Qcm9wZXJ0eSgnbXNsYWJlbCcpKSB7XG4gICAgIHRoaXMuc2Vzc2lvbiArPSBcImE9bXNpZC1zZW1hbnRpYzogV01TIFwiICsgbXNpZC5tc2xhYmVsICsgXCJcXHJcXG5cIjtcbiAgICAgfVxuICAgICAqL1xuXG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xufTtcblxuLy8gdHJhbnNsYXRlIGEgamluZ2xlIGNvbnRlbnQgZWxlbWVudCBpbnRvIGFuIGFuIFNEUCBtZWRpYSBwYXJ0XG5TRFAucHJvdG90eXBlLmppbmdsZTJtZWRpYSA9IGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgdmFyIG1lZGlhID0gJycsXG4gICAgICAgIGRlc2MgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uJyksXG4gICAgICAgIHNzcmMgPSBkZXNjLmF0dHIoJ3NzcmMnKSxcbiAgICAgICAgc2VsZiA9IHRoaXMsXG4gICAgICAgIHRtcDtcbiAgICB2YXIgc2N0cCA9IGNvbnRlbnQuZmluZChcbiAgICAgICAgJz50cmFuc3BvcnQ+c2N0cG1hcFt4bWxucz1cInVybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmR0bHMtc2N0cDoxXCJdJyk7XG5cbiAgICB0bXAgPSB7IG1lZGlhOiBkZXNjLmF0dHIoJ21lZGlhJykgfTtcbiAgICB0bXAucG9ydCA9ICcxJztcbiAgICBpZiAoY29udGVudC5hdHRyKCdzZW5kZXJzJykgPT0gJ3JlamVjdGVkJykge1xuICAgICAgICAvLyBlc3RvcyBoYWNrIHRvIHJlamVjdCBhbiBtLWxpbmUuXG4gICAgICAgIHRtcC5wb3J0ID0gJzAnO1xuICAgIH1cbiAgICBpZiAoY29udGVudC5maW5kKCc+dHJhbnNwb3J0PmZpbmdlcnByaW50JykubGVuZ3RoIHx8IGRlc2MuZmluZCgnZW5jcnlwdGlvbicpLmxlbmd0aCkge1xuICAgICAgICBpZiAoc2N0cC5sZW5ndGgpXG4gICAgICAgICAgICB0bXAucHJvdG8gPSAnRFRMUy9TQ1RQJztcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9TQVZQRic7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9BVlBGJztcbiAgICB9XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHRtcC5mbXQgPSBkZXNjLmZpbmQoJ3BheWxvYWQtdHlwZScpLm1hcChcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpOyB9KS5nZXQoKTtcbiAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9tbGluZSh0bXApICsgJ1xcclxcbic7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIG1lZGlhICs9ICdtPWFwcGxpY2F0aW9uIDEgRFRMUy9TQ1RQICcgKyBzY3RwLmF0dHIoJ251bWJlcicpICsgJ1xcclxcbic7XG4gICAgICAgIG1lZGlhICs9ICdhPXNjdHBtYXA6JyArIHNjdHAuYXR0cignbnVtYmVyJykgK1xuICAgICAgICAgICAgJyAnICsgc2N0cC5hdHRyKCdwcm90b2NvbCcpO1xuXG4gICAgICAgIHZhciBzdHJlYW1Db3VudCA9IHNjdHAuYXR0cignc3RyZWFtcycpO1xuICAgICAgICBpZiAoc3RyZWFtQ291bnQpXG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyBzdHJlYW1Db3VudCArICdcXHJcXG4nO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG5cbiAgICBtZWRpYSArPSAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbic7XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcDoxIElOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJz50cmFuc3BvcnRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjFcIl0nKTtcbiAgICBpZiAodG1wLmxlbmd0aCkge1xuICAgICAgICBpZiAodG1wLmF0dHIoJ3VmcmFnJykpIHtcbiAgICAgICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfaWNldWZyYWcodG1wLmF0dHIoJ3VmcmFnJykpICsgJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRtcC5hdHRyKCdwd2QnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9pY2Vwd2QodG1wLmF0dHIoJ3B3ZCcpKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIHRtcC5maW5kKCc+ZmluZ2VycHJpbnQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBjaGVjayBuYW1lc3BhY2UgYXQgc29tZSBwb2ludFxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdoYXNoJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyAkKHRoaXMpLnRleHQoKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJ2E9c2V0dXA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBzd2l0Y2ggKGNvbnRlbnQuYXR0cignc2VuZGVycycpKSB7XG4gICAgICAgIGNhc2UgJ2luaXRpYXRvcic6XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zZW5kb25seVxcclxcbic7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAncmVzcG9uZGVyJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXJlY3Zvbmx5XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdub25lJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdib3RoJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNlbmRyZWN2XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBtZWRpYSArPSAnYT1taWQ6JyArIGNvbnRlbnQuYXR0cignbmFtZScpICsgJ1xcclxcbic7XG5cbiAgICAvLyA8ZGVzY3JpcHRpb24+PHJ0Y3AtbXV4Lz48L2Rlc2NyaXB0aW9uPlxuICAgIC8vIHNlZSBodHRwOi8vY29kZS5nb29nbGUuY29tL3AvbGliamluZ2xlL2lzc3Vlcy9kZXRhaWw/aWQ9MzA5IC0tIG5vIHNwZWMgdGhvdWdoXG4gICAgLy8gYW5kIGh0dHA6Ly9tYWlsLmphYmJlci5vcmcvcGlwZXJtYWlsL2ppbmdsZS8yMDExLURlY2VtYmVyLzAwMTc2MS5odG1sXG4gICAgaWYgKGRlc2MuZmluZCgncnRjcC1tdXgnKS5sZW5ndGgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuICAgIH1cblxuICAgIGlmIChkZXNjLmZpbmQoJ2VuY3J5cHRpb24nKS5sZW5ndGgpIHtcbiAgICAgICAgZGVzYy5maW5kKCdlbmNyeXB0aW9uPmNyeXB0bycpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9Y3J5cHRvOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgndGFnJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgnY3J5cHRvLXN1aXRlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgna2V5LXBhcmFtcycpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXNzaW9uLXBhcmFtcycpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3Nlc3Npb24tcGFyYW1zJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIGRlc2MuZmluZCgncGF5bG9hZC10eXBlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfcnRwbWFwKHRoaXMpICsgJ1xcclxcbic7XG4gICAgICAgIGlmICgkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWZtdHA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpICsgJyAnO1xuICAgICAgICAgICAgbWVkaWEgKz0gJCh0aGlzKS5maW5kKCdwYXJhbWV0ZXInKS5tYXAoZnVuY3Rpb24gKCkgeyByZXR1cm4gKHRoaXMuZ2V0QXR0cmlidXRlKCduYW1lJykgPyAodGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICc9JykgOiAnJykgKyB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKTsgfSkuZ2V0KCkuam9pbignOyAnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIC8vIHhlcC0wMjkzXG4gICAgICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZSgkKHRoaXMpLCB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSk7XG4gICAgfSk7XG5cbiAgICAvLyB4ZXAtMDI5M1xuICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZShkZXNjLCAnKicpO1xuXG4gICAgLy8geGVwLTAyOTRcbiAgICB0bXAgPSBkZXNjLmZpbmQoJz5ydHAtaGRyZXh0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0cC1oZHJleHQ6MFwiXScpO1xuICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9ZXh0bWFwOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSArICcgJyArIHRoaXMuZ2V0QXR0cmlidXRlKCd1cmknKSArICdcXHJcXG4nO1xuICAgIH0pO1xuXG4gICAgY29udGVudC5maW5kKCc+dHJhbnNwb3J0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxXCJdPmNhbmRpZGF0ZScpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICBtZWRpYSArPSBTRFBVdGlsLmNhbmRpZGF0ZUZyb21KaW5nbGUodGhpcyk7XG4gICAgfSk7XG5cbiAgICAvLyBYRVAtMDMzOSBoYW5kbGUgc3NyYy1ncm91cCBhdHRyaWJ1dGVzXG4gICAgdG1wID0gY29udGVudC5maW5kKCdkZXNjcmlwdGlvbj5zc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgIHZhciBzc3JjcyA9ICQodGhpcykuZmluZCgnPnNvdXJjZScpLm1hcChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICB9KS5nZXQoKTtcblxuICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uPnNvdXJjZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzc3JjID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgJCh0aGlzKS5maW5kKCc+cGFyYW1ldGVyJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKTtcbiAgICAgICAgICAgIGlmICh0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKSAmJiB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJzonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3ZhbHVlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gbWVkaWE7XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gU0RQO1xuXG4iLCJmdW5jdGlvbiBTRFBEaWZmZXIobXlTRFAsIG90aGVyU0RQKSB7XG4gICAgdGhpcy5teVNEUCA9IG15U0RQO1xuICAgIHRoaXMub3RoZXJTRFAgPSBvdGhlclNEUDtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIG1hcCBvZiBNZWRpYUNoYW5uZWwgdGhhdCBjb250YWlucyBvbmx5IG1lZGlhIG5vdCBjb250YWluZWQgaW4gPHR0Pm90aGVyU2RwPC90dD4uIE1hcHBlZCBieSBjaGFubmVsIGlkeC5cbiAqIEBwYXJhbSBvdGhlclNkcCB0aGUgb3RoZXIgU0RQIHRvIGNoZWNrIHNzcmMgd2l0aC5cbiAqL1xuU0RQRGlmZmVyLnByb3RvdHlwZS5nZXROZXdNZWRpYSA9IGZ1bmN0aW9uKCkge1xuXG4gICAgLy8gdGhpcyBjb3VsZCBiZSB1c2VmdWwgaW4gQXJyYXkucHJvdG90eXBlLlxuICAgIGZ1bmN0aW9uIGFycmF5RXF1YWxzKGFycmF5KSB7XG4gICAgICAgIC8vIGlmIHRoZSBvdGhlciBhcnJheSBpcyBhIGZhbHN5IHZhbHVlLCByZXR1cm5cbiAgICAgICAgaWYgKCFhcnJheSlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICAvLyBjb21wYXJlIGxlbmd0aHMgLSBjYW4gc2F2ZSBhIGxvdCBvZiB0aW1lXG4gICAgICAgIGlmICh0aGlzLmxlbmd0aCAhPSBhcnJheS5sZW5ndGgpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGw9dGhpcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgICAgaWYgKHRoaXNbaV0gaW5zdGFuY2VvZiBBcnJheSAmJiBhcnJheVtpXSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmVjdXJzZSBpbnRvIHRoZSBuZXN0ZWQgYXJyYXlzXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzW2ldLmVxdWFscyhhcnJheVtpXSkpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKHRoaXNbaV0gIT0gYXJyYXlbaV0pIHtcbiAgICAgICAgICAgICAgICAvLyBXYXJuaW5nIC0gdHdvIGRpZmZlcmVudCBvYmplY3QgaW5zdGFuY2VzIHdpbGwgbmV2ZXIgYmUgZXF1YWw6IHt4OjIwfSAhPSB7eDoyMH1cbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgdmFyIG15TWVkaWFzID0gdGhpcy5teVNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgb3RoZXJzTWVkaWFzID0gdGhpcy5vdGhlclNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgbmV3TWVkaWEgPSB7fTtcbiAgICBPYmplY3Qua2V5cyhvdGhlcnNNZWRpYXMpLmZvckVhY2goZnVuY3Rpb24ob3RoZXJzTWVkaWFJZHgpIHtcbiAgICAgICAgdmFyIG15TWVkaWEgPSBteU1lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIHZhciBvdGhlcnNNZWRpYSA9IG90aGVyc01lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIGlmKCFteU1lZGlhICYmIG90aGVyc01lZGlhKSB7XG4gICAgICAgICAgICAvLyBBZGQgd2hvbGUgY2hhbm5lbFxuICAgICAgICAgICAgbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdID0gb3RoZXJzTWVkaWE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gTG9vayBmb3IgbmV3IHNzcmNzIGFjY3Jvc3MgdGhlIGNoYW5uZWxcbiAgICAgICAgT2JqZWN0LmtleXMob3RoZXJzTWVkaWEuc3NyY3MpLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgaWYoT2JqZWN0LmtleXMobXlNZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIHNzcmMgdGhhdCBkb2Vzbid0IGV4aXN0IGluIG91ciBjaGFubmVsXG4gICAgICAgICAgICAgICAgaWYoIW5ld01lZGlhW290aGVyc01lZGlhSWR4XSl7XG4gICAgICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhaW5kZXg6IG90aGVyc01lZGlhLm1lZGlhaW5kZXgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtaWQ6IG90aGVyc01lZGlhLm1pZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XS5zc3Jjc1tzc3JjXSA9IG90aGVyc01lZGlhLnNzcmNzW3NzcmNdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBMb29rIGZvciBuZXcgc3NyYyBncm91cHMgYWNyb3NzIHRoZSBjaGFubmVsc1xuICAgICAgICBvdGhlcnNNZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24ob3RoZXJTc3JjR3JvdXApe1xuXG4gICAgICAgICAgICAvLyB0cnkgdG8gbWF0Y2ggdGhlIG90aGVyIHNzcmMtZ3JvdXAgd2l0aCBhbiBzc3JjLWdyb3VwIG9mIG91cnNcbiAgICAgICAgICAgIHZhciBtYXRjaGVkID0gZmFsc2U7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG15TWVkaWEuc3NyY0dyb3Vwcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBteVNzcmNHcm91cCA9IG15TWVkaWEuc3NyY0dyb3Vwc1tpXTtcbiAgICAgICAgICAgICAgICBpZiAob3RoZXJTc3JjR3JvdXAuc2VtYW50aWNzID09IG15U3NyY0dyb3VwLnNlbWFudGljc1xuICAgICAgICAgICAgICAgICAgICAmJiBhcnJheUVxdWFscy5hcHBseShvdGhlclNzcmNHcm91cC5zc3JjcywgW215U3NyY0dyb3VwLnNzcmNzXSkpIHtcblxuICAgICAgICAgICAgICAgICAgICBtYXRjaGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIW1hdGNoZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIGFuIHNzcmMtZ3JvdXAgdGhhdCBkb2Vzbid0XG4gICAgICAgICAgICAgICAgLy8gZXhpc3QgaW4gb3VyIGNoYW5uZWxcblxuICAgICAgICAgICAgICAgIGlmKCFuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0pe1xuICAgICAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0gPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZWRpYWluZGV4OiBvdGhlcnNNZWRpYS5tZWRpYWluZGV4LFxuICAgICAgICAgICAgICAgICAgICAgICAgbWlkOiBvdGhlcnNNZWRpYS5taWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3Jjczoge30sXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3JjR3JvdXBzOiBbXVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0uc3NyY0dyb3Vwcy5wdXNoKG90aGVyU3NyY0dyb3VwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ld01lZGlhO1xufTtcblxuLyoqXG4gKiBTZW5kcyBTU1JDIHVwZGF0ZSBJUS5cbiAqIEBwYXJhbSBzZHBNZWRpYVNzcmNzIFNTUkNzIG1hcCBvYnRhaW5lZCBmcm9tIFNEUC5nZXROZXdNZWRpYS4gQ250YWlucyBTU1JDcyB0byBhZGQvcmVtb3ZlLlxuICogQHBhcmFtIHNpZCBzZXNzaW9uIGlkZW50aWZpZXIgdGhhdCB3aWxsIGJlIHB1dCBpbnRvIHRoZSBJUS5cbiAqIEBwYXJhbSBpbml0aWF0b3IgaW5pdGlhdG9yIGlkZW50aWZpZXIuXG4gKiBAcGFyYW0gdG9KaWQgZGVzdGluYXRpb24gSmlkXG4gKiBAcGFyYW0gaXNBZGQgaW5kaWNhdGVzIGlmIHRoaXMgaXMgcmVtb3ZlIG9yIGFkZCBvcGVyYXRpb24uXG4gKi9cblNEUERpZmZlci5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbihtb2RpZnkpIHtcbiAgICB2YXIgc2RwTWVkaWFTc3JjcyA9IHRoaXMuZ2V0TmV3TWVkaWEoKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBGSVhNRTogb25seSBhbm5vdW5jZSB2aWRlbyBzc3JjcyBzaW5jZSB3ZSBtaXggYXVkaW8gYW5kIGRvbnQgbmVlZFxuICAgIC8vICAgICAgdGhlIGF1ZGlvIHNzcmNzIHRoZXJlZm9yZVxuICAgIHZhciBtb2RpZmllZCA9IGZhbHNlO1xuICAgIE9iamVjdC5rZXlzKHNkcE1lZGlhU3NyY3MpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIG1vZGlmaWVkID0gdHJ1ZTtcbiAgICAgICAgdmFyIG1lZGlhID0gc2RwTWVkaWFTc3Jjc1ttZWRpYWluZGV4XTtcbiAgICAgICAgbW9kaWZ5LmMoJ2NvbnRlbnQnLCB7bmFtZTogbWVkaWEubWlkfSk7XG5cbiAgICAgICAgbW9kaWZ5LmMoJ2Rlc2NyaXB0aW9uJywge3htbG5zOid1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScsIG1lZGlhOiBtZWRpYS5taWR9KTtcbiAgICAgICAgLy8gRklYTUU6IG5vdCBjb21wbGV0bHkgc3VyZSB0aGlzIG9wZXJhdGVzIG9uIGJsb2NrcyBhbmQgLyBvciBoYW5kbGVzIGRpZmZlcmVudCBzc3JjcyBjb3JyZWN0bHlcbiAgICAgICAgLy8gZ2VuZXJhdGUgc291cmNlcyBmcm9tIGxpbmVzXG4gICAgICAgIE9iamVjdC5rZXlzKG1lZGlhLnNzcmNzKS5mb3JFYWNoKGZ1bmN0aW9uKHNzcmNOdW0pIHtcbiAgICAgICAgICAgIHZhciBtZWRpYVNzcmMgPSBtZWRpYS5zc3Jjc1tzc3JjTnVtXTtcbiAgICAgICAgICAgIG1vZGlmeS5jKCdzb3VyY2UnLCB7IHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICBtb2RpZnkuYXR0cnMoe3NzcmM6IG1lZGlhU3NyYy5zc3JjfSk7XG4gICAgICAgICAgICAvLyBpdGVyYXRlIG92ZXIgc3NyYyBsaW5lc1xuICAgICAgICAgICAgbWVkaWFTc3JjLmxpbmVzLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgaWR4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgbW9kaWZ5LmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgIGlmIChrdi5pbmRleE9mKCc6JykgPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3YgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3Yuc3BsaXQoJzonLCAyKVswXSB9KTtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgdmFsdWU6IGt2LnNwbGl0KCc6JywgMilbMV0gfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgcGFyYW1ldGVyXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc291cmNlXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIGdlbmVyYXRlIHNvdXJjZSBncm91cHMgZnJvbSBsaW5lc1xuICAgICAgICBtZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24oc3NyY0dyb3VwKSB7XG4gICAgICAgICAgICBpZiAoc3NyY0dyb3VwLnNzcmNzLmxlbmd0aCAhPSAwKSB7XG5cbiAgICAgICAgICAgICAgICBtb2RpZnkuYygnc3NyYy1ncm91cCcsIHtcbiAgICAgICAgICAgICAgICAgICAgc2VtYW50aWNzOiBzc3JjR3JvdXAuc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBzc3JjR3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAudXAoKTsgLy8gZW5kIG9mIHNvdXJjZVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc3NyYy1ncm91cFxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBtb2RpZnkudXAoKTsgLy8gZW5kIG9mIGRlc2NyaXB0aW9uXG4gICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIG1vZGlmaWVkO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTRFBEaWZmZXI7IiwiU0RQVXRpbCA9IHtcbiAgICBpY2VwYXJhbXM6IGZ1bmN0aW9uIChtZWRpYWRlc2MsIHNlc3Npb25kZXNjKSB7XG4gICAgICAgIHZhciBkYXRhID0gbnVsbDtcbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKG1lZGlhZGVzYywgJ2E9aWNlLXVmcmFnOicsIHNlc3Npb25kZXNjKSAmJlxuICAgICAgICAgICAgU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSkge1xuICAgICAgICAgICAgZGF0YSA9IHtcbiAgICAgICAgICAgICAgICB1ZnJhZzogU0RQVXRpbC5wYXJzZV9pY2V1ZnJhZyhTRFBVdGlsLmZpbmRfbGluZShtZWRpYWRlc2MsICdhPWljZS11ZnJhZzonLCBzZXNzaW9uZGVzYykpLFxuICAgICAgICAgICAgICAgIHB3ZDogU0RQVXRpbC5wYXJzZV9pY2Vwd2QoU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSlcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBwYXJzZV9pY2V1ZnJhZzogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgcmV0dXJuIGxpbmUuc3Vic3RyaW5nKDEyKTtcbiAgICB9LFxuICAgIGJ1aWxkX2ljZXVmcmFnOiBmdW5jdGlvbiAoZnJhZykge1xuICAgICAgICByZXR1cm4gJ2E9aWNlLXVmcmFnOicgKyBmcmFnO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlcHdkOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICByZXR1cm4gbGluZS5zdWJzdHJpbmcoMTApO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlcHdkOiBmdW5jdGlvbiAocHdkKSB7XG4gICAgICAgIHJldHVybiAnYT1pY2UtcHdkOicgKyBwd2Q7XG4gICAgfSxcbiAgICBwYXJzZV9taWQ6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHJldHVybiBsaW5lLnN1YnN0cmluZyg2KTtcbiAgICB9LFxuICAgIHBhcnNlX21saW5lOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygyKS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLm1lZGlhID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wb3J0ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wcm90byA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChwYXJ0c1twYXJ0cy5sZW5ndGggLSAxXSA9PT0gJycpIHsgLy8gdHJhaWxpbmcgd2hpdGVzcGFjZVxuICAgICAgICAgICAgcGFydHMucG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgZGF0YS5mbXQgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBidWlsZF9tbGluZTogZnVuY3Rpb24gKG1saW5lKSB7XG4gICAgICAgIHJldHVybiAnbT0nICsgbWxpbmUubWVkaWEgKyAnICcgKyBtbGluZS5wb3J0ICsgJyAnICsgbWxpbmUucHJvdG8gKyAnICcgKyBtbGluZS5mbXQuam9pbignICcpO1xuICAgIH0sXG4gICAgcGFyc2VfcnRwbWFwOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmlkID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuICAgICAgICBkYXRhLm5hbWUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmNsb2NrcmF0ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPyBwYXJ0cy5zaGlmdCgpIDogJzEnO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIFBhcnNlcyBTRFAgbGluZSBcImE9c2N0cG1hcDouLi5cIiBhbmQgZXh0cmFjdHMgU0NUUCBwb3J0IGZyb20gaXQuXG4gICAgICogQHBhcmFtIGxpbmUgZWcuIFwiYT1zY3RwbWFwOjUwMDAgd2VicnRjLWRhdGFjaGFubmVsXCJcbiAgICAgKiBAcmV0dXJucyBbU0NUUCBwb3J0IG51bWJlciwgcHJvdG9jb2wsIHN0cmVhbXNdXG4gICAgICovXG4gICAgcGFyc2Vfc2N0cG1hcDogZnVuY3Rpb24gKGxpbmUpXG4gICAge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMCkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIHNjdHBQb3J0ID0gcGFydHNbMF07XG4gICAgICAgIHZhciBwcm90b2NvbCA9IHBhcnRzWzFdO1xuICAgICAgICAvLyBTdHJlYW0gY291bnQgaXMgb3B0aW9uYWxcbiAgICAgICAgdmFyIHN0cmVhbUNvdW50ID0gcGFydHMubGVuZ3RoID4gMiA/IHBhcnRzWzJdIDogbnVsbDtcbiAgICAgICAgcmV0dXJuIFtzY3RwUG9ydCwgcHJvdG9jb2wsIHN0cmVhbUNvdW50XTsvLyBTQ1RQIHBvcnRcbiAgICB9LFxuICAgIGJ1aWxkX3J0cG1hcDogZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9cnRwbWFwOicgKyBlbC5nZXRBdHRyaWJ1dGUoJ2lkJykgKyAnICcgKyBlbC5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICcvJyArIGVsLmdldEF0dHJpYnV0ZSgnY2xvY2tyYXRlJyk7XG4gICAgICAgIGlmIChlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJykgJiYgZWwuZ2V0QXR0cmlidXRlKCdjaGFubmVscycpICE9ICcxJykge1xuICAgICAgICAgICAgbGluZSArPSAnLycgKyBlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxpbmU7XG4gICAgfSxcbiAgICBwYXJzZV9jcnlwdG86IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEudGFnID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YVsnY3J5cHRvLXN1aXRlJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhWydrZXktcGFyYW1zJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBpZiAocGFydHMubGVuZ3RoKSB7XG4gICAgICAgICAgICBkYXRhWydzZXNzaW9uLXBhcmFtcyddID0gcGFydHMuam9pbignICcpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfZmluZ2VycHJpbnQ6IGZ1bmN0aW9uIChsaW5lKSB7IC8vIFJGQyA0NTcyXG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDE0KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmhhc2ggPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmZpbmdlcnByaW50ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgLy8gVE9ETyBhc3NlcnQgdGhhdCBmaW5nZXJwcmludCBzYXRpc2ZpZXMgMlVIRVggKihcIjpcIiAyVUhFWCkgP1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2ZtdHA6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3BsaXQoJyAnKSxcbiAgICAgICAgICAgIGksIGtleSwgdmFsdWUsXG4gICAgICAgICAgICBkYXRhID0gW107XG4gICAgICAgIHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIHBhcnRzID0gcGFydHMuam9pbignICcpLnNwbGl0KCc7Jyk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAga2V5ID0gcGFydHNbaV0uc3BsaXQoJz0nKVswXTtcbiAgICAgICAgICAgIHdoaWxlIChrZXkubGVuZ3RoICYmIGtleVswXSA9PSAnICcpIHtcbiAgICAgICAgICAgICAgICBrZXkgPSBrZXkuc3Vic3RyaW5nKDEpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFsdWUgPSBwYXJ0c1tpXS5zcGxpdCgnPScpWzFdO1xuICAgICAgICAgICAgaWYgKGtleSAmJiB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIGRhdGEucHVzaCh7bmFtZToga2V5LCB2YWx1ZTogdmFsdWV9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmZjIDQ3MzMgKERUTUYpIHN0eWxlIHN0dWZmXG4gICAgICAgICAgICAgICAgZGF0YS5wdXNoKHtuYW1lOiAnJywgdmFsdWU6IGtleX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgY2FuZGlkYXRlID0ge30sXG4gICAgICAgICAgICBlbGVtcyA9IGxpbmUuc3BsaXQoJyAnKTtcbiAgICAgICAgY2FuZGlkYXRlLmZvdW5kYXRpb24gPSBlbGVtc1swXS5zdWJzdHJpbmcoMTIpO1xuICAgICAgICBjYW5kaWRhdGUuY29tcG9uZW50ID0gZWxlbXNbMV07XG4gICAgICAgIGNhbmRpZGF0ZS5wcm90b2NvbCA9IGVsZW1zWzJdLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNhbmRpZGF0ZS5wcmlvcml0eSA9IGVsZW1zWzNdO1xuICAgICAgICBjYW5kaWRhdGUuaXAgPSBlbGVtc1s0XTtcbiAgICAgICAgY2FuZGlkYXRlLnBvcnQgPSBlbGVtc1s1XTtcbiAgICAgICAgLy8gZWxlbXNbNl0gPT4gXCJ0eXBcIlxuICAgICAgICBjYW5kaWRhdGUudHlwZSA9IGVsZW1zWzddO1xuICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IDA7IC8vIGRlZmF1bHQgdmFsdWUsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKHZhciBpID0gODsgaSA8IGVsZW1zLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICBzd2l0Y2ggKGVsZW1zW2ldKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAncmFkZHInOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1hZGRyJ10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlWydyZWwtcG9ydCddID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdnZW5lcmF0aW9uJzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RjcHR5cGUnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUudGNwdHlwZSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDogLy8gVE9ET1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VfaWNlY2FuZGlkYXRlIG5vdCB0cmFuc2xhdGluZyBcIicgKyBlbGVtc1tpXSArICdcIiA9IFwiJyArIGVsZW1zW2kgKyAxXSArICdcIicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZS5uZXR3b3JrID0gJzEnO1xuICAgICAgICBjYW5kaWRhdGUuaWQgPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApOyAvLyBub3QgYXBwbGljYWJsZSB0byBTRFAgLS0gRklYTUU6IHNob3VsZCBiZSB1bmlxdWUsIG5vdCBqdXN0IHJhbmRvbVxuICAgICAgICByZXR1cm4gY2FuZGlkYXRlO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAoY2FuZCkge1xuICAgICAgICB2YXIgbGluZSA9IFsnYT1jYW5kaWRhdGU6JyArIGNhbmQuZm91bmRhdGlvbiwgY2FuZC5jb21wb25lbnQsIGNhbmQucHJvdG9jb2wsIGNhbmQucHJpb3JpdHksIGNhbmQuaXAsIGNhbmQucG9ydCwgJ3R5cCcsIGNhbmQudHlwZV0uam9pbignICcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgc3dpdGNoIChjYW5kLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtYWRkciddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncnBvcnQnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtcG9ydCddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNhbmQuaGFzT3duQXR0cmlidXRlKCd0Y3B0eXBlJykpIHtcbiAgICAgICAgICAgIGxpbmUgKz0gJ3RjcHR5cGUnO1xuICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICBsaW5lICs9IGNhbmQudGNwdHlwZTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmhhc093bkF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpID8gY2FuZC5nZW5lcmF0aW9uIDogJzAnO1xuICAgICAgICByZXR1cm4gbGluZTtcbiAgICB9LFxuICAgIHBhcnNlX3NzcmM6IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgICAgIC8vIHByb3ByaWV0YXJ5IG1hcHBpbmcgb2YgYT1zc3JjIGxpbmVzXG4gICAgICAgIC8vIFRPRE86IHNlZSBcIkppbmdsZSBSVFAgU291cmNlIERlc2NyaXB0aW9uXCIgYnkgSnViZXJ0aSBhbmQgUC4gVGhhdGNoZXIgb24gZ29vZ2xlIGRvY3NcbiAgICAgICAgLy8gYW5kIHBhcnNlIGFjY29yZGluZyB0byB0aGF0XG4gICAgICAgIHZhciBsaW5lcyA9IGRlc2Muc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIDcpID09ICdhPXNzcmM6Jykge1xuICAgICAgICAgICAgICAgIHZhciBpZHggPSBsaW5lc1tpXS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgZGF0YVtsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVswXV0gPSBsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVsxXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX3J0Y3BmYjogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoMTApLnNwbGl0KCcgJyk7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGRhdGEucHQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnR5cGUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnBhcmFtcyA9IHBhcnRzO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2V4dG1hcDogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIGRhdGEgPSB7fTtcbiAgICAgICAgZGF0YS52YWx1ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSAhPSAtMSkge1xuICAgICAgICAgICAgZGF0YS5kaXJlY3Rpb24gPSBkYXRhLnZhbHVlLnN1YnN0cihkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSArIDEpO1xuICAgICAgICAgICAgZGF0YS52YWx1ZSA9IGRhdGEudmFsdWUuc3Vic3RyKDAsIGRhdGEudmFsdWUuaW5kZXhPZignLycpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRhdGEuZGlyZWN0aW9uID0gJ2JvdGgnO1xuICAgICAgICB9XG4gICAgICAgIGRhdGEudXJpID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wYXJhbXMgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBmaW5kX2xpbmU6IGZ1bmN0aW9uIChoYXlzdGFjaywgbmVlZGxlLCBzZXNzaW9ucGFydCkge1xuICAgICAgICB2YXIgbGluZXMgPSBoYXlzdGFjay5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGxpbmVzW2ldO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICghc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBzZWFyY2ggc2Vzc2lvbiBwYXJ0XG4gICAgICAgIGxpbmVzID0gc2Vzc2lvbnBhcnQuc3BsaXQoJ1xcclxcbicpO1xuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsaW5lc1tqXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBmaW5kX2xpbmVzOiBmdW5jdGlvbiAoaGF5c3RhY2ssIG5lZWRsZSwgc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgdmFyIGxpbmVzID0gaGF5c3RhY2suc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgbmVlZGxlcyA9IFtdO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSlcbiAgICAgICAgICAgICAgICBuZWVkbGVzLnB1c2gobGluZXNbaV0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChuZWVkbGVzLmxlbmd0aCB8fCAhc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBuZWVkbGVzO1xuICAgICAgICB9XG4gICAgICAgIC8vIHNlYXJjaCBzZXNzaW9uIHBhcnRcbiAgICAgICAgbGluZXMgPSBzZXNzaW9ucGFydC5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tqXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgbmVlZGxlcy5wdXNoKGxpbmVzW2pdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmVlZGxlcztcbiAgICB9LFxuICAgIGNhbmRpZGF0ZVRvSmluZ2xlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAvLyBhPWNhbmRpZGF0ZToyOTc5MTY2NjYyIDEgdWRwIDIxMTM5MzcxNTEgMTkyLjE2OC4yLjEwMCA1NzY5OCB0eXAgaG9zdCBnZW5lcmF0aW9uIDBcbiAgICAgICAgLy8gICAgICA8Y2FuZGlkYXRlIGNvbXBvbmVudD0uLi4gZm91bmRhdGlvbj0uLi4gZ2VuZXJhdGlvbj0uLi4gaWQ9Li4uIGlwPS4uLiBuZXR3b3JrPS4uLiBwb3J0PS4uLiBwcmlvcml0eT0uLi4gcHJvdG9jb2w9Li4uIHR5cGU9Li4uLz5cbiAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignY2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICBsaW5lID0gJ2E9JyArIGxpbmU7XG4gICAgICAgIH0gZWxzZSBpZiAobGluZS5zdWJzdHJpbmcoMCwgMTIpICE9ICdhPWNhbmRpZGF0ZTonKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VDYW5kaWRhdGUgY2FsbGVkIHdpdGggYSBsaW5lIHRoYXQgaXMgbm90IGEgY2FuZGlkYXRlIGxpbmUnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGxpbmUuc3Vic3RyaW5nKGxpbmUubGVuZ3RoIC0gMikgPT0gJ1xcclxcbicpIC8vIGNob21wIGl0XG4gICAgICAgICAgICBsaW5lID0gbGluZS5zdWJzdHJpbmcoMCwgbGluZS5sZW5ndGggLSAyKTtcbiAgICAgICAgdmFyIGNhbmRpZGF0ZSA9IHt9LFxuICAgICAgICAgICAgZWxlbXMgPSBsaW5lLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBpO1xuICAgICAgICBpZiAoZWxlbXNbNl0gIT0gJ3R5cCcpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdkaWQgbm90IGZpbmQgdHlwIGluIHRoZSByaWdodCBwbGFjZScpO1xuICAgICAgICAgICAgY29uc29sZS5sb2cobGluZSk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUuZm91bmRhdGlvbiA9IGVsZW1zWzBdLnN1YnN0cmluZygxMik7XG4gICAgICAgIGNhbmRpZGF0ZS5jb21wb25lbnQgPSBlbGVtc1sxXTtcbiAgICAgICAgY2FuZGlkYXRlLnByb3RvY29sID0gZWxlbXNbMl0udG9Mb3dlckNhc2UoKTtcbiAgICAgICAgY2FuZGlkYXRlLnByaW9yaXR5ID0gZWxlbXNbM107XG4gICAgICAgIGNhbmRpZGF0ZS5pcCA9IGVsZW1zWzRdO1xuICAgICAgICBjYW5kaWRhdGUucG9ydCA9IGVsZW1zWzVdO1xuICAgICAgICAvLyBlbGVtc1s2XSA9PiBcInR5cFwiXG4gICAgICAgIGNhbmRpZGF0ZS50eXBlID0gZWxlbXNbN107XG5cbiAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSAnMCc7IC8vIGRlZmF1bHQsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKGkgPSA4OyBpIDwgZWxlbXMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgICAgICAgIHN3aXRjaCAoZWxlbXNbaV0pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdyYWRkcic6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZVsncmVsLWFkZHInXSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncnBvcnQnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1wb3J0J10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2dlbmVyYXRpb24nOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZS50Y3B0eXBlID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBUT0RPXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdub3QgdHJhbnNsYXRpbmcgXCInICsgZWxlbXNbaV0gKyAnXCIgPSBcIicgKyBlbGVtc1tpICsgMV0gKyAnXCInKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUubmV0d29yayA9ICcxJztcbiAgICAgICAgY2FuZGlkYXRlLmlkID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDEwKTsgLy8gbm90IGFwcGxpY2FibGUgdG8gU0RQIC0tIEZJWE1FOiBzaG91bGQgYmUgdW5pcXVlLCBub3QganVzdCByYW5kb21cbiAgICAgICAgcmV0dXJuIGNhbmRpZGF0ZTtcbiAgICB9LFxuICAgIGNhbmRpZGF0ZUZyb21KaW5nbGU6IGZ1bmN0aW9uIChjYW5kKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9Y2FuZGlkYXRlOic7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2ZvdW5kYXRpb24nKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2NvbXBvbmVudCcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJvdG9jb2wnKTsgLy8udG9VcHBlckNhc2UoKTsgLy8gY2hyb21lIE0yMyBkb2Vzbid0IGxpa2UgdGhpc1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJpb3JpdHknKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2lwJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdwb3J0Jyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9ICd0eXAnO1xuICAgICAgICBsaW5lICs9ICcgJyArIGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBzd2l0Y2ggKGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncmVsLWFkZHInKTtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJ3Jwb3J0JztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3Byb3RvY29sJykudG9Mb3dlckNhc2UoKSA9PSAndGNwJykge1xuICAgICAgICAgICAgbGluZSArPSAndGNwdHlwZSc7XG4gICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3RjcHR5cGUnKTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpIHx8ICcwJztcbiAgICAgICAgcmV0dXJuIGxpbmUgKyAnXFxyXFxuJztcbiAgICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBTRFBVdGlsOyIsImZ1bmN0aW9uIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKGljZV9jb25maWcsIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBSVENQZWVyY29ubmVjdGlvbiA9IG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEgPyBtb3pSVENQZWVyQ29ubmVjdGlvbiA6IHdlYmtpdFJUQ1BlZXJDb25uZWN0aW9uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSBuZXcgUlRDUGVlcmNvbm5lY3Rpb24oaWNlX2NvbmZpZywgY29uc3RyYWludHMpO1xuICAgIHRoaXMudXBkYXRlTG9nID0gW107XG4gICAgdGhpcy5zdGF0cyA9IHt9O1xuICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgdGhpcy5tYXhzdGF0cyA9IDA7IC8vIGxpbWl0IHRvIDMwMCB2YWx1ZXMsIGkuZS4gNSBtaW51dGVzOyBzZXQgdG8gMCB0byBkaXNhYmxlXG5cbiAgICAvLyBvdmVycmlkZSBhcyBkZXNpcmVkXG4gICAgdGhpcy50cmFjZSA9IGZ1bmN0aW9uICh3aGF0LCBpbmZvKSB7XG4gICAgICAgIC8vY29uc29sZS53YXJuKCdXVFJBQ0UnLCB3aGF0LCBpbmZvKTtcbiAgICAgICAgc2VsZi51cGRhdGVMb2cucHVzaCh7XG4gICAgICAgICAgICB0aW1lOiBuZXcgRGF0ZSgpLFxuICAgICAgICAgICAgdHlwZTogd2hhdCxcbiAgICAgICAgICAgIHZhbHVlOiBpbmZvIHx8IFwiXCJcbiAgICAgICAgfSk7XG4gICAgfTtcbiAgICB0aGlzLm9uaWNlY2FuZGlkYXRlID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uaWNlY2FuZGlkYXRlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uaWNlY2FuZGlkYXRlJywgSlNPTi5zdHJpbmdpZnkoZXZlbnQuY2FuZGlkYXRlLCBudWxsLCAnICcpKTtcbiAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9uYWRkc3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uYWRkc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uYWRkc3RyZWFtJywgZXZlbnQuc3RyZWFtLmlkKTtcbiAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9ucmVtb3Zlc3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9ucmVtb3Zlc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29ucmVtb3Zlc3RyZWFtJywgZXZlbnQuc3RyZWFtLmlkKTtcbiAgICAgICAgaWYgKHNlbGYub25yZW1vdmVzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25yZW1vdmVzdHJlYW0oZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbnNpZ25hbGluZ3N0YXRlY2hhbmdlJywgc2VsZi5zaWduYWxpbmdTdGF0ZSk7XG4gICAgICAgIGlmIChzZWxmLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25zaWduYWxpbmdzdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25uZWdvdGlhdGlvbm5lZWRlZCcpO1xuICAgICAgICBpZiAoc2VsZi5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBzZWxmLm9uZGF0YWNoYW5uZWwgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25kYXRhY2hhbm5lbCA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbmRhdGFjaGFubmVsJywgZXZlbnQpO1xuICAgICAgICBpZiAoc2VsZi5vbmRhdGFjaGFubmVsICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9uZGF0YWNoYW5uZWwoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBpZiAoIW5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEgJiYgdGhpcy5tYXhzdGF0cykge1xuICAgICAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKGZ1bmN0aW9uKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlc3VsdHMubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhyZXN1bHRzW2ldLnR5cGUsIHJlc3VsdHNbaV0uaWQsIHJlc3VsdHNbaV0ubmFtZXMoKSlcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdHNbaV0ubmFtZXMoKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSByZXN1bHRzW2ldLmlkICsgJy0nICsgbmFtZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghc2VsZi5zdGF0c1tpZF0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRUaW1lOiBub3csXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZFRpbWU6IG5vdyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXM6IFtdXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLnZhbHVlcy5wdXNoKHJlc3VsdHNbaV0uc3RhdChuYW1lKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS50aW1lcy5wdXNoKG5vdy5nZXRUaW1lKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuc3RhdHNbaWRdLnZhbHVlcy5sZW5ndGggPiBzZWxmLm1heHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udmFsdWVzLnNoaWZ0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udGltZXMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLmVuZFRpbWUgPSBub3c7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIH0sIDEwMDApO1xuICAgIH1cbn07XG5cbmR1bXBTRFAgPSBmdW5jdGlvbihkZXNjcmlwdGlvbikge1xuICAgIHJldHVybiAndHlwZTogJyArIGRlc2NyaXB0aW9uLnR5cGUgKyAnXFxyXFxuJyArIGRlc2NyaXB0aW9uLnNkcDtcbn1cblxuaWYgKFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fICE9PSB1bmRlZmluZWQpIHtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnc2lnbmFsaW5nU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGU7IH0pO1xuICAgIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKCdpY2VDb25uZWN0aW9uU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlOyB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnbG9jYWxEZXNjcmlwdGlvbicsIGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgcHVibGljTG9jYWxEZXNjcmlwdGlvbiA9IHNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbih0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pO1xuICAgICAgICByZXR1cm4gcHVibGljTG9jYWxEZXNjcmlwdGlvbjtcbiAgICB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygncmVtb3RlRGVzY3JpcHRpb24nLCBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHB1YmxpY1JlbW90ZURlc2NyaXB0aW9uID0gc2ltdWxjYXN0LnJldmVyc2VUcmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbih0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uKTtcbiAgICAgICAgcmV0dXJuIHB1YmxpY1JlbW90ZURlc2NyaXB0aW9uO1xuICAgIH0pO1xufVxuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgIHRoaXMudHJhY2UoJ2FkZFN0cmVhbScsIHN0cmVhbS5pZCk7XG4gICAgc2ltdWxjYXN0LnJlc2V0U2VuZGVyKCk7XG4gICAgdHJ5XG4gICAge1xuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmFkZFN0cmVhbShzdHJlYW0pO1xuICAgIH1cbiAgICBjYXRjaCAoZSlcbiAgICB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSwgc3RvcFN0cmVhbXMpIHtcbiAgICB0aGlzLnRyYWNlKCdyZW1vdmVTdHJlYW0nLCBzdHJlYW0uaWQpO1xuICAgIHNpbXVsY2FzdC5yZXNldFNlbmRlcigpO1xuICAgIGlmKHN0b3BTdHJlYW1zKSB7XG4gICAgICAgIHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmZvckVhY2goZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICAgICAgICB0cmFjay5zdG9wKCk7XG4gICAgICAgIH0pO1xuICAgICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAgICAgdHJhY2suc3RvcCgpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oc3RyZWFtKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVEYXRhQ2hhbm5lbCA9IGZ1bmN0aW9uIChsYWJlbCwgb3B0cykge1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZURhdGFDaGFubmVsJywgbGFiZWwsIG9wdHMpO1xuICAgIHJldHVybiB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZURhdGFDaGFubmVsKGxhYmVsLCBvcHRzKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2NyaXB0aW9uLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBkZXNjcmlwdGlvbiA9IHNpbXVsY2FzdC50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKGRlc2NyaXB0aW9uKTtcbiAgICB0aGlzLnRyYWNlKCdzZXRMb2NhbERlc2NyaXB0aW9uJywgZHVtcFNEUChkZXNjcmlwdGlvbikpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihkZXNjcmlwdGlvbixcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0TG9jYWxEZXNjcmlwdGlvbk9uU3VjY2VzcycpO1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldExvY2FsRGVzY3JpcHRpb25PbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICApO1xuICAgIC8qXG4gICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5tYXhzdGF0cyA+IDApIHtcbiAgICAgLy8gc3RhcnQgZ2F0aGVyaW5nIHN0YXRzXG4gICAgIH1cbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjcmlwdGlvbiwgc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgZGVzY3JpcHRpb24gPSBzaW11bGNhc3QudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24oZGVzY3JpcHRpb24pO1xuICAgIHRoaXMudHJhY2UoJ3NldFJlbW90ZURlc2NyaXB0aW9uJywgZHVtcFNEUChkZXNjcmlwdGlvbikpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0UmVtb3RlRGVzY3JpcHRpb24oZGVzY3JpcHRpb24sXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldFJlbW90ZURlc2NyaXB0aW9uT25TdWNjZXNzJyk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0UmVtb3RlRGVzY3JpcHRpb25PbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICApO1xuICAgIC8qXG4gICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5tYXhzdGF0cyA+IDApIHtcbiAgICAgLy8gc3RhcnQgZ2F0aGVyaW5nIHN0YXRzXG4gICAgIH1cbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jbG9zZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnRyYWNlKCdzdG9wJyk7XG4gICAgaWYgKHRoaXMuc3RhdHNpbnRlcnZhbCAhPT0gbnVsbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbCh0aGlzLnN0YXRzaW50ZXJ2YWwpO1xuICAgICAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNsb3NlKCk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXIgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2ssIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZU9mZmVyJywgSlNPTi5zdHJpbmdpZnkoY29uc3RyYWludHMsIG51bGwsICcgJykpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY3JlYXRlT2ZmZXIoXG4gICAgICAgIGZ1bmN0aW9uIChvZmZlcikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlT2ZmZXJPblN1Y2Nlc3MnLCBkdW1wU0RQKG9mZmVyKSk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2sob2ZmZXIpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ2NyZWF0ZU9mZmVyT25GYWlsdXJlJywgZXJyKTtcbiAgICAgICAgICAgIGZhaWx1cmVDYWxsYmFjayhlcnIpO1xuICAgICAgICB9LFxuICAgICAgICBjb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24gKHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrLCBjb25zdHJhaW50cykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnRyYWNlKCdjcmVhdGVBbnN3ZXInLCBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cywgbnVsbCwgJyAnKSk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgIGZ1bmN0aW9uIChhbnN3ZXIpIHtcbiAgICAgICAgICAgIGFuc3dlciA9IHNpbXVsY2FzdC50cmFuc2Zvcm1BbnN3ZXIoYW5zd2VyKTtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ2NyZWF0ZUFuc3dlck9uU3VjY2VzcycsIGR1bXBTRFAoYW5zd2VyKSk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soYW5zd2VyKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24oZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVBbnN3ZXJPbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoY2FuZGlkYXRlLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGUnLCBKU09OLnN0cmluZ2lmeShjYW5kaWRhdGUsIG51bGwsICcgJykpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgLyogbWF5YmUgbGF0ZXJcbiAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5hZGRJY2VDYW5kaWRhdGUoY2FuZGlkYXRlLFxuICAgICBmdW5jdGlvbiAoKSB7XG4gICAgIHNlbGYudHJhY2UoJ2FkZEljZUNhbmRpZGF0ZU9uU3VjY2VzcycpO1xuICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgfSxcbiAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICBzZWxmLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGVPbkZhaWx1cmUnLCBlcnIpO1xuICAgICBmYWlsdXJlQ2FsbGJhY2soZXJyKTtcbiAgICAgfVxuICAgICApO1xuICAgICAqL1xufTtcblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24oY2FsbGJhY2ssIGVycmJhY2spIHtcbiAgICBpZiAobmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSkge1xuICAgICAgICAvLyBpZ25vcmUgZm9yIG5vdy4uLlxuICAgICAgICBpZighZXJyYmFjaylcbiAgICAgICAgICAgIGVycmJhY2sgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhudWxsLGNhbGxiYWNrLGVycmJhY2spO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoY2FsbGJhY2spO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gVHJhY2VhYmxlUGVlckNvbm5lY3Rpb247XG5cbiIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgVUksIG1lc3NhZ2VIYW5kbGVyLFxuIHJvb21OYW1lLCBzZXNzaW9uVGVybWluYXRlZCwgU3Ryb3BoZSwgVXRpbCAqL1xuLyoqXG4gKiBDb250YWlucyBsb2dpYyByZXNwb25zaWJsZSBmb3IgZW5hYmxpbmcvZGlzYWJsaW5nIGZ1bmN0aW9uYWxpdHkgYXZhaWxhYmxlXG4gKiBvbmx5IHRvIG1vZGVyYXRvciB1c2Vycy5cbiAqL1xudmFyIGNvbm5lY3Rpb24gPSBudWxsO1xudmFyIGZvY3VzVXNlckppZDtcbnZhciBnZXROZXh0VGltZW91dCA9IFV0aWwuY3JlYXRlRXhwQmFja29mZlRpbWVyKDEwMDApO1xudmFyIGdldE5leHRFcnJvclRpbWVvdXQgPSBVdGlsLmNyZWF0ZUV4cEJhY2tvZmZUaW1lcigxMDAwKTtcbi8vIEV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIHN0dWZmXG52YXIgZXh0ZXJuYWxBdXRoRW5hYmxlZCA9IGZhbHNlO1xuLy8gU2lwIGdhdGV3YXkgY2FuIGJlIGVuYWJsZWQgYnkgY29uZmlndXJpbmcgSmlnYXNpIGhvc3QgaW4gY29uZmlnLmpzIG9yXG4vLyBpdCB3aWxsIGJlIGVuYWJsZWQgYXV0b21hdGljYWxseSBpZiBmb2N1cyBkZXRlY3RzIHRoZSBjb21wb25lbnQgdGhyb3VnaFxuLy8gc2VydmljZSBkaXNjb3ZlcnkuXG52YXIgc2lwR2F0ZXdheUVuYWJsZWQgPSBjb25maWcuaG9zdHMuY2FsbF9jb250cm9sICE9PSB1bmRlZmluZWQ7XG5cbnZhciBNb2RlcmF0b3IgPSB7XG4gICAgaXNNb2RlcmF0b3I6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5lbXVjLmlzTW9kZXJhdG9yKCk7XG4gICAgfSxcblxuICAgIGlzUGVlck1vZGVyYXRvcjogZnVuY3Rpb24gKHBlZXJKaWQpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24gJiZcbiAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5nZXRNZW1iZXJSb2xlKHBlZXJKaWQpID09PSAnbW9kZXJhdG9yJztcbiAgICB9LFxuXG4gICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBleHRlcm5hbEF1dGhFbmFibGVkO1xuICAgIH0sXG5cbiAgICBpc1NpcEdhdGV3YXlFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzaXBHYXRld2F5RW5hYmxlZDtcbiAgICB9LFxuXG4gICAgc2V0Q29ubmVjdGlvbjogZnVuY3Rpb24gKGNvbikge1xuICAgICAgICBjb25uZWN0aW9uID0gY29uO1xuICAgIH0sXG5cbiAgICBpbml0OiBmdW5jdGlvbiAoeG1wcCkge1xuICAgICAgICB0aGlzLnhtcHBTZXJ2aWNlID0geG1wcDtcbiAgICAgICAgdGhpcy5vbkxvY2FsUm9sZUNoYW5nZSA9IGZ1bmN0aW9uIChmcm9tLCBtZW1iZXIsIHByZXMpIHtcbiAgICAgICAgICAgIFVJLm9uTW9kZXJhdG9yU3RhdHVzQ2hhbmdlZChNb2RlcmF0b3IuaXNNb2RlcmF0b3IoKSk7XG4gICAgICAgIH07XG4gICAgfSxcblxuICAgIG9uTXVjTGVmdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25zb2xlLmluZm8oXCJTb21lb25lIGxlZnQgaXMgaXQgZm9jdXMgPyBcIiArIGppZCk7XG4gICAgICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIGlmIChyZXNvdXJjZSA9PT0gJ2ZvY3VzJyAmJiAhdGhpcy54bXBwU2VydmljZS5zZXNzaW9uVGVybWluYXRlZCkge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgIFwiRm9jdXMgaGFzIGxlZnQgdGhlIHJvb20gLSBsZWF2aW5nIGNvbmZlcmVuY2VcIik7XG4gICAgICAgICAgICAvL2hhbmdVcCgpO1xuICAgICAgICAgICAgLy8gV2UnZCByYXRoZXIgcmVsb2FkIHRvIGhhdmUgZXZlcnl0aGluZyByZS1pbml0aWFsaXplZFxuICAgICAgICAgICAgLy8gRklYTUU6IHNob3cgc29tZSBtZXNzYWdlIGJlZm9yZSByZWxvYWRcbiAgICAgICAgICAgIGxvY2F0aW9uLnJlbG9hZCgpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBcbiAgICBzZXRGb2N1c1VzZXJKaWQ6IGZ1bmN0aW9uIChmb2N1c0ppZCkge1xuICAgICAgICBpZiAoIWZvY3VzVXNlckppZCkge1xuICAgICAgICAgICAgZm9jdXNVc2VySmlkID0gZm9jdXNKaWQ7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJGb2N1cyBqaWQgc2V0IHRvOiBcIiArIGZvY3VzVXNlckppZCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgZ2V0Rm9jdXNVc2VySmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBmb2N1c1VzZXJKaWQ7XG4gICAgfSxcblxuICAgIGdldEZvY3VzQ29tcG9uZW50OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIEdldCBmb2N1cyBjb21wb25lbnQgYWRkcmVzc1xuICAgICAgICB2YXIgZm9jdXNDb21wb25lbnQgPSBjb25maWcuaG9zdHMuZm9jdXM7XG4gICAgICAgIC8vIElmIG5vdCBzcGVjaWZpZWQgdXNlIGRlZmF1bHQ6ICdmb2N1cy5kb21haW4nXG4gICAgICAgIGlmICghZm9jdXNDb21wb25lbnQpIHtcbiAgICAgICAgICAgIGZvY3VzQ29tcG9uZW50ID0gJ2ZvY3VzLicgKyBjb25maWcuaG9zdHMuZG9tYWluO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmb2N1c0NvbXBvbmVudDtcbiAgICB9LFxuXG4gICAgY3JlYXRlQ29uZmVyZW5jZUlxOiBmdW5jdGlvbiAocm9vbU5hbWUpIHtcbiAgICAgICAgLy8gR2VuZXJhdGUgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ3NldCd9KTtcbiAgICAgICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmJyaWRnZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdicmlkZ2UnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmJyaWRnZX0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGVsbCB0aGUgZm9jdXMgd2UgaGF2ZSBKaWdhc2kgY29uZmlndXJlZFxuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjYWxsX2NvbnRyb2wnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5jaGFubmVsTGFzdE4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnY2hhbm5lbExhc3ROJywgdmFsdWU6IGNvbmZpZy5jaGFubmVsTGFzdE59KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuYWRhcHRpdmVMYXN0TiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdhZGFwdGl2ZUxhc3ROJywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZUxhc3ROfSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLmFkYXB0aXZlU2ltdWxjYXN0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2FkYXB0aXZlU2ltdWxjYXN0JywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZVNpbXVsY2FzdH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5vcGVuU2N0cCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdvcGVuU2N0cCcsIHZhbHVlOiBjb25maWcub3BlblNjdHB9KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlRmlyZWZveFN1cHBvcnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnZW5hYmxlRmlyZWZveEhhY2tzJyxcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGNvbmZpZy5lbmFibGVGaXJlZm94U3VwcG9ydH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICByZXR1cm4gZWxlbTtcbiAgICB9LFxuXG4gICAgcGFyc2VDb25maWdPcHRpb25zOiBmdW5jdGlvbiAocmVzdWx0SXEpIHtcbiAgICBcbiAgICAgICAgTW9kZXJhdG9yLnNldEZvY3VzVXNlckppZChcbiAgICAgICAgICAgICQocmVzdWx0SXEpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdmb2N1c2ppZCcpKTtcbiAgICBcbiAgICAgICAgdmFyIGV4dEF1dGhQYXJhbVxuICAgICAgICAgICAgPSAkKHJlc3VsdElxKS5maW5kKCc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ2V4dGVybmFsQXV0aFxcJ10nKTtcbiAgICAgICAgaWYgKGV4dEF1dGhQYXJhbS5sZW5ndGgpIHtcbiAgICAgICAgICAgIGV4dGVybmFsQXV0aEVuYWJsZWQgPSBleHRBdXRoUGFyYW0uYXR0cigndmFsdWUnKSA9PT0gJ3RydWUnO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIkV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIGVuYWJsZWQ6IFwiICsgZXh0ZXJuYWxBdXRoRW5hYmxlZCk7XG4gICAgXG4gICAgICAgIC8vIENoZWNrIGlmIGZvY3VzIGhhcyBhdXRvLWRldGVjdGVkIEppZ2FzaSBjb21wb25lbnQodGhpcyB3aWxsIGJlIGFsc29cbiAgICAgICAgLy8gaW5jbHVkZWQgaWYgd2UgaGF2ZSBwYXNzZWQgb3VyIGhvc3QgZnJvbSB0aGUgY29uZmlnKVxuICAgICAgICBpZiAoJChyZXN1bHRJcSkuZmluZChcbiAgICAgICAgICAgICc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ3NpcEdhdGV3YXlFbmFibGVkXFwnXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2lwR2F0ZXdheUVuYWJsZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlNpcCBnYXRld2F5IGVuYWJsZWQ6IFwiICsgc2lwR2F0ZXdheUVuYWJsZWQpO1xuICAgIH0sXG5cbiAgICAvLyBGSVhNRTogd2UgbmVlZCB0byBzaG93IHRoZSBmYWN0IHRoYXQgd2UncmUgd2FpdGluZyBmb3IgdGhlIGZvY3VzXG4gICAgLy8gdG8gdGhlIHVzZXIob3IgdGhhdCBmb2N1cyBpcyBub3QgYXZhaWxhYmxlKVxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbiAocm9vbU5hbWUsIGNhbGxiYWNrKSB7XG4gICAgICAgIC8vIFRyeSB0byB1c2UgZm9jdXMgdXNlciBKSUQgZnJvbSB0aGUgY29uZmlnXG4gICAgICAgIE1vZGVyYXRvci5zZXRGb2N1c1VzZXJKaWQoY29uZmlnLmZvY3VzVXNlckppZCk7XG4gICAgICAgIC8vIFNlbmQgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGlxID0gTW9kZXJhdG9yLmNyZWF0ZUNvbmZlcmVuY2VJcShyb29tTmFtZSk7XG4gICAgICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgaXEsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCd0cnVlJyA9PT0gJChyZXN1bHQpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdyZWFkeScpKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFJlc2V0IGJvdGggdGltZXJzXG4gICAgICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBnZXROZXh0RXJyb3JUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAvLyBTZXR1cCBjb25maWcgb3B0aW9uc1xuICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IucGFyc2VDb25maWdPcHRpb25zKHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEV4ZWMgY2FsbGJhY2tcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dFRpbWVvdXQoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiV2FpdGluZyBmb3IgdGhlIGZvY3VzLi4uIFwiICsgd2FpdE1zKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gUmVzZXQgZXJyb3IgdGltZW91dFxuICAgICAgICAgICAgICAgICAgICBnZXROZXh0RXJyb3JUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LCB3YWl0TXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAvLyBOb3QgYXV0aG9yaXplZCB0byBjcmVhdGUgbmV3IHJvb21cbiAgICAgICAgICAgICAgICBpZiAoJChlcnJvcikuZmluZCgnPmVycm9yPm5vdC1hdXRob3JpemVkJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlVuYXV0aG9yaXplZCB0byBzdGFydCB0aGUgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgICAgICAgICAgICAgVUkub25BdXRoZW50aWNhdGlvblJlcXVpcmVkKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5hbGxvY2F0ZUNvbmZlcmVuY2VGb2N1cyhyb29tTmFtZSwgY2FsbGJhY2spO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dEVycm9yVGltZW91dCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGb2N1cyBlcnJvciwgcmV0cnkgYWZ0ZXIgXCIgKyB3YWl0TXMsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAvLyBTaG93IG1lc3NhZ2VcbiAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5ub3RpZnkoXG4gICAgICAgICAgICAgICAgICAgICdDb25mZXJlbmNlIGZvY3VzJywgJ2Rpc2Nvbm5lY3RlZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAnIG5vdCBhdmFpbGFibGUgLSByZXRyeSBpbiAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICh3YWl0TXMgLyAxMDAwKSArICcgc2VjJyk7XG4gICAgICAgICAgICAgICAgLy8gUmVzZXQgcmVzcG9uc2UgdGltZW91dFxuICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMocm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgfSwgd2FpdE1zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuXG4gICAgZ2V0QXV0aFVybDogZnVuY3Rpb24gKHJvb21OYW1lLCB1cmxDYWxsYmFjaykge1xuICAgICAgICB2YXIgaXEgPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ2dldCd9KTtcbiAgICAgICAgaXEuYygnYXV0aC11cmwnLCB7XG4gICAgICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvZm9jdXMnLFxuICAgICAgICAgICAgcm9vbTogcm9vbU5hbWVcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgaXEsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgdmFyIHVybCA9ICQocmVzdWx0KS5maW5kKCdhdXRoLXVybCcpLmF0dHIoJ3VybCcpO1xuICAgICAgICAgICAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiR290IGF1dGggdXJsOiBcIiArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIHVybENhbGxiYWNrKHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiRmFpbGVkIHRvIGdldCBhdXRoIHVybCBmcm8gbXRoZSBmb2N1c1wiLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiR2V0IGF1dGggdXJsIGVycm9yXCIsIGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1vZGVyYXRvcjtcblxuXG5cbiIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgZm9jdXNNdWNKaWQsIG1lc3NhZ2VIYW5kbGVyLCBNb2RlcmF0b3IsXG4gICBUb29sYmFyLCBVdGlsICovXG52YXIgTW9kZXJhdG9yID0gcmVxdWlyZShcIi4vbW9kZXJhdG9yXCIpO1xuXG5cbnZhciByZWNvcmRpbmdUb2tlbiA9IG51bGw7XG52YXIgcmVjb3JkaW5nRW5hYmxlZDtcblxuLyoqXG4gKiBXaGV0aGVyIHRvIHVzZSBhIGppcmVjb24gY29tcG9uZW50IGZvciByZWNvcmRpbmcsIG9yIHVzZSB0aGUgdmlkZW9icmlkZ2VcbiAqIHRocm91Z2ggQ09MSUJSSS5cbiAqL1xudmFyIHVzZUppcmVjb24gPSAodHlwZW9mIGNvbmZpZy5ob3N0cy5qaXJlY29uICE9IFwidW5kZWZpbmVkXCIpO1xuXG4vKipcbiAqIFRoZSBJRCBvZiB0aGUgamlyZWNvbiByZWNvcmRpbmcgc2Vzc2lvbi4gSmlyZWNvbiBnZW5lcmF0ZXMgaXQgd2hlbiB3ZVxuICogaW5pdGlhbGx5IHN0YXJ0IHJlY29yZGluZywgYW5kIGl0IG5lZWRzIHRvIGJlIHVzZWQgaW4gc3Vic2VxdWVudCByZXF1ZXN0c1xuICogdG8gamlyZWNvbi5cbiAqL1xudmFyIGppcmVjb25SaWQgPSBudWxsO1xuXG5mdW5jdGlvbiBzZXRSZWNvcmRpbmdUb2tlbih0b2tlbikge1xuICAgIHJlY29yZGluZ1Rva2VuID0gdG9rZW47XG59XG5cbmZ1bmN0aW9uIHNldFJlY29yZGluZyhzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgaWYgKHVzZUppcmVjb24pe1xuICAgICAgICB0aGlzLnNldFJlY29yZGluZ0ppcmVjb24oc3RhdGUsIHRva2VuLCBjYWxsYmFjayk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zZXRSZWNvcmRpbmdDb2xpYnJpKHN0YXRlLCB0b2tlbiwgY2FsbGJhY2spO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgaWYgKHN0YXRlID09IHJlY29yZGluZ0VuYWJsZWQpe1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGlxID0gJGlxKHt0bzogY29uZmlnLmhvc3RzLmppcmVjb24sIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ3JlY29yZGluZycsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvamlyZWNvbicsXG4gICAgICAgICAgICBhY3Rpb246IHN0YXRlID8gJ3N0YXJ0JyA6ICdzdG9wJyxcbiAgICAgICAgICAgIG11Y2ppZDogY29ubmVjdGlvbi5lbXVjLnJvb21qaWR9KTtcbiAgICBpZiAoIXN0YXRlKXtcbiAgICAgICAgaXEuYXR0cnMoe3JpZDogamlyZWNvblJpZH0pO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdTdGFydCByZWNvcmRpbmcnKTtcblxuICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICBpcSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgLy8gVE9ETyB3YWl0IGZvciBhbiBJUSB3aXRoIHRoZSByZWFsIHN0YXR1cywgc2luY2UgdGhpcyBpc1xuICAgICAgICAgICAgLy8gcHJvdmlzaW9uYWw/XG4gICAgICAgICAgICBqaXJlY29uUmlkID0gJChyZXN1bHQpLmZpbmQoJ3JlY29yZGluZycpLmF0dHIoJ3JpZCcpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1JlY29yZGluZyAnICsgKHN0YXRlID8gJ3N0YXJ0ZWQnIDogJ3N0b3BwZWQnKSArXG4gICAgICAgICAgICAgICAgJyhqaXJlY29uKScgKyByZXN1bHQpO1xuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IHN0YXRlO1xuICAgICAgICAgICAgaWYgKCFzdGF0ZSl7XG4gICAgICAgICAgICAgICAgamlyZWNvblJpZCA9IG51bGw7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKHN0YXRlKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRmFpbGVkIHRvIHN0YXJ0IHJlY29yZGluZywgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9KTtcbn1cblxuLy8gU2VuZHMgYSBDT0xJQlJJIG1lc3NhZ2Ugd2hpY2ggZW5hYmxlcyBvciBkaXNhYmxlcyAoYWNjb3JkaW5nIHRvICdzdGF0ZScpXG4vLyB0aGUgcmVjb3JkaW5nIG9uIHRoZSBicmlkZ2UuIFdhaXRzIGZvciB0aGUgcmVzdWx0IElRIGFuZCBjYWxscyAnY2FsbGJhY2snXG4vLyB3aXRoIHRoZSBuZXcgcmVjb3JkaW5nIHN0YXRlLCBhY2NvcmRpbmcgdG8gdGhlIElRLlxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nQ29saWJyaShzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBmb2N1c011Y0ppZCwgdHlwZTogJ3NldCd9KTtcbiAgICBlbGVtLmMoJ2NvbmZlcmVuY2UnLCB7XG4gICAgICAgIHhtbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9wcm90b2NvbC9jb2xpYnJpJ1xuICAgIH0pO1xuICAgIGVsZW0uYygncmVjb3JkaW5nJywge3N0YXRlOiBzdGF0ZSwgdG9rZW46IHRva2VufSk7XG5cbiAgICBjb25uZWN0aW9uLnNlbmRJUShlbGVtLFxuICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnU2V0IHJlY29yZGluZyBcIicsIHN0YXRlLCAnXCIuIFJlc3VsdDonLCByZXN1bHQpO1xuICAgICAgICAgICAgdmFyIHJlY29yZGluZ0VsZW0gPSAkKHJlc3VsdCkuZmluZCgnPmNvbmZlcmVuY2U+cmVjb3JkaW5nJyk7XG4gICAgICAgICAgICB2YXIgbmV3U3RhdGUgPSAoJ3RydWUnID09PSByZWNvcmRpbmdFbGVtLmF0dHIoJ3N0YXRlJykpO1xuXG4gICAgICAgICAgICByZWNvcmRpbmdFbmFibGVkID0gbmV3U3RhdGU7XG4gICAgICAgICAgICBjYWxsYmFjayhuZXdTdGF0ZSk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9XG4gICAgKTtcbn1cblxudmFyIFJlY29yZGluZyA9IHtcbiAgICB0b2dnbGVSZWNvcmRpbmc6IGZ1bmN0aW9uICh0b2tlbkVtcHR5Q2FsbGJhY2ssXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRpbmdDYWxsYmFjaywgc3RhcnRlZENhbGxiYWNrKSB7XG4gICAgICAgIGlmICghTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAnbm9uLWZvY3VzLCBvciBjb25mZXJlbmNlIG5vdCB5ZXQgb3JnYW5pemVkOicgK1xuICAgICAgICAgICAgICAgICAgICAnIG5vdCBlbmFibGluZyByZWNvcmRpbmcnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEppcmVjb24gZG9lcyBub3QgKGN1cnJlbnRseSkgc3VwcG9ydCBhIHRva2VuLlxuICAgICAgICBpZiAoIXJlY29yZGluZ1Rva2VuICYmICF1c2VKaXJlY29uKSB7XG4gICAgICAgICAgICB0b2tlbkVtcHR5Q2FsbGJhY2soZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgc2V0UmVjb3JkaW5nVG9rZW4odmFsdWUpO1xuICAgICAgICAgICAgICAgIHRoaXMudG9nZ2xlUmVjb3JkaW5nKCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIG9sZFN0YXRlID0gcmVjb3JkaW5nRW5hYmxlZDtcbiAgICAgICAgc3RhcnRpbmdDYWxsYmFjayghb2xkU3RhdGUpO1xuICAgICAgICBzZXRSZWNvcmRpbmcoIW9sZFN0YXRlLFxuICAgICAgICAgICAgcmVjb3JkaW5nVG9rZW4sXG4gICAgICAgICAgICBmdW5jdGlvbiAoc3RhdGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIk5ldyByZWNvcmRpbmcgc3RhdGU6IFwiLCBzdGF0ZSk7XG4gICAgICAgICAgICAgICAgaWYgKHN0YXRlID09PSBvbGRTdGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogbmV3IGZvY3VzOlxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzIHdpbGwgbm90IHdvcmsgd2hlbiBtb2RlcmF0b3IgY2hhbmdlc1xuICAgICAgICAgICAgICAgICAgICAvLyBkdXJpbmcgYWN0aXZlIHNlc3Npb24uIFRoZW4gaXQgd2lsbCBhc3N1bWUgdGhhdFxuICAgICAgICAgICAgICAgICAgICAvLyByZWNvcmRpbmcgc3RhdHVzIGhhcyBjaGFuZ2VkIHRvIHRydWUsIGJ1dCBpdCBtaWdodCBoYXZlXG4gICAgICAgICAgICAgICAgICAgIC8vIGJlZW4gYWxyZWFkeSB0cnVlKGFuZCB3ZSBvbmx5IHJlY2VpdmVkIGFjdHVhbCBzdGF0dXMgZnJvbVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgZm9jdXMpLlxuICAgICAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgICAgICAvLyBTTyB3ZSBzdGFydCB3aXRoIHN0YXR1cyBudWxsLCBzbyB0aGF0IGl0IGlzIGluaXRpYWxpemVkXG4gICAgICAgICAgICAgICAgICAgIC8vIGhlcmUgYW5kIHdpbGwgZmFpbCBvbmx5IGFmdGVyIHNlY29uZCBjbGljaywgc28gaWYgaW52YWxpZFxuICAgICAgICAgICAgICAgICAgICAvLyB0b2tlbiB3YXMgdXNlZCB3ZSBoYXZlIHRvIHByZXNzIHRoZSBidXR0b24gdHdpY2UgYmVmb3JlXG4gICAgICAgICAgICAgICAgICAgIC8vIGN1cnJlbnQgc3RhdHVzIHdpbGwgYmUgZmV0Y2hlZCBhbmQgdG9rZW4gd2lsbCBiZSByZXNldC5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVsaWFibGUgd2F5IHdvdWxkIGJlIHRvIHJldHVybiBhdXRoZW50aWNhdGlvbiBlcnJvci5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igc3RhdHVzIHVwZGF0ZSB3aGVuIG1vZGVyYXRvciBjb25uZWN0cy5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igd2UgaGF2ZSB0byBzdG9wIHJlY29yZGluZyBzZXNzaW9uIHdoZW4gY3VycmVudFxuICAgICAgICAgICAgICAgICAgICAvLyBtb2RlcmF0b3IgbGVhdmVzIHRoZSByb29tLlxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZhaWxlZCB0byBjaGFuZ2UsIHJlc2V0IHRoZSB0b2tlbiBiZWNhdXNlIGl0IG1pZ2h0XG4gICAgICAgICAgICAgICAgICAgIC8vIGhhdmUgYmVlbiB3cm9uZ1xuICAgICAgICAgICAgICAgICAgICBzZXRSZWNvcmRpbmdUb2tlbihudWxsKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc3RhcnRlZENhbGxiYWNrKHN0YXRlKTtcblxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFJlY29yZGluZzsiLCIvKiBqc2hpbnQgLVcxMTcgKi9cbi8qIGEgc2ltcGxlIE1VQyBjb25uZWN0aW9uIHBsdWdpblxuICogY2FuIG9ubHkgaGFuZGxlIGEgc2luZ2xlIE1VQyByb29tXG4gKi9cblxudmFyIGJyaWRnZUlzRG93biA9IGZhbHNlO1xuXG52YXIgTW9kZXJhdG9yID0gcmVxdWlyZShcIi4vbW9kZXJhdG9yXCIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKFhNUFAsIGV2ZW50RW1pdHRlcikge1xuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignZW11YycsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgcm9vbWppZDogbnVsbCxcbiAgICAgICAgbXlyb29tamlkOiBudWxsLFxuICAgICAgICBtZW1iZXJzOiB7fSxcbiAgICAgICAgbGlzdF9tZW1iZXJzOiBbXSwgLy8gc28gd2UgY2FuIGVsZWN0IGEgbmV3IGZvY3VzXG4gICAgICAgIHByZXNNYXA6IHt9LFxuICAgICAgICBwcmV6aU1hcDoge30sXG4gICAgICAgIGpvaW5lZDogZmFsc2UsXG4gICAgICAgIGlzT3duZXI6IGZhbHNlLFxuICAgICAgICByb2xlOiBudWxsLFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdFByZXNlbmNlTWFwOiBmdW5jdGlvbiAobXlyb29tamlkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3RvJ10gPSBteXJvb21qaWQ7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3hucyddID0gJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211Yyc7XG4gICAgICAgIH0sXG4gICAgICAgIGRvSm9pbjogZnVuY3Rpb24gKGppZCwgcGFzc3dvcmQpIHtcbiAgICAgICAgICAgIHRoaXMubXlyb29tamlkID0gamlkO1xuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJKb2luZWQgTVVDIGFzIFwiICsgdGhpcy5teXJvb21qaWQpO1xuXG4gICAgICAgICAgICB0aGlzLmluaXRQcmVzZW5jZU1hcCh0aGlzLm15cm9vbWppZCk7XG5cbiAgICAgICAgICAgIGlmICghdGhpcy5yb29tamlkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yb29tamlkID0gU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgICAgIC8vIGFkZCBoYW5kbGVycyAoanVzdCBvbmNlKVxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25QcmVzZW5jZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCBudWxsLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VVbmF2YWlsYWJsZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAndW5hdmFpbGFibGUnLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VFcnJvci5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAnZXJyb3InLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uTWVzc2FnZS5iaW5kKHRoaXMpLCBudWxsLCAnbWVzc2FnZScsIG51bGwsIG51bGwsIHRoaXMucm9vbWppZCwge21hdGNoQmFyZTogdHJ1ZX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHBhc3N3b3JkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10gPSBwYXNzd29yZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuc2VuZFByZXNlbmNlKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGRvTGVhdmU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiZG8gbGVhdmVcIiwgdGhpcy5teXJvb21qaWQpO1xuICAgICAgICAgICAgdmFyIHByZXMgPSAkcHJlcyh7dG86IHRoaXMubXlyb29tamlkLCB0eXBlOiAndW5hdmFpbGFibGUnIH0pO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwLmxlbmd0aCA9IDA7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChwcmVzKTtcbiAgICAgICAgfSxcbiAgICAgICAgY3JlYXRlTm9uQW5vbnltb3VzUm9vbTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAwNDUuaHRtbCNjcmVhdGVyb29tLXJlc2VydmVkXG5cbiAgICAgICAgICAgIHZhciBnZXRGb3JtID0gJGlxKHt0eXBlOiAnZ2V0JywgdG86IHRoaXMucm9vbWppZH0pXG4gICAgICAgICAgICAgICAgLmMoJ3F1ZXJ5Jywge3htbG5zOiAnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI293bmVyJ30pXG4gICAgICAgICAgICAgICAgLmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShnZXRGb3JtLCBmdW5jdGlvbiAoZm9ybSkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCEkKGZvcm0pLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgICAgICAnPnF1ZXJ5PnhbeG1sbnM9XCJqYWJiZXI6eDpkYXRhXCJdJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnPmZpZWxkW3Zhcj1cIm11YyNyb29tY29uZmlnX3dob2lzXCJdJykubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbm9uLWFub255bW91cyByb29tcyBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB2YXIgZm9ybVN1Ym1pdCA9ICRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSk7XG5cbiAgICAgICAgICAgICAgICBmb3JtU3VibWl0LmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgICAgIGZvcm1TdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdGT1JNX1RZUEUnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3ZhbHVlJylcbiAgICAgICAgICAgICAgICAgICAgLnQoJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNyb29tY29uZmlnJykudXAoKS51cCgpO1xuXG4gICAgICAgICAgICAgICAgZm9ybVN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3dob2lzJ30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCd2YWx1ZScpLnQoJ2FueW9uZScpLnVwKCkudXAoKTtcblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoZm9ybVN1Ym1pdCk7XG5cbiAgICAgICAgICAgIH0sIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBnZXR0aW5nIHJvb20gY29uZmlndXJhdGlvbiBmb3JtXCIpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUHJlc2VuY2U6IGZ1bmN0aW9uIChwcmVzKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IHByZXMuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG5cbiAgICAgICAgICAgIC8vIFdoYXQgaXMgdGhpcyBmb3I/IEEgd29ya2Fyb3VuZCBmb3Igc29tZXRoaW5nP1xuICAgICAgICAgICAgaWYgKHByZXMuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgZXRoZXJwYWQgdGFnLlxuICAgICAgICAgICAgdmFyIGV0aGVycGFkID0gJChwcmVzKS5maW5kKCc+ZXRoZXJwYWQnKTtcbiAgICAgICAgICAgIGlmIChldGhlcnBhZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLmV0aGVycGFkX2Jhc2UgJiYgIU1vZGVyYXRvci5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICAgICAgICAgIFVJLmluaXRFdGhlcnBhZChldGhlcnBhZC50ZXh0KCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgcHJlemkgdGFnLlxuICAgICAgICAgICAgdmFyIHByZXNlbnRhdGlvbiA9ICQocHJlcykuZmluZCgnPnByZXppJyk7XG4gICAgICAgICAgICBpZiAocHJlc2VudGF0aW9uLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSBwcmVzZW50YXRpb24uYXR0cigndXJsJyk7XG4gICAgICAgICAgICAgICAgdmFyIGN1cnJlbnQgPSBwcmVzZW50YXRpb24uZmluZCgnPmN1cnJlbnQnKS50ZXh0KCk7XG5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncHJlc2VudGF0aW9uIGluZm8gcmVjZWl2ZWQgZnJvbScsIGZyb20sIHVybCk7XG5cbiAgICAgICAgICAgICAgICBpZiAodGhpcy5wcmV6aU1hcFtmcm9tXSA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucHJlemlNYXBbZnJvbV0gPSB1cmw7XG5cbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncHJlc2VudGF0aW9uYWRkZWQubXVjJywgW2Zyb20sIHVybCwgY3VycmVudF0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignZ290b3NsaWRlLm11YycsIFtmcm9tLCB1cmwsIGN1cnJlbnRdKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIGlmICh0aGlzLnByZXppTWFwW2Zyb21dICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5wcmV6aU1hcFtmcm9tXTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5wcmV6aU1hcFtmcm9tXTtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwcmVzZW50YXRpb25yZW1vdmVkLm11YycsIFtmcm9tLCB1cmxdKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgYXVkaW8gaW5mbyB0YWcuXG4gICAgICAgICAgICB2YXIgYXVkaW9NdXRlZCA9ICQocHJlcykuZmluZCgnPmF1ZGlvbXV0ZWQnKTtcbiAgICAgICAgICAgIGlmIChhdWRpb011dGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2F1ZGlvbXV0ZWQubXVjJywgW2Zyb20sIGF1ZGlvTXV0ZWQudGV4dCgpXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFBhcnNlIHZpZGVvIGluZm8gdGFnLlxuICAgICAgICAgICAgdmFyIHZpZGVvTXV0ZWQgPSAkKHByZXMpLmZpbmQoJz52aWRlb211dGVkJyk7XG4gICAgICAgICAgICBpZiAodmlkZW9NdXRlZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCd2aWRlb211dGVkLm11YycsIFtmcm9tLCB2aWRlb011dGVkLnRleHQoKV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgc3RhdHMgPSAkKHByZXMpLmZpbmQoJz5zdGF0cycpO1xuICAgICAgICAgICAgaWYgKHN0YXRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBzdGF0c09iaiA9IHt9O1xuICAgICAgICAgICAgICAgIFN0cm9waGUuZm9yRWFjaENoaWxkKHN0YXRzWzBdLCBcInN0YXRcIiwgZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0YXRzT2JqW2VsLmdldEF0dHJpYnV0ZShcIm5hbWVcIildID0gZWwuZ2V0QXR0cmlidXRlKFwidmFsdWVcIik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbnF1YWxpdHkudXBkYXRlUmVtb3RlU3RhdHMoZnJvbSwgc3RhdHNPYmopO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBzdGF0dXMuXG4gICAgICAgICAgICBpZiAoJChwcmVzKS5maW5kKCc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdPnN0YXR1c1tjb2RlPVwiMjAxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmNyZWF0ZU5vbkFub255bW91c1Jvb20oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2Ugcm9sZXMuXG4gICAgICAgICAgICB2YXIgbWVtYmVyID0ge307XG4gICAgICAgICAgICBtZW1iZXIuc2hvdyA9ICQocHJlcykuZmluZCgnPnNob3cnKS50ZXh0KCk7XG4gICAgICAgICAgICBtZW1iZXIuc3RhdHVzID0gJChwcmVzKS5maW5kKCc+c3RhdHVzJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIHRtcCA9ICQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5pdGVtJyk7XG4gICAgICAgICAgICBtZW1iZXIuYWZmaWxpYXRpb24gPSB0bXAuYXR0cignYWZmaWxpYXRpb24nKTtcbiAgICAgICAgICAgIG1lbWJlci5yb2xlID0gdG1wLmF0dHIoJ3JvbGUnKTtcblxuICAgICAgICAgICAgLy8gRm9jdXMgcmVjb2duaXRpb25cbiAgICAgICAgICAgIG1lbWJlci5qaWQgPSB0bXAuYXR0cignamlkJyk7XG4gICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IGZhbHNlO1xuICAgICAgICAgICAgaWYgKG1lbWJlci5qaWRcbiAgICAgICAgICAgICAgICAmJiBtZW1iZXIuamlkLmluZGV4T2YoTW9kZXJhdG9yLmdldEZvY3VzVXNlckppZCgpICsgXCIvXCIpID09IDApIHtcbiAgICAgICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBuaWNrdGFnID0gJChwcmVzKS5maW5kKCc+bmlja1t4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2tcIl0nKTtcbiAgICAgICAgICAgIG1lbWJlci5kaXNwbGF5TmFtZSA9IChuaWNrdGFnLmxlbmd0aCA+IDAgPyBuaWNrdGFnLmh0bWwoKSA6IG51bGwpO1xuXG4gICAgICAgICAgICBpZiAoZnJvbSA9PSB0aGlzLm15cm9vbWppZCkge1xuICAgICAgICAgICAgICAgIGlmIChtZW1iZXIuYWZmaWxpYXRpb24gPT0gJ293bmVyJykgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5yb2xlICE9PSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJvbGUgPSBtZW1iZXIucm9sZTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKE1vZGVyYXRvci5vbkxvY2FsUm9sZUNoYW5nZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5vbkxvY2FsUm9sZUNoYW5nZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuICAgICAgICAgICAgICAgICAgICBVSS5vbkxvY2FsUm9sZUNoYW5nZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuam9pbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuam9pbmVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5NVUNfSk9JTkVELCBmcm9tLCBtZW1iZXIpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxpc3RfbWVtYmVycy5wdXNoKGZyb20pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5tZW1iZXJzW2Zyb21dID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBuZXcgcGFydGljaXBhbnRcbiAgICAgICAgICAgICAgICB0aGlzLm1lbWJlcnNbZnJvbV0gPSBtZW1iZXI7XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0X21lbWJlcnMucHVzaChmcm9tKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnZW50ZXJlZCcsIGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICAgICAgaWYgKG1lbWJlci5pc0ZvY3VzKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvY3VzTXVjSmlkID0gZnJvbTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiSWdub3JlIGZvY3VzOiBcIiArIGZyb20gKyBcIiwgcmVhbCBKSUQ6IFwiICsgbWVtYmVyLmppZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSAkKHByZXMpLmZpbmQoJz51c2VySUQnKS50ZXh0KCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBlbWFpbCA9ICQocHJlcykuZmluZCgnPmVtYWlsJyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChlbWFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZCA9IGVtYWlsLnRleHQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBVSS5vbk11Y0VudGVyZWQoZnJvbSwgaWQsIG1lbWJlci5kaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJwYXJ0aWNpcGFudEpvaW5lZFwiLCB7amlkOiBmcm9tfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBQcmVzZW5jZSB1cGRhdGUgZm9yIGV4aXN0aW5nIHBhcnRpY2lwYW50XG4gICAgICAgICAgICAgICAgLy8gV2F0Y2ggcm9sZSBjaGFuZ2U6XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubWVtYmVyc1tmcm9tXS5yb2xlICE9IG1lbWJlci5yb2xlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubWVtYmVyc1tmcm9tXS5yb2xlID0gbWVtYmVyLnJvbGU7XG4gICAgICAgICAgICAgICAgICAgIFVJLm9uTXVjUm9sZUNoYW5nZWQobWVtYmVyLnJvbGUsIG1lbWJlci5kaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBBbHdheXMgdHJpZ2dlciBwcmVzZW5jZSB0byB1cGRhdGUgYmluZGluZ3NcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ByZXNlbmNlLm11YycsIFtmcm9tLCBtZW1iZXIsIHByZXNdKTtcbiAgICAgICAgICAgIHRoaXMucGFyc2VQcmVzZW5jZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuXG4gICAgICAgICAgICAvLyBUcmlnZ2VyIHN0YXR1cyBtZXNzYWdlIHVwZGF0ZVxuICAgICAgICAgICAgaWYgKG1lbWJlci5zdGF0dXMpIHtcbiAgICAgICAgICAgICAgICBVSS5vbk11Y1ByZXNlbmNlU3RhdHVzKGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBvblByZXNlbmNlVW5hdmFpbGFibGU6IGZ1bmN0aW9uIChwcmVzKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IHByZXMuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICAvLyBTdGF0dXMgY29kZSAxMTAgaW5kaWNhdGVzIHRoYXQgdGhpcyBub3RpZmljYXRpb24gaXMgXCJzZWxmLXByZXNlbmNlXCIuXG4gICAgICAgICAgICBpZiAoISQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5zdGF0dXNbY29kZT1cIjExMFwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbZnJvbV07XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0X21lbWJlcnMuc3BsaWNlKHRoaXMubGlzdF9tZW1iZXJzLmluZGV4T2YoZnJvbSksIDEpO1xuICAgICAgICAgICAgICAgIHRoaXMub25QYXJ0aWNpcGFudExlZnQoZnJvbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBJZiB0aGUgc3RhdHVzIGNvZGUgaXMgMTEwIHRoaXMgbWVhbnMgd2UncmUgbGVhdmluZyBhbmQgd2Ugd291bGQgbGlrZVxuICAgICAgICAgICAgLy8gdG8gcmVtb3ZlIGV2ZXJ5b25lIGVsc2UgZnJvbSBvdXIgdmlldywgc28gd2UgdHJpZ2dlciB0aGUgZXZlbnQuXG4gICAgICAgICAgICBlbHNlIGlmICh0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWVtYmVyID0gdGhpcy5saXN0X21lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnNwbGljZShpLCAxKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblBhcnRpY2lwYW50TGVmdChtZW1iZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICgkKHByZXMpLmZpbmQoJz54W3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI3VzZXJcIl0+c3RhdHVzW2NvZGU9XCIzMDdcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdraWNrZWQubXVjJywgW2Zyb21dKTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5teXJvb21qaWQgPT09IGZyb20pIHtcbiAgICAgICAgICAgICAgICAgICAgWE1QUC5kaXNwb3NlQ29uZmVyZW5jZShmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuS0lDS0VEKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZUVycm9yOiBmdW5jdGlvbiAocHJlcykge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBwcmVzLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgaWYgKCQocHJlcykuZmluZCgnPmVycm9yW3R5cGU9XCJhdXRoXCJdPm5vdC1hdXRob3JpemVkW3htbG5zPVwidXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXNcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnb24gcGFzc3dvcmQgcmVxdWlyZWQnLCBmcm9tKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICAgICAgVUkub25QYXNzd29yZFJlcWl1cmVkKGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmRvSm9pbihmcm9tLCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCQocHJlcykuZmluZChcbiAgICAgICAgICAgICAgICAnPmVycm9yW3R5cGU9XCJjYW5jZWxcIl0+bm90LWFsbG93ZWRbeG1sbnM9XCJ1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphc1wiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciB0b0RvbWFpbiA9IFN0cm9waGUuZ2V0RG9tYWluRnJvbUppZChwcmVzLmdldEF0dHJpYnV0ZSgndG8nKSk7XG4gICAgICAgICAgICAgICAgaWYgKHRvRG9tYWluID09PSBjb25maWcuaG9zdHMuYW5vbnltb3VzZG9tYWluKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHdlIGFyZSBjb25uZWN0ZWQgd2l0aCBhbm9ueW1vdXMgZG9tYWluIGFuZCBvbmx5IG5vbiBhbm9ueW1vdXMgdXNlcnMgY2FuIGNyZWF0ZSByb29tc1xuICAgICAgICAgICAgICAgICAgICAvLyB3ZSBtdXN0IGF1dGhvcml6ZSB0aGUgdXNlclxuICAgICAgICAgICAgICAgICAgICBYTVBQLnByb21wdExvZ2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdvblByZXNFcnJvciAnLCBwcmVzKTtcbiAgICAgICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgJ09vcHMhIFNvbWV0aGluZyB3ZW50IHdyb25nIGFuZCB3ZSBjb3VsZG5gdCBjb25uZWN0IHRvIHRoZSBjb25mZXJlbmNlLicsXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignb25QcmVzRXJyb3IgJywgcHJlcyk7XG4gICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGNvdWxkbmB0IGNvbm5lY3QgdG8gdGhlIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgICAgICAgICAgcHJlcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZE1lc3NhZ2U6IGZ1bmN0aW9uIChib2R5LCBuaWNrbmFtZSkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnYm9keScsIGJvZHkpLnVwKCk7XG4gICAgICAgICAgICBpZiAobmlja25hbWUpIHtcbiAgICAgICAgICAgICAgICBtc2cuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSkudChuaWNrbmFtZSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJvdXRnb2luZ01lc3NhZ2VcIiwge1wibWVzc2FnZVwiOiBib2R5fSk7XG4gICAgICAgIH0sXG4gICAgICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uIChzdWJqZWN0KSB7XG4gICAgICAgICAgICB2YXIgbXNnID0gJG1zZyh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ2dyb3VwY2hhdCd9KTtcbiAgICAgICAgICAgIG1zZy5jKCdzdWJqZWN0Jywgc3ViamVjdCk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChtc2cpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coXCJ0b3BpYyBjaGFuZ2VkIHRvIFwiICsgc3ViamVjdCk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uTWVzc2FnZTogZnVuY3Rpb24gKG1zZykge1xuICAgICAgICAgICAgLy8gRklYTUU6IHRoaXMgaXMgYSBoYWNrLiBidXQgamluZ2xlIG9uIG11YyBtYWtlcyBuaWNrY2hhbmdlcyBoYXJkXG4gICAgICAgICAgICB2YXIgZnJvbSA9IG1zZy5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIHZhciBuaWNrID0gJChtc2cpLmZpbmQoJz5uaWNrW3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmlja1wiXScpLnRleHQoKSB8fCBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKTtcblxuICAgICAgICAgICAgdmFyIHR4dCA9ICQobXNnKS5maW5kKCc+Ym9keScpLnRleHQoKTtcbiAgICAgICAgICAgIHZhciB0eXBlID0gbXNnLmdldEF0dHJpYnV0ZShcInR5cGVcIik7XG4gICAgICAgICAgICBpZiAodHlwZSA9PSBcImVycm9yXCIpIHtcbiAgICAgICAgICAgICAgICBVSS5jaGF0QWRkRXJyb3IoJChtc2cpLmZpbmQoJz50ZXh0JykudGV4dCgpLCB0eHQpO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgc3ViamVjdCA9ICQobXNnKS5maW5kKCc+c3ViamVjdCcpO1xuICAgICAgICAgICAgaWYgKHN1YmplY3QubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN1YmplY3RUZXh0ID0gc3ViamVjdC50ZXh0KCk7XG4gICAgICAgICAgICAgICAgaWYgKHN1YmplY3RUZXh0IHx8IHN1YmplY3RUZXh0ID09IFwiXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgVUkuY2hhdFNldFN1YmplY3Qoc3ViamVjdFRleHQpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlN1YmplY3QgaXMgY2hhbmdlZCB0byBcIiArIHN1YmplY3RUZXh0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgaWYgKHR4dCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdjaGF0JywgbmljaywgdHh0KTtcbiAgICAgICAgICAgICAgICBVSS51cGRhdGVDaGF0Q29udmVyc2F0aW9uKGZyb20sIG5pY2ssIHR4dCk7XG4gICAgICAgICAgICAgICAgaWYgKGZyb20gIT0gdGhpcy5teXJvb21qaWQpXG4gICAgICAgICAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJpbmNvbWluZ01lc3NhZ2VcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcImZyb21cIjogZnJvbSwgXCJuaWNrXCI6IG5pY2ssIFwibWVzc2FnZVwiOiB0eHR9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICAgICAgLy9odHRwOi8veG1wcC5vcmcvZXh0ZW5zaW9ucy94ZXAtMDA0NS5odG1sI3Jvb21jb25maWdcbiAgICAgICAgICAgIHZhciBvYiA9IHRoaXM7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKCRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ2dldCd9KS5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkKHJlcykuZmluZCgnPnF1ZXJ5PnhbeG1sbnM9XCJqYWJiZXI6eDpkYXRhXCJdPmZpZWxkW3Zhcj1cIm11YyNyb29tY29uZmlnX3Jvb21zZWNyZXRcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmb3Jtc3VibWl0ID0gJGlxKHt0bzogb2Iucm9vbWppZCwgdHlwZTogJ3NldCd9KS5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygneCcsIHt4bWxuczogJ2phYmJlcjp4OmRhdGEnLCB0eXBlOiAnc3VibWl0J30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ0ZPUk1fVFlQRSd9KS5jKCd2YWx1ZScpLnQoJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNyb29tY29uZmlnJykudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3Jvb21zZWNyZXQnfSkuYygndmFsdWUnKS50KGtleSkudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRml4ZXMgYSBidWcgaW4gcHJvc29keSAwLjkuKyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL2x4bXBwZC9pc3N1ZXMvZGV0YWlsP2lkPTM3M1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3dob2lzJ30pLmMoJ3ZhbHVlJykudCgnYW55b25lJykudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IGlzIG11YyNyb29tY29uZmlnX3Bhc3N3b3JkcHJvdGVjdGVkcm9vbSByZXF1aXJlZD9cbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoZm9ybXN1Ym1pdCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvblN1Y2Nlc3MsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb25FcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvbk5vdFN1cHBvcnRlZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSwgb25FcnJvcik7XG4gICAgICAgIH0sXG4gICAgICAgIGtpY2s6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIHZhciBraWNrSVEgPSAkaXEoe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgICAgICAuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjYWRtaW4nfSlcbiAgICAgICAgICAgICAgICAuYygnaXRlbScsIHtuaWNrOiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpLCByb2xlOiAnbm9uZSd9KVxuICAgICAgICAgICAgICAgIC5jKCdyZWFzb24nKS50KCdZb3UgaGF2ZSBiZWVuIGtpY2tlZC4nKS51cCgpLnVwKCkudXAoKTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICBraWNrSVEsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnS2ljayBwYXJ0aWNpcGFudCB3aXRoIGppZDogJywgamlkLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdLaWNrIHBhcnRpY2lwYW50IGVycm9yOiAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIHNlbmRQcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHByZXMgPSAkcHJlcyh7dG86IHRoaXMucHJlc01hcFsndG8nXSB9KTtcbiAgICAgICAgICAgIHByZXMuYygneCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWyd4bnMnXX0pO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydwYXNzd29yZCddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdwYXNzd29yZCcpLnQodGhpcy5wcmVzTWFwWydwYXNzd29yZCddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwcmVzLnVwKCk7XG5cbiAgICAgICAgICAgIC8vIFNlbmQgWEVQLTAxMTUgJ2MnIHN0YW56YSB0aGF0IGNvbnRhaW5zIG91ciBjYXBhYmlsaXRpZXMgaW5mb1xuICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvbi5jYXBzKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmNhcHMubm9kZSA9IGNvbmZpZy5jbGllbnROb2RlO1xuICAgICAgICAgICAgICAgIHByZXMuYygnYycsIHRoaXMuY29ubmVjdGlvbi5jYXBzLmdlbmVyYXRlQ2Fwc0F0dHJzKCkpLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMuYygndXNlci1hZ2VudCcsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC91c2VyLWFnZW50J30pXG4gICAgICAgICAgICAgICAgLnQobmF2aWdhdG9yLnVzZXJBZ2VudCkudXAoKTtcblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnYnJpZGdlSXNEb3duJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2JyaWRnZUlzRG93bicpLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2VtYWlsJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2VtYWlsJykudCh0aGlzLnByZXNNYXBbJ2VtYWlsJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3VzZXJJZCddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCd1c2VySWQnKS50KHRoaXMucHJlc01hcFsndXNlcklkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10pIHtcbiAgICAgICAgICAgICAgICAvLyBYRVAtMDE3MlxuICAgICAgICAgICAgICAgIHByZXMuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodGhpcy5wcmVzTWFwWydkaXNwbGF5TmFtZSddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydhdWRpb25zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2F1ZGlvbXV0ZWQnLCB7eG1sbnM6IHRoaXMucHJlc01hcFsnYXVkaW9ucyddfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodGhpcy5wcmVzTWFwWydhdWRpb211dGVkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3ZpZGVvbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygndmlkZW9tdXRlZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWyd2aWRlb25zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ3ZpZGVvbXV0ZWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnc3RhdHNucyddKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN0YXRzID0gcHJlcy5jKCdzdGF0cycsIHt4bWxuczogdGhpcy5wcmVzTWFwWydzdGF0c25zJ119KTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBzdGF0IGluIHRoaXMucHJlc01hcFtcInN0YXRzXCJdKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwW1wic3RhdHNcIl1bc3RhdF0gIT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRzLmMoXCJzdGF0XCIsIHtuYW1lOiBzdGF0LCB2YWx1ZTogdGhpcy5wcmVzTWFwW1wic3RhdHNcIl1bc3RhdF19KS51cCgpO1xuICAgICAgICAgICAgICAgIHByZXMudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsncHJlemlucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdwcmV6aScsXG4gICAgICAgICAgICAgICAgICAgIHt4bWxuczogdGhpcy5wcmVzTWFwWydwcmV6aW5zJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAndXJsJzogdGhpcy5wcmVzTWFwWydwcmV6aXVybCddfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ2N1cnJlbnQnKS50KHRoaXMucHJlc01hcFsncHJlemljdXJyZW50J10pLnVwKCkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZXRoZXJwYWRucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdldGhlcnBhZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWydldGhlcnBhZG5zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2V0aGVycGFkbmFtZSddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydtZWRpYW5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ21lZGlhJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ21lZGlhbnMnXX0pO1xuICAgICAgICAgICAgICAgIHZhciBzb3VyY2VOdW1iZXIgPSAwO1xuICAgICAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMucHJlc01hcCkuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignc291cmNlJykgPj0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlTnVtYmVyKys7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBpZiAoc291cmNlTnVtYmVyID4gMClcbiAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPD0gc291cmNlTnVtYmVyIC8gMzsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVzLmMoJ3NvdXJjZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge3R5cGU6IHRoaXMucHJlc01hcFsnc291cmNlJyArIGkgKyAnX3R5cGUnXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYzogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfc3NyYyddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246IHRoaXMucHJlc01hcFsnc291cmNlJyArIGkgKyAnX2RpcmVjdGlvbiddXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCAnc2VuZHJlY3YnIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwcmVzLnVwKCk7XG4vLyAgICAgICAgY29uc29sZS5kZWJ1ZyhwcmVzLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQocHJlcyk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZERpc3BsYXlOYW1lVG9QcmVzZW5jZTogZnVuY3Rpb24gKGRpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10gPSBkaXNwbGF5TmFtZTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkTWVkaWFUb1ByZXNlbmNlOiBmdW5jdGlvbiAoc291cmNlTnVtYmVyLCBtdHlwZSwgc3NyY3MsIGRpcmVjdGlvbikge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSlcbiAgICAgICAgICAgICAgICB0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSA9ICdodHRwOi8vZXN0b3MuZGUvbnMvbWpzJztcblxuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgc291cmNlTnVtYmVyICsgJ190eXBlJ10gPSBtdHlwZTtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc291cmNlJyArIHNvdXJjZU51bWJlciArICdfc3NyYyddID0gc3NyY3M7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBzb3VyY2VOdW1iZXIgKyAnX2RpcmVjdGlvbiddID0gZGlyZWN0aW9uO1xuICAgICAgICB9LFxuICAgICAgICBjbGVhclByZXNlbmNlTWVkaWE6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMucHJlc01hcCkuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCdzb3VyY2UnKSAhPSAtMSkge1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgc2VsZi5wcmVzTWFwW2tleV07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZFByZXppVG9QcmVzZW5jZTogZnVuY3Rpb24gKHVybCwgY3VycmVudFNsaWRlKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvcHJlemknO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aXVybCddID0gdXJsO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSA9IGN1cnJlbnRTbGlkZTtcbiAgICAgICAgfSxcbiAgICAgICAgcmVtb3ZlUHJlemlGcm9tUHJlc2VuY2U6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXTtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXNNYXBbJ3ByZXppdXJsJ107XG4gICAgICAgICAgICBkZWxldGUgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQ3VycmVudFNsaWRlVG9QcmVzZW5jZTogZnVuY3Rpb24gKGN1cnJlbnRTbGlkZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSA9IGN1cnJlbnRTbGlkZTtcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0UHJlemk6IGZ1bmN0aW9uIChyb29tamlkKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcmV6aU1hcFtyb29tamlkXTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkRXRoZXJwYWRUb1ByZXNlbmNlOiBmdW5jdGlvbiAoZXRoZXJwYWROYW1lKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2V0aGVycGFkbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvZXRoZXJwYWQnO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydldGhlcnBhZG5hbWUnXSA9IGV0aGVycGFkTmFtZTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQXVkaW9JbmZvVG9QcmVzZW5jZTogZnVuY3Rpb24gKGlzTXV0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnYXVkaW9ucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2F1ZGlvbXV0ZWQnXSA9IGlzTXV0ZWQudG9TdHJpbmcoKTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkVmlkZW9JbmZvVG9QcmVzZW5jZTogZnVuY3Rpb24gKGlzTXV0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsndmlkZW9ucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC92aWRlbyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ZpZGVvbXV0ZWQnXSA9IGlzTXV0ZWQudG9TdHJpbmcoKTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQ29ubmVjdGlvbkluZm9Ub1ByZXNlbmNlOiBmdW5jdGlvbiAoc3RhdHMpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc3RhdHNucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9zdGF0cyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3N0YXRzJ10gPSBzdGF0cztcbiAgICAgICAgfSxcbiAgICAgICAgZmluZEppZEZyb21SZXNvdXJjZTogZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICBpZiAocmVzb3VyY2VKaWQgJiZcbiAgICAgICAgICAgICAgICByZXNvdXJjZUppZCA9PT0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQodGhpcy5teXJvb21qaWQpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubXlyb29tamlkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHBlZXJKaWQgPSBudWxsO1xuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5tZW1iZXJzKS5zb21lKGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgICAgICBwZWVySmlkID0gamlkO1xuICAgICAgICAgICAgICAgIHJldHVybiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpID09PSByZXNvdXJjZUppZDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIHBlZXJKaWQ7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEJyaWRnZUlzRG93blRvUHJlc2VuY2U6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnYnJpZGdlSXNEb3duJ10gPSB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBhZGRFbWFpbFRvUHJlc2VuY2U6IGZ1bmN0aW9uIChlbWFpbCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydlbWFpbCddID0gZW1haWw7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZFVzZXJJZFRvUHJlc2VuY2U6IGZ1bmN0aW9uICh1c2VySWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsndXNlcklkJ10gPSB1c2VySWQ7XG4gICAgICAgIH0sXG4gICAgICAgIGlzTW9kZXJhdG9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yb2xlID09PSAnbW9kZXJhdG9yJztcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0TWVtYmVyUm9sZTogZnVuY3Rpb24gKHBlZXJKaWQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm1lbWJlcnNbcGVlckppZF0pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5tZW1iZXJzW3BlZXJKaWRdLnJvbGU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfSxcbiAgICAgICAgb25QYXJ0aWNpcGFudExlZnQ6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIFVJLm9uTXVjTGVmdChqaWQpO1xuXG4gICAgICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwicGFydGljaXBhbnRMZWZ0XCIsIHtqaWQ6IGppZH0pO1xuXG4gICAgICAgICAgICBkZWxldGUgamlkMlNzcmNbamlkXTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS50ZXJtaW5hdGVCeUppZChqaWQpO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5nZXRQcmV6aShqaWQpKSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncHJlc2VudGF0aW9ucmVtb3ZlZC5tdWMnLFxuICAgICAgICAgICAgICAgICAgICBbamlkLCB0aGlzLmdldFByZXppKGppZCldKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgTW9kZXJhdG9yLm9uTXVjTGVmdChqaWQpO1xuICAgICAgICB9LFxuICAgICAgICBwYXJzZVByZXNlbmNlOiBmdW5jdGlvbiAoZnJvbSwgbWVtZWJlciwgcHJlcykge1xuICAgICAgICAgICAgaWYoJChwcmVzKS5maW5kKFwiPmJyaWRnZUlzRG93blwiKS5sZW5ndGggPiAwICYmICFicmlkZ2VJc0Rvd24pIHtcbiAgICAgICAgICAgICAgICBicmlkZ2VJc0Rvd24gPSB0cnVlO1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuQlJJREdFX0RPV04pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZihtZW1lYmVyLmlzRm9jdXMpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgICAgICAvLyBSZW1vdmUgb2xkIHNzcmNzIGNvbWluZyBmcm9tIHRoZSBqaWRcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHNzcmMyamlkKS5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNzcmMyamlkW3NzcmNdID09IGppZCkge1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgc3NyYzJqaWRbc3NyY107XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzc3JjMnZpZGVvVHlwZVtzc3JjXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmFyIGNoYW5nZWRTdHJlYW1zID0gW107XG4gICAgICAgICAgICAkKHByZXMpLmZpbmQoJz5tZWRpYVt4bWxucz1cImh0dHA6Ly9lc3Rvcy5kZS9ucy9tanNcIl0+c291cmNlJykuZWFjaChmdW5jdGlvbiAoaWR4LCBzc3JjKSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhqaWQsICdhc3NvYyBzc3JjJywgc3NyYy5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSwgc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKSk7XG4gICAgICAgICAgICAgICAgdmFyIHNzcmNWID0gc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgICAgICBzc3JjMmppZFtzc3JjVl0gPSBmcm9tO1xuICAgICAgICAgICAgICAgIG5vdFJlY2VpdmVkU1NSQ3MucHVzaChzc3JjVik7XG5cbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IHNzcmMuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG4gICAgICAgICAgICAgICAgc3NyYzJ2aWRlb1R5cGVbc3NyY1ZdID0gdHlwZTtcblxuICAgICAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBzc3JjLmdldEF0dHJpYnV0ZSgnZGlyZWN0aW9uJyk7XG5cbiAgICAgICAgICAgICAgICBjaGFuZ2VkU3RyZWFtcy5wdXNoKHt0eXBlOiB0eXBlLCBkaXJlY3Rpb246IGRpcmVjdGlvbn0pO1xuXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5DSEFOR0VEX1NUUkVBTVMsIGZyb20sIGNoYW5nZWRTdHJlYW1zKTtcblxuICAgICAgICAgICAgdmFyIGRpc3BsYXlOYW1lID0gIWNvbmZpZy5kaXNwbGF5Smlkc1xuICAgICAgICAgICAgICAgID8gbWVtZWJlci5kaXNwbGF5TmFtZSA6IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGZyb20pO1xuXG4gICAgICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgIHtcbi8vICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Rpc3BsYXluYW1lY2hhbmdlZCcsXG4vLyAgICAgICAgICAgICAgICAgICAgW2ppZCwgZGlzcGxheU5hbWVdKTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkRJU1BMQVlfTkFNRV9DSEFOR0VELCBmcm9tLCBkaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgdmFyIGlkID0gJChwcmVzKS5maW5kKCc+dXNlcklEJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIGVtYWlsID0gJChwcmVzKS5maW5kKCc+ZW1haWwnKTtcbiAgICAgICAgICAgIGlmKGVtYWlsLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICBpZCA9IGVtYWlsLnRleHQoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5VU0VSX0lEX0NIQU5HRUQsIGZyb20sIGlkKTtcbiAgICAgICAgfVxuICAgIH0pO1xufTtcblxuIiwiLyoganNoaW50IC1XMTE3ICovXG5cbnZhciBKaW5nbGVTZXNzaW9uID0gcmVxdWlyZShcIi4vSmluZ2xlU2Vzc2lvblwiKTtcblxuZnVuY3Rpb24gQ2FsbEluY29taW5nSmluZ2xlKHNpZCwgY29ubmVjdGlvbikge1xuICAgIHZhciBzZXNzID0gY29ubmVjdGlvbi5qaW5nbGUuc2Vzc2lvbnNbc2lkXTtcblxuICAgIC8vIFRPRE86IGRvIHdlIGNoZWNrIGFjdGl2ZWNhbGwgPT0gbnVsbD9cbiAgICBhY3RpdmVjYWxsID0gc2VzcztcblxuICAgIHN0YXRpc3RpY3Mub25Db25mZXJlbmNlQ3JlYXRlZChzZXNzKTtcbiAgICBSVEMub25Db25mZXJlbmNlQ3JlYXRlZChzZXNzKTtcblxuICAgIC8vIFRPRE86IGNoZWNrIGFmZmlsaWF0aW9uIGFuZC9vciByb2xlXG4gICAgY29uc29sZS5sb2coJ2VtdWMgZGF0YSBmb3InLCBzZXNzLnBlZXJqaWQsIGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzW3Nlc3MucGVlcmppZF0pO1xuICAgIHNlc3MudXNlZHJpcCA9IHRydWU7IC8vIG5vdC1zby1uYWl2ZSB0cmlja2xlIGljZVxuICAgIHNlc3Muc2VuZEFuc3dlcigpO1xuICAgIHNlc3MuYWNjZXB0KCk7XG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oWE1QUClcbntcbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ2ppbmdsZScsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgc2Vzc2lvbnM6IHt9LFxuICAgICAgICBqaWQyc2Vzc2lvbjoge30sXG4gICAgICAgIGljZV9jb25maWc6IHtpY2VTZXJ2ZXJzOiBbXX0sXG4gICAgICAgIHBjX2NvbnN0cmFpbnRzOiB7fSxcbiAgICAgICAgbWVkaWFfY29uc3RyYWludHM6IHtcbiAgICAgICAgICAgIG1hbmRhdG9yeToge1xuICAgICAgICAgICAgICAgICdPZmZlclRvUmVjZWl2ZUF1ZGlvJzogdHJ1ZSxcbiAgICAgICAgICAgICAgICAnT2ZmZXJUb1JlY2VpdmVWaWRlbyc6IHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIE1vekRvbnRPZmZlckRhdGFDaGFubmVsOiB0cnVlIHdoZW4gdGhpcyBpcyBmaXJlZm94XG4gICAgICAgIH0sXG4gICAgICAgIGluaXQ6IGZ1bmN0aW9uIChjb25uKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAgICAgICAgIC8vIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMTY3Lmh0bWwjc3VwcG9ydFxuICAgICAgICAgICAgICAgIC8vIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMTc2Lmh0bWwjc3VwcG9ydFxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjEnKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmF1ZGlvJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDp2aWRlbycpO1xuXG5cbiAgICAgICAgICAgICAgICAvLyB0aGlzIGlzIGRlYWx0IHdpdGggYnkgU0RQIE8vQSBzbyB3ZSBkb24ndCBuZWVkIHRvIGFubm91Y2UgdGhpc1xuICAgICAgICAgICAgICAgIC8vdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnKTsgLy8gWEVQLTAyOTNcbiAgICAgICAgICAgICAgICAvL3RoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowJyk7IC8vIFhFUC0wMjk0XG4gICAgICAgICAgICAgICAgaWYgKGNvbmZpZy51c2VSdGNwTXV4KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46aWV0ZjpyZmM6NTc2MScpOyAvLyBydGNwLW11eFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLnVzZUJ1bmRsZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOmlldGY6cmZjOjU4ODgnKTsgLy8gYT1ncm91cCwgZS5nLiBidW5kbGVcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy90aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOmlldGY6cmZjOjU1NzYnKTsgLy8gYT1zc3JjXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uSmluZ2xlLmJpbmQodGhpcyksICd1cm46eG1wcDpqaW5nbGU6MScsICdpcScsICdzZXQnLCBudWxsLCBudWxsKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25KaW5nbGU6IGZ1bmN0aW9uIChpcSkge1xuICAgICAgICAgICAgdmFyIHNpZCA9ICQoaXEpLmZpbmQoJ2ppbmdsZScpLmF0dHIoJ3NpZCcpO1xuICAgICAgICAgICAgdmFyIGFjdGlvbiA9ICQoaXEpLmZpbmQoJ2ppbmdsZScpLmF0dHIoJ2FjdGlvbicpO1xuICAgICAgICAgICAgdmFyIGZyb21KaWQgPSBpcS5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIC8vIHNlbmQgYWNrIGZpcnN0XG4gICAgICAgICAgICB2YXIgYWNrID0gJGlxKHt0eXBlOiAncmVzdWx0JyxcbiAgICAgICAgICAgICAgICB0bzogZnJvbUppZCxcbiAgICAgICAgICAgICAgICBpZDogaXEuZ2V0QXR0cmlidXRlKCdpZCcpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdvbiBqaW5nbGUgJyArIGFjdGlvbiArICcgZnJvbSAnICsgZnJvbUppZCwgaXEpO1xuICAgICAgICAgICAgdmFyIHNlc3MgPSB0aGlzLnNlc3Npb25zW3NpZF07XG4gICAgICAgICAgICBpZiAoJ3Nlc3Npb24taW5pdGlhdGUnICE9IGFjdGlvbikge1xuICAgICAgICAgICAgICAgIGlmIChzZXNzID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGFjay50eXBlID0gJ2Vycm9yJztcbiAgICAgICAgICAgICAgICAgICAgYWNrLmMoJ2Vycm9yJywge3R5cGU6ICdjYW5jZWwnfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCdpdGVtLW5vdC1mb3VuZCcsIHt4bWxuczogJ3VybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzJ30pLnVwKClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCd1bmtub3duLXNlc3Npb24nLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6ZXJyb3JzOjEnfSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBjb21wYXJlIGZyb20gdG8gc2Vzcy5wZWVyamlkIChiYXJlIGppZCBjb21wYXJpc29uIGZvciBsYXRlciBjb21wYXQgd2l0aCBtZXNzYWdlLW1vZGUpXG4gICAgICAgICAgICAgICAgLy8gbG9jYWwgamlkIGlzIG5vdCBjaGVja2VkXG4gICAgICAgICAgICAgICAgaWYgKFN0cm9waGUuZ2V0QmFyZUppZEZyb21KaWQoZnJvbUppZCkgIT0gU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChzZXNzLnBlZXJqaWQpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignamlkIG1pc21hdGNoIGZvciBzZXNzaW9uIGlkJywgc2lkLCBmcm9tSmlkLCBzZXNzLnBlZXJqaWQpO1xuICAgICAgICAgICAgICAgICAgICBhY2sudHlwZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgIGFjay5jKCdlcnJvcicsIHt0eXBlOiAnY2FuY2VsJ30pXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygnaXRlbS1ub3QtZm91bmQnLCB7eG1sbnM6ICd1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphcyd9KS51cCgpXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygndW5rbm93bi1zZXNzaW9uJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOmVycm9yczoxJ30pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChhY2spO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHNlc3MgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIC8vIGV4aXN0aW5nIHNlc3Npb24gd2l0aCBzYW1lIHNlc3Npb24gaWRcbiAgICAgICAgICAgICAgICAvLyB0aGlzIG1pZ2h0IGJlIG91dC1vZi1vcmRlciBpZiB0aGUgc2Vzcy5wZWVyamlkIGlzIHRoZSBzYW1lIGFzIGZyb21cbiAgICAgICAgICAgICAgICBhY2sudHlwZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgYWNrLmMoJ2Vycm9yJywge3R5cGU6ICdjYW5jZWwnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3NlcnZpY2UtdW5hdmFpbGFibGUnLCB7eG1sbnM6ICd1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphcyd9KS51cCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignZHVwbGljYXRlIHNlc3Npb24gaWQnLCBzaWQpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGSVhNRTogY2hlY2sgZm9yIGEgZGVmaW5lZCBhY3Rpb25cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAvLyBzZWUgaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAxNjYuaHRtbCNjb25jZXB0cy1zZXNzaW9uXG4gICAgICAgICAgICBzd2l0Y2ggKGFjdGlvbikge1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Nlc3Npb24taW5pdGlhdGUnOlxuICAgICAgICAgICAgICAgICAgICBzZXNzID0gbmV3IEppbmdsZVNlc3Npb24oXG4gICAgICAgICAgICAgICAgICAgICAgICAkKGlxKS5hdHRyKCd0bycpLCAkKGlxKS5maW5kKCdqaW5nbGUnKS5hdHRyKCdzaWQnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiwgWE1QUCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGNvbmZpZ3VyZSBzZXNzaW9uXG5cbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5tZWRpYV9jb25zdHJhaW50cyA9IHRoaXMubWVkaWFfY29uc3RyYWludHM7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MucGNfY29uc3RyYWludHMgPSB0aGlzLnBjX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLmljZV9jb25maWcgPSB0aGlzLmljZV9jb25maWc7XG5cbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5pbml0aWF0ZShmcm9tSmlkLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBzZXRSZW1vdGVEZXNjcmlwdGlvbiBzaG91bGQgb25seSBiZSBkb25lIHdoZW4gdGhpcyBjYWxsIGlzIHRvIGJlIGFjY2VwdGVkXG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2V0UmVtb3RlRGVzY3JpcHRpb24oJChpcSkuZmluZCgnPmppbmdsZScpLCAnb2ZmZXInKTtcblxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXSA9IHNlc3M7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuamlkMnNlc3Npb25bc2Vzcy5wZWVyamlkXSA9IHNlc3M7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGNhbGxiYWNrIHNob3VsZCBlaXRoZXJcbiAgICAgICAgICAgICAgICAgICAgLy8gLnNlbmRBbnN3ZXIgYW5kIC5hY2NlcHRcbiAgICAgICAgICAgICAgICAgICAgLy8gb3IgLnNlbmRUZXJtaW5hdGUgLS0gbm90IG5lY2Vzc2FyaWx5IHN5bmNocm9udXNcbiAgICAgICAgICAgICAgICAgICAgQ2FsbEluY29taW5nSmluZ2xlKHNlc3Muc2lkLCB0aGlzLmNvbm5lY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdzZXNzaW9uLWFjY2VwdCc6XG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2V0UmVtb3RlRGVzY3JpcHRpb24oJChpcSkuZmluZCgnPmppbmdsZScpLCAnYW5zd2VyJyk7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuYWNjZXB0KCk7XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGxhY2NlcHRlZC5qaW5nbGUnLCBbc2Vzcy5zaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi10ZXJtaW5hdGUnOlxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB0aGlzIGlzIG5vdCB0aGUgZm9jdXMgc2VuZGluZyB0aGUgdGVybWluYXRlLCB3ZSBoYXZlXG4gICAgICAgICAgICAgICAgICAgIC8vIG5vdGhpbmcgbW9yZSB0byBkbyBoZXJlLlxuICAgICAgICAgICAgICAgICAgICBpZiAoT2JqZWN0LmtleXModGhpcy5zZXNzaW9ucykubGVuZ3RoIDwgMVxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgISh0aGlzLnNlc3Npb25zW09iamVjdC5rZXlzKHRoaXMuc2Vzc2lvbnMpWzBdXVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlb2YgSmluZ2xlU2Vzc2lvbikpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0ZXJtaW5hdGluZy4uLicsIHNlc3Muc2lkKTtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy50ZXJtaW5hdGUoc2Vzcy5zaWQpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChpcSkuZmluZCgnPmppbmdsZT5yZWFzb24nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXNzLnNpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXNzLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJChpcSkuZmluZCgnPmppbmdsZT5yZWFzb24+OmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkKGlxKS5maW5kKCc+amluZ2xlPnJlYXNvbj50ZXh0JykudGV4dCgpXG4gICAgICAgICAgICAgICAgICAgICAgICBdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBzZXNzLnBlZXJqaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICd0cmFuc3BvcnQtaW5mbyc6XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuYWRkSWNlQ2FuZGlkYXRlKCQoaXEpLmZpbmQoJz5qaW5nbGU+Y29udGVudCcpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi1pbmZvJzpcbiAgICAgICAgICAgICAgICAgICAgdmFyIGFmZmVjdGVkO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChpcSkuZmluZCgnPmppbmdsZT5yaW5naW5nW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncmluZ2luZy5qaW5nbGUnLCBbc2Vzcy5zaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhZmZlY3RlZCA9ICQoaXEpLmZpbmQoJz5qaW5nbGU+bXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdtdXRlLmppbmdsZScsIFtzZXNzLnNpZCwgYWZmZWN0ZWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPnVubXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFmZmVjdGVkID0gJChpcSkuZmluZCgnPmppbmdsZT51bm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykuYXR0cignbmFtZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigndW5tdXRlLmppbmdsZScsIFtzZXNzLnNpZCwgYWZmZWN0ZWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdhZGRzb3VyY2UnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnksIHVuLWppbmdsZWlzaFxuICAgICAgICAgICAgICAgIGNhc2UgJ3NvdXJjZS1hZGQnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnlcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5hZGRTb3VyY2UoJChpcSkuZmluZCgnPmppbmdsZT5jb250ZW50JyksIGZyb21KaWQpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdyZW1vdmVzb3VyY2UnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnksIHVuLWppbmdsZWlzaFxuICAgICAgICAgICAgICAgIGNhc2UgJ3NvdXJjZS1yZW1vdmUnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnlcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5yZW1vdmVTb3VyY2UoJChpcSkuZmluZCgnPmppbmdsZT5jb250ZW50JyksIGZyb21KaWQpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2ppbmdsZSBhY3Rpb24gbm90IGltcGxlbWVudGVkJywgYWN0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdGlhdGU6IGZ1bmN0aW9uIChwZWVyamlkLCBteWppZCkgeyAvLyBpbml0aWF0ZSBhIG5ldyBqaW5nbGVzZXNzaW9uIHRvIHBlZXJqaWRcbiAgICAgICAgICAgIHZhciBzZXNzID0gbmV3IEppbmdsZVNlc3Npb24obXlqaWQgfHwgdGhpcy5jb25uZWN0aW9uLmppZCxcbiAgICAgICAgICAgICAgICBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTIpLCAvLyByYW5kb20gc3RyaW5nXG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLCBYTVBQKTtcbiAgICAgICAgICAgIC8vIGNvbmZpZ3VyZSBzZXNzaW9uXG5cbiAgICAgICAgICAgIHNlc3MubWVkaWFfY29uc3RyYWludHMgPSB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgc2Vzcy5wY19jb25zdHJhaW50cyA9IHRoaXMucGNfY29uc3RyYWludHM7XG4gICAgICAgICAgICBzZXNzLmljZV9jb25maWcgPSB0aGlzLmljZV9jb25maWc7XG5cbiAgICAgICAgICAgIHNlc3MuaW5pdGlhdGUocGVlcmppZCwgdHJ1ZSk7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXSA9IHNlc3M7XG4gICAgICAgICAgICB0aGlzLmppZDJzZXNzaW9uW3Nlc3MucGVlcmppZF0gPSBzZXNzO1xuICAgICAgICAgICAgc2Vzcy5zZW5kT2ZmZXIoKTtcbiAgICAgICAgICAgIHJldHVybiBzZXNzO1xuICAgICAgICB9LFxuICAgICAgICB0ZXJtaW5hdGU6IGZ1bmN0aW9uIChzaWQsIHJlYXNvbiwgdGV4dCkgeyAvLyB0ZXJtaW5hdGUgYnkgc2Vzc2lvbmlkIChvciBhbGwgc2Vzc2lvbnMpXG4gICAgICAgICAgICBpZiAoc2lkID09PSBudWxsIHx8IHNpZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgZm9yIChzaWQgaW4gdGhpcy5zZXNzaW9ucykge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5zZXNzaW9uc1tzaWRdLnN0YXRlICE9ICdlbmRlZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2lkXS5zZW5kVGVybWluYXRlKHJlYXNvbiB8fCAoIXRoaXMuc2Vzc2lvbnNbc2lkXS5hY3RpdmUoKSkgPyAnY2FuY2VsJyA6IG51bGwsIHRleHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmppZDJzZXNzaW9uW3RoaXMuc2Vzc2lvbnNbc2lkXS5wZWVyamlkXTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc2Vzc2lvbnMuaGFzT3duUHJvcGVydHkoc2lkKSkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnNlc3Npb25zW3NpZF0uc3RhdGUgIT0gJ2VuZGVkJykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3NpZF0uc2VuZFRlcm1pbmF0ZShyZWFzb24gfHwgKCF0aGlzLnNlc3Npb25zW3NpZF0uYWN0aXZlKCkpID8gJ2NhbmNlbCcgOiBudWxsLCB0ZXh0KTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvblt0aGlzLnNlc3Npb25zW3NpZF0ucGVlcmppZF07XG4gICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gVXNlZCB0byB0ZXJtaW5hdGUgYSBzZXNzaW9uIHdoZW4gYW4gdW5hdmFpbGFibGUgcHJlc2VuY2UgaXMgcmVjZWl2ZWQuXG4gICAgICAgIHRlcm1pbmF0ZUJ5SmlkOiBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5qaWQyc2Vzc2lvbi5oYXNPd25Qcm9wZXJ0eShqaWQpKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlc3MgPSB0aGlzLmppZDJzZXNzaW9uW2ppZF07XG4gICAgICAgICAgICAgICAgaWYgKHNlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3BlZXIgd2VudCBhd2F5IHNpbGVudGx5JywgamlkKTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBqaWRdLCAnZ29uZScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgdGVybWluYXRlUmVtb3RlQnlKaWQ6IGZ1bmN0aW9uIChqaWQsIHJlYXNvbikge1xuICAgICAgICAgICAgaWYgKHRoaXMuamlkMnNlc3Npb24uaGFzT3duUHJvcGVydHkoamlkKSkge1xuICAgICAgICAgICAgICAgIHZhciBzZXNzID0gdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgIGlmIChzZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2VuZFRlcm1pbmF0ZShyZWFzb24gfHwgKCFzZXNzLmFjdGl2ZSgpKSA/ICdraWNrJyA6IG51bGwpO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndGVybWluYXRlIHBlZXIgd2l0aCBqaWQnLCBzZXNzLnNpZCwgamlkKTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBqaWQsICdraWNrZWQnXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBnZXRTdHVuQW5kVHVybkNyZWRlbnRpYWxzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAvLyBnZXQgc3R1biBhbmQgdHVybiBjb25maWd1cmF0aW9uIGZyb20gc2VydmVyIHZpYSB4ZXAtMDIxNVxuICAgICAgICAgICAgLy8gdXNlcyB0aW1lLWxpbWl0ZWQgY3JlZGVudGlhbHMgYXMgZGVzY3JpYmVkIGluXG4gICAgICAgICAgICAvLyBodHRwOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9kcmFmdC11YmVydGktYmVoYXZlLXR1cm4tcmVzdC0wMFxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIHNlZSBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3Byb3NvZHktbW9kdWxlcy9zb3VyY2UvYnJvd3NlL21vZF90dXJuY3JlZGVudGlhbHMvbW9kX3R1cm5jcmVkZW50aWFscy5sdWFcbiAgICAgICAgICAgIC8vIGZvciBhIHByb3NvZHkgbW9kdWxlIHdoaWNoIGltcGxlbWVudHMgdGhpc1xuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIGN1cnJlbnRseSwgdGhpcyBkb2Vzbid0IHdvcmsgd2l0aCB1cGRhdGVJY2UgYW5kIHRoZXJlZm9yZSBjcmVkZW50aWFscyB3aXRoIGEgbG9uZ1xuICAgICAgICAgICAgLy8gdmFsaWRpdHkgaGF2ZSB0byBiZSBmZXRjaGVkIGJlZm9yZSBjcmVhdGluZyB0aGUgcGVlcmNvbm5lY3Rpb25cbiAgICAgICAgICAgIC8vIFRPRE86IGltcGxlbWVudCByZWZyZXNoIHZpYSB1cGRhdGVJY2UgYXMgZGVzY3JpYmVkIGluXG4gICAgICAgICAgICAvLyAgICAgIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9MTY1MFxuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICAkaXEoe3R5cGU6ICdnZXQnLCB0bzogdGhpcy5jb25uZWN0aW9uLmRvbWFpbn0pXG4gICAgICAgICAgICAgICAgICAgIC5jKCdzZXJ2aWNlcycsIHt4bWxuczogJ3Vybjp4bXBwOmV4dGRpc2NvOjEnfSkuYygnc2VydmljZScsIHtob3N0OiAndHVybi4nICsgdGhpcy5jb25uZWN0aW9uLmRvbWFpbn0pLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGljZXNlcnZlcnMgPSBbXTtcbiAgICAgICAgICAgICAgICAgICAgJChyZXMpLmZpbmQoJz5zZXJ2aWNlcz5zZXJ2aWNlJykuZWFjaChmdW5jdGlvbiAoaWR4LCBlbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZWwgPSAkKGVsKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBkaWN0ID0ge307XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IGVsLmF0dHIoJ3R5cGUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3N0dW4nOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCA9ICdzdHVuOicgKyBlbC5hdHRyKCdob3N0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCdwb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc6JyArIGVsLmF0dHIoJ3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpY2VzZXJ2ZXJzLnB1c2goZGljdCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3R1cm4nOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3R1cm5zJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgPSB0eXBlICsgJzonO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigndXNlcm5hbWUnKSkgeyAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTE1MDhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKC9DaHJvbShlfGl1bSlcXC8oWzAtOV0rKVxcLi8pICYmIHBhcnNlSW50KG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0Nocm9tKGV8aXVtKVxcLyhbMC05XSspXFwuLylbMl0sIDEwKSA8IDI4KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gZWwuYXR0cigndXNlcm5hbWUnKSArICdAJztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51c2VybmFtZSA9IGVsLmF0dHIoJ3VzZXJuYW1lJyk7IC8vIG9ubHkgd29ya3MgaW4gTTI4XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gZWwuYXR0cignaG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigncG9ydCcpICYmIGVsLmF0dHIoJ3BvcnQnKSAhPSAnMzQ3OCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc6JyArIGVsLmF0dHIoJ3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigndHJhbnNwb3J0JykgJiYgZWwuYXR0cigndHJhbnNwb3J0JykgIT0gJ3VkcCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc/dHJhbnNwb3J0PScgKyBlbC5hdHRyKCd0cmFuc3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigncGFzc3dvcmQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC5jcmVkZW50aWFsID0gZWwuYXR0cigncGFzc3dvcmQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpY2VzZXJ2ZXJzLnB1c2goZGljdCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5pY2VfY29uZmlnLmljZVNlcnZlcnMgPSBpY2VzZXJ2ZXJzO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2dldHRpbmcgdHVybiBjcmVkZW50aWFscyBmYWlsZWQnLCBlcnIpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2lzIG1vZF90dXJuY3JlZGVudGlhbHMgb3Igc2ltaWxhciBpbnN0YWxsZWQ/Jyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIC8vIGltcGxlbWVudCBwdXNoP1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBQb3B1bGF0ZXMgdGhlIGxvZyBkYXRhXG4gICAgICAgICAqL1xuICAgICAgICBwb3B1bGF0ZURhdGE6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnNlc3Npb25zKS5mb3JFYWNoKGZ1bmN0aW9uIChzaWQpIHtcbiAgICAgICAgICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgICAgICBpZiAoc2Vzc2lvbi5wZWVyY29ubmVjdGlvbiAmJiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnVwZGF0ZUxvZykge1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogc2hvdWxkIHByb2JhYmx5IGJlIGEgLmR1bXAgY2FsbFxuICAgICAgICAgICAgICAgICAgICBkYXRhW1wiamluZ2xlX1wiICsgc2Vzc2lvbi5zaWRdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlTG9nOiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnVwZGF0ZUxvZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRzOiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnN0YXRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB3aW5kb3cubG9jYXRpb24uaHJlZlxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgIH1cbiAgICB9KTtcbn07XG5cbiIsIi8qIGdsb2JhbCBTdHJvcGhlICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignbG9nZ2VyJywge1xuICAgICAgICAvLyBsb2dzIHJhdyBzdGFuemFzIGFuZCBtYWtlcyB0aGVtIGF2YWlsYWJsZSBmb3IgZG93bmxvYWQgYXMgSlNPTlxuICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICBsb2c6IFtdLFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5yYXdJbnB1dCA9IHRoaXMubG9nX2luY29taW5nLmJpbmQodGhpcyk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24ucmF3T3V0cHV0ID0gdGhpcy5sb2dfb3V0Z29pbmcuYmluZCh0aGlzKTtcbiAgICAgICAgfSxcbiAgICAgICAgbG9nX2luY29taW5nOiBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5wdXNoKFtuZXcgRGF0ZSgpLmdldFRpbWUoKSwgJ2luY29taW5nJywgc3RhbnphXSk7XG4gICAgICAgIH0sXG4gICAgICAgIGxvZ19vdXRnb2luZzogZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdGhpcy5sb2cucHVzaChbbmV3IERhdGUoKS5nZXRUaW1lKCksICdvdXRnb2luZycsIHN0YW56YV0pO1xuICAgICAgICB9XG4gICAgfSk7XG59OyIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgZm9jdXNNdWNKaWQsIGZvcmNlTXV0ZWQsXG4gICBzZXRBdWRpb011dGVkLCBTdHJvcGhlICovXG4vKipcbiAqIE1vZGVyYXRlIGNvbm5lY3Rpb24gcGx1Z2luLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChYTVBQKSB7XG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdtb2RlcmF0ZScsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG5cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25NdXRlLmJpbmQodGhpcyksXG4gICAgICAgICAgICAgICAgJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbycsXG4gICAgICAgICAgICAgICAgJ2lxJyxcbiAgICAgICAgICAgICAgICAnc2V0JyxcbiAgICAgICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgICAgIG51bGwpO1xuICAgICAgICB9LFxuICAgICAgICBzZXRNdXRlOiBmdW5jdGlvbiAoamlkLCBtdXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJzZXQgbXV0ZVwiLCBtdXRlKTtcbiAgICAgICAgICAgIHZhciBpcVRvRm9jdXMgPSAkaXEoe3RvOiBmb2N1c011Y0ppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdtdXRlJywge1xuICAgICAgICAgICAgICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbycsXG4gICAgICAgICAgICAgICAgICAgIGppZDogamlkXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAudChtdXRlLnRvU3RyaW5nKCkpXG4gICAgICAgICAgICAgICAgLnVwKCk7XG5cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgaXFUb0ZvY3VzLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldCBtdXRlJywgcmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0IG11dGUgZXJyb3InLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uTXV0ZTogZnVuY3Rpb24gKGlxKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IGlxLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgaWYgKGZyb20gIT09IGZvY3VzTXVjSmlkKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiSWdub3JlZCBtdXRlIGZyb20gbm9uIGZvY3VzIHBlZXJcIik7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG11dGUgPSAkKGlxKS5maW5kKCdtdXRlJyk7XG4gICAgICAgICAgICBpZiAobXV0ZS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB2YXIgZG9NdXRlQXVkaW8gPSBtdXRlLnRleHQoKSA9PT0gXCJ0cnVlXCI7XG4gICAgICAgICAgICAgICAgVUkuc2V0QXVkaW9NdXRlZChkb011dGVBdWRpbyk7XG4gICAgICAgICAgICAgICAgWE1QUC5mb3JjZU11dGVkID0gZG9NdXRlQXVkaW87XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgZWplY3Q6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIC8vIFdlJ3JlIG5vdCB0aGUgZm9jdXMsIHNvIGNhbid0IHRlcm1pbmF0ZVxuICAgICAgICAgICAgLy9jb25uZWN0aW9uLmppbmdsZS50ZXJtaW5hdGVSZW1vdGVCeUppZChqaWQsICdraWNrJyk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5raWNrKGppZCk7XG4gICAgICAgIH1cbiAgICB9KTtcbn0iLCIvKiBqc2hpbnQgLVcxMTcgKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdyYXlvJyxcbiAgICAgICAge1xuICAgICAgICAgICAgUkFZT19YTUxOUzogJ3Vybjp4bXBwOnJheW86MScsXG4gICAgICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24uZGlzY28pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOnJheW86Y2xpZW50OjEnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcihcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblJheW8uYmluZCh0aGlzKSwgdGhpcy5SQVlPX1hNTE5TLCAnaXEnLCAnc2V0JywgbnVsbCwgbnVsbCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25SYXlvOiBmdW5jdGlvbiAoaXEpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJSYXlvIElRXCIsIGlxKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkaWFsOiBmdW5jdGlvbiAodG8sIGZyb20sIHJvb21OYW1lLCByb29tUGFzcykge1xuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiBmb2N1c011Y0ppZFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXEuYygnZGlhbCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlMsXG4gICAgICAgICAgICAgICAgICAgICAgICB0bzogdG8sXG4gICAgICAgICAgICAgICAgICAgICAgICBmcm9tOiBmcm9tXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJlcS5jKCdoZWFkZXInLFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiAnSnZiUm9vbU5hbWUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21OYW1lXG4gICAgICAgICAgICAgICAgICAgIH0pLnVwKCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocm9vbVBhc3MgIT09IG51bGwgJiYgcm9vbVBhc3MubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgcmVxLmMoJ2hlYWRlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogJ0p2YlJvb21QYXNzd29yZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21QYXNzXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS51cCgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgICAgIHJlcSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdEaWFsIHJlc3VsdCAnLCByZXN1bHQpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSAkKHJlc3VsdCkuZmluZCgncmVmJykuYXR0cigndXJpJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNhbGxfcmVzb3VyY2UgPSByZXNvdXJjZS5zdWJzdHIoJ3htcHA6Jy5sZW5ndGgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIlJlY2VpdmVkIGNhbGwgcmVzb3VyY2U6IFwiICsgdGhpcy5jYWxsX3Jlc291cmNlKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0RpYWwgZXJyb3IgJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBoYW5nX3VwOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmNhbGxfcmVzb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTm8gY2FsbCBpbiBwcm9ncmVzc1wiKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiB0aGlzLmNhbGxfcmVzb3VyY2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgcmVxLmMoJ2hhbmd1cCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlNcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgICAgICByZXEsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnSGFuZ3VwIHJlc3VsdCAnLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jYWxsX3Jlc291cmNlID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0hhbmd1cCBlcnJvciAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNhbGxfcmVzb3VyY2UgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICk7XG59O1xuIiwiLyoqXG4gKiBTdHJvcGhlIGxvZ2dlciBpbXBsZW1lbnRhdGlvbi4gTG9ncyBmcm9tIGxldmVsIFdBUk4gYW5kIGFib3ZlLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIFN0cm9waGUubG9nID0gZnVuY3Rpb24gKGxldmVsLCBtc2cpIHtcbiAgICAgICAgc3dpdGNoIChsZXZlbCkge1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLldBUk46XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiU3Ryb3BoZTogXCIgKyBtc2cpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkVSUk9SOlxuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkZBVEFMOlxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJTdHJvcGhlOiBcIiArIG1zZyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgU3Ryb3BoZS5nZXRTdGF0dXNTdHJpbmcgPSBmdW5jdGlvbiAoc3RhdHVzKSB7XG4gICAgICAgIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkVSUk9SOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkVSUk9SXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkNPTk5FQ1RJTkc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJDT05ORkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRU5USUNBVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRU5USUNBVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORUNURUQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVEVEXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkRJU0NPTk5FQ1RFRDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNURURcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNUSU5HXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkFUVEFDSEVEOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkFUVEFDSEVEXCI7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiBcInVua25vd25cIjtcbiAgICAgICAgfVxuICAgIH07XG59O1xuIiwidmFyIE1vZGVyYXRvciA9IHJlcXVpcmUoXCIuL21vZGVyYXRvclwiKTtcbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFJlY29yZGluZyA9IHJlcXVpcmUoXCIuL3JlY29yZGluZ1wiKTtcbnZhciBTRFAgPSByZXF1aXJlKFwiLi9TRFBcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgY29ubmVjdGlvbiA9IG51bGw7XG52YXIgYXV0aGVudGljYXRlZFVzZXIgPSBmYWxzZTtcbnZhciBhY3RpdmVjYWxsID0gbnVsbDtcblxuZnVuY3Rpb24gY29ubmVjdChqaWQsIHBhc3N3b3JkLCB1aUNyZWRlbnRpYWxzKSB7XG4gICAgdmFyIGJvc2hcbiAgICAgICAgPSB1aUNyZWRlbnRpYWxzLmJvc2ggfHwgY29uZmlnLmJvc2ggfHwgJy9odHRwLWJpbmQnO1xuICAgIGNvbm5lY3Rpb24gPSBuZXcgU3Ryb3BoZS5Db25uZWN0aW9uKGJvc2gpO1xuICAgIE1vZGVyYXRvci5zZXRDb25uZWN0aW9uKGNvbm5lY3Rpb24pO1xuXG4gICAgdmFyIHNldHRpbmdzID0gVUkuZ2V0U2V0dGluZ3MoKTtcbiAgICB2YXIgZW1haWwgPSBzZXR0aW5ncy5lbWFpbDtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSBzZXR0aW5ncy5kaXNwbGF5TmFtZTtcbiAgICBpZihlbWFpbCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRW1haWxUb1ByZXNlbmNlKGVtYWlsKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkVXNlcklkVG9QcmVzZW5jZShzZXR0aW5ncy51aWQpO1xuICAgIH1cbiAgICBpZihkaXNwbGF5TmFtZSkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRGlzcGxheU5hbWVUb1ByZXNlbmNlKGRpc3BsYXlOYW1lKTtcbiAgICB9XG5cbiAgICBpZiAoY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAvLyBmb3IgY2hyb21lLCBhZGQgbXVsdGlzdHJlYW0gY2FwXG4gICAgfVxuICAgIGNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzID0gUlRDLmdldFBDQ29uc3RyYWludHMoKTtcbiAgICBpZiAoY29uZmlnLnVzZUlQdjYpIHtcbiAgICAgICAgLy8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD0yODI4XG4gICAgICAgIGlmICghY29ubmVjdGlvbi5qaW5nbGUucGNfY29uc3RyYWludHMub3B0aW9uYWwpXG4gICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cy5vcHRpb25hbCA9IFtdO1xuICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cy5vcHRpb25hbC5wdXNoKHtnb29nSVB2NjogdHJ1ZX0pO1xuICAgIH1cblxuICAgIGlmKCFwYXNzd29yZClcbiAgICAgICAgcGFzc3dvcmQgPSB1aUNyZWRlbnRpYWxzLnBhc3N3b3JkO1xuXG4gICAgdmFyIGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQgPSBmYWxzZTtcbiAgICBjb25uZWN0aW9uLmNvbm5lY3QoamlkLCBwYXNzd29yZCwgZnVuY3Rpb24gKHN0YXR1cywgbXNnKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdTdHJvcGhlIHN0YXR1cyBjaGFuZ2VkIHRvJyxcbiAgICAgICAgICAgIFN0cm9waGUuZ2V0U3RhdHVzU3RyaW5nKHN0YXR1cykpO1xuICAgICAgICBpZiAoc3RhdHVzID09PSBTdHJvcGhlLlN0YXR1cy5DT05ORUNURUQpIHtcbiAgICAgICAgICAgIGlmIChjb25maWcudXNlU3R1blR1cm4pIHtcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5nZXRTdHVuQW5kVHVybkNyZWRlbnRpYWxzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBVSS5kaXNhYmxlQ29ubmVjdCgpO1xuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJNeSBKYWJiZXIgSUQ6IFwiICsgY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZihwYXNzd29yZClcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkVXNlciA9IHRydWU7XG4gICAgICAgICAgICBtYXliZURvSm9pbigpO1xuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQ09OTkZBSUwpIHtcbiAgICAgICAgICAgIGlmKG1zZyA9PT0gJ3gtc3Ryb3BoZS1iYWQtbm9uLWFub24tamlkJykge1xuICAgICAgICAgICAgICAgIGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICBpZihhbm9ueW1vdXNDb25uZWN0aW9uRmFpbGVkKSB7XG4gICAgICAgICAgICAgICAgLy8gcHJvbXB0IHVzZXIgZm9yIHVzZXJuYW1lIGFuZCBwYXNzd29yZFxuICAgICAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChzdGF0dXMgPT09IFN0cm9waGUuU3RhdHVzLkFVVEhGQUlMKSB7XG4gICAgICAgICAgICAvLyB3cm9uZyBwYXNzd29yZCBvciB1c2VybmFtZSwgcHJvbXB0IHVzZXJcbiAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcblxuICAgICAgICB9XG4gICAgfSk7XG59XG5cblxuXG5mdW5jdGlvbiBtYXliZURvSm9pbigpIHtcbiAgICBpZiAoY29ubmVjdGlvbiAmJiBjb25uZWN0aW9uLmNvbm5lY3RlZCAmJlxuICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChjb25uZWN0aW9uLmppZClcbiAgICAgICAgJiYgKFJUQy5sb2NhbEF1ZGlvIHx8IFJUQy5sb2NhbFZpZGVvKSkge1xuICAgICAgICAvLyAuY29ubmVjdGVkIGlzIHRydWUgd2hpbGUgY29ubmVjdGluZz9cbiAgICAgICAgZG9Kb2luKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBkb0pvaW4oKSB7XG4gICAgdmFyIHJvb21OYW1lID0gVUkuZ2VuZXJhdGVSb29tTmFtZSgpO1xuXG4gICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKFxuICAgICAgICByb29tTmFtZSwgVUkuY2hlY2tGb3JOaWNrbmFtZUFuZEpvaW4pO1xufVxuXG5mdW5jdGlvbiBpbml0U3Ryb3BoZVBsdWdpbnMoKVxue1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuZW11Y1wiKShYTVBQLCBldmVudEVtaXR0ZXIpO1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuamluZ2xlXCIpKCk7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS5tb2RlcmF0ZVwiKShYTVBQKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnV0aWxcIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnJheW9cIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLmxvZ2dlclwiKSgpO1xufVxuXG5mdW5jdGlvbiByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBSVEMuYWRkU3RyZWFtTGlzdGVuZXIobWF5YmVEb0pvaW4sXG4gICAgICAgIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DUkVBVEVEKTtcbn1cblxuZnVuY3Rpb24gc2V0dXBFdmVudHMoKSB7XG4gICAgJCh3aW5kb3cpLmJpbmQoJ2JlZm9yZXVubG9hZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5jb25uZWN0ZWQpIHtcbiAgICAgICAgICAgIC8vIGVuc3VyZSBzaWdub3V0XG4gICAgICAgICAgICAkLmFqYXgoe1xuICAgICAgICAgICAgICAgIHR5cGU6ICdQT1NUJyxcbiAgICAgICAgICAgICAgICB1cmw6IGNvbmZpZy5ib3NoLFxuICAgICAgICAgICAgICAgIGFzeW5jOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgY29udGVudFR5cGU6ICdhcHBsaWNhdGlvbi94bWwnLFxuICAgICAgICAgICAgICAgIGRhdGE6IFwiPGJvZHkgcmlkPSdcIiArIChjb25uZWN0aW9uLnJpZCB8fCBjb25uZWN0aW9uLl9wcm90by5yaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHhtbG5zPSdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9odHRwYmluZCcgc2lkPSdcIlxuICAgICAgICAgICAgICAgICAgICArIChjb25uZWN0aW9uLnNpZCB8fCBjb25uZWN0aW9uLl9wcm90by5zaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHR5cGU9J3Rlcm1pbmF0ZSc+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjxwcmVzZW5jZSB4bWxucz0namFiYmVyOmNsaWVudCcgdHlwZT0ndW5hdmFpbGFibGUnLz5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC9ib2R5PlwiLFxuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzaWduZWQgb3V0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZXJyb3I6IGZ1bmN0aW9uIChYTUxIdHRwUmVxdWVzdCwgdGV4dFN0YXR1cywgZXJyb3JUaHJvd24pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NpZ25vdXQgZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRTdGF0dXMgKyAnICgnICsgZXJyb3JUaHJvd24gKyAnKScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIFhNUFAuZGlzcG9zZUNvbmZlcmVuY2UodHJ1ZSk7XG4gICAgfSk7XG59XG5cbnZhciBYTVBQID0ge1xuICAgIHNlc3Npb25UZXJtaW5hdGVkOiBmYWxzZSxcbiAgICAvKipcbiAgICAgKiBSZW1lbWJlcnMgaWYgd2Ugd2VyZSBtdXRlZCBieSB0aGUgZm9jdXMuXG4gICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICovXG4gICAgZm9yY2VNdXRlZDogZmFsc2UsXG4gICAgc3RhcnQ6IGZ1bmN0aW9uICh1aUNyZWRlbnRpYWxzKSB7XG4gICAgICAgIHNldHVwRXZlbnRzKCk7XG4gICAgICAgIGluaXRTdHJvcGhlUGx1Z2lucygpO1xuICAgICAgICByZWdpc3Rlckxpc3RlbmVycygpO1xuICAgICAgICBNb2RlcmF0b3IuaW5pdCgpO1xuICAgICAgICB2YXIgamlkID0gdWlDcmVkZW50aWFscy5qaWQgfHxcbiAgICAgICAgICAgIGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4gfHxcbiAgICAgICAgICAgIGNvbmZpZy5ob3N0cy5kb21haW4gfHxcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZTtcbiAgICAgICAgY29ubmVjdChqaWQsIG51bGwsIHVpQ3JlZGVudGlhbHMpO1xuICAgIH0sXG4gICAgcHJvbXB0TG9naW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgVUkuc2hvd0xvZ2luUG9wdXAoY29ubmVjdCk7XG4gICAgfSxcbiAgICBqb2luUm9vb206IGZ1bmN0aW9uKHJvb21OYW1lLCB1c2VOaWNrcywgbmljaylcbiAgICB7XG4gICAgICAgIHZhciByb29tamlkO1xuICAgICAgICByb29tamlkID0gcm9vbU5hbWU7XG5cbiAgICAgICAgaWYgKHVzZU5pY2tzKSB7XG4gICAgICAgICAgICBpZiAobmljaykge1xuICAgICAgICAgICAgICAgIHJvb21qaWQgKz0gJy8nICsgbmljaztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyBTdHJvcGhlLmdldE5vZGVGcm9tSmlkKGNvbm5lY3Rpb24uamlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgdmFyIHRtcEppZCA9IFN0cm9waGUuZ2V0Tm9kZUZyb21KaWQoY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZighYXV0aGVudGljYXRlZFVzZXIpXG4gICAgICAgICAgICAgICAgdG1wSmlkID0gdG1wSmlkLnN1YnN0cigwLCA4KTtcblxuICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyB0bXBKaWQ7XG4gICAgICAgIH1cbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvSm9pbihyb29tamlkKTtcbiAgICB9LFxuICAgIG15SmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmVtdWMubXlyb29tamlkO1xuICAgIH0sXG4gICAgbXlSZXNvdXJjZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZighY29ubmVjdGlvbiB8fCAhIGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpO1xuICAgIH0sXG4gICAgZGlzcG9zZUNvbmZlcmVuY2U6IGZ1bmN0aW9uIChvblVubG9hZCkge1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkRJU1BPU0VfQ09ORkVSRU5DRSwgb25VbmxvYWQpO1xuICAgICAgICB2YXIgaGFuZGxlciA9IGFjdGl2ZWNhbGw7XG4gICAgICAgIGlmIChoYW5kbGVyICYmIGhhbmRsZXIucGVlcmNvbm5lY3Rpb24pIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBwcm9iYWJseSByZW1vdmluZyBzdHJlYW1zIGlzIG5vdCByZXF1aXJlZCBhbmQgY2xvc2UoKSBzaG91bGRcbiAgICAgICAgICAgIC8vIGJlIGVub3VnaFxuICAgICAgICAgICAgaWYgKFJUQy5sb2NhbEF1ZGlvKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oUlRDLmxvY2FsQXVkaW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKFJUQy5sb2NhbFZpZGVvKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIGFjdGl2ZWNhbGwgPSBudWxsO1xuICAgICAgICBpZighb25VbmxvYWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIHJlbW92ZUxpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbihyb29tTmFtZSwgY2FsbGJhY2spIHtcbiAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgfSxcbiAgICBpc01vZGVyYXRvcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCk7XG4gICAgfSxcbiAgICBpc1NpcEdhdGV3YXlFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNTaXBHYXRld2F5RW5hYmxlZCgpO1xuICAgIH0sXG4gICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNFeHRlcm5hbEF1dGhFbmFibGVkKCk7XG4gICAgfSxcbiAgICBzd2l0Y2hTdHJlYW1zOiBmdW5jdGlvbiAoc3RyZWFtLCBvbGRTdHJlYW0sIGNhbGxiYWNrKSB7XG4gICAgICAgIGlmIChhY3RpdmVjYWxsKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogd2lsbCBibG9jayBzd2l0Y2hJblByb2dyZXNzIG9uIHRydWUgdmFsdWUgaW4gY2FzZSBvZiBleGNlcHRpb25cbiAgICAgICAgICAgIGFjdGl2ZWNhbGwuc3dpdGNoU3RyZWFtcyhzdHJlYW0sIG9sZFN0cmVhbSwgY2FsbGJhY2spO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gV2UgYXJlIGRvbmUgaW1tZWRpYXRlbHlcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyBjb25mZXJlbmNlIGhhbmRsZXJcIik7XG4gICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ0Vycm9yJyxcbiAgICAgICAgICAgICAgICAnVW5hYmxlIHRvIHN3aXRjaCB2aWRlbyBzdHJlYW0uJyk7XG4gICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZXRWaWRlb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgICAgIGlmKGFjdGl2ZWNhbGwgJiYgY29ubmVjdGlvbiAmJiBSVEMubG9jYWxWaWRlbylcbiAgICAgICB7XG4gICAgICAgICAgIGFjdGl2ZWNhbGwuc2V0VmlkZW9NdXRlKG11dGUsIGNhbGxiYWNrLCBvcHRpb25zKTtcbiAgICAgICB9XG4gICAgfSxcbiAgICBzZXRBdWRpb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaykge1xuICAgICAgICBpZiAoIShjb25uZWN0aW9uICYmIFJUQy5sb2NhbEF1ZGlvKSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cblxuICAgICAgICBpZiAodGhpcy5mb3JjZU11dGVkICYmICFtdXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJBc2tpbmcgZm9jdXMgZm9yIHVubXV0ZVwiKTtcbiAgICAgICAgICAgIGNvbm5lY3Rpb24ubW9kZXJhdGUuc2V0TXV0ZShjb25uZWN0aW9uLmVtdWMubXlyb29tamlkLCBtdXRlKTtcbiAgICAgICAgICAgIC8vIEZJWE1FOiB3YWl0IGZvciByZXN1bHQgYmVmb3JlIHJlc2V0dGluZyBtdXRlZCBzdGF0dXNcbiAgICAgICAgICAgIHRoaXMuZm9yY2VNdXRlZCA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG11dGUgPT0gUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAvLyBOb3RoaW5nIHRvIGRvXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSByaWdodCB3YXkgdG8gaGFuZGxlIG11bHRpcGxlIHRyYWNrcy5cbiAgICAgICAgLy8gU28gYXQgbGVhc3QgbWFrZSBzdXJlIHRoYXQgdGhleSBhcmUgYWxsIG11dGVkIG9yIGFsbCB1bm11dGVkIGFuZFxuICAgICAgICAvLyB0aGF0IHdlIHNlbmQgcHJlc2VuY2UganVzdCBvbmNlLlxuICAgICAgICBSVEMubG9jYWxBdWRpby5tdXRlKCk7XG4gICAgICAgIC8vIGlzTXV0ZWQgaXMgdGhlIG9wcG9zaXRlIG9mIGF1ZGlvRW5hYmxlZFxuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQXVkaW9JbmZvVG9QcmVzZW5jZShtdXRlKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9LFxuICAgIC8vIFJlYWxseSBtdXRlIHZpZGVvLCBpLmUuIGRvbnQgZXZlbiBzZW5kIGJsYWNrIGZyYW1lc1xuICAgIG11dGVWaWRlbzogZnVuY3Rpb24gKHBjLCB1bm11dGUpIHtcbiAgICAgICAgLy8gRklYTUU6IHRoaXMgcHJvYmFibHkgbmVlZHMgYW5vdGhlciBvZiB0aG9zZSBsb3ZlbHkgc3RhdGUgc2FmZWd1YXJkcy4uLlxuICAgICAgICAvLyB3aGljaCBjaGVja3MgZm9yIGljZWNvbm4gPT0gY29ubmVjdGVkIGFuZCBzaWdzdGF0ZSA9PSBzdGFibGVcbiAgICAgICAgcGMuc2V0UmVtb3RlRGVzY3JpcHRpb24ocGMucmVtb3RlRGVzY3JpcHRpb24sXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcGMuY3JlYXRlQW5zd2VyKFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoYW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgc2RwID0gbmV3IFNEUChhbnN3ZXIuc2RwKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzZHAubWVkaWEubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh1bm11dGUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXNlbmRyZWN2JywgJ2E9cmVjdm9ubHknKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyLnNkcCA9IHNkcC5yYXc7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBwYy5zZXRMb2NhbERlc2NyaXB0aW9uKGFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlIFNMRCBvaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlIFNMRCBlcnJvcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ0Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGZhaWxlZCB0byAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnbXV0ZSEgKFNMRCBGYWlsdXJlKScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlVmlkZW8gU1JEIGVycm9yJyk7XG4gICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCdFcnJvcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGZhaWxlZCB0byBzdG9wIHZpZGVvIScgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyhTUkQgRmFpbHVyZSknKTtcblxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sXG4gICAgdG9nZ2xlUmVjb3JkaW5nOiBmdW5jdGlvbiAodG9rZW5FbXB0eUNhbGxiYWNrLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjaykge1xuICAgICAgICBSZWNvcmRpbmcudG9nZ2xlUmVjb3JkaW5nKHRva2VuRW1wdHlDYWxsYmFjayxcbiAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjayk7XG4gICAgfSxcbiAgICBhZGRUb1ByZXNlbmNlOiBmdW5jdGlvbiAobmFtZSwgdmFsdWUsIGRvbnRTZW5kKSB7XG4gICAgICAgIHN3aXRjaCAobmFtZSlcbiAgICAgICAge1xuICAgICAgICAgICAgY2FzZSBcImRpc3BsYXlOYW1lXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZERpc3BsYXlOYW1lVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiZXRoZXJwYWRcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRXRoZXJwYWRUb1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJwcmV6aVwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRQcmV6aVRvUHJlc2VuY2UodmFsdWUsIDApO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcInByZXppU2xpZGVcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQ3VycmVudFNsaWRlVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiY29ubmVjdGlvblF1YWxpdHlcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQ29ubmVjdGlvbkluZm9Ub1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJlbWFpbFwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRFbWFpbFRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgZGVmYXVsdCA6XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJVbmtub3duIHRhZyBmb3IgcHJlc2VuY2UuXCIpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZighZG9udFNlbmQpXG4gICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2VuZFByZXNlbmNlKCk7XG4gICAgfSxcbiAgICBzZW5kTG9nczogZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgLy8gWEVQLTAzMzctaXNoXG4gICAgICAgIHZhciBtZXNzYWdlID0gJG1zZyh7dG86IGZvY3VzTXVjSmlkLCB0eXBlOiAnbm9ybWFsJ30pO1xuICAgICAgICBtZXNzYWdlLmMoJ2xvZycsIHsgeG1sbnM6ICd1cm46eG1wcDpldmVudGxvZycsXG4gICAgICAgICAgICBpZDogJ1BlZXJDb25uZWN0aW9uU3RhdHMnfSk7XG4gICAgICAgIG1lc3NhZ2UuYygnbWVzc2FnZScpLnQoY29udGVudCkudXAoKTtcbiAgICAgICAgaWYgKGRlZmxhdGUpIHtcbiAgICAgICAgICAgIG1lc3NhZ2UuYygndGFnJywge25hbWU6IFwiZGVmbGF0ZWRcIiwgdmFsdWU6IFwidHJ1ZVwifSkudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlLnVwKCk7XG5cbiAgICAgICAgY29ubmVjdGlvbi5zZW5kKG1lc3NhZ2UpO1xuICAgIH0sXG4gICAgcG9wdWxhdGVEYXRhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGlmIChjb25uZWN0aW9uLmppbmdsZSkge1xuICAgICAgICAgICAgZGF0YSA9IGNvbm5lY3Rpb24uamluZ2xlLnBvcHVsYXRlRGF0YSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgZ2V0TG9nZ2VyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKGNvbm5lY3Rpb24ubG9nZ2VyKVxuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24ubG9nZ2VyLmxvZztcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSxcbiAgICBnZXRQcmV6aTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmdldFByZXppKHRoaXMubXlKaWQoKSk7XG4gICAgfSxcbiAgICByZW1vdmVQcmV6aUZyb21QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgc2VuZENoYXRNZXNzYWdlOiBmdW5jdGlvbiAobWVzc2FnZSwgbmlja25hbWUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRNZXNzYWdlKG1lc3NhZ2UsIG5pY2tuYW1lKTtcbiAgICB9LFxuICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uICh0b3BpYykge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2V0U3ViamVjdCh0b3BpYyk7XG4gICAgfSxcbiAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMubG9ja1Jvb20oa2V5LCBvblN1Y2Nlc3MsIG9uRXJyb3IsIG9uTm90U3VwcG9ydGVkKTtcbiAgICB9LFxuICAgIGRpYWw6IGZ1bmN0aW9uICh0bywgZnJvbSwgcm9vbU5hbWUscm9vbVBhc3MpIHtcbiAgICAgICAgY29ubmVjdGlvbi5yYXlvLmRpYWwodG8sIGZyb20sIHJvb21OYW1lLHJvb21QYXNzKTtcbiAgICB9LFxuICAgIHNldE11dGU6IGZ1bmN0aW9uIChqaWQsIG11dGUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGppZCwgbXV0ZSk7XG4gICAgfSxcbiAgICBlamVjdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25uZWN0aW9uLm1vZGVyYXRlLmVqZWN0KGppZCk7XG4gICAgfSxcbiAgICBmaW5kSmlkRnJvbVJlc291cmNlOiBmdW5jdGlvbiAocmVzb3VyY2UpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2UpO1xuICAgIH0sXG4gICAgZ2V0TWVtYmVyczogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLm1lbWJlcnM7XG4gICAgfVxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFhNUFA7IiwiLy8gQ29weXJpZ2h0IEpveWVudCwgSW5jLiBhbmQgb3RoZXIgTm9kZSBjb250cmlidXRvcnMuXG4vL1xuLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGFcbi8vIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGVcbi8vIFwiU29mdHdhcmVcIiksIHRvIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZ1xuLy8gd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLFxuLy8gZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdFxuLy8gcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlXG4vLyBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZFxuLy8gaW4gYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTU1xuLy8gT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRlxuLy8gTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTlxuLy8gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sXG4vLyBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1Jcbi8vIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEVcbi8vIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuXG5cbmZ1bmN0aW9uIEV2ZW50RW1pdHRlcigpIHtcbiAgdGhpcy5fZXZlbnRzID0gdGhpcy5fZXZlbnRzIHx8IHt9O1xuICB0aGlzLl9tYXhMaXN0ZW5lcnMgPSB0aGlzLl9tYXhMaXN0ZW5lcnMgfHwgdW5kZWZpbmVkO1xufVxubW9kdWxlLmV4cG9ydHMgPSBFdmVudEVtaXR0ZXI7XG5cbi8vIEJhY2t3YXJkcy1jb21wYXQgd2l0aCBub2RlIDAuMTAueFxuRXZlbnRFbWl0dGVyLkV2ZW50RW1pdHRlciA9IEV2ZW50RW1pdHRlcjtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5fZXZlbnRzID0gdW5kZWZpbmVkO1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5fbWF4TGlzdGVuZXJzID0gdW5kZWZpbmVkO1xuXG4vLyBCeSBkZWZhdWx0IEV2ZW50RW1pdHRlcnMgd2lsbCBwcmludCBhIHdhcm5pbmcgaWYgbW9yZSB0aGFuIDEwIGxpc3RlbmVycyBhcmVcbi8vIGFkZGVkIHRvIGl0LiBUaGlzIGlzIGEgdXNlZnVsIGRlZmF1bHQgd2hpY2ggaGVscHMgZmluZGluZyBtZW1vcnkgbGVha3MuXG5FdmVudEVtaXR0ZXIuZGVmYXVsdE1heExpc3RlbmVycyA9IDEwO1xuXG4vLyBPYnZpb3VzbHkgbm90IGFsbCBFbWl0dGVycyBzaG91bGQgYmUgbGltaXRlZCB0byAxMC4gVGhpcyBmdW5jdGlvbiBhbGxvd3Ncbi8vIHRoYXQgdG8gYmUgaW5jcmVhc2VkLiBTZXQgdG8gemVybyBmb3IgdW5saW1pdGVkLlxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5zZXRNYXhMaXN0ZW5lcnMgPSBmdW5jdGlvbihuKSB7XG4gIGlmICghaXNOdW1iZXIobikgfHwgbiA8IDAgfHwgaXNOYU4obikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCduIG11c3QgYmUgYSBwb3NpdGl2ZSBudW1iZXInKTtcbiAgdGhpcy5fbWF4TGlzdGVuZXJzID0gbjtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmVtaXQgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciBlciwgaGFuZGxlciwgbGVuLCBhcmdzLCBpLCBsaXN0ZW5lcnM7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMpXG4gICAgdGhpcy5fZXZlbnRzID0ge307XG5cbiAgLy8gSWYgdGhlcmUgaXMgbm8gJ2Vycm9yJyBldmVudCBsaXN0ZW5lciB0aGVuIHRocm93LlxuICBpZiAodHlwZSA9PT0gJ2Vycm9yJykge1xuICAgIGlmICghdGhpcy5fZXZlbnRzLmVycm9yIHx8XG4gICAgICAgIChpc09iamVjdCh0aGlzLl9ldmVudHMuZXJyb3IpICYmICF0aGlzLl9ldmVudHMuZXJyb3IubGVuZ3RoKSkge1xuICAgICAgZXIgPSBhcmd1bWVudHNbMV07XG4gICAgICBpZiAoZXIgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICB0aHJvdyBlcjsgLy8gVW5oYW5kbGVkICdlcnJvcicgZXZlbnRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IFR5cGVFcnJvcignVW5jYXVnaHQsIHVuc3BlY2lmaWVkIFwiZXJyb3JcIiBldmVudC4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBoYW5kbGVyID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIGlmIChpc1VuZGVmaW5lZChoYW5kbGVyKSlcbiAgICByZXR1cm4gZmFsc2U7XG5cbiAgaWYgKGlzRnVuY3Rpb24oaGFuZGxlcikpIHtcbiAgICBzd2l0Y2ggKGFyZ3VtZW50cy5sZW5ndGgpIHtcbiAgICAgIC8vIGZhc3QgY2FzZXNcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMsIGFyZ3VtZW50c1sxXSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICBoYW5kbGVyLmNhbGwodGhpcywgYXJndW1lbnRzWzFdLCBhcmd1bWVudHNbMl0pO1xuICAgICAgICBicmVhaztcbiAgICAgIC8vIHNsb3dlclxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgbGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICAgICAgYXJncyA9IG5ldyBBcnJheShsZW4gLSAxKTtcbiAgICAgICAgZm9yIChpID0gMTsgaSA8IGxlbjsgaSsrKVxuICAgICAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICAgICAgICBoYW5kbGVyLmFwcGx5KHRoaXMsIGFyZ3MpO1xuICAgIH1cbiAgfSBlbHNlIGlmIChpc09iamVjdChoYW5kbGVyKSkge1xuICAgIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgYXJncyA9IG5ldyBBcnJheShsZW4gLSAxKTtcbiAgICBmb3IgKGkgPSAxOyBpIDwgbGVuOyBpKyspXG4gICAgICBhcmdzW2kgLSAxXSA9IGFyZ3VtZW50c1tpXTtcblxuICAgIGxpc3RlbmVycyA9IGhhbmRsZXIuc2xpY2UoKTtcbiAgICBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoO1xuICAgIGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKylcbiAgICAgIGxpc3RlbmVyc1tpXS5hcHBseSh0aGlzLCBhcmdzKTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIHZhciBtO1xuXG4gIGlmICghaXNGdW5jdGlvbihsaXN0ZW5lcikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCdsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcblxuICBpZiAoIXRoaXMuX2V2ZW50cylcbiAgICB0aGlzLl9ldmVudHMgPSB7fTtcblxuICAvLyBUbyBhdm9pZCByZWN1cnNpb24gaW4gdGhlIGNhc2UgdGhhdCB0eXBlID09PSBcIm5ld0xpc3RlbmVyXCIhIEJlZm9yZVxuICAvLyBhZGRpbmcgaXQgdG8gdGhlIGxpc3RlbmVycywgZmlyc3QgZW1pdCBcIm5ld0xpc3RlbmVyXCIuXG4gIGlmICh0aGlzLl9ldmVudHMubmV3TGlzdGVuZXIpXG4gICAgdGhpcy5lbWl0KCduZXdMaXN0ZW5lcicsIHR5cGUsXG4gICAgICAgICAgICAgIGlzRnVuY3Rpb24obGlzdGVuZXIubGlzdGVuZXIpID9cbiAgICAgICAgICAgICAgbGlzdGVuZXIubGlzdGVuZXIgOiBsaXN0ZW5lcik7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgLy8gT3B0aW1pemUgdGhlIGNhc2Ugb2Ygb25lIGxpc3RlbmVyLiBEb24ndCBuZWVkIHRoZSBleHRyYSBhcnJheSBvYmplY3QuXG4gICAgdGhpcy5fZXZlbnRzW3R5cGVdID0gbGlzdGVuZXI7XG4gIGVsc2UgaWYgKGlzT2JqZWN0KHRoaXMuX2V2ZW50c1t0eXBlXSkpXG4gICAgLy8gSWYgd2UndmUgYWxyZWFkeSBnb3QgYW4gYXJyYXksIGp1c3QgYXBwZW5kLlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXS5wdXNoKGxpc3RlbmVyKTtcbiAgZWxzZVxuICAgIC8vIEFkZGluZyB0aGUgc2Vjb25kIGVsZW1lbnQsIG5lZWQgdG8gY2hhbmdlIHRvIGFycmF5LlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IFt0aGlzLl9ldmVudHNbdHlwZV0sIGxpc3RlbmVyXTtcblxuICAvLyBDaGVjayBmb3IgbGlzdGVuZXIgbGVha1xuICBpZiAoaXNPYmplY3QodGhpcy5fZXZlbnRzW3R5cGVdKSAmJiAhdGhpcy5fZXZlbnRzW3R5cGVdLndhcm5lZCkge1xuICAgIHZhciBtO1xuICAgIGlmICghaXNVbmRlZmluZWQodGhpcy5fbWF4TGlzdGVuZXJzKSkge1xuICAgICAgbSA9IHRoaXMuX21heExpc3RlbmVycztcbiAgICB9IGVsc2Uge1xuICAgICAgbSA9IEV2ZW50RW1pdHRlci5kZWZhdWx0TWF4TGlzdGVuZXJzO1xuICAgIH1cblxuICAgIGlmIChtICYmIG0gPiAwICYmIHRoaXMuX2V2ZW50c1t0eXBlXS5sZW5ndGggPiBtKSB7XG4gICAgICB0aGlzLl9ldmVudHNbdHlwZV0ud2FybmVkID0gdHJ1ZTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJyhub2RlKSB3YXJuaW5nOiBwb3NzaWJsZSBFdmVudEVtaXR0ZXIgbWVtb3J5ICcgK1xuICAgICAgICAgICAgICAgICAgICAnbGVhayBkZXRlY3RlZC4gJWQgbGlzdGVuZXJzIGFkZGVkLiAnICtcbiAgICAgICAgICAgICAgICAgICAgJ1VzZSBlbWl0dGVyLnNldE1heExpc3RlbmVycygpIHRvIGluY3JlYXNlIGxpbWl0LicsXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2V2ZW50c1t0eXBlXS5sZW5ndGgpO1xuICAgICAgaWYgKHR5cGVvZiBjb25zb2xlLnRyYWNlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIG5vdCBzdXBwb3J0ZWQgaW4gSUUgMTBcbiAgICAgICAgY29uc29sZS50cmFjZSgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbiA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUuYWRkTGlzdGVuZXI7XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub25jZSA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIGlmICghaXNGdW5jdGlvbihsaXN0ZW5lcikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCdsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcblxuICB2YXIgZmlyZWQgPSBmYWxzZTtcblxuICBmdW5jdGlvbiBnKCkge1xuICAgIHRoaXMucmVtb3ZlTGlzdGVuZXIodHlwZSwgZyk7XG5cbiAgICBpZiAoIWZpcmVkKSB7XG4gICAgICBmaXJlZCA9IHRydWU7XG4gICAgICBsaXN0ZW5lci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cbiAgfVxuXG4gIGcubGlzdGVuZXIgPSBsaXN0ZW5lcjtcbiAgdGhpcy5vbih0eXBlLCBnKTtcblxuICByZXR1cm4gdGhpcztcbn07XG5cbi8vIGVtaXRzIGEgJ3JlbW92ZUxpc3RlbmVyJyBldmVudCBpZmYgdGhlIGxpc3RlbmVyIHdhcyByZW1vdmVkXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUxpc3RlbmVyID0gZnVuY3Rpb24odHlwZSwgbGlzdGVuZXIpIHtcbiAgdmFyIGxpc3QsIHBvc2l0aW9uLCBsZW5ndGgsIGk7XG5cbiAgaWYgKCFpc0Z1bmN0aW9uKGxpc3RlbmVyKSlcbiAgICB0aHJvdyBUeXBlRXJyb3IoJ2xpc3RlbmVyIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzIHx8ICF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0dXJuIHRoaXM7XG5cbiAgbGlzdCA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcbiAgbGVuZ3RoID0gbGlzdC5sZW5ndGg7XG4gIHBvc2l0aW9uID0gLTE7XG5cbiAgaWYgKGxpc3QgPT09IGxpc3RlbmVyIHx8XG4gICAgICAoaXNGdW5jdGlvbihsaXN0Lmxpc3RlbmVyKSAmJiBsaXN0Lmxpc3RlbmVyID09PSBsaXN0ZW5lcikpIHtcbiAgICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuICAgIGlmICh0aGlzLl9ldmVudHMucmVtb3ZlTGlzdGVuZXIpXG4gICAgICB0aGlzLmVtaXQoJ3JlbW92ZUxpc3RlbmVyJywgdHlwZSwgbGlzdGVuZXIpO1xuXG4gIH0gZWxzZSBpZiAoaXNPYmplY3QobGlzdCkpIHtcbiAgICBmb3IgKGkgPSBsZW5ndGg7IGktLSA+IDA7KSB7XG4gICAgICBpZiAobGlzdFtpXSA9PT0gbGlzdGVuZXIgfHxcbiAgICAgICAgICAobGlzdFtpXS5saXN0ZW5lciAmJiBsaXN0W2ldLmxpc3RlbmVyID09PSBsaXN0ZW5lcikpIHtcbiAgICAgICAgcG9zaXRpb24gPSBpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAocG9zaXRpb24gPCAwKVxuICAgICAgcmV0dXJuIHRoaXM7XG5cbiAgICBpZiAobGlzdC5sZW5ndGggPT09IDEpIHtcbiAgICAgIGxpc3QubGVuZ3RoID0gMDtcbiAgICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIGxpc3Quc3BsaWNlKHBvc2l0aW9uLCAxKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5fZXZlbnRzLnJlbW92ZUxpc3RlbmVyKVxuICAgICAgdGhpcy5lbWl0KCdyZW1vdmVMaXN0ZW5lcicsIHR5cGUsIGxpc3RlbmVyKTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVBbGxMaXN0ZW5lcnMgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciBrZXksIGxpc3RlbmVycztcblxuICBpZiAoIXRoaXMuX2V2ZW50cylcbiAgICByZXR1cm4gdGhpcztcblxuICAvLyBub3QgbGlzdGVuaW5nIGZvciByZW1vdmVMaXN0ZW5lciwgbm8gbmVlZCB0byBlbWl0XG4gIGlmICghdGhpcy5fZXZlbnRzLnJlbW92ZUxpc3RlbmVyKSB7XG4gICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApXG4gICAgICB0aGlzLl9ldmVudHMgPSB7fTtcbiAgICBlbHNlIGlmICh0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLy8gZW1pdCByZW1vdmVMaXN0ZW5lciBmb3IgYWxsIGxpc3RlbmVycyBvbiBhbGwgZXZlbnRzXG4gIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgZm9yIChrZXkgaW4gdGhpcy5fZXZlbnRzKSB7XG4gICAgICBpZiAoa2V5ID09PSAncmVtb3ZlTGlzdGVuZXInKSBjb250aW51ZTtcbiAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKGtleSk7XG4gICAgfVxuICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCdyZW1vdmVMaXN0ZW5lcicpO1xuICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIGlmIChpc0Z1bmN0aW9uKGxpc3RlbmVycykpIHtcbiAgICB0aGlzLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVycyk7XG4gIH0gZWxzZSB7XG4gICAgLy8gTElGTyBvcmRlclxuICAgIHdoaWxlIChsaXN0ZW5lcnMubGVuZ3RoKVxuICAgICAgdGhpcy5yZW1vdmVMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lcnNbbGlzdGVuZXJzLmxlbmd0aCAtIDFdKTtcbiAgfVxuICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciByZXQ7XG4gIGlmICghdGhpcy5fZXZlbnRzIHx8ICF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0ID0gW107XG4gIGVsc2UgaWYgKGlzRnVuY3Rpb24odGhpcy5fZXZlbnRzW3R5cGVdKSlcbiAgICByZXQgPSBbdGhpcy5fZXZlbnRzW3R5cGVdXTtcbiAgZWxzZVxuICAgIHJldCA9IHRoaXMuX2V2ZW50c1t0eXBlXS5zbGljZSgpO1xuICByZXR1cm4gcmV0O1xufTtcblxuRXZlbnRFbWl0dGVyLmxpc3RlbmVyQ291bnQgPSBmdW5jdGlvbihlbWl0dGVyLCB0eXBlKSB7XG4gIHZhciByZXQ7XG4gIGlmICghZW1pdHRlci5fZXZlbnRzIHx8ICFlbWl0dGVyLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0ID0gMDtcbiAgZWxzZSBpZiAoaXNGdW5jdGlvbihlbWl0dGVyLl9ldmVudHNbdHlwZV0pKVxuICAgIHJldCA9IDE7XG4gIGVsc2VcbiAgICByZXQgPSBlbWl0dGVyLl9ldmVudHNbdHlwZV0ubGVuZ3RoO1xuICByZXR1cm4gcmV0O1xufTtcblxuZnVuY3Rpb24gaXNGdW5jdGlvbihhcmcpIHtcbiAgcmV0dXJuIHR5cGVvZiBhcmcgPT09ICdmdW5jdGlvbic7XG59XG5cbmZ1bmN0aW9uIGlzTnVtYmVyKGFyZykge1xuICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ251bWJlcic7XG59XG5cbmZ1bmN0aW9uIGlzT2JqZWN0KGFyZykge1xuICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ29iamVjdCcgJiYgYXJnICE9PSBudWxsO1xufVxuXG5mdW5jdGlvbiBpc1VuZGVmaW5lZChhcmcpIHtcbiAgcmV0dXJuIGFyZyA9PT0gdm9pZCAwO1xufVxuIl19
diff --git a/libs/rayo.js b/libs/rayo.js
deleted file mode 100644
index 3298093f8..000000000
--- a/libs/rayo.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/* jshint -W117 */
-Strophe.addConnectionPlugin('rayo',
- {
- RAYO_XMLNS: 'urn:xmpp:rayo:1',
- connection: null,
- init: function (conn)
- {
- this.connection = conn;
- if (this.connection.disco)
- {
- this.connection.disco.addFeature('urn:xmpp:rayo:client:1');
- }
-
- this.connection.addHandler(
- this.onRayo.bind(this), this.RAYO_XMLNS, 'iq', 'set', null, null);
- },
- onRayo: function (iq)
- {
- console.info("Rayo IQ", iq);
- },
- dial: function (to, from, roomName, roomPass)
- {
- var self = this;
- var req = $iq(
- {
- type: 'set',
- to: focusMucJid
- }
- );
- req.c('dial',
- {
- xmlns: this.RAYO_XMLNS,
- to: to,
- from: from
- });
- req.c('header',
- {
- name: 'JvbRoomName',
- value: roomName
- }).up();
-
- if (roomPass !== null && roomPass.length) {
-
- req.c('header',
- {
- name: 'JvbRoomPassword',
- value: roomPass
- }).up();
- }
-
- this.connection.sendIQ(
- req,
- function (result)
- {
- console.info('Dial result ', result);
-
- var resource = $(result).find('ref').attr('uri');
- this.call_resource = resource.substr('xmpp:'.length);
- console.info(
- "Received call resource: " + this.call_resource);
- },
- function (error)
- {
- console.info('Dial error ', error);
- }
- );
- },
- hang_up: function ()
- {
- if (!this.call_resource)
- {
- console.warn("No call in progress");
- return;
- }
-
- var self = this;
- var req = $iq(
- {
- type: 'set',
- to: this.call_resource
- }
- );
- req.c('hangup',
- {
- xmlns: this.RAYO_XMLNS
- });
-
- this.connection.sendIQ(
- req,
- function (result)
- {
- console.info('Hangup result ', result);
- self.call_resource = null;
- },
- function (error)
- {
- console.info('Hangup error ', error);
- self.call_resource = null;
- }
- );
- }
- }
-);
\ No newline at end of file
diff --git a/libs/strophe/strophe.jingle.js b/libs/strophe/strophe.jingle.js
deleted file mode 100644
index cbc081798..000000000
--- a/libs/strophe/strophe.jingle.js
+++ /dev/null
@@ -1,327 +0,0 @@
-/* jshint -W117 */
-
-
-function CallIncomingJingle(sid) {
- var sess = connection.jingle.sessions[sid];
-
- // TODO: do we check activecall == null?
- activecall = sess;
-
- statistics.onConferenceCreated(sess);
- RTC.onConferenceCreated(sess);
-
- // TODO: check affiliation and/or role
- console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
- sess.usedrip = true; // not-so-naive trickle ice
- sess.sendAnswer();
- sess.accept();
-
-};
-
-Strophe.addConnectionPlugin('jingle', {
- connection: null,
- sessions: {},
- jid2session: {},
- ice_config: {iceServers: []},
- pc_constraints: {},
- media_constraints: {
- mandatory: {
- 'OfferToReceiveAudio': true,
- 'OfferToReceiveVideo': true
- }
- // MozDontOfferDataChannel: true when this is firefox
- },
- init: function (conn) {
- this.connection = conn;
- if (this.connection.disco) {
- // http://xmpp.org/extensions/xep-0167.html#support
- // http://xmpp.org/extensions/xep-0176.html#support
- this.connection.disco.addFeature('urn:xmpp:jingle:1');
- this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
- this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
- this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
- this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');
-
-
- // this is dealt with by SDP O/A so we don't need to annouce this
- //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
- //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
- if (config.useRtcpMux) {
- this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
- }
- if (config.useBundle) {
- this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
- }
- //this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
- }
- this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
- },
- onJingle: function (iq) {
- var sid = $(iq).find('jingle').attr('sid');
- var action = $(iq).find('jingle').attr('action');
- var fromJid = iq.getAttribute('from');
- // send ack first
- var ack = $iq({type: 'result',
- to: fromJid,
- id: iq.getAttribute('id')
- });
- console.log('on jingle ' + action + ' from ' + fromJid, iq);
- var sess = this.sessions[sid];
- if ('session-initiate' != action) {
- if (sess === null) {
- ack.type = 'error';
- ack.c('error', {type: 'cancel'})
- .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
- .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
- this.connection.send(ack);
- return true;
- }
- // compare from to sess.peerjid (bare jid comparison for later compat with message-mode)
- // local jid is not checked
- if (Strophe.getBareJidFromJid(fromJid) != Strophe.getBareJidFromJid(sess.peerjid)) {
- console.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
- ack.type = 'error';
- ack.c('error', {type: 'cancel'})
- .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
- .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
- this.connection.send(ack);
- return true;
- }
- } else if (sess !== undefined) {
- // existing session with same session id
- // this might be out-of-order if the sess.peerjid is the same as from
- ack.type = 'error';
- ack.c('error', {type: 'cancel'})
- .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
- console.warn('duplicate session id', sid);
- this.connection.send(ack);
- return true;
- }
- // FIXME: check for a defined action
- this.connection.send(ack);
- // see http://xmpp.org/extensions/xep-0166.html#concepts-session
- switch (action) {
- case 'session-initiate':
- sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
- // configure session
-
- sess.media_constraints = this.media_constraints;
- sess.pc_constraints = this.pc_constraints;
- sess.ice_config = this.ice_config;
-
- sess.initiate(fromJid, false);
- // FIXME: setRemoteDescription should only be done when this call is to be accepted
- sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
-
- this.sessions[sess.sid] = sess;
- this.jid2session[sess.peerjid] = sess;
-
- // the callback should either
- // .sendAnswer and .accept
- // or .sendTerminate -- not necessarily synchronus
- CallIncomingJingle(sess.sid);
- break;
- case 'session-accept':
- sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
- sess.accept();
- $(document).trigger('callaccepted.jingle', [sess.sid]);
- break;
- case 'session-terminate':
- // If this is not the focus sending the terminate, we have
- // nothing more to do here.
- if (Object.keys(this.sessions).length < 1
- || !(this.sessions[Object.keys(this.sessions)[0]]
- instanceof JingleSession))
- {
- break;
- }
- console.log('terminating...', sess.sid);
- sess.terminate();
- this.terminate(sess.sid);
- if ($(iq).find('>jingle>reason').length) {
- $(document).trigger('callterminated.jingle', [
- sess.sid,
- sess.peerjid,
- $(iq).find('>jingle>reason>:first')[0].tagName,
- $(iq).find('>jingle>reason>text').text()
- ]);
- } else {
- $(document).trigger('callterminated.jingle',
- [sess.sid, sess.peerjid]);
- }
- break;
- case 'transport-info':
- sess.addIceCandidate($(iq).find('>jingle>content'));
- break;
- case 'session-info':
- var affected;
- if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
- $(document).trigger('ringing.jingle', [sess.sid]);
- } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
- affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
- $(document).trigger('mute.jingle', [sess.sid, affected]);
- } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
- affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
- $(document).trigger('unmute.jingle', [sess.sid, affected]);
- }
- break;
- case 'addsource': // FIXME: proprietary, un-jingleish
- case 'source-add': // FIXME: proprietary
- sess.addSource($(iq).find('>jingle>content'), fromJid);
- break;
- case 'removesource': // FIXME: proprietary, un-jingleish
- case 'source-remove': // FIXME: proprietary
- sess.removeSource($(iq).find('>jingle>content'), fromJid);
- break;
- default:
- console.warn('jingle action not implemented', action);
- break;
- }
- return true;
- },
- initiate: function (peerjid, myjid) { // initiate a new jinglesession to peerjid
- var sess = new JingleSession(myjid || this.connection.jid,
- Math.random().toString(36).substr(2, 12), // random string
- this.connection);
- // configure session
-
- sess.media_constraints = this.media_constraints;
- sess.pc_constraints = this.pc_constraints;
- sess.ice_config = this.ice_config;
-
- sess.initiate(peerjid, true);
- this.sessions[sess.sid] = sess;
- this.jid2session[sess.peerjid] = sess;
- sess.sendOffer();
- return sess;
- },
- terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
- if (sid === null || sid === undefined) {
- for (sid in this.sessions) {
- if (this.sessions[sid].state != 'ended') {
- this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
- this.sessions[sid].terminate();
- }
- delete this.jid2session[this.sessions[sid].peerjid];
- delete this.sessions[sid];
- }
- } else if (this.sessions.hasOwnProperty(sid)) {
- if (this.sessions[sid].state != 'ended') {
- this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
- this.sessions[sid].terminate();
- }
- delete this.jid2session[this.sessions[sid].peerjid];
- delete this.sessions[sid];
- }
- },
- // Used to terminate a session when an unavailable presence is received.
- terminateByJid: function (jid) {
- if (this.jid2session.hasOwnProperty(jid)) {
- var sess = this.jid2session[jid];
- if (sess) {
- sess.terminate();
- console.log('peer went away silently', jid);
- delete this.sessions[sess.sid];
- delete this.jid2session[jid];
- $(document).trigger('callterminated.jingle',
- [sess.sid, jid], 'gone');
- }
- }
- },
- terminateRemoteByJid: function (jid, reason) {
- if (this.jid2session.hasOwnProperty(jid)) {
- var sess = this.jid2session[jid];
- if (sess) {
- sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
- sess.terminate();
- console.log('terminate peer with jid', sess.sid, jid);
- delete this.sessions[sess.sid];
- delete this.jid2session[jid];
- $(document).trigger('callterminated.jingle',
- [sess.sid, jid, 'kicked']);
- }
- }
- },
- getStunAndTurnCredentials: function () {
- // get stun and turn configuration from server via xep-0215
- // uses time-limited credentials as described in
- // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
- //
- // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
- // for a prosody module which implements this
- //
- // currently, this doesn't work with updateIce and therefore credentials with a long
- // validity have to be fetched before creating the peerconnection
- // TODO: implement refresh via updateIce as described in
- // https://code.google.com/p/webrtc/issues/detail?id=1650
- var self = this;
- this.connection.sendIQ(
- $iq({type: 'get', to: this.connection.domain})
- .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
- function (res) {
- var iceservers = [];
- $(res).find('>services>service').each(function (idx, el) {
- el = $(el);
- var dict = {};
- var type = el.attr('type');
- switch (type) {
- case 'stun':
- dict.url = 'stun:' + el.attr('host');
- if (el.attr('port')) {
- dict.url += ':' + el.attr('port');
- }
- iceservers.push(dict);
- break;
- case 'turn':
- case 'turns':
- dict.url = type + ':';
- if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
- if (navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10) < 28) {
- dict.url += el.attr('username') + '@';
- } else {
- dict.username = el.attr('username'); // only works in M28
- }
- }
- dict.url += el.attr('host');
- if (el.attr('port') && el.attr('port') != '3478') {
- dict.url += ':' + el.attr('port');
- }
- if (el.attr('transport') && el.attr('transport') != 'udp') {
- dict.url += '?transport=' + el.attr('transport');
- }
- if (el.attr('password')) {
- dict.credential = el.attr('password');
- }
- iceservers.push(dict);
- break;
- }
- });
- self.ice_config.iceServers = iceservers;
- },
- function (err) {
- console.warn('getting turn credentials failed', err);
- console.warn('is mod_turncredentials or similar installed?');
- }
- );
- // implement push?
- },
-
- /**
- * Populates the log data
- */
- populateData: function () {
- var data = {};
- Object.keys(this.sessions).forEach(function (sid) {
- var session = this.sessions[sid];
- if (session.peerconnection && session.peerconnection.updateLog) {
- // FIXME: should probably be a .dump call
- data["jingle_" + session.sid] = {
- updateLog: session.peerconnection.updateLog,
- stats: session.peerconnection.stats,
- url: window.location.href
- };
- }
- });
- return data;
- }
-});
diff --git a/libs/strophe/strophe.util.js b/libs/strophe/strophe.util.js
deleted file mode 100644
index 126ecf633..000000000
--- a/libs/strophe/strophe.util.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Strophe logger implementation. Logs from level WARN and above.
- */
-Strophe.log = function (level, msg) {
- switch(level) {
- case Strophe.LogLevel.WARN:
- console.warn("Strophe: "+msg);
- break;
- case Strophe.LogLevel.ERROR:
- case Strophe.LogLevel.FATAL:
- console.error("Strophe: "+msg);
- break;
- }
-};
-
-Strophe.getStatusString = function(status)
-{
- switch (status)
- {
- case Strophe.Status.ERROR:
- return "ERROR";
- case Strophe.Status.CONNECTING:
- return "CONNECTING";
- case Strophe.Status.CONNFAIL:
- return "CONNFAIL";
- case Strophe.Status.AUTHENTICATING:
- return "AUTHENTICATING";
- case Strophe.Status.AUTHFAIL:
- return "AUTHFAIL";
- case Strophe.Status.CONNECTED:
- return "CONNECTED";
- case Strophe.Status.DISCONNECTED:
- return "DISCONNECTED";
- case Strophe.Status.DISCONNECTING:
- return "DISCONNECTING";
- case Strophe.Status.ATTACHED:
- return "ATTACHED";
- default:
- return "unknown";
- }
-};
diff --git a/moderatemuc.js b/moderatemuc.js
deleted file mode 100644
index e64821dd8..000000000
--- a/moderatemuc.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* global $, $iq, config, connection, focusMucJid, forceMuted,
- setAudioMuted, Strophe, toggleAudio */
-/**
- * Moderate connection plugin.
- */
-Strophe.addConnectionPlugin('moderate', {
- connection: null,
- init: function (conn) {
- this.connection = conn;
-
- this.connection.addHandler(this.onMute.bind(this),
- 'http://jitsi.org/jitmeet/audio',
- 'iq',
- 'set',
- null,
- null);
- },
- setMute: function (jid, mute) {
- console.info("set mute", mute);
- var iqToFocus = $iq({to: focusMucJid, type: 'set'})
- .c('mute', {
- xmlns: 'http://jitsi.org/jitmeet/audio',
- jid: jid
- })
- .t(mute.toString())
- .up();
-
- this.connection.sendIQ(
- iqToFocus,
- function (result) {
- console.log('set mute', result);
- },
- function (error) {
- console.log('set mute error', error);
- });
- },
- onMute: function (iq) {
- var from = iq.getAttribute('from');
- if (from !== focusMucJid) {
- console.warn("Ignored mute from non focus peer");
- return false;
- }
- var mute = $(iq).find('mute');
- if (mute.length) {
- var doMuteAudio = mute.text() === "true";
- setAudioMuted(doMuteAudio);
- forceMuted = doMuteAudio;
- }
- return true;
- },
- eject: function (jid) {
- // We're not the focus, so can't terminate
- //connection.jingle.terminateRemoteByJid(jid, 'kick');
- connection.emuc.kick(jid);
- }
-});
\ No newline at end of file
diff --git a/modules/API/API.js b/modules/API/API.js
index 934841629..b00a77e50 100644
--- a/modules/API/API.js
+++ b/modules/API/API.js
@@ -18,8 +18,8 @@
var commands =
{
displayName: UI.inputDisplayNameHandler,
- muteAudio: toggleAudio,
- muteVideo: toggleVideo,
+ muteAudio: UI.toggleAudio,
+ muteVideo: UI.toggleVideo,
toggleFilmStrip: UI.toggleFilmStrip,
toggleChat: UI.toggleChat,
toggleContactList: UI.toggleContactList
diff --git a/modules/RTC/DataChannels.js b/modules/RTC/DataChannels.js
index bf981af30..b9e74e994 100644
--- a/modules/RTC/DataChannels.js
+++ b/modules/RTC/DataChannels.js
@@ -1,4 +1,4 @@
-/* global connection, Strophe, updateLargeVideo, focusedVideoSrc*/
+/* global Strophe, updateLargeVideo, focusedVideoSrc*/
// cache datachannels to avoid garbage collection
// https://code.google.com/p/chromium/issues/detail?id=405545
@@ -91,7 +91,7 @@ var DataChannels =
newValue = new Boolean(newValue).valueOf();
}
}
- $(document).trigger('inlastnchanged', [oldValue, newValue]);
+ UI.onLastNChanged(oldValue, newValue);
}
else if ("LastNEndpointsChangeEvent" === colibriClass)
{
diff --git a/modules/RTC/RTC.js b/modules/RTC/RTC.js
index c65ef0b69..9b269c8a4 100644
--- a/modules/RTC/RTC.js
+++ b/modules/RTC/RTC.js
@@ -58,7 +58,7 @@ var RTC = {
createRemoteStream: function (data, sid, thessrc) {
var remoteStream = new MediaStream(data, sid, thessrc, eventEmitter,
this.getBrowserType());
- var jid = data.peerjid || connection.emuc.myroomjid;
+ var jid = data.peerjid || xmpp.myJid();
if(!this.remoteStreams[jid]) {
this.remoteStreams[jid] = {};
}
@@ -144,16 +144,7 @@ var RTC = {
RTC.localVideo = this.createLocalStream(stream, type, true);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
- if (activecall) {
- // FIXME: will block switchInProgress on true value in case of exception
- activecall.switchStreams(stream, oldStream, callback);
- } else {
- // We are done immediately
- console.error("No conference handler");
- UI.messageHandler.showError('Error',
- 'Unable to switch video stream.');
- callback();
- }
+ xmpp.switchStreams(stream, oldStream,callback);
}
};
diff --git a/modules/UI/UI.js b/modules/UI/UI.js
index 5ef0deb48..0fa850941 100644
--- a/modules/UI/UI.js
+++ b/modules/UI/UI.js
@@ -17,9 +17,11 @@ var PanelToggler = require("./side_pannels/SidePanelToggler");
var RoomNameGenerator = require("./welcome_page/RoomnameGenerator");
UI.messageHandler = require("./util/MessageHandler");
var messageHandler = UI.messageHandler;
+var Authentication = require("./authentication/Authentication");
+var UIUtil = require("./util/UIUtil");
//var eventEmitter = new EventEmitter();
-
+var roomName = null;
function setupPrezi()
@@ -39,7 +41,7 @@ function setupChat()
}
function setupToolbars() {
- Toolbar.init();
+ Toolbar.init(UI);
Toolbar.setupButtonsFromConfig();
BottomToolbar.init();
}
@@ -62,6 +64,16 @@ function streamHandler(stream) {
}
}
+function onDisposeConference(unload) {
+ Toolbar.showAuthenticateButton(false);
+};
+
+function onDisplayNameChanged(jid, displayName) {
+ ContactList.onDisplayNameChange(jid, displayName);
+ SettingsMenu.onDisplayNameChange(jid, displayName);
+ VideoLayout.onDisplayNameChanged(jid, displayName);
+}
+
function registerListeners() {
RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
@@ -70,14 +82,7 @@ function registerListeners() {
VideoLayout.onRemoteStreamAdded(stream);
}, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
- // Listen for large video size updates
- document.getElementById('largeVideo')
- .addEventListener('loadedmetadata', function (e) {
- currentVideoWidth = this.videoWidth;
- currentVideoHeight = this.videoHeight;
- VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
- });
-
+ VideoLayout.init();
statistics.addAudioLevelListener(function(jid, audioLevel)
{
@@ -104,8 +109,38 @@ function registerListeners() {
desktopsharing.addListener(
Toolbar.changeDesktopSharingButtonState,
DesktopSharingEventTypes.SWITCHING_DONE);
+ xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
+ xmpp.addListener(XMPPEvents.KICKED, function () {
+ messageHandler.openMessageDialog("Session Terminated",
+ "Ouch! You have been kicked out of the meet!");
+ });
+ xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () {
+ messageHandler.showError("Error",
+ "Jitsi Videobridge is currently unavailable. Please try again later!");
+ });
+ xmpp.addListener(XMPPEvents.USER_ID_CHANGED, Avatar.setUserAvatar);
+ xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
+ for(stream in changedStreams)
+ {
+ // might need to update the direction if participant just went from sendrecv to recvonly
+ if (stream.type === 'video' || stream.type === 'screen') {
+ var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>video');
+ switch (stream.direction) {
+ case 'sendrecv':
+ el.show();
+ break;
+ case 'recvonly':
+ el.hide();
+ // FIXME: Check if we have to change large video
+ //VideoLayout.updateLargeVideo(el);
+ break;
+ }
+ }
+ }
-
+ });
+ xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged);
+ xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined);
}
function bindEvents()
@@ -117,10 +152,6 @@ function bindEvents()
function () {
VideoLayout.resizeLargeVideoContainer();
VideoLayout.positionLarge();
- isFullScreen = document.fullScreen ||
- document.mozFullScreen ||
- document.webkitIsFullScreen;
-
}
);
@@ -255,11 +286,6 @@ UI.start = function () {
};
-
-UI.setUserAvatar = function (jid, id) {
- Avatar.setUserAvatar(jid, id);
-};
-
UI.toggleSmileys = function () {
Chat.toggleSmileys();
};
@@ -278,7 +304,7 @@ UI.updateChatConversation = function (from, displayName, message) {
return Chat.updateChatConversation(from, displayName, message);
};
-UI.onMucJoined = function (jid, info) {
+function onMucJoined(jid, info) {
Toolbar.updateRoomUrl(window.location.href);
document.getElementById('localNick').appendChild(
document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)')
@@ -293,15 +319,14 @@ UI.onMucJoined = function (jid, info) {
// Show authenticate button if needed
Toolbar.showAuthenticateButton(
- Moderator.isExternalAuthEnabled() && !Moderator.isModerator());
+ xmpp.isExternalAuthEnabled() && !xmpp.isModerator());
var displayName = !config.displayJids
? info.displayName : Strophe.getResourceFromJid(jid);
if (displayName)
- $(document).trigger('displaynamechanged',
- ['localVideoContainer', displayName + ' (me)']);
-};
+ onDisplayNameChanged('localVideoContainer', displayName + ' (me)');
+}
UI.initEtherpad = function (name) {
Etherpad.init(name);
@@ -357,23 +382,19 @@ UI.toggleContactList = function () {
UI.onLocalRoleChange = function (jid, info, pres) {
console.info("My role changed, new role: " + info.role);
- var isModerator = Moderator.isModerator();
+ var isModerator = xmpp.isModerator();
VideoLayout.showModeratorIndicator();
Toolbar.showAuthenticateButton(
- Moderator.isExternalAuthEnabled() && !isModerator);
+ xmpp.isExternalAuthEnabled() && !isModerator);
if (isModerator) {
- Toolbar.closeAuthenticationWindow();
+ Authentication.closeAuthenticationWindow();
messageHandler.notify(
'Me', 'connected', 'Moderator rights granted !');
}
};
-UI.onDisposeConference = function (unload) {
- Toolbar.showAuthenticateButton(false);
-};
-
UI.onModeratorStatusChanged = function (isModerator) {
Toolbar.showSipCallButton(isModerator);
@@ -414,40 +435,11 @@ UI.onPasswordReqiured = function (callback) {
);
};
-UI.onAuthenticationRequired = function () {
- // This is the loop that will wait for the room to be created by
- // someone else. 'auth_required.moderator' will bring us back here.
- authRetryId = window.setTimeout(
- function () {
- Moderator.allocateConferenceFocus(roomName, doJoinAfterFocus);
- }, 5000);
- // Show prompt only if it's not open
- if (authDialog !== null) {
- return;
- }
- // extract room name from 'room@muc.server.net'
- var room = roomName.substr(0, roomName.indexOf('@'));
-
- authDialog = messageHandler.openDialog(
- 'Stop',
- 'Authentication is required to create room: ' + room +
- ' You can either authenticate to create the room or ' +
- 'just wait for someone else to do so.',
- true,
- {
- Authenticate: 'authNow'
- },
- function (onSubmitEvent, submitValue) {
-
- // Do not close the dialog yet
- onSubmitEvent.preventDefault();
-
- // Open login popup
- if (submitValue === 'authNow') {
- Toolbar.authenticateClicked();
- }
- }
- );
+UI.onAuthenticationRequired = function (intervalCallback) {
+ Authentication.openAuthenticationDialog(
+ roomName, intervalCallback, function () {
+ Toolbar.authenticateClicked();
+ });
};
UI.setRecordingButtonState = function (state) {
@@ -511,6 +503,8 @@ UI.showLocalAudioIndicator = function (mute) {
};
UI.generateRoomName = function() {
+ if(roomName)
+ return roomName;
var roomnode = null;
var path = window.location.pathname;
@@ -540,6 +534,7 @@ UI.generateRoomName = function() {
}
roomName = roomnode + '@' + config.hosts.muc;
+ return roomName;
};
@@ -556,25 +551,146 @@ UI.dockToolbar = function (isDock) {
return ToolbarToggler.dockToolbar(isDock);
};
+UI.getCreadentials = function () {
+ return {
+ bosh: document.getElementById('boshURL').value,
+ password: document.getElementById('password').value,
+ jid: document.getElementById('jid').value
+ };
+};
+
+UI.disableConnect = function () {
+ document.getElementById('connect').disabled = true;
+};
+
+UI.showLoginPopup = function(callback)
+{
+ console.log('password is required');
+
+ UI.messageHandler.openTwoButtonDialog(null,
+ '
Password required
' +
+ '' +
+ '',
+ true,
+ "Ok",
+ function (e, v, m, f) {
+ if (v) {
+ var username = document.getElementById('passwordrequired.username');
+ var password = document.getElementById('passwordrequired.password');
+
+ if (username.value !== null && password.value != null) {
+ callback(username.value, password.value);
+ }
+ }
+ },
+ function (event) {
+ document.getElementById('passwordrequired.username').focus();
+ }
+ );
+}
+
+UI.checkForNicknameAndJoin = function () {
+
+ Authentication.closeAuthenticationDialog();
+ Authentication.stopInterval();
+
+ var nick = null;
+ if (config.useNicks) {
+ nick = window.prompt('Your nickname (optional)');
+ }
+ xmpp.joinRooom(roomName, config.useNicks, nick);
+}
+
+
function dump(elem, filename) {
elem = elem.parentNode;
elem.download = filename || 'meetlog.json';
elem.href = 'data:application/json;charset=utf-8,\n';
- var data = {};
- if (connection.jingle) {
- data = connection.jingle.populateData();
- }
+ var data = xmpp.populateData();
var metadata = {};
metadata.time = new Date();
metadata.url = window.location.href;
metadata.ua = navigator.userAgent;
- if (connection.logger) {
- metadata.xmpp = connection.logger.log;
+ var log = xmpp.getLogger();
+ if (log) {
+ metadata.xmpp = log;
}
data.metadata = metadata;
elem.href += encodeURIComponent(JSON.stringify(data, null, ' '));
return false;
}
+UI.getRoomName = function () {
+ return roomName;
+}
+
+/**
+ * Mutes/unmutes the local video.
+ *
+ * @param mute true to mute the local video; otherwise, false
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision taken by the application logic)
+ */
+function setVideoMute(mute, options) {
+ xmpp.setVideoMute(
+ mute,
+ function (mute) {
+ var video = $('#video');
+ var communicativeClass = "icon-camera";
+ var muteClass = "icon-camera icon-camera-disabled";
+
+ if (mute) {
+ video.removeClass(communicativeClass);
+ video.addClass(muteClass);
+ } else {
+ video.removeClass(muteClass);
+ video.addClass(communicativeClass);
+ }
+ },
+ options);
+}
+
+/**
+ * Mutes/unmutes the local video.
+ */
+UI.toggleVideo = function () {
+ UIUtil.buttonClick("#video", "icon-camera icon-camera-disabled");
+
+ setVideoMute(!RTC.localVideo.isMuted());
+};
+
+/**
+ * Mutes / unmutes audio for the local participant.
+ */
+UI.toggleAudio = function() {
+ UI.setAudioMuted(!RTC.localAudio.isMuted());
+};
+
+/**
+ * Sets muted audio state for the local participant.
+ */
+UI.setAudioMuted = function (mute) {
+
+ if(!xmpp.setAudioMute(mute, function () {
+ UI.showLocalAudioIndicator(mute);
+
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ }))
+ {
+ // We still click the button.
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ return;
+ }
+
+}
+
+UI.onLastNChanged = function (oldValue, newValue) {
+ if (config.muteLocalVideoIfNotInLastN) {
+ setVideoMute(!newValue, { 'byUser': false });
+ }
+}
+
module.exports = UI;
diff --git a/modules/UI/audio_levels/AudioLevels.js b/modules/UI/audio_levels/AudioLevels.js
index 350c353f0..115c54510 100644
--- a/modules/UI/audio_levels/AudioLevels.js
+++ b/modules/UI/audio_levels/AudioLevels.js
@@ -87,10 +87,10 @@ var AudioLevels = (function(my) {
drawContext.drawImage(canvasCache, 0, 0);
if(resourceJid === AudioLevels.LOCAL_LEVEL) {
- if(!connection.emuc.myroomjid) {
+ if(!xmpp.myJid()) {
return;
}
- resourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ resourceJid = xmpp.myResource();
}
if(resourceJid === largeVideoResourceJid) {
@@ -221,8 +221,8 @@ var AudioLevels = (function(my) {
function getVideoSpanId(resourceJid) {
var videoSpanId = null;
if (resourceJid === AudioLevels.LOCAL_LEVEL
- || (connection.emuc.myroomjid && resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid)))
+ || (xmpp.myResource() && resourceJid
+ === xmpp.myResource()))
videoSpanId = 'localVideoContainer';
else
videoSpanId = 'participant_' + resourceJid;
diff --git a/modules/UI/authentication/Authentication.js b/modules/UI/authentication/Authentication.js
new file mode 100644
index 000000000..1a568d5ce
--- /dev/null
+++ b/modules/UI/authentication/Authentication.js
@@ -0,0 +1,84 @@
+/* Initial "authentication required" dialog */
+var authDialog = null;
+/* Loop retry ID that wits for other user to create the room */
+var authRetryId = null;
+var authenticationWindow = null;
+
+var Authentication = {
+ openAuthenticationDialog: function (roomName, intervalCallback, callback) {
+ // This is the loop that will wait for the room to be created by
+ // someone else. 'auth_required.moderator' will bring us back here.
+ authRetryId = window.setTimeout(intervalCallback , 5000);
+ // Show prompt only if it's not open
+ if (authDialog !== null) {
+ return;
+ }
+ // extract room name from 'room@muc.server.net'
+ var room = roomName.substr(0, roomName.indexOf('@'));
+
+ authDialog = messageHandler.openDialog(
+ 'Stop',
+ 'Authentication is required to create room: ' + room +
+ ' You can either authenticate to create the room or ' +
+ 'just wait for someone else to do so.',
+ true,
+ {
+ Authenticate: 'authNow'
+ },
+ function (onSubmitEvent, submitValue) {
+
+ // Do not close the dialog yet
+ onSubmitEvent.preventDefault();
+
+ // Open login popup
+ if (submitValue === 'authNow') {
+ callback();
+ }
+ }
+ );
+ },
+ closeAuthenticationWindow:function () {
+ if (authenticationWindow) {
+ authenticationWindow.close();
+ authenticationWindow = null;
+ }
+ },
+ focusAuthenticationWindow: function () {
+ // If auth window exists just bring it to the front
+ if (authenticationWindow) {
+ authenticationWindow.focus();
+ return;
+ }
+ },
+ closeAuthenticationDialog: function () {
+ // Close authentication dialog if opened
+ if (authDialog) {
+ UI.messageHandler.closeDialog();
+ authDialog = null;
+ }
+ },
+ createAuthenticationWindow: function (callback, url) {
+ authenticationWindow = messageHandler.openCenteredPopup(
+ url, 910, 660,
+ // On closed
+ function () {
+ // Close authentication dialog if opened
+ if (authDialog) {
+ messageHandler.closeDialog();
+ authDialog = null;
+ }
+ callback();
+ authenticationWindow = null;
+ });
+ return authenticationWindow;
+ },
+ stopInterval: function () {
+ // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice
+ if (authRetryId) {
+ window.clearTimeout(authRetryId);
+ authRetryId = null;
+ }
+ }
+};
+
+module.exports = Authentication;
\ No newline at end of file
diff --git a/modules/UI/avatar/Avatar.js b/modules/UI/avatar/Avatar.js
index 60825ef7e..0e18477cd 100644
--- a/modules/UI/avatar/Avatar.js
+++ b/modules/UI/avatar/Avatar.js
@@ -12,7 +12,7 @@ function setVisibility(selector, show) {
function isUserMuted(jid) {
// XXX(gp) we may want to rename this method to something like
// isUserStreaming, for example.
- if (jid && jid != connection.emuc.myroomjid) {
+ if (jid && jid != xmpp.myJid()) {
var resource = Strophe.getResourceFromJid(jid);
if (!require("../videolayout/VideoLayout").isInLastN(resource)) {
return true;
@@ -26,7 +26,7 @@ function isUserMuted(jid) {
}
function getGravatarUrl(id, size) {
- if(id === connection.emuc.myroomjid || !id) {
+ if(id === xmpp.myJid() || !id) {
id = Settings.getSettings().uid;
}
return 'https://www.gravatar.com/avatar/' +
@@ -57,7 +57,7 @@ var Avatar = {
// set the avatar in the settings menu if it is local user and get the
// local video container
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
$('#avatar').get(0).src = thumbUrl;
thumbnail = $('#localVideoContainer');
}
@@ -100,7 +100,7 @@ var Avatar = {
var video = $('#participant_' + resourceJid + '>video');
var avatar = $('#avatar_' + resourceJid);
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
video = $('#localVideoWrapper>video');
}
if (show === undefined || show === null) {
@@ -130,7 +130,7 @@ var Avatar = {
*/
updateActiveSpeakerAvatarSrc: function (jid) {
if (!jid) {
- jid = connection.emuc.findJidFromResource(
+ jid = xmpp.findJidFromResource(
require("../videolayout/VideoLayout").getLargeVideoState().userResourceJid);
}
var avatar = $("#activeSpeakerAvatar")[0];
diff --git a/modules/UI/etherpad/Etherpad.js b/modules/UI/etherpad/Etherpad.js
index 8fc4a25f2..6dcfab458 100644
--- a/modules/UI/etherpad/Etherpad.js
+++ b/modules/UI/etherpad/Etherpad.js
@@ -1,4 +1,4 @@
-/* global $, config, connection, dockToolbar, Moderator,
+/* global $, config, dockToolbar,
setLargeVideoVisible, Util */
var VideoLayout = require("../videolayout/VideoLayout");
@@ -30,8 +30,7 @@ function resize() {
* Shares the Etherpad name with other participants.
*/
function shareEtherpad() {
- connection.emuc.addEtherpadToPresence(etherpadName);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("etherpad", etherpadName);
}
/**
diff --git a/modules/UI/prezi/Prezi.js b/modules/UI/prezi/Prezi.js
index 03885c1c4..7ceb70c2e 100644
--- a/modules/UI/prezi/Prezi.js
+++ b/modules/UI/prezi/Prezi.js
@@ -30,7 +30,7 @@ var Prezi = {
* to load.
*/
openPreziDialog: function() {
- var myprezi = connection.emuc.getPrezi(connection.emuc.myroomjid);
+ var myprezi = xmpp.getPrezi();
if (myprezi) {
messageHandler.openTwoButtonDialog("Remove Prezi",
"Are you sure you would like to remove your Prezi?",
@@ -38,8 +38,7 @@ var Prezi = {
"Remove",
function(e,v,m,f) {
if(v) {
- connection.emuc.removePreziFromPresence();
- connection.emuc.sendPresence();
+ xmpp.removePreziFromPresence();
}
}
);
@@ -91,9 +90,7 @@ var Prezi = {
return false;
}
else {
- connection.emuc
- .addPreziToPresence(urlValue, 0);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("prezi", urlValue);
$.prompt.close();
}
}
@@ -151,7 +148,7 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
VideoLayout.resizeThumbnails();
var controlsEnabled = false;
- if (jid === connection.emuc.myroomjid)
+ if (jid === xmpp.myJid())
controlsEnabled = true;
setPresentationVisible(true);
@@ -191,15 +188,14 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
console.log("prezi status", event.value);
if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
- if (jid != connection.emuc.myroomjid)
+ if (jid != xmpp.myJid())
preziPlayer.flyToStep(currentSlide);
}
});
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
console.log("event value", event.value);
- connection.emuc.addCurrentSlideToPresence(event.value);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("preziSlide", event.value);
});
$("#" + elementId).css( 'background-image',
diff --git a/modules/UI/side_pannels/SidePanelToggler.js b/modules/UI/side_pannels/SidePanelToggler.js
index 44bbf042d..938e869d4 100644
--- a/modules/UI/side_pannels/SidePanelToggler.js
+++ b/modules/UI/side_pannels/SidePanelToggler.js
@@ -4,6 +4,7 @@ var Settings = require("./settings/Settings");
var SettingsMenu = require("./settings/SettingsMenu");
var VideoLayout = require("../videolayout/VideoLayout");
var ToolbarToggler = require("../toolbars/ToolbarToggler");
+var UIUtil = require("../util/UIUtil");
/**
* Toggler for the chat, contact list, settings menu, etc..
@@ -110,7 +111,7 @@ var PanelToggler = (function(my) {
* @param onClose function to be called if the window is going to be closed
*/
var toggle = function(object, selector, onOpenComplete, onOpen, onClose) {
- buttonClick(buttons[selector], "active");
+ UIUtil.buttonClick(buttons[selector], "active");
if (object.isVisible()) {
$("#toast-container").animate({
@@ -140,7 +141,7 @@ var PanelToggler = (function(my) {
if(currentlyOpen) {
var current = $(currentlyOpen);
- buttonClick(buttons[currentlyOpen], "active");
+ UIUtil.buttonClick(buttons[currentlyOpen], "active");
current.css('z-index', 4);
setTimeout(function () {
current.css('display', 'none');
diff --git a/modules/UI/side_pannels/chat/Chat.js b/modules/UI/side_pannels/chat/Chat.js
index 09683fea0..6ce82bf30 100644
--- a/modules/UI/side_pannels/chat/Chat.js
+++ b/modules/UI/side_pannels/chat/Chat.js
@@ -1,4 +1,4 @@
-/* global $, Util, connection, nickname:true, showToolbar */
+/* global $, Util, nickname:true, showToolbar */
var Replacement = require("./Replacement");
var CommandsProcessor = require("./Commands");
var ToolbarToggler = require("../../toolbars/ToolbarToggler");
@@ -184,8 +184,7 @@ var Chat = (function (my) {
nickname = val;
window.localStorage.displayname = nickname;
- connection.emuc.addDisplayNameToPresence(nickname);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("displayName", nickname);
Chat.setChatConversationMode(true);
@@ -208,7 +207,7 @@ var Chat = (function (my) {
else
{
var message = Util.escapeHtml(value);
- connection.emuc.sendMessage(message, nickname);
+ xmpp.sendChatMessage(message, nickname);
}
}
});
@@ -234,7 +233,7 @@ var Chat = (function (my) {
my.updateChatConversation = function (from, displayName, message) {
var divClassName = '';
- if (connection.emuc.myroomjid === from) {
+ if (xmpp.myJid() === from) {
divClassName = "localuser";
}
else {
diff --git a/modules/UI/side_pannels/chat/Commands.js b/modules/UI/side_pannels/chat/Commands.js
index a9d926f83..6883268c3 100644
--- a/modules/UI/side_pannels/chat/Commands.js
+++ b/modules/UI/side_pannels/chat/Commands.js
@@ -32,7 +32,7 @@ function getCommand(message)
function processTopic(commandArguments)
{
var topic = Util.escapeHtml(commandArguments);
- connection.emuc.setSubject(topic);
+ xmpp.setSubject(topic);
}
/**
diff --git a/modules/UI/side_pannels/contactlist/ContactList.js b/modules/UI/side_pannels/contactlist/ContactList.js
index 8a1b8ec8c..b4f3feb7f 100644
--- a/modules/UI/side_pannels/contactlist/ContactList.js
+++ b/modules/UI/side_pannels/contactlist/ContactList.js
@@ -46,23 +46,6 @@ function createDisplayNameParagraph(displayName) {
}
-/**
- * Indicates that the display name has changed.
- */
-$(document).bind( 'displaynamechanged',
- function (event, peerJid, displayName) {
- if (peerJid === 'localVideoContainer')
- peerJid = connection.emuc.myroomjid;
-
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contactName = $('#contactlist #' + resourceJid + '>p');
-
- if (contactName && displayName && displayName.length > 0)
- contactName.html(displayName);
- });
-
-
function stopGlowing(glower) {
window.clearInterval(notificationInterval);
notificationInterval = false;
@@ -127,7 +110,7 @@ var ContactList = {
var clElement = contactlist.get(0);
- if (resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid)
+ if (resourceJid === xmpp.myResource()
&& $('#contactlist>ul .title')[0].nextSibling.nextSibling) {
clElement.insertBefore(newContact,
$('#contactlist>ul .title')[0].nextSibling.nextSibling);
@@ -182,6 +165,18 @@ var ContactList = {
} else {
contact.removeClass('clickable');
}
+ },
+
+ onDisplayNameChange: function (peerJid, displayName) {
+ if (peerJid === 'localVideoContainer')
+ peerJid = xmpp.myJid();
+
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactName = $('#contactlist #' + resourceJid + '>p');
+
+ if (contactName && displayName && displayName.length > 0)
+ contactName.html(displayName);
}
};
diff --git a/modules/UI/side_pannels/settings/SettingsMenu.js b/modules/UI/side_pannels/settings/SettingsMenu.js
index 82ee205f5..25fca6145 100644
--- a/modules/UI/side_pannels/settings/SettingsMenu.js
+++ b/modules/UI/side_pannels/settings/SettingsMenu.js
@@ -10,16 +10,15 @@ var SettingsMenu = {
if(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
- connection.emuc.addDisplayNameToPresence(displayName);
+ xmpp.addToPresence("displayName", displayName, true);
}
- connection.emuc.addEmailToPresence(newEmail);
+ xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(newEmail);
- connection.emuc.sendPresence();
- Avatar.setUserAvatar(connection.emuc.myroomjid, email);
+ Avatar.setUserAvatar(xmpp.myJid(), email);
},
isVisible: function() {
@@ -29,14 +28,15 @@ var SettingsMenu = {
setDisplayName: function(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
$('#setDisplayName').get(0).value = displayName;
+ },
+
+ onDisplayNameChange: function(peerJid, newDisplayName) {
+ if(peerJid === 'localVideoContainer' ||
+ peerJid === xmpp.myJid()) {
+ this.setDisplayName(newDisplayName);
+ }
}
};
-$(document).bind('displaynamechanged', function(event, peerJid, newDisplayName) {
- if(peerJid === 'localVideoContainer' ||
- peerJid === connection.emuc.myroomjid) {
- SettingsMenu.setDisplayName(newDisplayName);
- }
-});
module.exports = SettingsMenu;
\ No newline at end of file
diff --git a/modules/UI/toolbars/Toolbar.js b/modules/UI/toolbars/Toolbar.js
index 87597ed1b..55ba8949f 100644
--- a/modules/UI/toolbars/Toolbar.js
+++ b/modules/UI/toolbars/Toolbar.js
@@ -1,22 +1,24 @@
-/* global $, buttonClick, config, lockRoom, Moderator, roomName,
- setSharedKey, sharedKey, Util */
+/* global $, buttonClick, config, lockRoom,
+ setSharedKey, Util */
var messageHandler = require("../util/MessageHandler");
var BottomToolbar = require("./BottomToolbar");
var Prezi = require("../prezi/Prezi");
var Etherpad = require("../etherpad/Etherpad");
var PanelToggler = require("../side_pannels/SidePanelToggler");
+var Authentication = require("../authentication/Authentication");
+var UIUtil = require("../util/UIUtil");
var roomUrl = null;
var sharedKey = '';
-var authenticationWindow = null;
+var UI = null;
var buttonHandlers =
{
"toolbar_button_mute": function () {
- return toggleAudio();
+ return UI.toggleAudio();
},
"toolbar_button_camera": function () {
- return toggleVideo();
+ return UI.toggleVideo();
},
"toolbar_button_authentication": function () {
return Toolbar.authenticateClicked();
@@ -44,7 +46,7 @@ var buttonHandlers =
},
"toolbar_button_fullScreen": function()
{
- buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
+ UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
return Toolbar.toggleFullScreen();
},
"toolbar_button_sip": function () {
@@ -59,9 +61,7 @@ var buttonHandlers =
};
function hangup() {
- disposeConference();
- sessionTerminated = true;
- connection.emuc.doLeave();
+ xmpp.disposeConference();
if(config.enableWelcomePage)
{
setTimeout(function()
@@ -90,7 +90,29 @@ function hangup() {
*/
function toggleRecording() {
- Recording.toggleRecording();
+ xmpp.toggleRecording(function (callback) {
+ UI.messageHandler.openTwoButtonDialog(null,
+ '
Enter recording token
' +
+ '',
+ false,
+ "Save",
+ function (e, v, m, f) {
+ if (v) {
+ var token = document.getElementById('recordingToken');
+
+ if (token.value) {
+ callback(Util.escapeHtml(token.value));
+ }
+ }
+ },
+ function (event) {
+ document.getElementById('recordingToken').focus();
+ },
+ function () {
+ }
+ );
+ }, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
}
/**
@@ -101,7 +123,7 @@ function lockRoom(lock) {
if (lock)
currentSharedKey = sharedKey;
- connection.emuc.lockRoom(currentSharedKey, function (res) {
+ xmpp.lockRoom(currentSharedKey, function (res) {
// password is required
if (sharedKey)
{
@@ -183,9 +205,8 @@ function callSipButtonClicked()
if (v) {
var numberInput = document.getElementById('sipNumber');
if (numberInput.value) {
- connection.rayo.dial(
- numberInput.value, 'fromnumber',
- roomName, sharedKey);
+ xmpp.dial(numberInput.value, 'fromnumber',
+ UI.getRoomName(), sharedKey);
}
}
},
@@ -197,9 +218,10 @@ function callSipButtonClicked()
var Toolbar = (function (my) {
- my.init = function () {
+ my.init = function (ui) {
for(var k in buttonHandlers)
$("#" + k).click(buttonHandlers[k]);
+ UI = ui;
}
/**
@@ -210,35 +232,15 @@ var Toolbar = (function (my) {
sharedKey = sKey;
};
- my.closeAuthenticationWindow = function () {
- if (authenticationWindow) {
- authenticationWindow.close();
- authenticationWindow = null;
- }
- }
-
my.authenticateClicked = function () {
- // If auth window exists just bring it to the front
- if (authenticationWindow) {
- authenticationWindow.focus();
- return;
- }
+ Authentication.focusAuthenticationWindow();
// Get authentication URL
- Moderator.getAuthUrl(function (url) {
+ xmpp.getAuthUrl(UI.getRoomName(), function (url) {
// Open popup with authentication URL
- authenticationWindow = messageHandler.openCenteredPopup(
- url, 910, 660,
- // On closed
- function () {
- // Close authentication dialog if opened
- if (authDialog) {
- messageHandler.closeDialog();
- authDialog = null;
- }
- // On popup closed - retry room allocation
- Moderator.allocateConferenceFocus(roomName, doJoinAfterFocus);
- authenticationWindow = null;
- });
+ var authenticationWindow = Authentication.createAuthenticationWindow(function () {
+ // On popup closed - retry room allocation
+ xmpp.allocateConferenceFocus(UI.getRoomName(), UI.checkForNicknameAndJoin);
+ }, url);
if (!authenticationWindow) {
Toolbar.showAuthenticateButton(true);
messageHandler.openMessageDialog(
@@ -279,7 +281,7 @@ var Toolbar = (function (my) {
*/
my.openLockDialog = function () {
// Only the focus is able to set a shared key.
- if (!Moderator.isModerator()) {
+ if (!xmpp.isModerator()) {
if (sharedKey) {
messageHandler.openMessageDialog(null,
"This conversation is currently protected by" +
@@ -436,14 +438,14 @@ var Toolbar = (function (my) {
*/
my.unlockLockButton = function () {
if ($("#lockIcon").hasClass("icon-security-locked"))
- buttonClick("#lockIcon", "icon-security icon-security-locked");
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
* Updates the lock button state to locked.
*/
my.lockLockButton = function () {
if ($("#lockIcon").hasClass("icon-security"))
- buttonClick("#lockIcon", "icon-security icon-security-locked");
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
@@ -486,7 +488,7 @@ var Toolbar = (function (my) {
// Shows or hides SIP calls button
my.showSipCallButton = function (show) {
- if (Moderator.isSipGatewayEnabled() && show) {
+ if (xmpp.isSipGatewayEnabled() && show) {
$('#sipCallButton').css({display: "inline"});
} else {
$('#sipCallButton').css({display: "none"});
diff --git a/modules/UI/toolbars/ToolbarToggler.js b/modules/UI/toolbars/ToolbarToggler.js
index 6a5655702..f64664684 100644
--- a/modules/UI/toolbars/ToolbarToggler.js
+++ b/modules/UI/toolbars/ToolbarToggler.js
@@ -67,7 +67,7 @@ var ToolbarToggler = {
toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
}
- if (Moderator.isModerator())
+ if (xmpp.isModerator())
{
// TODO: Enable settings functionality.
// Need to uncomment the settings button in index.html.
diff --git a/modules/UI/util/UIUtil.js b/modules/UI/util/UIUtil.js
index fa0f4f87c..efb8a2b7b 100644
--- a/modules/UI/util/UIUtil.js
+++ b/modules/UI/util/UIUtil.js
@@ -11,6 +11,13 @@ module.exports = {
= PanelToggler.isVisible() ? PanelToggler.getPanelSize()[0] : 0;
return window.innerWidth - rightPanelWidth;
+ },
+ /**
+ * Changes the style class of the element given by id.
+ */
+ buttonClick: function(id, classname) {
+ $(id).toggleClass(classname); // add the class to the clicked element
}
+
};
\ No newline at end of file
diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js
index 6061055a6..024755dd4 100644
--- a/modules/UI/videolayout/VideoLayout.js
+++ b/modules/UI/videolayout/VideoLayout.js
@@ -16,8 +16,99 @@ var largeVideoState = {
newSrc: ''
};
+/**
+ * Indicates if we have muted our audio before the conference has started.
+ * @type {boolean}
+ */
+var preMuted = false;
+
+var mutedAudios = {};
+
+var flipXLocalVideo = true;
+var currentVideoWidth = null;
+var currentVideoHeight = null;
+
+var localVideoSrc = null;
+
var defaultLocalDisplayName = "Me";
+function videoactive( videoelem) {
+ if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
+ // ignore mixedmslabela0 and v0
+
+ videoelem.show();
+ VideoLayout.resizeThumbnails();
+
+ var videoParent = videoelem.parent();
+ var parentResourceJid = null;
+ if (videoParent)
+ parentResourceJid
+ = VideoLayout.getPeerContainerResourceJid(videoParent[0]);
+
+ // Update the large video to the last added video only if there's no
+ // current dominant, focused speaker or prezi playing or update it to
+ // the current dominant speaker.
+ if ((!focusedVideoInfo &&
+ !VideoLayout.getDominantSpeakerResourceJid() &&
+ !require("../prezi/Prezi").isPresentationVisible()) ||
+ (parentResourceJid &&
+ VideoLayout.getDominantSpeakerResourceJid() === parentResourceJid)) {
+ VideoLayout.updateLargeVideo(
+ RTC.getVideoSrc(videoelem[0]),
+ 1,
+ parentResourceJid);
+ }
+
+ VideoLayout.showModeratorIndicator();
+ }
+}
+
+function waitForRemoteVideo(selector, ssrc, stream, jid) {
+ // XXX(gp) so, every call to this function is *always* preceded by a call
+ // to the RTC.attachMediaStream() function but that call is *not* followed
+ // by an update to the videoSrcToSsrc map!
+ //
+ // The above way of doing things results in video SRCs that don't correspond
+ // to any SSRC for a short period of time (to be more precise, for as long
+ // the waitForRemoteVideo takes to complete). This causes problems (see
+ // bellow).
+ //
+ // I'm wondering why we need to do that; i.e. why call RTC.attachMediaStream()
+ // a second time in here and only then update the videoSrcToSsrc map? Why
+ // not simply update the videoSrcToSsrc map when the RTC.attachMediaStream()
+ // is called the first time? I actually do that in the lastN changed event
+ // handler because the "orphan" video SRC is causing troubles there. The
+ // purpose of this method would then be to fire the "videoactive.jingle".
+ //
+ // Food for though I guess :-)
+
+ if (selector.removed || !selector.parent().is(":visible")) {
+ console.warn("Media removed before had started", selector);
+ return;
+ }
+
+ if (stream.id === 'mixedmslabel') return;
+
+ if (selector[0].currentTime > 0) {
+ var videoStream = simulcast.getReceivingVideoStream(stream);
+ RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
+
+ // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
+ // in order to get rid of too many maps
+ if (ssrc && jid) {
+ jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
+ } else {
+ console.warn("No ssrc given for jid", jid);
+ }
+
+ videoactive(selector);
+ } else {
+ setTimeout(function () {
+ waitForRemoteVideo(selector, ssrc, stream, jid);
+ }, 250);
+ }
+}
+
/**
* Returns an array of the video horizontal and vertical indents,
* so that if fits its parent.
@@ -194,7 +285,7 @@ function getParticipantContainer(resourceJid)
if (!resourceJid)
return null;
- if (resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ if (resourceJid === xmpp.myResource())
return $("#localVideoContainer");
else
return $("#participant_" + resourceJid);
@@ -270,7 +361,8 @@ function addRemoteVideoMenu(jid, parentElement) {
event.preventDefault();
}
var isMute = mutedAudios[jid] == true;
- connection.moderate.setMute(jid, !isMute);
+ xmpp.setMute(jid, !isMute);
+
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
@@ -292,7 +384,7 @@ function addRemoteVideoMenu(jid, parentElement) {
var ejectLinkItem = document.createElement('a');
ejectLinkItem.innerHTML = ejectIndicator + ' Kick out';
ejectLinkItem.onclick = function(){
- connection.moderate.eject(jid);
+ xmpp.eject(jid);
popupmenuElement.setAttribute('style', 'display:none;');
};
@@ -400,6 +492,43 @@ function createModeratorIndicatorElement(parentElement) {
}
+/**
+ * Checks if video identified by given src is desktop stream.
+ * @param videoSrc eg.
+ * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
+ * @returns {boolean}
+ */
+function isVideoSrcDesktop(jid) {
+ // FIXME: fix this mapping mess...
+ // figure out if large video is desktop stream or just a camera
+
+ if(!jid)
+ return false;
+ var isDesktop = false;
+ if (xmpp.myJid() &&
+ xmpp.myResource() === jid) {
+ // local video
+ isDesktop = desktopsharing.isUsingScreenStream();
+ } else {
+ // Do we have associations...
+ var videoSsrc = jid2Ssrc[jid];
+ if (videoSsrc) {
+ var videoType = ssrc2videoType[videoSsrc];
+ if (videoType) {
+ // Finally there...
+ isDesktop = videoType === 'screen';
+ } else {
+ console.error("No video type for ssrc: " + videoSsrc);
+ }
+ } else {
+ console.error("No ssrc for jid: " + jid);
+ }
+ }
+ return isDesktop;
+}
+
+
+
var VideoLayout = (function (my) {
my.connectionIndicators = {};
@@ -407,6 +536,16 @@ var VideoLayout = (function (my) {
my.getVideoSize = getCameraVideoSize;
my.getVideoPosition = getCameraVideoPosition;
+ my.init = function () {
+ // Listen for large video size updates
+ document.getElementById('largeVideo')
+ .addEventListener('loadedmetadata', function (e) {
+ currentVideoWidth = this.videoWidth;
+ currentVideoHeight = this.videoHeight;
+ VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
+ });
+ };
+
my.isInLastN = function(resource) {
return lastNCount < 0 // lastN is disabled, return true
|| (lastNCount > 0 && lastNEndpointsCache.length == 0) // lastNEndpoints cache not built yet, return true
@@ -422,7 +561,10 @@ var VideoLayout = (function (my) {
document.getElementById('localAudio').autoplay = true;
document.getElementById('localAudio').volume = 0;
if (preMuted) {
- setAudioMuted(true);
+ if(!UI.setAudioMuted(true))
+ {
+ preMuted = mute;
+ }
preMuted = false;
}
};
@@ -459,14 +601,14 @@ var VideoLayout = (function (my) {
VideoLayout.handleVideoThumbClicked(
RTC.getVideoSrc(localVideo),
false,
- Strophe.getResourceFromJid(connection.emuc.myroomjid));
+ xmpp.myResource());
});
$('#localVideoContainer').click(function (event) {
event.stopPropagation();
VideoLayout.handleVideoThumbClicked(
RTC.getVideoSrc(localVideo),
false,
- Strophe.getResourceFromJid(connection.emuc.myroomjid));
+ xmpp.myResource());
});
// Add hover handler
@@ -496,11 +638,8 @@ var VideoLayout = (function (my) {
localVideoSrc = RTC.getVideoSrc(localVideo);
- var myResourceJid = null;
- if(connection.emuc.myroomjid)
- {
- myResourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
- }
+ var myResourceJid = xmpp.myResource();
+
VideoLayout.updateLargeVideo(localVideoSrc, 0,
myResourceJid);
@@ -539,7 +678,7 @@ var VideoLayout = (function (my) {
{
if(container.id == "localVideoWrapper")
{
- jid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ jid = xmpp.myResource();
}
else
{
@@ -617,9 +756,9 @@ var VideoLayout = (function (my) {
largeVideoState.isVisible = $('#largeVideo').is(':visible');
largeVideoState.isDesktop = isVideoSrcDesktop(resourceJid);
if(jid2Ssrc[largeVideoState.userResourceJid] ||
- (connection && connection.emuc.myroomjid &&
+ (xmpp.myResource() &&
largeVideoState.userResourceJid ===
- Strophe.getResourceFromJid(connection.emuc.myroomjid))) {
+ xmpp.myResource())) {
largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
} else {
largeVideoState.oldResourceJid = null;
@@ -643,7 +782,7 @@ var VideoLayout = (function (my) {
var doUpdate = function () {
Avatar.updateActiveSpeakerAvatarSrc(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.userResourceJid));
if (!userChanged && largeVideoState.preload &&
@@ -723,7 +862,7 @@ var VideoLayout = (function (my) {
if(userChanged) {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.oldResourceJid));
}
@@ -738,7 +877,7 @@ var VideoLayout = (function (my) {
}
} else {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(
+ xmpp.findJidFromResource(
largeVideoState.userResourceJid));
}
@@ -877,7 +1016,7 @@ var VideoLayout = (function (my) {
focusedVideoInfo = null;
if(focusResourceJid) {
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(focusResourceJid));
+ xmpp.findJidFromResource(focusResourceJid));
}
}
}
@@ -949,7 +1088,7 @@ var VideoLayout = (function (my) {
// If the peerJid is null then this video span couldn't be directly
// associated with a participant (this could happen in the case of prezi).
- if (Moderator.isModerator() && peerJid !== null)
+ if (xmpp.isModerator() && peerJid !== null)
addRemoteVideoMenu(peerJid, container);
remotes.appendChild(container);
@@ -1134,13 +1273,13 @@ var VideoLayout = (function (my) {
if (state == 'show')
{
// peerContainer.css('-webkit-filter', '');
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
Avatar.showUserAvatar(jid, false);
}
else // if (state == 'avatar')
{
// peerContainer.css('-webkit-filter', 'grayscale(100%)');
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
Avatar.showUserAvatar(jid, true);
}
}
@@ -1166,8 +1305,7 @@ var VideoLayout = (function (my) {
if (name && nickname !== name) {
nickname = name;
window.localStorage.displayname = nickname;
- connection.emuc.addDisplayNameToPresence(nickname);
- connection.emuc.sendPresence();
+ xmpp.addToPresence("displayName", nickname);
Chat.setChatConversationMode(true);
}
@@ -1238,7 +1376,7 @@ var VideoLayout = (function (my) {
*/
my.showModeratorIndicator = function () {
- var isModerator = Moderator.isModerator();
+ var isModerator = xmpp.isModerator();
if (isModerator) {
var indicatorSpan = $('#localVideoContainer .focusindicator');
@@ -1247,7 +1385,10 @@ var VideoLayout = (function (my) {
createModeratorIndicatorElement(indicatorSpan[0]);
}
}
- Object.keys(connection.emuc.members).forEach(function (jid) {
+
+ var members = xmpp.getMembers();
+
+ Object.keys(members).forEach(function (jid) {
if (Strophe.getResourceFromJid(jid) === 'focus') {
// Skip server side focus
@@ -1263,7 +1404,7 @@ var VideoLayout = (function (my) {
return;
}
- var member = connection.emuc.members[jid];
+ var member = members[jid];
if (member.role === 'moderator') {
// Remove menu if peer is moderator
@@ -1435,7 +1576,7 @@ var VideoLayout = (function (my) {
var videoSpanId = null;
var videoContainerId = null;
if (resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid)) {
+ === xmpp.myResource()) {
videoSpanId = 'localVideoWrapper';
videoContainerId = 'localVideoContainer';
}
@@ -1478,7 +1619,7 @@ var VideoLayout = (function (my) {
}
Avatar.showUserAvatar(
- connection.emuc.findJidFromResource(resourceJid));
+ xmpp.findJidFromResource(resourceJid));
}
};
@@ -1603,7 +1744,7 @@ var VideoLayout = (function (my) {
lastNPickupJid = jid;
$(document).trigger("pinnedendpointchanged", [jid]);
}
- } else if (jid == connection.emuc.myroomjid) {
+ } else if (jid == xmpp.myJid()) {
$("#localVideoContainer").click();
}
}
@@ -1615,13 +1756,13 @@ var VideoLayout = (function (my) {
$(document).bind('audiomuted.muc', function (event, jid, isMuted) {
/*
// FIXME: but focus can not mute in this case ? - check
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
// The local mute indicator is controlled locally
return;
}*/
var videoSpanId = null;
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
@@ -1630,7 +1771,7 @@ var VideoLayout = (function (my) {
mutedAudios[jid] = isMuted;
- if (Moderator.isModerator()) {
+ if (xmpp.isModerator()) {
VideoLayout.updateRemoteVideoMenu(jid, isMuted);
}
@@ -1648,7 +1789,7 @@ var VideoLayout = (function (my) {
Avatar.showUserAvatar(jid, isMuted);
var videoSpanId = null;
- if (jid === connection.emuc.myroomjid) {
+ if (jid === xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
@@ -1662,11 +1803,11 @@ var VideoLayout = (function (my) {
/**
* Display name changed.
*/
- $(document).bind('displaynamechanged',
- function (event, jid, displayName, status) {
+ my.onDisplayNameChanged =
+ function (jid, displayName, status) {
var name = null;
if (jid === 'localVideoContainer'
- || jid === connection.emuc.myroomjid) {
+ || jid === xmpp.myJid()) {
name = nickname;
setDisplayName('localVideoContainer',
displayName);
@@ -1680,10 +1821,10 @@ var VideoLayout = (function (my) {
}
if(jid === 'localVideoContainer')
- jid = connection.emuc.myroomjid;
+ jid = xmpp.myJid();
if(!name || name != displayName)
API.triggerEvent("displayNameChange",{jid: jid, displayname: displayName});
- });
+ };
/**
* On dominant speaker changed event.
@@ -1691,7 +1832,7 @@ var VideoLayout = (function (my) {
$(document).bind('dominantspeakerchanged', function (event, resourceJid) {
// We ignore local user events.
if (resourceJid
- === Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ === xmpp.myResource())
return;
// Update the current dominant speaker.
@@ -1822,7 +1963,7 @@ var VideoLayout = (function (my) {
if (!isVisible) {
console.log("Add to last N", resourceJid);
- var jid = connection.emuc.findJidFromResource(resourceJid);
+ var jid = xmpp.findJidFromResource(resourceJid);
var mediaStream = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
var sel = $('#participant_' + resourceJid + '>video');
@@ -1855,7 +1996,7 @@ var VideoLayout = (function (my) {
var resource, container, src;
var myResource
- = Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ = xmpp.myResource();
// Find out which endpoint to show in the large video.
for (var i = 0; i < lastNEndpoints.length; i++) {
@@ -1879,37 +2020,6 @@ var VideoLayout = (function (my) {
}
});
- $(document).bind('videoactive.jingle', function (event, videoelem) {
- if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
- // ignore mixedmslabela0 and v0
-
- videoelem.show();
- VideoLayout.resizeThumbnails();
-
- var videoParent = videoelem.parent();
- var parentResourceJid = null;
- if (videoParent)
- parentResourceJid
- = VideoLayout.getPeerContainerResourceJid(videoParent[0]);
-
- // Update the large video to the last added video only if there's no
- // current dominant, focused speaker or prezi playing or update it to
- // the current dominant speaker.
- if ((!focusedVideoInfo &&
- !VideoLayout.getDominantSpeakerResourceJid() &&
- !require("../prezi/Prezi").isPresentationVisible()) ||
- (parentResourceJid &&
- VideoLayout.getDominantSpeakerResourceJid() === parentResourceJid)) {
- VideoLayout.updateLargeVideo(
- RTC.getVideoSrc(videoelem[0]),
- 1,
- parentResourceJid);
- }
-
- VideoLayout.showModeratorIndicator();
- }
- });
-
$(document).bind('simulcastlayerschanging', function (event, endpointSimulcastLayers) {
endpointSimulcastLayers.forEach(function (esl) {
@@ -1930,13 +2040,13 @@ var VideoLayout = (function (my) {
// Get session and stream from primary ssrc.
var res = simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
- var session = res.session;
+ var sid = res.sid;
var electedStream = res.stream;
- if (session && electedStream) {
+ if (sid && electedStream) {
var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
- console.info([esl, primarySSRC, msid, session, electedStream]);
+ console.info([esl, primarySSRC, msid, sid, electedStream]);
var msidParts = msid.split(' ');
@@ -1956,7 +2066,7 @@ var VideoLayout = (function (my) {
}
} else {
- console.error('Could not find a stream or a session.', session, electedStream);
+ console.error('Could not find a stream or a session.', sid, electedStream);
}
});
});
@@ -1988,17 +2098,17 @@ var VideoLayout = (function (my) {
// Get session and stream from primary ssrc.
var res = simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
- var session = res.session;
+ var sid = res.sid;
var electedStream = res.stream;
- if (session && electedStream) {
+ if (sid && electedStream) {
var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
console.info('Switching simulcast substream.');
- console.info([esl, primarySSRC, msid, session, electedStream]);
+ console.info([esl, primarySSRC, msid, sid, electedStream]);
var msidParts = msid.split(' ');
- var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
+ var selRemoteVideo = $(['#', 'remoteVideo_', sid, '_', msidParts[0]].join(''));
var updateLargeVideo = (Strophe.getResourceFromJid(ssrc2jid[primarySSRC])
== largeVideoState.userResourceJid);
@@ -2035,7 +2145,7 @@ var VideoLayout = (function (my) {
}
var videoId;
- if(resource == Strophe.getResourceFromJid(connection.emuc.myroomjid))
+ if(resource == xmpp.myResource())
{
videoId = "localVideoContainer";
}
@@ -2048,7 +2158,7 @@ var VideoLayout = (function (my) {
connectionIndicator.updatePopoverData();
} else {
- console.error('Could not find a stream or a session.', session, electedStream);
+ console.error('Could not find a stream or a sid.', sid, electedStream);
}
});
});
@@ -2063,8 +2173,8 @@ var VideoLayout = (function (my) {
if(object.resolution !== null)
{
resolution = object.resolution;
- object.resolution = resolution[connection.emuc.myroomjid];
- delete resolution[connection.emuc.myroomjid];
+ object.resolution = resolution[xmpp.myJid()];
+ delete resolution[xmpp.myJid()];
}
updateStatsIndicator("localVideoContainer", percent, object);
for(var jid in resolution)
diff --git a/modules/connectionquality/connectionquality.js b/modules/connectionquality/connectionquality.js
index 4a668a002..7652caec6 100644
--- a/modules/connectionquality/connectionquality.js
+++ b/modules/connectionquality/connectionquality.js
@@ -29,8 +29,7 @@ function startSendingStats() {
* Sends statistics to other participants
*/
function sendStats() {
- connection.emuc.addConnectionInfoToPresence(convertToMUCStats(stats));
- connection.emuc.sendPresence();
+ xmpp.addToPresence("connectionQuality", convertToMUCStats(stats));
}
/**
diff --git a/modules/desktopsharing/desktopsharing.js b/modules/desktopsharing/desktopsharing.js
index ebc943ae8..42b633f9d 100644
--- a/modules/desktopsharing/desktopsharing.js
+++ b/modules/desktopsharing/desktopsharing.js
@@ -1,4 +1,4 @@
-/* global $, alert, changeLocalVideo, chrome, config, connection, getConferenceHandler, getUserMediaWithConstraints */
+/* global $, alert, changeLocalVideo, chrome, config, getConferenceHandler, getUserMediaWithConstraints */
/**
* Indicates that desktop stream is currently in use(for toggle purpose).
* @type {boolean}
diff --git a/modules/simulcast/SimulcastReceiver.js b/modules/simulcast/SimulcastReceiver.js
index 863c5a826..c019a299f 100644
--- a/modules/simulcast/SimulcastReceiver.js
+++ b/modules/simulcast/SimulcastReceiver.js
@@ -159,43 +159,19 @@ SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
// If we haven't receiving a "changed" event yet, then we must be receiving
// low quality (that the sender always streams).
- if (!ssrc && connection.jingle) {
- var session;
- var i, j, k;
-
- var keys = Object.keys(connection.jingle.sessions);
- for (i = 0; i < keys.length; i++) {
- var sid = keys[i];
-
- if (ssrc) {
- // stream found, stop.
- break;
- }
-
- session = connection.jingle.sessions[sid];
- if (session.remoteStreams) {
- for (j = 0; j < session.remoteStreams.length; j++) {
- var remoteStream = session.remoteStreams[j];
-
- if (ssrc) {
- // stream found, stop.
- break;
- }
- var tracks = remoteStream.getVideoTracks();
- if (tracks) {
- for (k = 0; k < tracks.length; k++) {
- var track = tracks[k];
- var msid = [remoteStream.id, track.id].join(' ');
- var _ssrc = this._remoteMaps.msid2ssrc[msid];
- var _jid = ssrc2jid[_ssrc];
- var quality = this._remoteMaps.msid2Quality[msid];
- if (jid == _jid && quality == 0) {
- ssrc = _ssrc;
- // stream found, stop.
- break;
- }
- }
- }
+ if(!ssrc)
+ {
+ var remoteStreamObject = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
+ var remoteStream = remoteStreamObject.getOriginalStream();
+ var tracks = remoteStream.getVideoTracks();
+ if (tracks) {
+ for (var k = 0; k < tracks.length; k++) {
+ var track = tracks[k];
+ var msid = [remoteStream.id, track.id].join(' ');
+ var _ssrc = this._remoteMaps.msid2ssrc[msid];
+ var quality = this._remoteMaps.msid2Quality[msid];
+ if (quality == 0) {
+ ssrc = _ssrc;
}
}
}
@@ -206,47 +182,32 @@ SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
{
- var session, electedStream;
+ var sid, electedStream;
var i, j, k;
- if (connection.jingle) {
- var keys = Object.keys(connection.jingle.sessions);
- for (i = 0; i < keys.length; i++) {
- var sid = keys[i];
-
- if (electedStream) {
- // stream found, stop.
- break;
- }
-
- session = connection.jingle.sessions[sid];
- if (session.remoteStreams) {
- for (j = 0; j < session.remoteStreams.length; j++) {
- var remoteStream = session.remoteStreams[j];
-
- if (electedStream) {
- // stream found, stop.
- break;
- }
- var tracks = remoteStream.getVideoTracks();
- if (tracks) {
- for (k = 0; k < tracks.length; k++) {
- var track = tracks[k];
- var msid = [remoteStream.id, track.id].join(' ');
- var tmp = this._remoteMaps.msid2ssrc[msid];
- if (tmp == ssrc) {
- electedStream = new webkitMediaStream([track]);
- // stream found, stop.
- break;
- }
- }
- }
+ var jid = ssrc2jid[ssrc];
+ if(jid)
+ {
+ var remoteStreamObject = RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
+ var remoteStream = remoteStreamObject.getOriginalStream();
+ var tracks = remoteStream.getVideoTracks();
+ if (tracks) {
+ for (k = 0; k < tracks.length; k++) {
+ var track = tracks[k];
+ var msid = [remoteStream.id, track.id].join(' ');
+ var tmp = this._remoteMaps.msid2ssrc[msid];
+ if (tmp == ssrc) {
+ electedStream = new webkitMediaStream([track]);
+ sid = remoteStreamObject.sid;
+ // stream found, stop.
+ break;
}
}
}
+
}
return {
- session: session,
+ sid: sid,
stream: electedStream
};
};
diff --git a/modules/statistics/RTPStatsCollector.js b/modules/statistics/RTPStatsCollector.js
index 3b070dda1..c7901a23b 100644
--- a/modules/statistics/RTPStatsCollector.js
+++ b/modules/statistics/RTPStatsCollector.js
@@ -329,30 +329,9 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
};
StatsCollector.prototype.logStats = function () {
- if (!focusMucJid) {
+
+ if(!xmpp.sendLogs(this.statsToBeLogged))
return;
- }
-
- var deflate = true;
-
- var content = JSON.stringify(this.statsToBeLogged);
- if (deflate) {
- content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
- }
- content = Base64.encode(content);
-
- // XEP-0337-ish
- var message = $msg({to: focusMucJid, type: 'normal'});
- message.c('log', { xmlns: 'urn:xmpp:eventlog',
- id: 'PeerConnectionStats'});
- message.c('message').t(content).up();
- if (deflate) {
- message.c('tag', {name: "deflated", value: "true"}).up();
- }
- message.up();
-
- connection.send(message);
-
// Reset the stats
this.statsToBeLogged.stats = {};
this.statsToBeLogged.timestamps = [];
@@ -700,7 +679,7 @@ StatsCollector.prototype.processAudioLevelReport = function ()
// but it seems to vary between 0 and around 32k.
audioLevel = audioLevel / 32767;
jidStats.setSsrcAudioLevel(ssrc, audioLevel);
- if(jid != connection.emuc.myroomjid)
+ if(jid != xmpp.myJid())
this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel);
}
diff --git a/modules/statistics/statistics.js b/modules/statistics/statistics.js
index 828c401e9..f0449cb7a 100644
--- a/modules/statistics/statistics.js
+++ b/modules/statistics/statistics.js
@@ -59,6 +59,14 @@ function onStreamCreated(stream)
localStats.start();
}
+function onDisposeConference(onUnload) {
+ stopRemote();
+ if(onUnload) {
+ stopLocal();
+ eventEmitter.removeAllListeners();
+ }
+}
+
var statistics =
{
@@ -117,19 +125,12 @@ var statistics =
startRemoteStats(event.peerconnection);
},
- onDisposeConference: function (onUnload) {
- stopRemote();
- if(onUnload) {
- stopLocal();
- eventEmitter.removeAllListeners();
- }
- },
-
start: function () {
this.addConnectionStatsListener(connectionquality.updateLocalStats);
this.addRemoteStatsStopListener(connectionquality.stopSendingStats);
RTC.addStreamListener(onStreamCreated,
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
+ xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
}
};
diff --git a/libs/strophe/strophe.jingle.session.js b/modules/xmpp/JingleSession.js
similarity index 83%
rename from libs/strophe/strophe.jingle.session.js
rename to modules/xmpp/JingleSession.js
index 2517520e6..8ff3d0efa 100644
--- a/libs/strophe/strophe.jingle.session.js
+++ b/modules/xmpp/JingleSession.js
@@ -1,6 +1,11 @@
/* jshint -W117 */
+var TraceablePeerConnection = require("./TraceablePeerConnection");
+var SDPDiffer = require("./SDPDiffer");
+var SDPUtil = require("./SDPUtil");
+var SDP = require("./SDP");
+
// Jingle stuff
-function JingleSession(me, sid, connection) {
+function JingleSession(me, sid, connection, service) {
this.me = me;
this.sid = sid;
this.connection = connection;
@@ -12,13 +17,13 @@ function JingleSession(me, sid, connection) {
this.localSDP = null;
this.remoteSDP = null;
this.relayedStreams = [];
- this.remoteStreams = [];
this.startTime = null;
this.stopTime = null;
this.media_constraints = null;
this.pc_constraints = null;
this.ice_config = {};
this.drip_container = [];
+ this.service = service;
this.usetrickle = true;
this.usepranswer = false; // early transport warmup -- mind you, this might fail. depends on webrtc issue 1718
@@ -73,16 +78,11 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
self.sendIceCandidate(event.candidate);
};
this.peerconnection.onaddstream = function (event) {
- self.remoteStreams.push(event.stream);
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
- $(document).trigger('remotestreamadded.jingle', [event, self.sid]);
+ self.remoteStreamAdded(event);
};
this.peerconnection.onremovestream = function (event) {
// Remove the stream from remoteStreams
- var streamIdx = self.remoteStreams.indexOf(event.stream);
- if(streamIdx !== -1){
- self.remoteStreams.splice(streamIdx, 1);
- }
// FIXME: remotestreamremoved.jingle not defined anywhere(unused)
$(document).trigger('remotestreamremoved.jingle', [event, self.sid]);
};
@@ -99,7 +99,7 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
this.stopTime = new Date();
break;
}
- $(document).trigger('iceconnectionstatechange.jingle', [self.sid, self]);
+ onIceConnectionStateChange(self.sid, self);
};
// add any local and relayed stream
RTC.localStreams.forEach(function(stream) {
@@ -110,6 +110,49 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
});
};
+function onIceConnectionStateChange(sid, session) {
+ switch (session.peerconnection.iceConnectionState) {
+ case 'checking':
+ session.timeChecking = (new Date()).getTime();
+ session.firstconnect = true;
+ break;
+ case 'completed': // on caller side
+ case 'connected':
+ if (session.firstconnect) {
+ session.firstconnect = false;
+ var metadata = {};
+ metadata.setupTime
+ = (new Date()).getTime() - session.timeChecking;
+ session.peerconnection.getStats(function (res) {
+ if(res && res.result) {
+ res.result().forEach(function (report) {
+ if (report.type == 'googCandidatePair' &&
+ report.stat('googActiveConnection') == 'true') {
+ metadata.localCandidateType
+ = report.stat('googLocalCandidateType');
+ metadata.remoteCandidateType
+ = report.stat('googRemoteCandidateType');
+
+ // log pair as well so we can get nice pie
+ // charts
+ metadata.candidatePair
+ = report.stat('googLocalCandidateType') +
+ ';' +
+ report.stat('googRemoteCandidateType');
+
+ if (report.stat('googRemoteAddress').indexOf('[') === 0)
+ {
+ metadata.ipv6 = true;
+ }
+ }
+ });
+ }
+ });
+ }
+ break;
+ }
+}
+
JingleSession.prototype.accept = function () {
var self = this;
this.state = 'active';
@@ -145,12 +188,13 @@ JingleSession.prototype.accept = function () {
// FIXME: change any inactive to sendrecv or whatever they were originally
sdp = sdp.replace('a=inactive', 'a=sendrecv');
}
+ var self = this;
this.peerconnection.setLocalDescription(new RTCSessionDescription({type: 'answer', sdp: sdp}),
function () {
//console.log('setLocalDescription success');
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
+ self.setLocalDescription();
- this.connection.sendIQ(accept,
+ self.connection.sendIQ(accept,
function () {
var ack = {};
ack.source = 'answer';
@@ -347,8 +391,8 @@ JingleSession.prototype.createdOffer = function (sdp) {
action: 'session-initiate',
initiator: this.initiator,
sid: this.sid});
- this.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
- this.connection.sendIQ(init,
+ self.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
+ self.connection.sendIQ(init,
function () {
var ack = {};
ack.source = 'offer';
@@ -369,13 +413,11 @@ JingleSession.prototype.createdOffer = function (sdp) {
sdp.sdp = this.localSDP.raw;
this.peerconnection.setLocalDescription(sdp,
function () {
- if(this.usetrickle)
+ if(self.usetrickle)
{
sendJingle();
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
}
- else
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
+ self.setLocalDescription();
//console.log('setLocalDescription success');
},
function (e) {
@@ -587,7 +629,7 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
var publicLocalSDP = new SDP(publicLocalDesc.sdp);
publicLocalSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
- this.connection.sendIQ(accept,
+ self.connection.sendIQ(accept,
function () {
var ack = {};
ack.source = 'answer';
@@ -610,10 +652,8 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
//console.log('setLocalDescription success');
if (self.usetrickle && !self.usepranswer) {
sendJingle();
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
}
- else
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
+ self.setLocalDescription();
},
function (e) {
console.error('setLocalDescription failed', e);
@@ -799,7 +839,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
if (this.peerconnection.signalingState == 'closed') return;
if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){
// There is nothing to do since scheduled job might have been executed by another succeeding call
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
+ this.setLocalDescription();
if(successCallback){
successCallback();
}
@@ -889,7 +929,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
self.peerconnection.setLocalDescription(modifiedAnswer,
function() {
//console.log('modified setLocalDescription ok');
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
+ self.setLocalDescription();
if(successCallback){
successCallback();
}
@@ -1064,12 +1104,20 @@ JingleSession.prototype.setVideoMute = function (mute, callback, options) {
} else if (this.videoMuteByUser) {
return;
}
+
+ var self = this;
+ var localCallback = function (mute) {
+ self.connection.emuc.addVideoInfoToPresence(mute);
+ self.connection.emuc.sendPresence();
+ return callback(mute)
+ };
+
if (mute == RTC.localVideo.isMuted())
{
// Even if no change occurs, the specified callback is to be executed.
// The specified callback may, optionally, return a successCallback
// which is to be executed as well.
- var successCallback = callback(mute);
+ var successCallback = localCallback(mute);
if (successCallback) {
successCallback();
@@ -1079,14 +1127,14 @@ JingleSession.prototype.setVideoMute = function (mute, callback, options) {
this.hardMuteVideo(mute);
- this.modifySources(callback(mute));
+ this.modifySources(localCallback(mute));
}
};
// SDP-based mute by going recvonly/sendrecv
// FIXME: should probably black out the screen as well
JingleSession.prototype.toggleVideoMute = function (callback) {
- setVideoMute(RTC.localVideo.isMuted(), callback);
+ this.service.setVideoMute(RTC.localVideo.isMuted(), callback);
};
JingleSession.prototype.hardMuteVideo = function (muted) {
@@ -1172,8 +1220,170 @@ JingleSession.onJingleError = function (session, error)
JingleSession.onJingleFatalError = function (session, error)
{
- sessionTerminated = true;
+ this.service.sessionTerminated = true;
connection.emuc.doLeave();
UI.messageHandler.showError( "Sorry",
"Internal application error[setRemoteDescription]");
-}
\ No newline at end of file
+}
+
+JingleSession.prototype.setLocalDescription = function () {
+ // put our ssrcs into presence so other clients can identify our stream
+ var newssrcs = [];
+ var media = simulcast.parseMedia(this.peerconnection.localDescription);
+ media.forEach(function (media) {
+
+ if(Object.keys(media.sources).length > 0) {
+ // TODO(gp) maybe exclude FID streams?
+ Object.keys(media.sources).forEach(function (ssrc) {
+ newssrcs.push({
+ 'ssrc': ssrc,
+ 'type': media.type,
+ 'direction': media.direction
+ });
+ });
+ }
+ else if(this.localStreamsSSRC && this.localStreamsSSRC[media.type])
+ {
+ newssrcs.push({
+ 'ssrc': this.localStreamsSSRC[media.type],
+ 'type': media.type,
+ 'direction': media.direction
+ });
+ }
+
+ });
+
+ console.log('new ssrcs', newssrcs);
+
+ // Have to clear presence map to get rid of removed streams
+ this.connection.emuc.clearPresenceMedia();
+
+ if (newssrcs.length > 0) {
+ for (var i = 1; i <= newssrcs.length; i ++) {
+ // Change video type to screen
+ if (newssrcs[i-1].type === 'video' && desktopsharing.isUsingScreenStream()) {
+ newssrcs[i-1].type = 'screen';
+ }
+ this.connection.emuc.addMediaToPresence(i,
+ newssrcs[i-1].type, newssrcs[i-1].ssrc, newssrcs[i-1].direction);
+ }
+
+ this.connection.emuc.sendPresence();
+ }
+}
+
+// an attempt to work around https://github.com/jitsi/jitmeet/issues/32
+function sendKeyframe(pc) {
+ console.log('sendkeyframe', pc.iceConnectionState);
+ if (pc.iceConnectionState !== 'connected') return; // safe...
+ pc.setRemoteDescription(
+ pc.remoteDescription,
+ function () {
+ pc.createAnswer(
+ function (modifiedAnswer) {
+ pc.setLocalDescription(
+ modifiedAnswer,
+ function () {
+ // noop
+ },
+ function (error) {
+ console.log('triggerKeyframe setLocalDescription failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe createAnswer failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe setRemoteDescription failed', error);
+ UI.messageHandler.showError();
+ }
+ );
+}
+
+
+JingleSession.prototype.remoteStreamAdded = function (data) {
+ var self = this;
+ var thessrc;
+
+ // look up an associated JID for a stream id
+ if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
+ // look only at a=ssrc: and _not_ at a=ssrc-group: lines
+
+ var ssrclines
+ = SDPUtil.find_lines(this.peerconnection.remoteDescription.sdp, 'a=ssrc:');
+ ssrclines = ssrclines.filter(function (line) {
+ // NOTE(gp) previously we filtered on the mslabel, but that property
+ // is not always present.
+ // return line.indexOf('mslabel:' + data.stream.label) !== -1;
+
+ return ((line.indexOf('msid:' + data.stream.id) !== -1));
+ });
+ if (ssrclines.length) {
+ thessrc = ssrclines[0].substring(7).split(' ')[0];
+
+ // We signal our streams (through Jingle to the focus) before we set
+ // our presence (through which peers associate remote streams to
+ // jids). So, it might arrive that a remote stream is added but
+ // ssrc2jid is not yet updated and thus data.peerjid cannot be
+ // successfully set. Here we wait for up to a second for the
+ // presence to arrive.
+
+ if (!ssrc2jid[thessrc]) {
+ // TODO(gp) limit wait duration to 1 sec.
+ setTimeout(function(d) {
+ return function() {
+ self.remoteStreamAdded(d);
+ }
+ }(data), 250);
+ return;
+ }
+
+ // ok to overwrite the one from focus? might save work in colibri.js
+ console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
+ if (ssrc2jid[thessrc]) {
+ data.peerjid = ssrc2jid[thessrc];
+ }
+ }
+ }
+
+ //TODO: this code should be removed when firefox implement multistream support
+ if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
+ {
+ if((notReceivedSSRCs.length == 0) ||
+ !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
+ {
+ // TODO(gp) limit wait duration to 1 sec.
+ setTimeout(function(d) {
+ return function() {
+ self.remoteStreamAdded(d);
+ }
+ }(data), 250);
+ return;
+ }
+
+ thessrc = notReceivedSSRCs.pop();
+ if (ssrc2jid[thessrc]) {
+ data.peerjid = ssrc2jid[thessrc];
+ }
+ }
+
+ RTC.createRemoteStream(data, this.sid, thessrc);
+
+ var isVideo = data.stream.getVideoTracks().length > 0;
+ // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
+ if (isVideo &&
+ data.peerjid && this.peerjid === data.peerjid &&
+ data.stream.getVideoTracks().length === 0 &&
+ RTC.localVideo.getTracks().length > 0) {
+ window.setTimeout(function () {
+ sendKeyframe(self.peerconnection);
+ }, 3000);
+ }
+}
+
+module.exports = JingleSession;
\ No newline at end of file
diff --git a/libs/strophe/strophe.jingle.sdp.js b/modules/xmpp/SDP.js
similarity index 55%
rename from libs/strophe/strophe.jingle.sdp.js
rename to modules/xmpp/SDP.js
index 03dbceb08..4103fbe13 100644
--- a/libs/strophe/strophe.jingle.sdp.js
+++ b/modules/xmpp/SDP.js
@@ -1,4 +1,6 @@
/* jshint -W117 */
+var SDPUtil = require("./SDPUtil");
+
// SDP STUFF
function SDP(sdp) {
this.media = sdp.split('\r\nm=');
@@ -71,169 +73,6 @@ SDP.prototype.containsSSRC = function(ssrc) {
return contains;
};
-function SDPDiffer(mySDP, otherSDP) {
- this.mySDP = mySDP;
- this.otherSDP = otherSDP;
-}
-
-/**
- * Returns map of MediaChannel that contains only media not contained in otherSdp. Mapped by channel idx.
- * @param otherSdp the other SDP to check ssrc with.
- */
-SDPDiffer.prototype.getNewMedia = function() {
-
- // this could be useful in Array.prototype.
- function arrayEquals(array) {
- // if the other array is a falsy value, return
- if (!array)
- return false;
-
- // compare lengths - can save a lot of time
- if (this.length != array.length)
- return false;
-
- for (var i = 0, l=this.length; i < l; i++) {
- // Check if we have nested arrays
- if (this[i] instanceof Array && array[i] instanceof Array) {
- // recurse into the nested arrays
- if (!this[i].equals(array[i]))
- return false;
- }
- else if (this[i] != array[i]) {
- // Warning - two different object instances will never be equal: {x:20} != {x:20}
- return false;
- }
- }
- return true;
- }
-
- var myMedias = this.mySDP.getMediaSsrcMap();
- var othersMedias = this.otherSDP.getMediaSsrcMap();
- var newMedia = {};
- Object.keys(othersMedias).forEach(function(othersMediaIdx) {
- var myMedia = myMedias[othersMediaIdx];
- var othersMedia = othersMedias[othersMediaIdx];
- if(!myMedia && othersMedia) {
- // Add whole channel
- newMedia[othersMediaIdx] = othersMedia;
- return;
- }
- // Look for new ssrcs accross the channel
- Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
- if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
- // Allocate channel if we've found ssrc that doesn't exist in our channel
- if(!newMedia[othersMediaIdx]){
- newMedia[othersMediaIdx] = {
- mediaindex: othersMedia.mediaindex,
- mid: othersMedia.mid,
- ssrcs: {},
- ssrcGroups: []
- };
- }
- newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
- }
- });
-
- // Look for new ssrc groups across the channels
- othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
-
- // try to match the other ssrc-group with an ssrc-group of ours
- var matched = false;
- for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
- var mySsrcGroup = myMedia.ssrcGroups[i];
- if (otherSsrcGroup.semantics == mySsrcGroup.semantics
- && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
-
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- // Allocate channel if we've found an ssrc-group that doesn't
- // exist in our channel
-
- if(!newMedia[othersMediaIdx]){
- newMedia[othersMediaIdx] = {
- mediaindex: othersMedia.mediaindex,
- mid: othersMedia.mid,
- ssrcs: {},
- ssrcGroups: []
- };
- }
- newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
- }
- });
- });
- return newMedia;
-};
-
-/**
- * Sends SSRC update IQ.
- * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
- * @param sid session identifier that will be put into the IQ.
- * @param initiator initiator identifier.
- * @param toJid destination Jid
- * @param isAdd indicates if this is remove or add operation.
- */
-SDPDiffer.prototype.toJingle = function(modify) {
- var sdpMediaSsrcs = this.getNewMedia();
- var self = this;
-
- // FIXME: only announce video ssrcs since we mix audio and dont need
- // the audio ssrcs therefore
- var modified = false;
- Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
- modified = true;
- var media = sdpMediaSsrcs[mediaindex];
- modify.c('content', {name: media.mid});
-
- modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
- // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
- // generate sources from lines
- Object.keys(media.ssrcs).forEach(function(ssrcNum) {
- var mediaSsrc = media.ssrcs[ssrcNum];
- modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
- modify.attrs({ssrc: mediaSsrc.ssrc});
- // iterate over ssrc lines
- mediaSsrc.lines.forEach(function (line) {
- var idx = line.indexOf(' ');
- var kv = line.substr(idx + 1);
- modify.c('parameter');
- if (kv.indexOf(':') == -1) {
- modify.attrs({ name: kv });
- } else {
- modify.attrs({ name: kv.split(':', 2)[0] });
- modify.attrs({ value: kv.split(':', 2)[1] });
- }
- modify.up(); // end of parameter
- });
- modify.up(); // end of source
- });
-
- // generate source groups from lines
- media.ssrcGroups.forEach(function(ssrcGroup) {
- if (ssrcGroup.ssrcs.length != 0) {
-
- modify.c('ssrc-group', {
- semantics: ssrcGroup.semantics,
- xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
- });
-
- ssrcGroup.ssrcs.forEach(function (ssrc) {
- modify.c('source', { ssrc: ssrc })
- .up(); // end of source
- });
- modify.up(); // end of ssrc-group
- }
- });
-
- modify.up(); // end of description
- modify.up(); // end of content
- });
-
- return modified;
-};
// remove iSAC and CN from SDP
SDP.prototype.mangle = function () {
@@ -776,352 +615,6 @@ SDP.prototype.jingle2media = function (content) {
return media;
};
-SDPUtil = {
- iceparams: function (mediadesc, sessiondesc) {
- var data = null;
- if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
- SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
- data = {
- ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
- pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
- };
- }
- return data;
- },
- parse_iceufrag: function (line) {
- return line.substring(12);
- },
- build_iceufrag: function (frag) {
- return 'a=ice-ufrag:' + frag;
- },
- parse_icepwd: function (line) {
- return line.substring(10);
- },
- build_icepwd: function (pwd) {
- return 'a=ice-pwd:' + pwd;
- },
- parse_mid: function (line) {
- return line.substring(6);
- },
- parse_mline: function (line) {
- var parts = line.substring(2).split(' '),
- data = {};
- data.media = parts.shift();
- data.port = parts.shift();
- data.proto = parts.shift();
- if (parts[parts.length - 1] === '') { // trailing whitespace
- parts.pop();
- }
- data.fmt = parts;
- return data;
- },
- build_mline: function (mline) {
- return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
- },
- parse_rtpmap: function (line) {
- var parts = line.substring(9).split(' '),
- data = {};
- data.id = parts.shift();
- parts = parts[0].split('/');
- data.name = parts.shift();
- data.clockrate = parts.shift();
- data.channels = parts.length ? parts.shift() : '1';
- return data;
- },
- /**
- * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
- * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
- * @returns [SCTP port number, protocol, streams]
- */
- parse_sctpmap: function (line)
- {
- var parts = line.substring(10).split(' ');
- var sctpPort = parts[0];
- var protocol = parts[1];
- // Stream count is optional
- var streamCount = parts.length > 2 ? parts[2] : null;
- return [sctpPort, protocol, streamCount];// SCTP port
- },
- build_rtpmap: function (el) {
- var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
- if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
- line += '/' + el.getAttribute('channels');
- }
- return line;
- },
- parse_crypto: function (line) {
- var parts = line.substring(9).split(' '),
- data = {};
- data.tag = parts.shift();
- data['crypto-suite'] = parts.shift();
- data['key-params'] = parts.shift();
- if (parts.length) {
- data['session-params'] = parts.join(' ');
- }
- return data;
- },
- parse_fingerprint: function (line) { // RFC 4572
- var parts = line.substring(14).split(' '),
- data = {};
- data.hash = parts.shift();
- data.fingerprint = parts.shift();
- // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
- return data;
- },
- parse_fmtp: function (line) {
- var parts = line.split(' '),
- i, key, value,
- data = [];
- parts.shift();
- parts = parts.join(' ').split(';');
- for (i = 0; i < parts.length; i++) {
- key = parts[i].split('=')[0];
- while (key.length && key[0] == ' ') {
- key = key.substring(1);
- }
- value = parts[i].split('=')[1];
- if (key && value) {
- data.push({name: key, value: value});
- } else if (key) {
- // rfc 4733 (DTMF) style stuff
- data.push({name: '', value: key});
- }
- }
- return data;
- },
- parse_icecandidate: function (line) {
- var candidate = {},
- elems = line.split(' ');
- candidate.foundation = elems[0].substring(12);
- candidate.component = elems[1];
- candidate.protocol = elems[2].toLowerCase();
- candidate.priority = elems[3];
- candidate.ip = elems[4];
- candidate.port = elems[5];
- // elems[6] => "typ"
- candidate.type = elems[7];
- candidate.generation = 0; // default value, may be overwritten below
- for (var i = 8; i < elems.length; i += 2) {
- switch (elems[i]) {
- case 'raddr':
- candidate['rel-addr'] = elems[i + 1];
- break;
- case 'rport':
- candidate['rel-port'] = elems[i + 1];
- break;
- case 'generation':
- candidate.generation = elems[i + 1];
- break;
- case 'tcptype':
- candidate.tcptype = elems[i + 1];
- break;
- default: // TODO
- console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
- }
- }
- candidate.network = '1';
- candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
- return candidate;
- },
- build_icecandidate: function (cand) {
- var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
- line += ' ';
- switch (cand.type) {
- case 'srflx':
- case 'prflx':
- case 'relay':
- if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
- line += 'raddr';
- line += ' ';
- line += cand['rel-addr'];
- line += ' ';
- line += 'rport';
- line += ' ';
- line += cand['rel-port'];
- line += ' ';
- }
- break;
- }
- if (cand.hasOwnAttribute('tcptype')) {
- line += 'tcptype';
- line += ' ';
- line += cand.tcptype;
- line += ' ';
- }
- line += 'generation';
- line += ' ';
- line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
- return line;
- },
- parse_ssrc: function (desc) {
- // proprietary mapping of a=ssrc lines
- // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
- // and parse according to that
- var lines = desc.split('\r\n'),
- data = {};
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, 7) == 'a=ssrc:') {
- var idx = lines[i].indexOf(' ');
- data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
- }
- }
- return data;
- },
- parse_rtcpfb: function (line) {
- var parts = line.substr(10).split(' ');
- var data = {};
- data.pt = parts.shift();
- data.type = parts.shift();
- data.params = parts;
- return data;
- },
- parse_extmap: function (line) {
- var parts = line.substr(9).split(' ');
- var data = {};
- data.value = parts.shift();
- if (data.value.indexOf('/') != -1) {
- data.direction = data.value.substr(data.value.indexOf('/') + 1);
- data.value = data.value.substr(0, data.value.indexOf('/'));
- } else {
- data.direction = 'both';
- }
- data.uri = parts.shift();
- data.params = parts;
- return data;
- },
- find_line: function (haystack, needle, sessionpart) {
- var lines = haystack.split('\r\n');
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, needle.length) == needle) {
- return lines[i];
- }
- }
- if (!sessionpart) {
- return false;
- }
- // search session part
- lines = sessionpart.split('\r\n');
- for (var j = 0; j < lines.length; j++) {
- if (lines[j].substring(0, needle.length) == needle) {
- return lines[j];
- }
- }
- return false;
- },
- find_lines: function (haystack, needle, sessionpart) {
- var lines = haystack.split('\r\n'),
- needles = [];
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, needle.length) == needle)
- needles.push(lines[i]);
- }
- if (needles.length || !sessionpart) {
- return needles;
- }
- // search session part
- lines = sessionpart.split('\r\n');
- for (var j = 0; j < lines.length; j++) {
- if (lines[j].substring(0, needle.length) == needle) {
- needles.push(lines[j]);
- }
- }
- return needles;
- },
- candidateToJingle: function (line) {
- // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
- //
- if (line.indexOf('candidate:') === 0) {
- line = 'a=' + line;
- } else if (line.substring(0, 12) != 'a=candidate:') {
- console.log('parseCandidate called with a line that is not a candidate line');
- console.log(line);
- return null;
- }
- if (line.substring(line.length - 2) == '\r\n') // chomp it
- line = line.substring(0, line.length - 2);
- var candidate = {},
- elems = line.split(' '),
- i;
- if (elems[6] != 'typ') {
- console.log('did not find typ in the right place');
- console.log(line);
- return null;
- }
- candidate.foundation = elems[0].substring(12);
- candidate.component = elems[1];
- candidate.protocol = elems[2].toLowerCase();
- candidate.priority = elems[3];
- candidate.ip = elems[4];
- candidate.port = elems[5];
- // elems[6] => "typ"
- candidate.type = elems[7];
- candidate.generation = '0'; // default, may be overwritten below
- for (i = 8; i < elems.length; i += 2) {
- switch (elems[i]) {
- case 'raddr':
- candidate['rel-addr'] = elems[i + 1];
- break;
- case 'rport':
- candidate['rel-port'] = elems[i + 1];
- break;
- case 'generation':
- candidate.generation = elems[i + 1];
- break;
- case 'tcptype':
- candidate.tcptype = elems[i + 1];
- break;
- default: // TODO
- console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
- }
- }
- candidate.network = '1';
- candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
- return candidate;
- },
- candidateFromJingle: function (cand) {
- var line = 'a=candidate:';
- line += cand.getAttribute('foundation');
- line += ' ';
- line += cand.getAttribute('component');
- line += ' ';
- line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
- line += ' ';
- line += cand.getAttribute('priority');
- line += ' ';
- line += cand.getAttribute('ip');
- line += ' ';
- line += cand.getAttribute('port');
- line += ' ';
- line += 'typ';
- line += ' ' + cand.getAttribute('type');
- line += ' ';
- switch (cand.getAttribute('type')) {
- case 'srflx':
- case 'prflx':
- case 'relay':
- if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
- line += 'raddr';
- line += ' ';
- line += cand.getAttribute('rel-addr');
- line += ' ';
- line += 'rport';
- line += ' ';
- line += cand.getAttribute('rel-port');
- line += ' ';
- }
- break;
- }
- if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
- line += 'tcptype';
- line += ' ';
- line += cand.getAttribute('tcptype');
- line += ' ';
- }
- line += 'generation';
- line += ' ';
- line += cand.getAttribute('generation') || '0';
- return line + '\r\n';
- }
-};
+module.exports = SDP;
diff --git a/modules/xmpp/SDPDiffer.js b/modules/xmpp/SDPDiffer.js
new file mode 100644
index 000000000..ebaaadb13
--- /dev/null
+++ b/modules/xmpp/SDPDiffer.js
@@ -0,0 +1,165 @@
+function SDPDiffer(mySDP, otherSDP) {
+ this.mySDP = mySDP;
+ this.otherSDP = otherSDP;
+}
+
+/**
+ * Returns map of MediaChannel that contains only media not contained in otherSdp. Mapped by channel idx.
+ * @param otherSdp the other SDP to check ssrc with.
+ */
+SDPDiffer.prototype.getNewMedia = function() {
+
+ // this could be useful in Array.prototype.
+ function arrayEquals(array) {
+ // if the other array is a falsy value, return
+ if (!array)
+ return false;
+
+ // compare lengths - can save a lot of time
+ if (this.length != array.length)
+ return false;
+
+ for (var i = 0, l=this.length; i < l; i++) {
+ // Check if we have nested arrays
+ if (this[i] instanceof Array && array[i] instanceof Array) {
+ // recurse into the nested arrays
+ if (!this[i].equals(array[i]))
+ return false;
+ }
+ else if (this[i] != array[i]) {
+ // Warning - two different object instances will never be equal: {x:20} != {x:20}
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var myMedias = this.mySDP.getMediaSsrcMap();
+ var othersMedias = this.otherSDP.getMediaSsrcMap();
+ var newMedia = {};
+ Object.keys(othersMedias).forEach(function(othersMediaIdx) {
+ var myMedia = myMedias[othersMediaIdx];
+ var othersMedia = othersMedias[othersMediaIdx];
+ if(!myMedia && othersMedia) {
+ // Add whole channel
+ newMedia[othersMediaIdx] = othersMedia;
+ return;
+ }
+ // Look for new ssrcs accross the channel
+ Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
+ if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
+ // Allocate channel if we've found ssrc that doesn't exist in our channel
+ if(!newMedia[othersMediaIdx]){
+ newMedia[othersMediaIdx] = {
+ mediaindex: othersMedia.mediaindex,
+ mid: othersMedia.mid,
+ ssrcs: {},
+ ssrcGroups: []
+ };
+ }
+ newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
+ }
+ });
+
+ // Look for new ssrc groups across the channels
+ othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
+
+ // try to match the other ssrc-group with an ssrc-group of ours
+ var matched = false;
+ for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
+ var mySsrcGroup = myMedia.ssrcGroups[i];
+ if (otherSsrcGroup.semantics == mySsrcGroup.semantics
+ && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
+
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ // Allocate channel if we've found an ssrc-group that doesn't
+ // exist in our channel
+
+ if(!newMedia[othersMediaIdx]){
+ newMedia[othersMediaIdx] = {
+ mediaindex: othersMedia.mediaindex,
+ mid: othersMedia.mid,
+ ssrcs: {},
+ ssrcGroups: []
+ };
+ }
+ newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
+ }
+ });
+ });
+ return newMedia;
+};
+
+/**
+ * Sends SSRC update IQ.
+ * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
+ * @param sid session identifier that will be put into the IQ.
+ * @param initiator initiator identifier.
+ * @param toJid destination Jid
+ * @param isAdd indicates if this is remove or add operation.
+ */
+SDPDiffer.prototype.toJingle = function(modify) {
+ var sdpMediaSsrcs = this.getNewMedia();
+ var self = this;
+
+ // FIXME: only announce video ssrcs since we mix audio and dont need
+ // the audio ssrcs therefore
+ var modified = false;
+ Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
+ modified = true;
+ var media = sdpMediaSsrcs[mediaindex];
+ modify.c('content', {name: media.mid});
+
+ modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
+ // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
+ // generate sources from lines
+ Object.keys(media.ssrcs).forEach(function(ssrcNum) {
+ var mediaSsrc = media.ssrcs[ssrcNum];
+ modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
+ modify.attrs({ssrc: mediaSsrc.ssrc});
+ // iterate over ssrc lines
+ mediaSsrc.lines.forEach(function (line) {
+ var idx = line.indexOf(' ');
+ var kv = line.substr(idx + 1);
+ modify.c('parameter');
+ if (kv.indexOf(':') == -1) {
+ modify.attrs({ name: kv });
+ } else {
+ modify.attrs({ name: kv.split(':', 2)[0] });
+ modify.attrs({ value: kv.split(':', 2)[1] });
+ }
+ modify.up(); // end of parameter
+ });
+ modify.up(); // end of source
+ });
+
+ // generate source groups from lines
+ media.ssrcGroups.forEach(function(ssrcGroup) {
+ if (ssrcGroup.ssrcs.length != 0) {
+
+ modify.c('ssrc-group', {
+ semantics: ssrcGroup.semantics,
+ xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
+ });
+
+ ssrcGroup.ssrcs.forEach(function (ssrc) {
+ modify.c('source', { ssrc: ssrc })
+ .up(); // end of source
+ });
+ modify.up(); // end of ssrc-group
+ }
+ });
+
+ modify.up(); // end of description
+ modify.up(); // end of content
+ });
+
+ return modified;
+};
+
+module.exports = SDPDiffer;
\ No newline at end of file
diff --git a/modules/xmpp/SDPUtil.js b/modules/xmpp/SDPUtil.js
new file mode 100644
index 000000000..d75d35f7a
--- /dev/null
+++ b/modules/xmpp/SDPUtil.js
@@ -0,0 +1,349 @@
+SDPUtil = {
+ iceparams: function (mediadesc, sessiondesc) {
+ var data = null;
+ if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
+ SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
+ data = {
+ ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
+ pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
+ };
+ }
+ return data;
+ },
+ parse_iceufrag: function (line) {
+ return line.substring(12);
+ },
+ build_iceufrag: function (frag) {
+ return 'a=ice-ufrag:' + frag;
+ },
+ parse_icepwd: function (line) {
+ return line.substring(10);
+ },
+ build_icepwd: function (pwd) {
+ return 'a=ice-pwd:' + pwd;
+ },
+ parse_mid: function (line) {
+ return line.substring(6);
+ },
+ parse_mline: function (line) {
+ var parts = line.substring(2).split(' '),
+ data = {};
+ data.media = parts.shift();
+ data.port = parts.shift();
+ data.proto = parts.shift();
+ if (parts[parts.length - 1] === '') { // trailing whitespace
+ parts.pop();
+ }
+ data.fmt = parts;
+ return data;
+ },
+ build_mline: function (mline) {
+ return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
+ },
+ parse_rtpmap: function (line) {
+ var parts = line.substring(9).split(' '),
+ data = {};
+ data.id = parts.shift();
+ parts = parts[0].split('/');
+ data.name = parts.shift();
+ data.clockrate = parts.shift();
+ data.channels = parts.length ? parts.shift() : '1';
+ return data;
+ },
+ /**
+ * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
+ * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
+ * @returns [SCTP port number, protocol, streams]
+ */
+ parse_sctpmap: function (line)
+ {
+ var parts = line.substring(10).split(' ');
+ var sctpPort = parts[0];
+ var protocol = parts[1];
+ // Stream count is optional
+ var streamCount = parts.length > 2 ? parts[2] : null;
+ return [sctpPort, protocol, streamCount];// SCTP port
+ },
+ build_rtpmap: function (el) {
+ var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
+ if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
+ line += '/' + el.getAttribute('channels');
+ }
+ return line;
+ },
+ parse_crypto: function (line) {
+ var parts = line.substring(9).split(' '),
+ data = {};
+ data.tag = parts.shift();
+ data['crypto-suite'] = parts.shift();
+ data['key-params'] = parts.shift();
+ if (parts.length) {
+ data['session-params'] = parts.join(' ');
+ }
+ return data;
+ },
+ parse_fingerprint: function (line) { // RFC 4572
+ var parts = line.substring(14).split(' '),
+ data = {};
+ data.hash = parts.shift();
+ data.fingerprint = parts.shift();
+ // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
+ return data;
+ },
+ parse_fmtp: function (line) {
+ var parts = line.split(' '),
+ i, key, value,
+ data = [];
+ parts.shift();
+ parts = parts.join(' ').split(';');
+ for (i = 0; i < parts.length; i++) {
+ key = parts[i].split('=')[0];
+ while (key.length && key[0] == ' ') {
+ key = key.substring(1);
+ }
+ value = parts[i].split('=')[1];
+ if (key && value) {
+ data.push({name: key, value: value});
+ } else if (key) {
+ // rfc 4733 (DTMF) style stuff
+ data.push({name: '', value: key});
+ }
+ }
+ return data;
+ },
+ parse_icecandidate: function (line) {
+ var candidate = {},
+ elems = line.split(' ');
+ candidate.foundation = elems[0].substring(12);
+ candidate.component = elems[1];
+ candidate.protocol = elems[2].toLowerCase();
+ candidate.priority = elems[3];
+ candidate.ip = elems[4];
+ candidate.port = elems[5];
+ // elems[6] => "typ"
+ candidate.type = elems[7];
+ candidate.generation = 0; // default value, may be overwritten below
+ for (var i = 8; i < elems.length; i += 2) {
+ switch (elems[i]) {
+ case 'raddr':
+ candidate['rel-addr'] = elems[i + 1];
+ break;
+ case 'rport':
+ candidate['rel-port'] = elems[i + 1];
+ break;
+ case 'generation':
+ candidate.generation = elems[i + 1];
+ break;
+ case 'tcptype':
+ candidate.tcptype = elems[i + 1];
+ break;
+ default: // TODO
+ console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
+ }
+ }
+ candidate.network = '1';
+ candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
+ return candidate;
+ },
+ build_icecandidate: function (cand) {
+ var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
+ line += ' ';
+ switch (cand.type) {
+ case 'srflx':
+ case 'prflx':
+ case 'relay':
+ if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
+ line += 'raddr';
+ line += ' ';
+ line += cand['rel-addr'];
+ line += ' ';
+ line += 'rport';
+ line += ' ';
+ line += cand['rel-port'];
+ line += ' ';
+ }
+ break;
+ }
+ if (cand.hasOwnAttribute('tcptype')) {
+ line += 'tcptype';
+ line += ' ';
+ line += cand.tcptype;
+ line += ' ';
+ }
+ line += 'generation';
+ line += ' ';
+ line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
+ return line;
+ },
+ parse_ssrc: function (desc) {
+ // proprietary mapping of a=ssrc lines
+ // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
+ // and parse according to that
+ var lines = desc.split('\r\n'),
+ data = {};
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, 7) == 'a=ssrc:') {
+ var idx = lines[i].indexOf(' ');
+ data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
+ }
+ }
+ return data;
+ },
+ parse_rtcpfb: function (line) {
+ var parts = line.substr(10).split(' ');
+ var data = {};
+ data.pt = parts.shift();
+ data.type = parts.shift();
+ data.params = parts;
+ return data;
+ },
+ parse_extmap: function (line) {
+ var parts = line.substr(9).split(' ');
+ var data = {};
+ data.value = parts.shift();
+ if (data.value.indexOf('/') != -1) {
+ data.direction = data.value.substr(data.value.indexOf('/') + 1);
+ data.value = data.value.substr(0, data.value.indexOf('/'));
+ } else {
+ data.direction = 'both';
+ }
+ data.uri = parts.shift();
+ data.params = parts;
+ return data;
+ },
+ find_line: function (haystack, needle, sessionpart) {
+ var lines = haystack.split('\r\n');
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, needle.length) == needle) {
+ return lines[i];
+ }
+ }
+ if (!sessionpart) {
+ return false;
+ }
+ // search session part
+ lines = sessionpart.split('\r\n');
+ for (var j = 0; j < lines.length; j++) {
+ if (lines[j].substring(0, needle.length) == needle) {
+ return lines[j];
+ }
+ }
+ return false;
+ },
+ find_lines: function (haystack, needle, sessionpart) {
+ var lines = haystack.split('\r\n'),
+ needles = [];
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].substring(0, needle.length) == needle)
+ needles.push(lines[i]);
+ }
+ if (needles.length || !sessionpart) {
+ return needles;
+ }
+ // search session part
+ lines = sessionpart.split('\r\n');
+ for (var j = 0; j < lines.length; j++) {
+ if (lines[j].substring(0, needle.length) == needle) {
+ needles.push(lines[j]);
+ }
+ }
+ return needles;
+ },
+ candidateToJingle: function (line) {
+ // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
+ //
+ if (line.indexOf('candidate:') === 0) {
+ line = 'a=' + line;
+ } else if (line.substring(0, 12) != 'a=candidate:') {
+ console.log('parseCandidate called with a line that is not a candidate line');
+ console.log(line);
+ return null;
+ }
+ if (line.substring(line.length - 2) == '\r\n') // chomp it
+ line = line.substring(0, line.length - 2);
+ var candidate = {},
+ elems = line.split(' '),
+ i;
+ if (elems[6] != 'typ') {
+ console.log('did not find typ in the right place');
+ console.log(line);
+ return null;
+ }
+ candidate.foundation = elems[0].substring(12);
+ candidate.component = elems[1];
+ candidate.protocol = elems[2].toLowerCase();
+ candidate.priority = elems[3];
+ candidate.ip = elems[4];
+ candidate.port = elems[5];
+ // elems[6] => "typ"
+ candidate.type = elems[7];
+
+ candidate.generation = '0'; // default, may be overwritten below
+ for (i = 8; i < elems.length; i += 2) {
+ switch (elems[i]) {
+ case 'raddr':
+ candidate['rel-addr'] = elems[i + 1];
+ break;
+ case 'rport':
+ candidate['rel-port'] = elems[i + 1];
+ break;
+ case 'generation':
+ candidate.generation = elems[i + 1];
+ break;
+ case 'tcptype':
+ candidate.tcptype = elems[i + 1];
+ break;
+ default: // TODO
+ console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
+ }
+ }
+ candidate.network = '1';
+ candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
+ return candidate;
+ },
+ candidateFromJingle: function (cand) {
+ var line = 'a=candidate:';
+ line += cand.getAttribute('foundation');
+ line += ' ';
+ line += cand.getAttribute('component');
+ line += ' ';
+ line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
+ line += ' ';
+ line += cand.getAttribute('priority');
+ line += ' ';
+ line += cand.getAttribute('ip');
+ line += ' ';
+ line += cand.getAttribute('port');
+ line += ' ';
+ line += 'typ';
+ line += ' ' + cand.getAttribute('type');
+ line += ' ';
+ switch (cand.getAttribute('type')) {
+ case 'srflx':
+ case 'prflx':
+ case 'relay':
+ if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
+ line += 'raddr';
+ line += ' ';
+ line += cand.getAttribute('rel-addr');
+ line += ' ';
+ line += 'rport';
+ line += ' ';
+ line += cand.getAttribute('rel-port');
+ line += ' ';
+ }
+ break;
+ }
+ if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
+ line += 'tcptype';
+ line += ' ';
+ line += cand.getAttribute('tcptype');
+ line += ' ';
+ }
+ line += 'generation';
+ line += ' ';
+ line += cand.getAttribute('generation') || '0';
+ return line + '\r\n';
+ }
+};
+module.exports = SDPUtil;
\ No newline at end of file
diff --git a/libs/strophe/strophe.jingle.adapter.js b/modules/xmpp/TraceablePeerConnection.js
similarity index 99%
rename from libs/strophe/strophe.jingle.adapter.js
rename to modules/xmpp/TraceablePeerConnection.js
index 035c43ab3..c8db15337 100644
--- a/libs/strophe/strophe.jingle.adapter.js
+++ b/modules/xmpp/TraceablePeerConnection.js
@@ -262,3 +262,5 @@ TraceablePeerConnection.prototype.getStats = function(callback, errback) {
}
};
+module.exports = TraceablePeerConnection;
+
diff --git a/moderator.js b/modules/xmpp/moderator.js
similarity index 70%
rename from moderator.js
rename to modules/xmpp/moderator.js
index 5f0ed3bd8..439d70311 100644
--- a/moderator.js
+++ b/modules/xmpp/moderator.js
@@ -1,47 +1,53 @@
-/* global $, $iq, config, connection, Etherpad, hangUp, messageHandler,
+/* global $, $iq, config, connection, UI, messageHandler,
roomName, sessionTerminated, Strophe, Util */
/**
* Contains logic responsible for enabling/disabling functionality available
* only to moderator users.
*/
-var Moderator = (function (my) {
+var connection = null;
+var focusUserJid;
+var getNextTimeout = Util.createExpBackoffTimer(1000);
+var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
+// External authentication stuff
+var externalAuthEnabled = false;
+// Sip gateway can be enabled by configuring Jigasi host in config.js or
+// it will be enabled automatically if focus detects the component through
+// service discovery.
+var sipGatewayEnabled = config.hosts.call_control !== undefined;
- var focusUserJid;
- var getNextTimeout = Util.createExpBackoffTimer(1000);
- var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
- // External authentication stuff
- var externalAuthEnabled = false;
- // Sip gateway can be enabled by configuring Jigasi host in config.js or
- // it will be enabled automatically if focus detects the component through
- // service discovery.
- var sipGatewayEnabled = config.hosts.call_control !== undefined;
-
- my.isModerator = function () {
+var Moderator = {
+ isModerator: function () {
return connection && connection.emuc.isModerator();
- };
+ },
- my.isPeerModerator = function (peerJid) {
- return connection && connection.emuc.getMemberRole(peerJid) === 'moderator';
- };
+ isPeerModerator: function (peerJid) {
+ return connection &&
+ connection.emuc.getMemberRole(peerJid) === 'moderator';
+ },
- my.isExternalAuthEnabled = function () {
+ isExternalAuthEnabled: function () {
return externalAuthEnabled;
- };
+ },
- my.isSipGatewayEnabled = function () {
+ isSipGatewayEnabled: function () {
return sipGatewayEnabled;
- };
+ },
- my.init = function () {
- Moderator.onLocalRoleChange = function (from, member, pres) {
+ setConnection: function (con) {
+ connection = con;
+ },
+
+ init: function (xmpp) {
+ this.xmppService = xmpp;
+ this.onLocalRoleChange = function (from, member, pres) {
UI.onModeratorStatusChanged(Moderator.isModerator());
};
- };
+ },
- my.onMucLeft = function (jid) {
+ onMucLeft: function (jid) {
console.info("Someone left is it focus ? " + jid);
var resource = Strophe.getResourceFromJid(jid);
- if (resource === 'focus' && !sessionTerminated) {
+ if (resource === 'focus' && !this.xmppService.sessionTerminated) {
console.info(
"Focus has left the room - leaving conference");
//hangUp();
@@ -49,20 +55,20 @@ var Moderator = (function (my) {
// FIXME: show some message before reload
location.reload();
}
- }
-
- my.setFocusUserJid = function (focusJid) {
+ },
+
+ setFocusUserJid: function (focusJid) {
if (!focusUserJid) {
focusUserJid = focusJid;
console.info("Focus jid set to: " + focusUserJid);
}
- };
+ },
- my.getFocusUserJid = function () {
+ getFocusUserJid: function () {
return focusUserJid;
- };
+ },
- my.getFocusComponent = function () {
+ getFocusComponent: function () {
// Get focus component address
var focusComponent = config.hosts.focus;
// If not specified use default: 'focus.domain'
@@ -70,99 +76,93 @@ var Moderator = (function (my) {
focusComponent = 'focus.' + config.hosts.domain;
}
return focusComponent;
- };
+ },
- my.createConferenceIq = function () {
+ createConferenceIq: function (roomName) {
// Generate create conference IQ
var elem = $iq({to: Moderator.getFocusComponent(), type: 'set'});
elem.c('conference', {
xmlns: 'http://jitsi.org/protocol/focus',
room: roomName
});
- if (config.hosts.bridge !== undefined)
- {
+ if (config.hosts.bridge !== undefined) {
elem.c(
'property',
{ name: 'bridge', value: config.hosts.bridge})
.up();
}
// Tell the focus we have Jigasi configured
- if (config.hosts.call_control !== undefined)
- {
+ if (config.hosts.call_control !== undefined) {
elem.c(
'property',
{ name: 'call_control', value: config.hosts.call_control})
.up();
}
- if (config.channelLastN !== undefined)
- {
+ if (config.channelLastN !== undefined) {
elem.c(
'property',
{ name: 'channelLastN', value: config.channelLastN})
.up();
}
- if (config.adaptiveLastN !== undefined)
- {
+ if (config.adaptiveLastN !== undefined) {
elem.c(
'property',
{ name: 'adaptiveLastN', value: config.adaptiveLastN})
.up();
}
- if (config.adaptiveSimulcast !== undefined)
- {
+ if (config.adaptiveSimulcast !== undefined) {
elem.c(
'property',
{ name: 'adaptiveSimulcast', value: config.adaptiveSimulcast})
.up();
}
- if (config.openSctp !== undefined)
- {
+ if (config.openSctp !== undefined) {
elem.c(
'property',
{ name: 'openSctp', value: config.openSctp})
.up();
}
- if (config.enableFirefoxSupport !== undefined)
- {
+ if (config.enableFirefoxSupport !== undefined) {
elem.c(
'property',
- { name: 'enableFirefoxHacks', value: config.enableFirefoxSupport})
+ { name: 'enableFirefoxHacks',
+ value: config.enableFirefoxSupport})
.up();
}
elem.up();
return elem;
- };
-
- my.parseConfigOptions = function (resultIq) {
+ },
+ parseConfigOptions: function (resultIq) {
+
Moderator.setFocusUserJid(
$(resultIq).find('conference').attr('focusjid'));
-
+
var extAuthParam
= $(resultIq).find('>conference>property[name=\'externalAuth\']');
if (extAuthParam.length) {
externalAuthEnabled = extAuthParam.attr('value') === 'true';
}
-
+
console.info("External authentication enabled: " + externalAuthEnabled);
-
+
// Check if focus has auto-detected Jigasi component(this will be also
// included if we have passed our host from the config)
if ($(resultIq).find(
- '>conference>property[name=\'sipGatewayEnabled\']').length) {
+ '>conference>property[name=\'sipGatewayEnabled\']').length) {
sipGatewayEnabled = true;
}
-
+
console.info("Sip gateway enabled: " + sipGatewayEnabled);
- };
+ },
// FIXME: we need to show the fact that we're waiting for the focus
// to the user(or that focus is not available)
- my.allocateConferenceFocus = function (roomName, callback) {
+ allocateConferenceFocus: function (roomName, callback) {
// Try to use focus user JID from the config
Moderator.setFocusUserJid(config.focusUserJid);
// Send create conference IQ
- var iq = Moderator.createConferenceIq();
+ var iq = Moderator.createConferenceIq(roomName);
connection.sendIQ(
iq,
function (result) {
@@ -190,7 +190,9 @@ var Moderator = (function (my) {
// Not authorized to create new room
if ($(error).find('>error>not-authorized').length) {
console.warn("Unauthorized to start the conference");
- UI.onAuthenticationRequired();
+ UI.onAuthenticationRequired(function () {
+ Moderator.allocateConferenceFocus(roomName, callback);
+ });
return;
}
var waitMs = getNextErrorTimeout();
@@ -198,8 +200,9 @@ var Moderator = (function (my) {
// Show message
UI.messageHandler.notify(
'Conference focus', 'disconnected',
- Moderator.getFocusComponent() +
- ' not available - retry in ' + (waitMs / 1000) + ' sec');
+ Moderator.getFocusComponent() +
+ ' not available - retry in ' +
+ (waitMs / 1000) + ' sec');
// Reset response timeout
getNextTimeout(true);
window.setTimeout(
@@ -208,9 +211,9 @@ var Moderator = (function (my) {
}, waitMs);
}
);
- };
+ },
- my.getAuthUrl = function (urlCallback) {
+ getAuthUrl: function (roomName, urlCallback) {
var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
iq.c('auth-url', {
xmlns: 'http://jitsi.org/protocol/focus',
@@ -232,10 +235,10 @@ var Moderator = (function (my) {
console.error("Get auth url error", error);
}
);
- };
+ }
+};
- return my;
-}(Moderator || {}));
+module.exports = Moderator;
diff --git a/modules/xmpp/recording.js b/modules/xmpp/recording.js
new file mode 100644
index 000000000..245260c49
--- /dev/null
+++ b/modules/xmpp/recording.js
@@ -0,0 +1,152 @@
+/* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
+ Toolbar, Util */
+var Moderator = require("./moderator");
+
+
+var recordingToken = null;
+var recordingEnabled;
+
+/**
+ * Whether to use a jirecon component for recording, or use the videobridge
+ * through COLIBRI.
+ */
+var useJirecon = (typeof config.hosts.jirecon != "undefined");
+
+/**
+ * The ID of the jirecon recording session. Jirecon generates it when we
+ * initially start recording, and it needs to be used in subsequent requests
+ * to jirecon.
+ */
+var jireconRid = null;
+
+function setRecordingToken(token) {
+ recordingToken = token;
+}
+
+function setRecording(state, token, callback) {
+ if (useJirecon){
+ this.setRecordingJirecon(state, token, callback);
+ } else {
+ this.setRecordingColibri(state, token, callback);
+ }
+}
+
+function setRecordingJirecon(state, token, callback) {
+ if (state == recordingEnabled){
+ return;
+ }
+
+ var iq = $iq({to: config.hosts.jirecon, type: 'set'})
+ .c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
+ action: state ? 'start' : 'stop',
+ mucjid: connection.emuc.roomjid});
+ if (!state){
+ iq.attrs({rid: jireconRid});
+ }
+
+ console.log('Start recording');
+
+ connection.sendIQ(
+ iq,
+ function (result) {
+ // TODO wait for an IQ with the real status, since this is
+ // provisional?
+ jireconRid = $(result).find('recording').attr('rid');
+ console.log('Recording ' + (state ? 'started' : 'stopped') +
+ '(jirecon)' + result);
+ recordingEnabled = state;
+ if (!state){
+ jireconRid = null;
+ }
+
+ callback(state);
+ },
+ function (error) {
+ console.log('Failed to start recording, error: ', error);
+ callback(recordingEnabled);
+ });
+}
+
+// Sends a COLIBRI message which enables or disables (according to 'state')
+// the recording on the bridge. Waits for the result IQ and calls 'callback'
+// with the new recording state, according to the IQ.
+function setRecordingColibri(state, token, callback) {
+ var elem = $iq({to: focusMucJid, type: 'set'});
+ elem.c('conference', {
+ xmlns: 'http://jitsi.org/protocol/colibri'
+ });
+ elem.c('recording', {state: state, token: token});
+
+ connection.sendIQ(elem,
+ function (result) {
+ console.log('Set recording "', state, '". Result:', result);
+ var recordingElem = $(result).find('>conference>recording');
+ var newState = ('true' === recordingElem.attr('state'));
+
+ recordingEnabled = newState;
+ callback(newState);
+ },
+ function (error) {
+ console.warn(error);
+ callback(recordingEnabled);
+ }
+ );
+}
+
+var Recording = {
+ toggleRecording: function (tokenEmptyCallback,
+ startingCallback, startedCallback) {
+ if (!Moderator.isModerator()) {
+ console.log(
+ 'non-focus, or conference not yet organized:' +
+ ' not enabling recording');
+ return;
+ }
+
+ // Jirecon does not (currently) support a token.
+ if (!recordingToken && !useJirecon) {
+ tokenEmptyCallback(function (value) {
+ setRecordingToken(value);
+ this.toggleRecording();
+ });
+
+ return;
+ }
+
+ var oldState = recordingEnabled;
+ startingCallback(!oldState);
+ setRecording(!oldState,
+ recordingToken,
+ function (state) {
+ console.log("New recording state: ", state);
+ if (state === oldState) {
+ // FIXME: new focus:
+ // this will not work when moderator changes
+ // during active session. Then it will assume that
+ // recording status has changed to true, but it might have
+ // been already true(and we only received actual status from
+ // the focus).
+ //
+ // SO we start with status null, so that it is initialized
+ // here and will fail only after second click, so if invalid
+ // token was used we have to press the button twice before
+ // current status will be fetched and token will be reset.
+ //
+ // Reliable way would be to return authentication error.
+ // Or status update when moderator connects.
+ // Or we have to stop recording session when current
+ // moderator leaves the room.
+
+ // Failed to change, reset the token because it might
+ // have been wrong
+ setRecordingToken(null);
+ }
+ startedCallback(state);
+
+ }
+ );
+ }
+
+}
+
+module.exports = Recording;
\ No newline at end of file
diff --git a/modules/xmpp/strophe.emuc.js b/modules/xmpp/strophe.emuc.js
new file mode 100644
index 000000000..63bf14713
--- /dev/null
+++ b/modules/xmpp/strophe.emuc.js
@@ -0,0 +1,607 @@
+/* jshint -W117 */
+/* a simple MUC connection plugin
+ * can only handle a single MUC room
+ */
+
+var bridgeIsDown = false;
+
+var Moderator = require("./moderator");
+
+module.exports = function(XMPP, eventEmitter) {
+ Strophe.addConnectionPlugin('emuc', {
+ connection: null,
+ roomjid: null,
+ myroomjid: null,
+ members: {},
+ list_members: [], // so we can elect a new focus
+ presMap: {},
+ preziMap: {},
+ joined: false,
+ isOwner: false,
+ role: null,
+ init: function (conn) {
+ this.connection = conn;
+ },
+ initPresenceMap: function (myroomjid) {
+ this.presMap['to'] = myroomjid;
+ this.presMap['xns'] = 'http://jabber.org/protocol/muc';
+ },
+ doJoin: function (jid, password) {
+ this.myroomjid = jid;
+
+ console.info("Joined MUC as " + this.myroomjid);
+
+ this.initPresenceMap(this.myroomjid);
+
+ if (!this.roomjid) {
+ this.roomjid = Strophe.getBareJidFromJid(jid);
+ // add handlers (just once)
+ this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
+ this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
+ }
+ if (password !== undefined) {
+ this.presMap['password'] = password;
+ }
+ this.sendPresence();
+ },
+ doLeave: function () {
+ console.log("do leave", this.myroomjid);
+ var pres = $pres({to: this.myroomjid, type: 'unavailable' });
+ this.presMap.length = 0;
+ this.connection.send(pres);
+ },
+ createNonAnonymousRoom: function () {
+ // http://xmpp.org/extensions/xep-0045.html#createroom-reserved
+
+ var getForm = $iq({type: 'get', to: this.roomjid})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
+ .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+
+ this.connection.sendIQ(getForm, function (form) {
+
+ if (!$(form).find(
+ '>query>x[xmlns="jabber:x:data"]' +
+ '>field[var="muc#roomconfig_whois"]').length) {
+
+ console.error('non-anonymous rooms not supported');
+ return;
+ }
+
+ var formSubmit = $iq({to: this.roomjid, type: 'set'})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
+
+ formSubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+
+ formSubmit.c('field', {'var': 'FORM_TYPE'})
+ .c('value')
+ .t('http://jabber.org/protocol/muc#roomconfig').up().up();
+
+ formSubmit.c('field', {'var': 'muc#roomconfig_whois'})
+ .c('value').t('anyone').up().up();
+
+ this.connection.sendIQ(formSubmit);
+
+ }, function (error) {
+ console.error("Error getting room configuration form");
+ });
+ },
+ onPresence: function (pres) {
+ var from = pres.getAttribute('from');
+
+ // What is this for? A workaround for something?
+ if (pres.getAttribute('type')) {
+ return true;
+ }
+
+ // Parse etherpad tag.
+ var etherpad = $(pres).find('>etherpad');
+ if (etherpad.length) {
+ if (config.etherpad_base && !Moderator.isModerator()) {
+ UI.initEtherpad(etherpad.text());
+ }
+ }
+
+ // Parse prezi tag.
+ var presentation = $(pres).find('>prezi');
+ if (presentation.length) {
+ var url = presentation.attr('url');
+ var current = presentation.find('>current').text();
+
+ console.log('presentation info received from', from, url);
+
+ if (this.preziMap[from] == null) {
+ this.preziMap[from] = url;
+
+ $(document).trigger('presentationadded.muc', [from, url, current]);
+ }
+ else {
+ $(document).trigger('gotoslide.muc', [from, url, current]);
+ }
+ }
+ else if (this.preziMap[from] != null) {
+ var url = this.preziMap[from];
+ delete this.preziMap[from];
+ $(document).trigger('presentationremoved.muc', [from, url]);
+ }
+
+ // Parse audio info tag.
+ var audioMuted = $(pres).find('>audiomuted');
+ if (audioMuted.length) {
+ $(document).trigger('audiomuted.muc', [from, audioMuted.text()]);
+ }
+
+ // Parse video info tag.
+ var videoMuted = $(pres).find('>videomuted');
+ if (videoMuted.length) {
+ $(document).trigger('videomuted.muc', [from, videoMuted.text()]);
+ }
+
+ var stats = $(pres).find('>stats');
+ if (stats.length) {
+ var statsObj = {};
+ Strophe.forEachChild(stats[0], "stat", function (el) {
+ statsObj[el.getAttribute("name")] = el.getAttribute("value");
+ });
+ connectionquality.updateRemoteStats(from, statsObj);
+ }
+
+ // Parse status.
+ if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
+ this.isOwner = true;
+ this.createNonAnonymousRoom();
+ }
+
+ // Parse roles.
+ var member = {};
+ member.show = $(pres).find('>show').text();
+ member.status = $(pres).find('>status').text();
+ var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
+ member.affiliation = tmp.attr('affiliation');
+ member.role = tmp.attr('role');
+
+ // Focus recognition
+ member.jid = tmp.attr('jid');
+ member.isFocus = false;
+ if (member.jid
+ && member.jid.indexOf(Moderator.getFocusUserJid() + "/") == 0) {
+ member.isFocus = true;
+ }
+
+ var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
+ member.displayName = (nicktag.length > 0 ? nicktag.html() : null);
+
+ if (from == this.myroomjid) {
+ if (member.affiliation == 'owner') this.isOwner = true;
+ if (this.role !== member.role) {
+ this.role = member.role;
+ if (Moderator.onLocalRoleChange)
+ Moderator.onLocalRoleChange(from, member, pres);
+ UI.onLocalRoleChange(from, member, pres);
+ }
+ if (!this.joined) {
+ this.joined = true;
+ eventEmitter.emit(XMPPEvents.MUC_JOINED, from, member);
+ this.list_members.push(from);
+ }
+ } else if (this.members[from] === undefined) {
+ // new participant
+ this.members[from] = member;
+ this.list_members.push(from);
+ console.log('entered', from, member);
+ if (member.isFocus) {
+ focusMucJid = from;
+ console.info("Ignore focus: " + from + ", real JID: " + member.jid);
+ }
+ else {
+ var id = $(pres).find('>userID').text();
+ var email = $(pres).find('>email');
+ if (email.length > 0) {
+ id = email.text();
+ }
+ UI.onMucEntered(from, id, member.displayName);
+ API.triggerEvent("participantJoined", {jid: from});
+ }
+ } else {
+ // Presence update for existing participant
+ // Watch role change:
+ if (this.members[from].role != member.role) {
+ this.members[from].role = member.role;
+ UI.onMucRoleChanged(member.role, member.displayName);
+ }
+ }
+
+ // Always trigger presence to update bindings
+ $(document).trigger('presence.muc', [from, member, pres]);
+ this.parsePresence(from, member, pres);
+
+ // Trigger status message update
+ if (member.status) {
+ UI.onMucPresenceStatus(from, member);
+ }
+
+ return true;
+ },
+ onPresenceUnavailable: function (pres) {
+ var from = pres.getAttribute('from');
+ // Status code 110 indicates that this notification is "self-presence".
+ if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
+ delete this.members[from];
+ this.list_members.splice(this.list_members.indexOf(from), 1);
+ this.onParticipantLeft(from);
+ }
+ // If the status code is 110 this means we're leaving and we would like
+ // to remove everyone else from our view, so we trigger the event.
+ else if (this.list_members.length > 1) {
+ for (var i = 0; i < this.list_members.length; i++) {
+ var member = this.list_members[i];
+ delete this.members[i];
+ this.list_members.splice(i, 1);
+ this.onParticipantLeft(member);
+ }
+ }
+ if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length) {
+ $(document).trigger('kicked.muc', [from]);
+ if (this.myroomjid === from) {
+ XMPP.disposeConference(false);
+ eventEmitter.emit(XMPPEvents.KICKED);
+ }
+ }
+ return true;
+ },
+ onPresenceError: function (pres) {
+ var from = pres.getAttribute('from');
+ if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
+ console.log('on password required', from);
+ var self = this;
+ UI.onPasswordReqiured(function (value) {
+ self.doJoin(from, value);
+ });
+ } else if ($(pres).find(
+ '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
+ var toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
+ if (toDomain === config.hosts.anonymousdomain) {
+ // we are connected with anonymous domain and only non anonymous users can create rooms
+ // we must authorize the user
+ XMPP.promptLogin();
+ } else {
+ console.warn('onPresError ', pres);
+ UI.messageHandler.openReportDialog(null,
+ 'Oops! Something went wrong and we couldn`t connect to the conference.',
+ pres);
+ }
+ } else {
+ console.warn('onPresError ', pres);
+ UI.messageHandler.openReportDialog(null,
+ 'Oops! Something went wrong and we couldn`t connect to the conference.',
+ pres);
+ }
+ return true;
+ },
+ sendMessage: function (body, nickname) {
+ var msg = $msg({to: this.roomjid, type: 'groupchat'});
+ msg.c('body', body).up();
+ if (nickname) {
+ msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
+ }
+ this.connection.send(msg);
+ API.triggerEvent("outgoingMessage", {"message": body});
+ },
+ setSubject: function (subject) {
+ var msg = $msg({to: this.roomjid, type: 'groupchat'});
+ msg.c('subject', subject);
+ this.connection.send(msg);
+ console.log("topic changed to " + subject);
+ },
+ onMessage: function (msg) {
+ // FIXME: this is a hack. but jingle on muc makes nickchanges hard
+ var from = msg.getAttribute('from');
+ var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(from);
+
+ var txt = $(msg).find('>body').text();
+ var type = msg.getAttribute("type");
+ if (type == "error") {
+ UI.chatAddError($(msg).find('>text').text(), txt);
+ return true;
+ }
+
+ var subject = $(msg).find('>subject');
+ if (subject.length) {
+ var subjectText = subject.text();
+ if (subjectText || subjectText == "") {
+ UI.chatSetSubject(subjectText);
+ console.log("Subject is changed to " + subjectText);
+ }
+ }
+
+
+ if (txt) {
+ console.log('chat', nick, txt);
+ UI.updateChatConversation(from, nick, txt);
+ if (from != this.myroomjid)
+ API.triggerEvent("incomingMessage",
+ {"from": from, "nick": nick, "message": txt});
+ }
+ return true;
+ },
+ lockRoom: function (key, onSuccess, onError, onNotSupported) {
+ //http://xmpp.org/extensions/xep-0045.html#roomconfig
+ var ob = this;
+ this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
+ function (res) {
+ if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
+ var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
+ formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
+ formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
+ formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
+ // Fixes a bug in prosody 0.9.+ https://code.google.com/p/lxmppd/issues/detail?id=373
+ formsubmit.c('field', {'var': 'muc#roomconfig_whois'}).c('value').t('anyone').up().up();
+ // FIXME: is muc#roomconfig_passwordprotectedroom required?
+ this.connection.sendIQ(formsubmit,
+ onSuccess,
+ onError);
+ } else {
+ onNotSupported();
+ }
+ }, onError);
+ },
+ kick: function (jid) {
+ var kickIQ = $iq({to: this.roomjid, type: 'set'})
+ .c('query', {xmlns: 'http://jabber.org/protocol/muc#admin'})
+ .c('item', {nick: Strophe.getResourceFromJid(jid), role: 'none'})
+ .c('reason').t('You have been kicked.').up().up().up();
+
+ this.connection.sendIQ(
+ kickIQ,
+ function (result) {
+ console.log('Kick participant with jid: ', jid, result);
+ },
+ function (error) {
+ console.log('Kick participant error: ', error);
+ });
+ },
+ sendPresence: function () {
+ var pres = $pres({to: this.presMap['to'] });
+ pres.c('x', {xmlns: this.presMap['xns']});
+
+ if (this.presMap['password']) {
+ pres.c('password').t(this.presMap['password']).up();
+ }
+
+ pres.up();
+
+ // Send XEP-0115 'c' stanza that contains our capabilities info
+ if (this.connection.caps) {
+ this.connection.caps.node = config.clientNode;
+ pres.c('c', this.connection.caps.generateCapsAttrs()).up();
+ }
+
+ pres.c('user-agent', {xmlns: 'http://jitsi.org/jitmeet/user-agent'})
+ .t(navigator.userAgent).up();
+
+ if (this.presMap['bridgeIsDown']) {
+ pres.c('bridgeIsDown').up();
+ }
+
+ if (this.presMap['email']) {
+ pres.c('email').t(this.presMap['email']).up();
+ }
+
+ if (this.presMap['userId']) {
+ pres.c('userId').t(this.presMap['userId']).up();
+ }
+
+ if (this.presMap['displayName']) {
+ // XEP-0172
+ pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'})
+ .t(this.presMap['displayName']).up();
+ }
+
+ if (this.presMap['audions']) {
+ pres.c('audiomuted', {xmlns: this.presMap['audions']})
+ .t(this.presMap['audiomuted']).up();
+ }
+
+ if (this.presMap['videons']) {
+ pres.c('videomuted', {xmlns: this.presMap['videons']})
+ .t(this.presMap['videomuted']).up();
+ }
+
+ if (this.presMap['statsns']) {
+ var stats = pres.c('stats', {xmlns: this.presMap['statsns']});
+ for (var stat in this.presMap["stats"])
+ if (this.presMap["stats"][stat] != null)
+ stats.c("stat", {name: stat, value: this.presMap["stats"][stat]}).up();
+ pres.up();
+ }
+
+ if (this.presMap['prezins']) {
+ pres.c('prezi',
+ {xmlns: this.presMap['prezins'],
+ 'url': this.presMap['preziurl']})
+ .c('current').t(this.presMap['prezicurrent']).up().up();
+ }
+
+ if (this.presMap['etherpadns']) {
+ pres.c('etherpad', {xmlns: this.presMap['etherpadns']})
+ .t(this.presMap['etherpadname']).up();
+ }
+
+ if (this.presMap['medians']) {
+ pres.c('media', {xmlns: this.presMap['medians']});
+ var sourceNumber = 0;
+ Object.keys(this.presMap).forEach(function (key) {
+ if (key.indexOf('source') >= 0) {
+ sourceNumber++;
+ }
+ });
+ if (sourceNumber > 0)
+ for (var i = 1; i <= sourceNumber / 3; i++) {
+ pres.c('source',
+ {type: this.presMap['source' + i + '_type'],
+ ssrc: this.presMap['source' + i + '_ssrc'],
+ direction: this.presMap['source' + i + '_direction']
+ || 'sendrecv' }
+ ).up();
+ }
+ }
+
+ pres.up();
+// console.debug(pres.toString());
+ this.connection.send(pres);
+ },
+ addDisplayNameToPresence: function (displayName) {
+ this.presMap['displayName'] = displayName;
+ },
+ addMediaToPresence: function (sourceNumber, mtype, ssrcs, direction) {
+ if (!this.presMap['medians'])
+ this.presMap['medians'] = 'http://estos.de/ns/mjs';
+
+ this.presMap['source' + sourceNumber + '_type'] = mtype;
+ this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
+ this.presMap['source' + sourceNumber + '_direction'] = direction;
+ },
+ clearPresenceMedia: function () {
+ var self = this;
+ Object.keys(this.presMap).forEach(function (key) {
+ if (key.indexOf('source') != -1) {
+ delete self.presMap[key];
+ }
+ });
+ },
+ addPreziToPresence: function (url, currentSlide) {
+ this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
+ this.presMap['preziurl'] = url;
+ this.presMap['prezicurrent'] = currentSlide;
+ },
+ removePreziFromPresence: function () {
+ delete this.presMap['prezins'];
+ delete this.presMap['preziurl'];
+ delete this.presMap['prezicurrent'];
+ },
+ addCurrentSlideToPresence: function (currentSlide) {
+ this.presMap['prezicurrent'] = currentSlide;
+ },
+ getPrezi: function (roomjid) {
+ return this.preziMap[roomjid];
+ },
+ addEtherpadToPresence: function (etherpadName) {
+ this.presMap['etherpadns'] = 'http://jitsi.org/jitmeet/etherpad';
+ this.presMap['etherpadname'] = etherpadName;
+ },
+ addAudioInfoToPresence: function (isMuted) {
+ this.presMap['audions'] = 'http://jitsi.org/jitmeet/audio';
+ this.presMap['audiomuted'] = isMuted.toString();
+ },
+ addVideoInfoToPresence: function (isMuted) {
+ this.presMap['videons'] = 'http://jitsi.org/jitmeet/video';
+ this.presMap['videomuted'] = isMuted.toString();
+ },
+ addConnectionInfoToPresence: function (stats) {
+ this.presMap['statsns'] = 'http://jitsi.org/jitmeet/stats';
+ this.presMap['stats'] = stats;
+ },
+ findJidFromResource: function (resourceJid) {
+ if (resourceJid &&
+ resourceJid === Strophe.getResourceFromJid(this.myroomjid)) {
+ return this.myroomjid;
+ }
+ var peerJid = null;
+ Object.keys(this.members).some(function (jid) {
+ peerJid = jid;
+ return Strophe.getResourceFromJid(jid) === resourceJid;
+ });
+ return peerJid;
+ },
+ addBridgeIsDownToPresence: function () {
+ this.presMap['bridgeIsDown'] = true;
+ },
+ addEmailToPresence: function (email) {
+ this.presMap['email'] = email;
+ },
+ addUserIdToPresence: function (userId) {
+ this.presMap['userId'] = userId;
+ },
+ isModerator: function () {
+ return this.role === 'moderator';
+ },
+ getMemberRole: function (peerJid) {
+ if (this.members[peerJid]) {
+ return this.members[peerJid].role;
+ }
+ return null;
+ },
+ onParticipantLeft: function (jid) {
+ UI.onMucLeft(jid);
+
+ API.triggerEvent("participantLeft", {jid: jid});
+
+ delete jid2Ssrc[jid];
+
+ this.connection.jingle.terminateByJid(jid);
+
+ if (this.getPrezi(jid)) {
+ $(document).trigger('presentationremoved.muc',
+ [jid, this.getPrezi(jid)]);
+ }
+
+ Moderator.onMucLeft(jid);
+ },
+ parsePresence: function (from, memeber, pres) {
+ if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
+ bridgeIsDown = true;
+ eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);
+ }
+
+ if(memeber.isFocus)
+ return;
+
+ // Remove old ssrcs coming from the jid
+ Object.keys(ssrc2jid).forEach(function (ssrc) {
+ if (ssrc2jid[ssrc] == jid) {
+ delete ssrc2jid[ssrc];
+ delete ssrc2videoType[ssrc];
+ }
+ });
+
+ var changedStreams = [];
+ $(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
+ //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
+ var ssrcV = ssrc.getAttribute('ssrc');
+ ssrc2jid[ssrcV] = from;
+ notReceivedSSRCs.push(ssrcV);
+
+ var type = ssrc.getAttribute('type');
+ ssrc2videoType[ssrcV] = type;
+
+ var direction = ssrc.getAttribute('direction');
+
+ changedStreams.push({type: type, direction: direction});
+
+ });
+
+ eventEmitter.emit(XMPPEvents.CHANGED_STREAMS, from, changedStreams);
+
+ var displayName = !config.displayJids
+ ? memeber.displayName : Strophe.getResourceFromJid(from);
+
+ if (displayName && displayName.length > 0)
+ {
+// $(document).trigger('displaynamechanged',
+// [jid, displayName]);
+ eventEmitter.emit(XMPPEvents.DISPLAY_NAME_CHANGED, from, displayName);
+ }
+
+
+ var id = $(pres).find('>userID').text();
+ var email = $(pres).find('>email');
+ if(email.length > 0) {
+ id = email.text();
+ }
+
+ eventEmitter.emit(XMPPEvents.USER_ID_CHANGED, from, id);
+ }
+ });
+};
+
diff --git a/modules/xmpp/strophe.jingle.js b/modules/xmpp/strophe.jingle.js
new file mode 100644
index 000000000..4878d587c
--- /dev/null
+++ b/modules/xmpp/strophe.jingle.js
@@ -0,0 +1,334 @@
+/* jshint -W117 */
+
+var JingleSession = require("./JingleSession");
+
+function CallIncomingJingle(sid, connection) {
+ var sess = connection.jingle.sessions[sid];
+
+ // TODO: do we check activecall == null?
+ activecall = sess;
+
+ statistics.onConferenceCreated(sess);
+ RTC.onConferenceCreated(sess);
+
+ // TODO: check affiliation and/or role
+ console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
+ sess.usedrip = true; // not-so-naive trickle ice
+ sess.sendAnswer();
+ sess.accept();
+
+};
+
+module.exports = function(XMPP)
+{
+ Strophe.addConnectionPlugin('jingle', {
+ connection: null,
+ sessions: {},
+ jid2session: {},
+ ice_config: {iceServers: []},
+ pc_constraints: {},
+ media_constraints: {
+ mandatory: {
+ 'OfferToReceiveAudio': true,
+ 'OfferToReceiveVideo': true
+ }
+ // MozDontOfferDataChannel: true when this is firefox
+ },
+ init: function (conn) {
+ this.connection = conn;
+ if (this.connection.disco) {
+ // http://xmpp.org/extensions/xep-0167.html#support
+ // http://xmpp.org/extensions/xep-0176.html#support
+ this.connection.disco.addFeature('urn:xmpp:jingle:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
+ this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');
+
+
+ // this is dealt with by SDP O/A so we don't need to annouce this
+ //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
+ //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
+ if (config.useRtcpMux) {
+ this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
+ }
+ if (config.useBundle) {
+ this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
+ }
+ //this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
+ }
+ this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
+ },
+ onJingle: function (iq) {
+ var sid = $(iq).find('jingle').attr('sid');
+ var action = $(iq).find('jingle').attr('action');
+ var fromJid = iq.getAttribute('from');
+ // send ack first
+ var ack = $iq({type: 'result',
+ to: fromJid,
+ id: iq.getAttribute('id')
+ });
+ console.log('on jingle ' + action + ' from ' + fromJid, iq);
+ var sess = this.sessions[sid];
+ if ('session-initiate' != action) {
+ if (sess === null) {
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
+ .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
+ this.connection.send(ack);
+ return true;
+ }
+ // compare from to sess.peerjid (bare jid comparison for later compat with message-mode)
+ // local jid is not checked
+ if (Strophe.getBareJidFromJid(fromJid) != Strophe.getBareJidFromJid(sess.peerjid)) {
+ console.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
+ .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
+ this.connection.send(ack);
+ return true;
+ }
+ } else if (sess !== undefined) {
+ // existing session with same session id
+ // this might be out-of-order if the sess.peerjid is the same as from
+ ack.type = 'error';
+ ack.c('error', {type: 'cancel'})
+ .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
+ console.warn('duplicate session id', sid);
+ this.connection.send(ack);
+ return true;
+ }
+ // FIXME: check for a defined action
+ this.connection.send(ack);
+ // see http://xmpp.org/extensions/xep-0166.html#concepts-session
+ switch (action) {
+ case 'session-initiate':
+ sess = new JingleSession(
+ $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
+ this.connection, XMPP);
+ // configure session
+
+ sess.media_constraints = this.media_constraints;
+ sess.pc_constraints = this.pc_constraints;
+ sess.ice_config = this.ice_config;
+
+ sess.initiate(fromJid, false);
+ // FIXME: setRemoteDescription should only be done when this call is to be accepted
+ sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
+
+ this.sessions[sess.sid] = sess;
+ this.jid2session[sess.peerjid] = sess;
+
+ // the callback should either
+ // .sendAnswer and .accept
+ // or .sendTerminate -- not necessarily synchronus
+ CallIncomingJingle(sess.sid, this.connection);
+ break;
+ case 'session-accept':
+ sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
+ sess.accept();
+ $(document).trigger('callaccepted.jingle', [sess.sid]);
+ break;
+ case 'session-terminate':
+ // If this is not the focus sending the terminate, we have
+ // nothing more to do here.
+ if (Object.keys(this.sessions).length < 1
+ || !(this.sessions[Object.keys(this.sessions)[0]]
+ instanceof JingleSession))
+ {
+ break;
+ }
+ console.log('terminating...', sess.sid);
+ sess.terminate();
+ this.terminate(sess.sid);
+ if ($(iq).find('>jingle>reason').length) {
+ $(document).trigger('callterminated.jingle', [
+ sess.sid,
+ sess.peerjid,
+ $(iq).find('>jingle>reason>:first')[0].tagName,
+ $(iq).find('>jingle>reason>text').text()
+ ]);
+ } else {
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, sess.peerjid]);
+ }
+ break;
+ case 'transport-info':
+ sess.addIceCandidate($(iq).find('>jingle>content'));
+ break;
+ case 'session-info':
+ var affected;
+ if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ $(document).trigger('ringing.jingle', [sess.sid]);
+ } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
+ $(document).trigger('mute.jingle', [sess.sid, affected]);
+ } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
+ affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
+ $(document).trigger('unmute.jingle', [sess.sid, affected]);
+ }
+ break;
+ case 'addsource': // FIXME: proprietary, un-jingleish
+ case 'source-add': // FIXME: proprietary
+ sess.addSource($(iq).find('>jingle>content'), fromJid);
+ break;
+ case 'removesource': // FIXME: proprietary, un-jingleish
+ case 'source-remove': // FIXME: proprietary
+ sess.removeSource($(iq).find('>jingle>content'), fromJid);
+ break;
+ default:
+ console.warn('jingle action not implemented', action);
+ break;
+ }
+ return true;
+ },
+ initiate: function (peerjid, myjid) { // initiate a new jinglesession to peerjid
+ var sess = new JingleSession(myjid || this.connection.jid,
+ Math.random().toString(36).substr(2, 12), // random string
+ this.connection, XMPP);
+ // configure session
+
+ sess.media_constraints = this.media_constraints;
+ sess.pc_constraints = this.pc_constraints;
+ sess.ice_config = this.ice_config;
+
+ sess.initiate(peerjid, true);
+ this.sessions[sess.sid] = sess;
+ this.jid2session[sess.peerjid] = sess;
+ sess.sendOffer();
+ return sess;
+ },
+ terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
+ if (sid === null || sid === undefined) {
+ for (sid in this.sessions) {
+ if (this.sessions[sid].state != 'ended') {
+ this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
+ this.sessions[sid].terminate();
+ }
+ delete this.jid2session[this.sessions[sid].peerjid];
+ delete this.sessions[sid];
+ }
+ } else if (this.sessions.hasOwnProperty(sid)) {
+ if (this.sessions[sid].state != 'ended') {
+ this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
+ this.sessions[sid].terminate();
+ }
+ delete this.jid2session[this.sessions[sid].peerjid];
+ delete this.sessions[sid];
+ }
+ },
+ // Used to terminate a session when an unavailable presence is received.
+ terminateByJid: function (jid) {
+ if (this.jid2session.hasOwnProperty(jid)) {
+ var sess = this.jid2session[jid];
+ if (sess) {
+ sess.terminate();
+ console.log('peer went away silently', jid);
+ delete this.sessions[sess.sid];
+ delete this.jid2session[jid];
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, jid], 'gone');
+ }
+ }
+ },
+ terminateRemoteByJid: function (jid, reason) {
+ if (this.jid2session.hasOwnProperty(jid)) {
+ var sess = this.jid2session[jid];
+ if (sess) {
+ sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
+ sess.terminate();
+ console.log('terminate peer with jid', sess.sid, jid);
+ delete this.sessions[sess.sid];
+ delete this.jid2session[jid];
+ $(document).trigger('callterminated.jingle',
+ [sess.sid, jid, 'kicked']);
+ }
+ }
+ },
+ getStunAndTurnCredentials: function () {
+ // get stun and turn configuration from server via xep-0215
+ // uses time-limited credentials as described in
+ // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
+ //
+ // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
+ // for a prosody module which implements this
+ //
+ // currently, this doesn't work with updateIce and therefore credentials with a long
+ // validity have to be fetched before creating the peerconnection
+ // TODO: implement refresh via updateIce as described in
+ // https://code.google.com/p/webrtc/issues/detail?id=1650
+ var self = this;
+ this.connection.sendIQ(
+ $iq({type: 'get', to: this.connection.domain})
+ .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
+ function (res) {
+ var iceservers = [];
+ $(res).find('>services>service').each(function (idx, el) {
+ el = $(el);
+ var dict = {};
+ var type = el.attr('type');
+ switch (type) {
+ case 'stun':
+ dict.url = 'stun:' + el.attr('host');
+ if (el.attr('port')) {
+ dict.url += ':' + el.attr('port');
+ }
+ iceservers.push(dict);
+ break;
+ case 'turn':
+ case 'turns':
+ dict.url = type + ':';
+ if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
+ if (navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10) < 28) {
+ dict.url += el.attr('username') + '@';
+ } else {
+ dict.username = el.attr('username'); // only works in M28
+ }
+ }
+ dict.url += el.attr('host');
+ if (el.attr('port') && el.attr('port') != '3478') {
+ dict.url += ':' + el.attr('port');
+ }
+ if (el.attr('transport') && el.attr('transport') != 'udp') {
+ dict.url += '?transport=' + el.attr('transport');
+ }
+ if (el.attr('password')) {
+ dict.credential = el.attr('password');
+ }
+ iceservers.push(dict);
+ break;
+ }
+ });
+ self.ice_config.iceServers = iceservers;
+ },
+ function (err) {
+ console.warn('getting turn credentials failed', err);
+ console.warn('is mod_turncredentials or similar installed?');
+ }
+ );
+ // implement push?
+ },
+
+ /**
+ * Populates the log data
+ */
+ populateData: function () {
+ var data = {};
+ Object.keys(this.sessions).forEach(function (sid) {
+ var session = this.sessions[sid];
+ if (session.peerconnection && session.peerconnection.updateLog) {
+ // FIXME: should probably be a .dump call
+ data["jingle_" + session.sid] = {
+ updateLog: session.peerconnection.updateLog,
+ stats: session.peerconnection.stats,
+ url: window.location.href
+ };
+ }
+ });
+ return data;
+ }
+ });
+};
+
diff --git a/modules/xmpp/strophe.logger.js b/modules/xmpp/strophe.logger.js
new file mode 100644
index 000000000..4866ff89b
--- /dev/null
+++ b/modules/xmpp/strophe.logger.js
@@ -0,0 +1,20 @@
+/* global Strophe */
+module.exports = function () {
+
+ Strophe.addConnectionPlugin('logger', {
+ // logs raw stanzas and makes them available for download as JSON
+ connection: null,
+ log: [],
+ init: function (conn) {
+ this.connection = conn;
+ this.connection.rawInput = this.log_incoming.bind(this);
+ this.connection.rawOutput = this.log_outgoing.bind(this);
+ },
+ log_incoming: function (stanza) {
+ this.log.push([new Date().getTime(), 'incoming', stanza]);
+ },
+ log_outgoing: function (stanza) {
+ this.log.push([new Date().getTime(), 'outgoing', stanza]);
+ }
+ });
+};
\ No newline at end of file
diff --git a/modules/xmpp/strophe.moderate.js b/modules/xmpp/strophe.moderate.js
new file mode 100644
index 000000000..64a8bccfa
--- /dev/null
+++ b/modules/xmpp/strophe.moderate.js
@@ -0,0 +1,58 @@
+/* global $, $iq, config, connection, focusMucJid, forceMuted,
+ setAudioMuted, Strophe */
+/**
+ * Moderate connection plugin.
+ */
+module.exports = function (XMPP) {
+ Strophe.addConnectionPlugin('moderate', {
+ connection: null,
+ init: function (conn) {
+ this.connection = conn;
+
+ this.connection.addHandler(this.onMute.bind(this),
+ 'http://jitsi.org/jitmeet/audio',
+ 'iq',
+ 'set',
+ null,
+ null);
+ },
+ setMute: function (jid, mute) {
+ console.info("set mute", mute);
+ var iqToFocus = $iq({to: focusMucJid, type: 'set'})
+ .c('mute', {
+ xmlns: 'http://jitsi.org/jitmeet/audio',
+ jid: jid
+ })
+ .t(mute.toString())
+ .up();
+
+ this.connection.sendIQ(
+ iqToFocus,
+ function (result) {
+ console.log('set mute', result);
+ },
+ function (error) {
+ console.log('set mute error', error);
+ });
+ },
+ onMute: function (iq) {
+ var from = iq.getAttribute('from');
+ if (from !== focusMucJid) {
+ console.warn("Ignored mute from non focus peer");
+ return false;
+ }
+ var mute = $(iq).find('mute');
+ if (mute.length) {
+ var doMuteAudio = mute.text() === "true";
+ UI.setAudioMuted(doMuteAudio);
+ XMPP.forceMuted = doMuteAudio;
+ }
+ return true;
+ },
+ eject: function (jid) {
+ // We're not the focus, so can't terminate
+ //connection.jingle.terminateRemoteByJid(jid, 'kick');
+ this.connection.emuc.kick(jid);
+ }
+ });
+}
\ No newline at end of file
diff --git a/modules/xmpp/strophe.rayo.js b/modules/xmpp/strophe.rayo.js
new file mode 100644
index 000000000..9d0db5547
--- /dev/null
+++ b/modules/xmpp/strophe.rayo.js
@@ -0,0 +1,95 @@
+/* jshint -W117 */
+module.exports = function() {
+ Strophe.addConnectionPlugin('rayo',
+ {
+ RAYO_XMLNS: 'urn:xmpp:rayo:1',
+ connection: null,
+ init: function (conn) {
+ this.connection = conn;
+ if (this.connection.disco) {
+ this.connection.disco.addFeature('urn:xmpp:rayo:client:1');
+ }
+
+ this.connection.addHandler(
+ this.onRayo.bind(this), this.RAYO_XMLNS, 'iq', 'set', null, null);
+ },
+ onRayo: function (iq) {
+ console.info("Rayo IQ", iq);
+ },
+ dial: function (to, from, roomName, roomPass) {
+ var self = this;
+ var req = $iq(
+ {
+ type: 'set',
+ to: focusMucJid
+ }
+ );
+ req.c('dial',
+ {
+ xmlns: this.RAYO_XMLNS,
+ to: to,
+ from: from
+ });
+ req.c('header',
+ {
+ name: 'JvbRoomName',
+ value: roomName
+ }).up();
+
+ if (roomPass !== null && roomPass.length) {
+
+ req.c('header',
+ {
+ name: 'JvbRoomPassword',
+ value: roomPass
+ }).up();
+ }
+
+ this.connection.sendIQ(
+ req,
+ function (result) {
+ console.info('Dial result ', result);
+
+ var resource = $(result).find('ref').attr('uri');
+ this.call_resource = resource.substr('xmpp:'.length);
+ console.info(
+ "Received call resource: " + this.call_resource);
+ },
+ function (error) {
+ console.info('Dial error ', error);
+ }
+ );
+ },
+ hang_up: function () {
+ if (!this.call_resource) {
+ console.warn("No call in progress");
+ return;
+ }
+
+ var self = this;
+ var req = $iq(
+ {
+ type: 'set',
+ to: this.call_resource
+ }
+ );
+ req.c('hangup',
+ {
+ xmlns: this.RAYO_XMLNS
+ });
+
+ this.connection.sendIQ(
+ req,
+ function (result) {
+ console.info('Hangup result ', result);
+ self.call_resource = null;
+ },
+ function (error) {
+ console.info('Hangup error ', error);
+ self.call_resource = null;
+ }
+ );
+ }
+ }
+ );
+};
diff --git a/modules/xmpp/strophe.util.js b/modules/xmpp/strophe.util.js
new file mode 100644
index 000000000..b7d834828
--- /dev/null
+++ b/modules/xmpp/strophe.util.js
@@ -0,0 +1,42 @@
+/**
+ * Strophe logger implementation. Logs from level WARN and above.
+ */
+module.exports = function () {
+
+ Strophe.log = function (level, msg) {
+ switch (level) {
+ case Strophe.LogLevel.WARN:
+ console.warn("Strophe: " + msg);
+ break;
+ case Strophe.LogLevel.ERROR:
+ case Strophe.LogLevel.FATAL:
+ console.error("Strophe: " + msg);
+ break;
+ }
+ };
+
+ Strophe.getStatusString = function (status) {
+ switch (status) {
+ case Strophe.Status.ERROR:
+ return "ERROR";
+ case Strophe.Status.CONNECTING:
+ return "CONNECTING";
+ case Strophe.Status.CONNFAIL:
+ return "CONNFAIL";
+ case Strophe.Status.AUTHENTICATING:
+ return "AUTHENTICATING";
+ case Strophe.Status.AUTHFAIL:
+ return "AUTHFAIL";
+ case Strophe.Status.CONNECTED:
+ return "CONNECTED";
+ case Strophe.Status.DISCONNECTED:
+ return "DISCONNECTED";
+ case Strophe.Status.DISCONNECTING:
+ return "DISCONNECTING";
+ case Strophe.Status.ATTACHED:
+ return "ATTACHED";
+ default:
+ return "unknown";
+ }
+ };
+};
diff --git a/modules/xmpp/xmpp.js b/modules/xmpp/xmpp.js
new file mode 100644
index 000000000..d7ad4d2dc
--- /dev/null
+++ b/modules/xmpp/xmpp.js
@@ -0,0 +1,422 @@
+var Moderator = require("./moderator");
+var EventEmitter = require("events");
+var Recording = require("./recording");
+var SDP = require("./SDP");
+
+var eventEmitter = new EventEmitter();
+var connection = null;
+var authenticatedUser = false;
+var activecall = null;
+
+function connect(jid, password, uiCredentials) {
+ var bosh
+ = uiCredentials.bosh || config.bosh || '/http-bind';
+ connection = new Strophe.Connection(bosh);
+ Moderator.setConnection(connection);
+
+ var settings = UI.getSettings();
+ var email = settings.email;
+ var displayName = settings.displayName;
+ if(email) {
+ connection.emuc.addEmailToPresence(email);
+ } else {
+ connection.emuc.addUserIdToPresence(settings.uid);
+ }
+ if(displayName) {
+ connection.emuc.addDisplayNameToPresence(displayName);
+ }
+
+ if (connection.disco) {
+ // for chrome, add multistream cap
+ }
+ connection.jingle.pc_constraints = RTC.getPCConstraints();
+ if (config.useIPv6) {
+ // https://code.google.com/p/webrtc/issues/detail?id=2828
+ if (!connection.jingle.pc_constraints.optional)
+ connection.jingle.pc_constraints.optional = [];
+ connection.jingle.pc_constraints.optional.push({googIPv6: true});
+ }
+
+ if(!password)
+ password = uiCredentials.password;
+
+ var anonymousConnectionFailed = false;
+ connection.connect(jid, password, function (status, msg) {
+ console.log('Strophe status changed to',
+ Strophe.getStatusString(status));
+ if (status === Strophe.Status.CONNECTED) {
+ if (config.useStunTurn) {
+ connection.jingle.getStunAndTurnCredentials();
+ }
+ UI.disableConnect();
+
+ console.info("My Jabber ID: " + connection.jid);
+
+ if(password)
+ authenticatedUser = true;
+ maybeDoJoin();
+ } else if (status === Strophe.Status.CONNFAIL) {
+ if(msg === 'x-strophe-bad-non-anon-jid') {
+ anonymousConnectionFailed = true;
+ }
+ } else if (status === Strophe.Status.DISCONNECTED) {
+ if(anonymousConnectionFailed) {
+ // prompt user for username and password
+ XMPP.promptLogin();
+ }
+ } else if (status === Strophe.Status.AUTHFAIL) {
+ // wrong password or username, prompt user
+ XMPP.promptLogin();
+
+ }
+ });
+}
+
+
+
+function maybeDoJoin() {
+ if (connection && connection.connected &&
+ Strophe.getResourceFromJid(connection.jid)
+ && (RTC.localAudio || RTC.localVideo)) {
+ // .connected is true while connecting?
+ doJoin();
+ }
+}
+
+function doJoin() {
+ var roomName = UI.generateRoomName();
+
+ Moderator.allocateConferenceFocus(
+ roomName, UI.checkForNicknameAndJoin);
+}
+
+function initStrophePlugins()
+{
+ require("./strophe.emuc")(XMPP, eventEmitter);
+ require("./strophe.jingle")();
+ require("./strophe.moderate")(XMPP);
+ require("./strophe.util")();
+ require("./strophe.rayo")();
+ require("./strophe.logger")();
+}
+
+function registerListeners() {
+ RTC.addStreamListener(maybeDoJoin,
+ StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
+}
+
+function setupEvents() {
+ $(window).bind('beforeunload', function () {
+ if (connection && connection.connected) {
+ // ensure signout
+ $.ajax({
+ type: 'POST',
+ url: config.bosh,
+ async: false,
+ cache: false,
+ contentType: 'application/xml',
+ data: "" +
+ "" +
+ "",
+ success: function (data) {
+ console.log('signed out');
+ console.log(data);
+ },
+ error: function (XMLHttpRequest, textStatus, errorThrown) {
+ console.log('signout error',
+ textStatus + ' (' + errorThrown + ')');
+ }
+ });
+ }
+ XMPP.disposeConference(true);
+ });
+}
+
+var XMPP = {
+ sessionTerminated: false,
+ /**
+ * Remembers if we were muted by the focus.
+ * @type {boolean}
+ */
+ forceMuted: false,
+ start: function (uiCredentials) {
+ setupEvents();
+ initStrophePlugins();
+ registerListeners();
+ Moderator.init();
+ var jid = uiCredentials.jid ||
+ config.hosts.anonymousdomain ||
+ config.hosts.domain ||
+ window.location.hostname;
+ connect(jid, null, uiCredentials);
+ },
+ promptLogin: function () {
+ UI.showLoginPopup(connect);
+ },
+ joinRooom: function(roomName, useNicks, nick)
+ {
+ var roomjid;
+ roomjid = roomName;
+
+ if (useNicks) {
+ if (nick) {
+ roomjid += '/' + nick;
+ } else {
+ roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
+ }
+ } else {
+
+ var tmpJid = Strophe.getNodeFromJid(connection.jid);
+
+ if(!authenticatedUser)
+ tmpJid = tmpJid.substr(0, 8);
+
+ roomjid += '/' + tmpJid;
+ }
+ connection.emuc.doJoin(roomjid);
+ },
+ myJid: function () {
+ if(!connection)
+ return null;
+ return connection.emuc.myroomjid;
+ },
+ myResource: function () {
+ if(!connection || ! connection.emuc.myroomjid)
+ return null;
+ return Strophe.getResourceFromJid(connection.emuc.myroomjid);
+ },
+ disposeConference: function (onUnload) {
+ eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
+ var handler = activecall;
+ if (handler && handler.peerconnection) {
+ // FIXME: probably removing streams is not required and close() should
+ // be enough
+ if (RTC.localAudio) {
+ handler.peerconnection.removeStream(RTC.localAudio.getOriginalStream(), onUnload);
+ }
+ if (RTC.localVideo) {
+ handler.peerconnection.removeStream(RTC.localVideo.getOriginalStream(), onUnload);
+ }
+ handler.peerconnection.close();
+ }
+ activecall = null;
+ if(!onUnload)
+ {
+ this.sessionTerminated = true;
+ connection.emuc.doLeave();
+ }
+ },
+ addListener: function(type, listener)
+ {
+ eventEmitter.on(type, listener);
+ },
+ removeListener: function (type, listener) {
+ eventEmitter.removeListener(type, listener);
+ },
+ allocateConferenceFocus: function(roomName, callback) {
+ Moderator.allocateConferenceFocus(roomName, callback);
+ },
+ isModerator: function () {
+ return Moderator.isModerator();
+ },
+ isSipGatewayEnabled: function () {
+ return Moderator.isSipGatewayEnabled();
+ },
+ isExternalAuthEnabled: function () {
+ return Moderator.isExternalAuthEnabled();
+ },
+ switchStreams: function (stream, oldStream, callback) {
+ if (activecall) {
+ // FIXME: will block switchInProgress on true value in case of exception
+ activecall.switchStreams(stream, oldStream, callback);
+ } else {
+ // We are done immediately
+ console.error("No conference handler");
+ UI.messageHandler.showError('Error',
+ 'Unable to switch video stream.');
+ callback();
+ }
+ },
+ setVideoMute: function (mute, callback, options) {
+ if(activecall && connection && RTC.localVideo)
+ {
+ activecall.setVideoMute(mute, callback, options);
+ }
+ },
+ setAudioMute: function (mute, callback) {
+ if (!(connection && RTC.localAudio)) {
+ return false;
+ }
+
+
+ if (this.forceMuted && !mute) {
+ console.info("Asking focus for unmute");
+ connection.moderate.setMute(connection.emuc.myroomjid, mute);
+ // FIXME: wait for result before resetting muted status
+ this.forceMuted = false;
+ }
+
+ if (mute == RTC.localAudio.isMuted()) {
+ // Nothing to do
+ return true;
+ }
+
+ // It is not clear what is the right way to handle multiple tracks.
+ // So at least make sure that they are all muted or all unmuted and
+ // that we send presence just once.
+ RTC.localAudio.mute();
+ // isMuted is the opposite of audioEnabled
+ connection.emuc.addAudioInfoToPresence(mute);
+ connection.emuc.sendPresence();
+ callback();
+ return true;
+ },
+ // Really mute video, i.e. dont even send black frames
+ muteVideo: function (pc, unmute) {
+ // FIXME: this probably needs another of those lovely state safeguards...
+ // which checks for iceconn == connected and sigstate == stable
+ pc.setRemoteDescription(pc.remoteDescription,
+ function () {
+ pc.createAnswer(
+ function (answer) {
+ var sdp = new SDP(answer.sdp);
+ if (sdp.media.length > 1) {
+ if (unmute)
+ sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
+ else
+ sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
+ sdp.raw = sdp.session + sdp.media.join('');
+ answer.sdp = sdp.raw;
+ }
+ pc.setLocalDescription(answer,
+ function () {
+ console.log('mute SLD ok');
+ },
+ function (error) {
+ console.log('mute SLD error');
+ UI.messageHandler.showError('Error',
+ 'Oops! Something went wrong and we failed to ' +
+ 'mute! (SLD Failure)');
+ }
+ );
+ },
+ function (error) {
+ console.log(error);
+ UI.messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('muteVideo SRD error');
+ UI.messageHandler.showError('Error',
+ 'Oops! Something went wrong and we failed to stop video!' +
+ '(SRD Failure)');
+
+ }
+ );
+ },
+ toggleRecording: function (tokenEmptyCallback,
+ startingCallback, startedCallback) {
+ Recording.toggleRecording(tokenEmptyCallback,
+ startingCallback, startedCallback);
+ },
+ addToPresence: function (name, value, dontSend) {
+ switch (name)
+ {
+ case "displayName":
+ connection.emuc.addDisplayNameToPresence(value);
+ break;
+ case "etherpad":
+ connection.emuc.addEtherpadToPresence(value);
+ break;
+ case "prezi":
+ connection.emuc.addPreziToPresence(value, 0);
+ break;
+ case "preziSlide":
+ connection.emuc.addCurrentSlideToPresence(value);
+ break;
+ case "connectionQuality":
+ connection.emuc.addConnectionInfoToPresence(value);
+ break;
+ case "email":
+ connection.emuc.addEmailToPresence(value);
+ default :
+ console.log("Unknown tag for presence.");
+ return;
+ }
+ if(!dontSend)
+ connection.emuc.sendPresence();
+ },
+ sendLogs: function (data) {
+ if(!focusMucJid)
+ return;
+
+ var deflate = true;
+
+ var content = JSON.stringify(dataYes);
+ if (deflate) {
+ content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
+ }
+ content = Base64.encode(content);
+ // XEP-0337-ish
+ var message = $msg({to: focusMucJid, type: 'normal'});
+ message.c('log', { xmlns: 'urn:xmpp:eventlog',
+ id: 'PeerConnectionStats'});
+ message.c('message').t(content).up();
+ if (deflate) {
+ message.c('tag', {name: "deflated", value: "true"}).up();
+ }
+ message.up();
+
+ connection.send(message);
+ },
+ populateData: function () {
+ var data = {};
+ if (connection.jingle) {
+ data = connection.jingle.populateData();
+ }
+ return data;
+ },
+ getLogger: function () {
+ if(connection.logger)
+ return connection.logger.log;
+ return null;
+ },
+ getPrezi: function () {
+ return connection.emuc.getPrezi(this.myJid());
+ },
+ removePreziFromPresence: function () {
+ connection.emuc.removePreziFromPresence();
+ connection.emuc.sendPresence();
+ },
+ sendChatMessage: function (message, nickname) {
+ connection.emuc.sendMessage(message, nickname);
+ },
+ setSubject: function (topic) {
+ connection.emuc.setSubject(topic);
+ },
+ lockRoom: function (key, onSuccess, onError, onNotSupported) {
+ connection.emuc.lockRoom(key, onSuccess, onError, onNotSupported);
+ },
+ dial: function (to, from, roomName,roomPass) {
+ connection.rayo.dial(to, from, roomName,roomPass);
+ },
+ setMute: function (jid, mute) {
+ connection.moderate.setMute(jid, mute);
+ },
+ eject: function (jid) {
+ connection.moderate.eject(jid);
+ },
+ findJidFromResource: function (resource) {
+ connection.emuc.findJidFromResource(resource);
+ },
+ getMembers: function () {
+ return connection.emuc.members;
+ }
+
+};
+
+module.exports = XMPP;
\ No newline at end of file
diff --git a/muc.js b/muc.js
deleted file mode 100644
index 98b347337..000000000
--- a/muc.js
+++ /dev/null
@@ -1,548 +0,0 @@
-/* jshint -W117 */
-/* a simple MUC connection plugin
- * can only handle a single MUC room
- */
-Strophe.addConnectionPlugin('emuc', {
- connection: null,
- roomjid: null,
- myroomjid: null,
- members: {},
- list_members: [], // so we can elect a new focus
- presMap: {},
- preziMap: {},
- joined: false,
- isOwner: false,
- role: null,
- init: function (conn) {
- this.connection = conn;
- },
- initPresenceMap: function (myroomjid) {
- this.presMap['to'] = myroomjid;
- this.presMap['xns'] = 'http://jabber.org/protocol/muc';
- },
- doJoin: function (jid, password) {
- this.myroomjid = jid;
-
- console.info("Joined MUC as " + this.myroomjid);
-
- this.initPresenceMap(this.myroomjid);
-
- if (!this.roomjid) {
- this.roomjid = Strophe.getBareJidFromJid(jid);
- // add handlers (just once)
- this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
- this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
- this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
- this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
- }
- if (password !== undefined) {
- this.presMap['password'] = password;
- }
- this.sendPresence();
- },
- doLeave: function() {
- console.log("do leave", this.myroomjid);
- var pres = $pres({to: this.myroomjid, type: 'unavailable' });
- this.presMap.length = 0;
- this.connection.send(pres);
- },
- createNonAnonymousRoom: function() {
- // http://xmpp.org/extensions/xep-0045.html#createroom-reserved
-
- var getForm = $iq({type: 'get', to: this.roomjid})
- .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
- .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
-
- this.connection.sendIQ(getForm, function (form){
-
- if (!$(form).find(
- '>query>x[xmlns="jabber:x:data"]' +
- '>field[var="muc#roomconfig_whois"]').length) {
-
- console.error('non-anonymous rooms not supported');
- return;
- }
-
- var formSubmit = $iq({to: this.roomjid, type: 'set'})
- .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
-
- formSubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
-
- formSubmit.c('field', {'var': 'FORM_TYPE'})
- .c('value')
- .t('http://jabber.org/protocol/muc#roomconfig').up().up();
-
- formSubmit.c('field', {'var': 'muc#roomconfig_whois'})
- .c('value').t('anyone').up().up();
-
- this.connection.sendIQ(formSubmit);
-
- }, function (error){
- console.error("Error getting room configuration form");
- });
- },
- onPresence: function (pres) {
- var from = pres.getAttribute('from');
-
- // What is this for? A workaround for something?
- if (pres.getAttribute('type')) {
- return true;
- }
-
- // Parse etherpad tag.
- var etherpad = $(pres).find('>etherpad');
- if (etherpad.length) {
- if (config.etherpad_base && !Moderator.isModerator()) {
- UI.initEtherpad(etherpad.text());
- }
- }
-
- // Parse prezi tag.
- var presentation = $(pres).find('>prezi');
- if (presentation.length)
- {
- var url = presentation.attr('url');
- var current = presentation.find('>current').text();
-
- console.log('presentation info received from', from, url);
-
- if (this.preziMap[from] == null) {
- this.preziMap[from] = url;
-
- $(document).trigger('presentationadded.muc', [from, url, current]);
- }
- else {
- $(document).trigger('gotoslide.muc', [from, url, current]);
- }
- }
- else if (this.preziMap[from] != null) {
- var url = this.preziMap[from];
- delete this.preziMap[from];
- $(document).trigger('presentationremoved.muc', [from, url]);
- }
-
- // Parse audio info tag.
- var audioMuted = $(pres).find('>audiomuted');
- if (audioMuted.length) {
- $(document).trigger('audiomuted.muc', [from, audioMuted.text()]);
- }
-
- // Parse video info tag.
- var videoMuted = $(pres).find('>videomuted');
- if (videoMuted.length) {
- $(document).trigger('videomuted.muc', [from, videoMuted.text()]);
- }
-
- var stats = $(pres).find('>stats');
- if(stats.length)
- {
- var statsObj = {};
- Strophe.forEachChild(stats[0], "stat", function (el) {
- statsObj[el.getAttribute("name")] = el.getAttribute("value");
- });
- connectionquality.updateRemoteStats(from, statsObj);
- }
-
- // Parse status.
- if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
- this.isOwner = true;
- this.createNonAnonymousRoom();
- }
-
- // Parse roles.
- var member = {};
- member.show = $(pres).find('>show').text();
- member.status = $(pres).find('>status').text();
- var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
- member.affiliation = tmp.attr('affiliation');
- member.role = tmp.attr('role');
-
- // Focus recognition
- member.jid = tmp.attr('jid');
- member.isFocus = false;
- if (member.jid
- && member.jid.indexOf(Moderator.getFocusUserJid() + "/") == 0) {
- member.isFocus = true;
- }
-
- var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
- member.displayName = (nicktag.length > 0 ? nicktag.html() : null);
-
- if (from == this.myroomjid) {
- if (member.affiliation == 'owner') this.isOwner = true;
- if (this.role !== member.role) {
- this.role = member.role;
- if(Moderator.onLocalRoleChange)
- Moderator.onLocalRoleChange(from, member, pres);
- UI.onLocalRoleChange(from, member, pres);
- }
- if (!this.joined) {
- this.joined = true;
- $(document).trigger('joined.muc', [from, member]);
- UI.onMucJoined(from, member);
- this.list_members.push(from);
- }
- } else if (this.members[from] === undefined) {
- // new participant
- this.members[from] = member;
- this.list_members.push(from);
- console.log('entered', from, member);
- if (member.isFocus)
- {
- focusMucJid = from;
- console.info("Ignore focus: " + from +", real JID: " + member.jid);
- }
- else {
- var id = $(pres).find('>userID').text();
- var email = $(pres).find('>email');
- if (email.length > 0) {
- id = email.text();
- }
- UI.onMucEntered(from, id, member.displayName);
- API.triggerEvent("participantJoined",{jid: from});
- }
- } else {
- // Presence update for existing participant
- // Watch role change:
- if (this.members[from].role != member.role) {
- this.members[from].role = member.role;
- UI.onMucRoleChanged(member.role, member.displayName);
- }
- }
-
- // Always trigger presence to update bindings
- $(document).trigger('presence.muc', [from, member, pres]);
-
- // Trigger status message update
- if (member.status) {
- UI.onMucPresenceStatus(from, member);
- }
-
- return true;
- },
- onPresenceUnavailable: function (pres) {
- var from = pres.getAttribute('from');
- // Status code 110 indicates that this notification is "self-presence".
- if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
- delete this.members[from];
- this.list_members.splice(this.list_members.indexOf(from), 1);
- this.onParticipantLeft(from);
- }
- // If the status code is 110 this means we're leaving and we would like
- // to remove everyone else from our view, so we trigger the event.
- else if (this.list_members.length > 1) {
- for (var i = 0; i < this.list_members.length; i++) {
- var member = this.list_members[i];
- delete this.members[i];
- this.list_members.splice(i, 1);
- this.onParticipantLeft(member);
- }
- }
- if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length) {
- $(document).trigger('kicked.muc', [from]);
- }
- return true;
- },
- onPresenceError: function (pres) {
- var from = pres.getAttribute('from');
- if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
- console.log('on password required', from);
-
- UI.onPasswordReqiured(function (value) {
- connection.emuc.doJoin(from, value);
- })
- } else if ($(pres).find(
- '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
- var toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
- if(toDomain === config.hosts.anonymousdomain) {
- // we are connected with anonymous domain and only non anonymous users can create rooms
- // we must authorize the user
- $(document).trigger('passwordrequired.main');
- } else {
- console.warn('onPresError ', pres);
- UI.messageHandler.openReportDialog(null,
- 'Oops! Something went wrong and we couldn`t connect to the conference.',
- pres);
- }
- } else {
- console.warn('onPresError ', pres);
- UI.messageHandler.openReportDialog(null,
- 'Oops! Something went wrong and we couldn`t connect to the conference.',
- pres);
- }
- return true;
- },
- sendMessage: function (body, nickname) {
- var msg = $msg({to: this.roomjid, type: 'groupchat'});
- msg.c('body', body).up();
- if (nickname) {
- msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
- }
- this.connection.send(msg);
- API.triggerEvent("outgoingMessage", {"message": body});
- },
- setSubject: function (subject){
- var msg = $msg({to: this.roomjid, type: 'groupchat'});
- msg.c('subject', subject);
- this.connection.send(msg);
- console.log("topic changed to " + subject);
- },
- onMessage: function (msg) {
- // FIXME: this is a hack. but jingle on muc makes nickchanges hard
- var from = msg.getAttribute('from');
- var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(from);
-
- var txt = $(msg).find('>body').text();
- var type = msg.getAttribute("type");
- if(type == "error")
- {
- UI.chatAddError($(msg).find('>text').text(), txt);
- return true;
- }
-
- var subject = $(msg).find('>subject');
- if(subject.length)
- {
- var subjectText = subject.text();
- if(subjectText || subjectText == "") {
- UI.chatSetSubject(subjectText);
- console.log("Subject is changed to " + subjectText);
- }
- }
-
-
- if (txt) {
- console.log('chat', nick, txt);
- UI.updateChatConversation(from, nick, txt);
- if(from != this.myroomjid)
- API.triggerEvent("incomingMessage",
- {"from": from, "nick": nick, "message": txt});
- }
- return true;
- },
- lockRoom: function (key, onSuccess, onError, onNotSupported) {
- //http://xmpp.org/extensions/xep-0045.html#roomconfig
- var ob = this;
- this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
- function (res) {
- if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
- var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
- formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
- formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
- formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
- // Fixes a bug in prosody 0.9.+ https://code.google.com/p/lxmppd/issues/detail?id=373
- formsubmit.c('field', {'var': 'muc#roomconfig_whois'}).c('value').t('anyone').up().up();
- // FIXME: is muc#roomconfig_passwordprotectedroom required?
- this.connection.sendIQ(formsubmit,
- onSuccess,
- onError);
- } else {
- onNotSupported();
- }
- }, onError);
- },
- kick: function (jid) {
- var kickIQ = $iq({to: this.roomjid, type: 'set'})
- .c('query', {xmlns: 'http://jabber.org/protocol/muc#admin'})
- .c('item', {nick: Strophe.getResourceFromJid(jid), role: 'none'})
- .c('reason').t('You have been kicked.').up().up().up();
-
- this.connection.sendIQ(
- kickIQ,
- function (result) {
- console.log('Kick participant with jid: ', jid, result);
- },
- function (error) {
- console.log('Kick participant error: ', error);
- });
- },
- sendPresence: function () {
- var pres = $pres({to: this.presMap['to'] });
- pres.c('x', {xmlns: this.presMap['xns']});
-
- if (this.presMap['password']) {
- pres.c('password').t(this.presMap['password']).up();
- }
-
- pres.up();
-
- // Send XEP-0115 'c' stanza that contains our capabilities info
- if (connection.caps) {
- connection.caps.node = config.clientNode;
- pres.c('c', connection.caps.generateCapsAttrs()).up();
- }
-
- pres.c('user-agent', {xmlns: 'http://jitsi.org/jitmeet/user-agent'})
- .t(navigator.userAgent).up();
-
- if(this.presMap['bridgeIsDown']) {
- pres.c('bridgeIsDown').up();
- }
-
- if(this.presMap['email']) {
- pres.c('email').t(this.presMap['email']).up();
- }
-
- if(this.presMap['userId']) {
- pres.c('userId').t(this.presMap['userId']).up();
- }
-
- if (this.presMap['displayName']) {
- // XEP-0172
- pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'})
- .t(this.presMap['displayName']).up();
- }
-
- if (this.presMap['audions']) {
- pres.c('audiomuted', {xmlns: this.presMap['audions']})
- .t(this.presMap['audiomuted']).up();
- }
-
- if (this.presMap['videons']) {
- pres.c('videomuted', {xmlns: this.presMap['videons']})
- .t(this.presMap['videomuted']).up();
- }
-
- if(this.presMap['statsns'])
- {
- var stats = pres.c('stats', {xmlns: this.presMap['statsns']});
- for(var stat in this.presMap["stats"])
- if(this.presMap["stats"][stat] != null)
- stats.c("stat",{name: stat, value: this.presMap["stats"][stat]}).up();
- pres.up();
- }
-
- if (this.presMap['prezins']) {
- pres.c('prezi',
- {xmlns: this.presMap['prezins'],
- 'url': this.presMap['preziurl']})
- .c('current').t(this.presMap['prezicurrent']).up().up();
- }
-
- if (this.presMap['etherpadns']) {
- pres.c('etherpad', {xmlns: this.presMap['etherpadns']})
- .t(this.presMap['etherpadname']).up();
- }
-
- if (this.presMap['medians'])
- {
- pres.c('media', {xmlns: this.presMap['medians']});
- var sourceNumber = 0;
- Object.keys(this.presMap).forEach(function (key) {
- if (key.indexOf('source') >= 0) {
- sourceNumber++;
- }
- });
- if (sourceNumber > 0)
- for (var i = 1; i <= sourceNumber/3; i ++) {
- pres.c('source',
- {type: this.presMap['source' + i + '_type'],
- ssrc: this.presMap['source' + i + '_ssrc'],
- direction: this.presMap['source'+ i + '_direction']
- || 'sendrecv' }
- ).up();
- }
- }
-
- pres.up();
-// console.debug(pres.toString());
- connection.send(pres);
- },
- addDisplayNameToPresence: function (displayName) {
- this.presMap['displayName'] = displayName;
- },
- addMediaToPresence: function (sourceNumber, mtype, ssrcs, direction) {
- if (!this.presMap['medians'])
- this.presMap['medians'] = 'http://estos.de/ns/mjs';
-
- this.presMap['source' + sourceNumber + '_type'] = mtype;
- this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
- this.presMap['source' + sourceNumber + '_direction'] = direction;
- },
- clearPresenceMedia: function () {
- var self = this;
- Object.keys(this.presMap).forEach( function(key) {
- if(key.indexOf('source') != -1) {
- delete self.presMap[key];
- }
- });
- },
- addPreziToPresence: function (url, currentSlide) {
- this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
- this.presMap['preziurl'] = url;
- this.presMap['prezicurrent'] = currentSlide;
- },
- removePreziFromPresence: function () {
- delete this.presMap['prezins'];
- delete this.presMap['preziurl'];
- delete this.presMap['prezicurrent'];
- },
- addCurrentSlideToPresence: function (currentSlide) {
- this.presMap['prezicurrent'] = currentSlide;
- },
- getPrezi: function (roomjid) {
- return this.preziMap[roomjid];
- },
- addEtherpadToPresence: function(etherpadName) {
- this.presMap['etherpadns'] = 'http://jitsi.org/jitmeet/etherpad';
- this.presMap['etherpadname'] = etherpadName;
- },
- addAudioInfoToPresence: function(isMuted) {
- this.presMap['audions'] = 'http://jitsi.org/jitmeet/audio';
- this.presMap['audiomuted'] = isMuted.toString();
- },
- addVideoInfoToPresence: function(isMuted) {
- this.presMap['videons'] = 'http://jitsi.org/jitmeet/video';
- this.presMap['videomuted'] = isMuted.toString();
- },
- addConnectionInfoToPresence: function(stats) {
- this.presMap['statsns'] = 'http://jitsi.org/jitmeet/stats';
- this.presMap['stats'] = stats;
- },
- findJidFromResource: function(resourceJid) {
- if(resourceJid &&
- resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid)) {
- return connection.emuc.myroomjid;
- }
- var peerJid = null;
- Object.keys(this.members).some(function (jid) {
- peerJid = jid;
- return Strophe.getResourceFromJid(jid) === resourceJid;
- });
- return peerJid;
- },
- addBridgeIsDownToPresence: function() {
- this.presMap['bridgeIsDown'] = true;
- },
- addEmailToPresence: function(email) {
- this.presMap['email'] = email;
- },
- addUserIdToPresence: function(userId) {
- this.presMap['userId'] = userId;
- },
- isModerator: function() {
- return this.role === 'moderator';
- },
- getMemberRole: function(peerJid) {
- if (this.members[peerJid]) {
- return this.members[peerJid].role;
- }
- return null;
- },
- onParticipantLeft: function (jid) {
- UI.onMucLeft(jid);
-
- API.triggerEvent("participantLeft",{jid: jid});
-
- delete jid2Ssrc[jid];
-
- connection.jingle.terminateByJid(jid);
-
- if (connection.emuc.getPrezi(jid)) {
- $(document).trigger('presentationremoved.muc',
- [jid, connection.emuc.getPrezi(jid)]);
- }
-
- Moderator.onMucLeft(jid);
- }
-});
diff --git a/recording.js b/recording.js
deleted file mode 100644
index d60402582..000000000
--- a/recording.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
- Toolbar, Util */
-var Recording = (function (my) {
- var recordingToken = null;
- var recordingEnabled;
-
- /**
- * Whether to use a jirecon component for recording, or use the videobridge
- * through COLIBRI.
- */
- var useJirecon = (typeof config.hosts.jirecon != "undefined");
-
- /**
- * The ID of the jirecon recording session. Jirecon generates it when we
- * initially start recording, and it needs to be used in subsequent requests
- * to jirecon.
- */
- var jireconRid = null;
-
- my.setRecordingToken = function (token) {
- recordingToken = token;
- };
-
- my.setRecording = function (state, token, callback) {
- if (useJirecon){
- this.setRecordingJirecon(state, token, callback);
- } else {
- this.setRecordingColibri(state, token, callback);
- }
- };
-
- my.setRecordingJirecon = function (state, token, callback) {
- if (state == recordingEnabled){
- return;
- }
-
- var iq = $iq({to: config.hosts.jirecon, type: 'set'})
- .c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
- action: state ? 'start' : 'stop',
- mucjid: connection.emuc.roomjid});
- if (!state){
- iq.attrs({rid: jireconRid});
- }
-
- console.log('Start recording');
-
- connection.sendIQ(
- iq,
- function (result) {
- // TODO wait for an IQ with the real status, since this is
- // provisional?
- jireconRid = $(result).find('recording').attr('rid');
- console.log('Recording ' + (state ? 'started' : 'stopped') +
- '(jirecon)' + result);
- recordingEnabled = state;
- if (!state){
- jireconRid = null;
- }
-
- callback(state);
- },
- function (error) {
- console.log('Failed to start recording, error: ', error);
- callback(recordingEnabled);
- });
- };
-
- // Sends a COLIBRI message which enables or disables (according to 'state')
- // the recording on the bridge. Waits for the result IQ and calls 'callback'
- // with the new recording state, according to the IQ.
- my.setRecordingColibri = function (state, token, callback) {
- var elem = $iq({to: focusMucJid, type: 'set'});
- elem.c('conference', {
- xmlns: 'http://jitsi.org/protocol/colibri'
- });
- elem.c('recording', {state: state, token: token});
-
- connection.sendIQ(elem,
- function (result) {
- console.log('Set recording "', state, '". Result:', result);
- var recordingElem = $(result).find('>conference>recording');
- var newState = ('true' === recordingElem.attr('state'));
-
- recordingEnabled = newState;
- callback(newState);
- },
- function (error) {
- console.warn(error);
- callback(recordingEnabled);
- }
- );
- };
-
- my.toggleRecording = function () {
- if (!Moderator.isModerator()) {
- console.log(
- 'non-focus, or conference not yet organized:' +
- ' not enabling recording');
- return;
- }
-
- // Jirecon does not (currently) support a token.
- if (!recordingToken && !useJirecon)
- {
- UI.messageHandler.openTwoButtonDialog(null,
- '
Enter recording token
' +
- '',
- false,
- "Save",
- function (e, v, m, f) {
- if (v) {
- var token = document.getElementById('recordingToken');
-
- if (token.value) {
- my.setRecordingToken(
- Util.escapeHtml(token.value));
- my.toggleRecording();
- }
- }
- },
- function (event) {
- document.getElementById('recordingToken').focus();
- },
- function () {}
- );
-
- return;
- }
-
- var oldState = recordingEnabled;
- UI.setRecordingButtonState(!oldState);
- my.setRecording(!oldState,
- recordingToken,
- function (state) {
- console.log("New recording state: ", state);
- if (state === oldState)
- {
- // FIXME: new focus:
- // this will not work when moderator changes
- // during active session. Then it will assume that
- // recording status has changed to true, but it might have
- // been already true(and we only received actual status from
- // the focus).
- //
- // SO we start with status null, so that it is initialized
- // here and will fail only after second click, so if invalid
- // token was used we have to press the button twice before
- // current status will be fetched and token will be reset.
- //
- // Reliable way would be to return authentication error.
- // Or status update when moderator connects.
- // Or we have to stop recording session when current
- // moderator leaves the room.
-
- // Failed to change, reset the token because it might
- // have been wrong
- my.setRecordingToken(null);
- }
- // Update with returned status
- UI.setRecordingButtonState(state);
- }
- );
- };
-
- return my;
-}(Recording || {}));
diff --git a/service/xmpp/XMPPEvents.js b/service/xmpp/XMPPEvents.js
new file mode 100644
index 000000000..ccc2e1d1b
--- /dev/null
+++ b/service/xmpp/XMPPEvents.js
@@ -0,0 +1,14 @@
+var XMPPEvents = {
+ CONFERENCE_CERATED: "xmpp.conferenceCreated.jingle",
+ CALL_TERMINATED: "xmpp.callterminated.jingle",
+ CALL_INCOMING: "xmpp.callincoming.jingle",
+ DISPOSE_CONFERENCE: "xmpp.dispoce_confernce",
+ KICKED: "xmpp.kicked",
+ BRIDGE_DOWN: "xmpp.bridge_down",
+ USER_ID_CHANGED: "xmpp.user_id_changed",
+ CHANGED_STREAMS: "xmpp.changed_streams",
+ MUC_JOINED: "xmpp.muc_joined",
+ DISPLAY_NAME_CHANGED: "xmpp.display_name_changed",
+ REMOTE_STATS: "xmpp.remote_stats"
+};
+//module.exports = XMPPEvents;
\ No newline at end of file