jiti-meet/libs/app.bundle.js

27465 lines
2.3 MiB

!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.APP=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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* jshint -W117 */
/* application specific logic */
var APP =
{
init: function () {
this.UI = require("./modules/UI/UI");
this.API = require("./modules/API/API");
this.connectionquality = require("./modules/connectionquality/connectionquality");
this.statistics = require("./modules/statistics/statistics");
this.RTC = require("./modules/RTC/RTC");
this.simulcast = require("./modules/simulcast/simulcast");
this.desktopsharing = require("./modules/desktopsharing/desktopsharing");
this.xmpp = require("./modules/xmpp/xmpp");
this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut");
this.translation = require("./modules/translation/translation");
this.settings = require("./modules/settings/Settings");
}
};
function init() {
APP.RTC.start();
APP.xmpp.start();
APP.statistics.start();
APP.connectionquality.init();
// Set default desktop sharing method
APP.desktopsharing.init();
APP.keyboardshortcut.init();
}
$(document).ready(function () {
APP.init();
APP.translation.init();
if(APP.API.isEnabled())
APP.API.init();
APP.UI.start(init);
});
$(window).bind('beforeunload', function () {
if(APP.API.isEnabled())
APP.API.dispose();
});
module.exports = APP;
},{"./modules/API/API":2,"./modules/RTC/RTC":6,"./modules/UI/UI":8,"./modules/connectionquality/connectionquality":35,"./modules/desktopsharing/desktopsharing":36,"./modules/keyboardshortcut/keyboardshortcut":37,"./modules/settings/Settings":38,"./modules/simulcast/simulcast":43,"./modules/statistics/statistics":46,"./modules/translation/translation":47,"./modules/xmpp/xmpp":61}],2:[function(require,module,exports){
/**
* Implements API class that communicates with external api class
* and provides interface to access Jitsi Meet features by external
* applications that embed Jitsi Meet
*/
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
/**
* List of the available commands.
* @type {{
* displayName: inputDisplayNameHandler,
* muteAudio: toggleAudio,
* muteVideo: toggleVideo,
* filmStrip: toggleFilmStrip
* }}
*/
var commands =
{
displayName: APP.UI.inputDisplayNameHandler,
muteAudio: APP.UI.toggleAudio,
muteVideo: APP.UI.toggleVideo,
toggleFilmStrip: APP.UI.toggleFilmStrip,
toggleChat: APP.UI.toggleChat,
toggleContactList: APP.UI.toggleContactList
};
/**
* Maps the supported events and their status
* (true it the event is enabled and false if it is disabled)
* @type {{
* incomingMessage: boolean,
* outgoingMessage: boolean,
* displayNameChange: boolean,
* participantJoined: boolean,
* participantLeft: boolean
* }}
*/
var events =
{
incomingMessage: false,
outgoingMessage:false,
displayNameChange: false,
participantJoined: false,
participantLeft: false
};
var displayName = {};
/**
* Processes commands from external applicaiton.
* @param message the object with the command
*/
function processCommand(message)
{
if(message.action != "execute")
{
console.error("Unknown action of the message");
return;
}
for(var key in message)
{
if(commands[key])
commands[key].apply(null, message[key]);
}
}
/**
* Processes events objects from external applications
* @param event the event
*/
function processEvent(event) {
if(!event.action)
{
console.error("Event with no action is received.");
return;
}
var i = 0;
switch(event.action)
{
case "add":
for(; i < event.events.length; i++)
{
events[event.events[i]] = true;
}
break;
case "remove":
for(; i < event.events.length; i++)
{
events[event.events[i]] = false;
}
break;
default:
console.error("Unknown action for event.");
}
}
/**
* Sends message to the external application.
* @param object
*/
function sendMessage(object) {
window.parent.postMessage(JSON.stringify(object), "*");
}
/**
* Processes a message event from the external application
* @param event the message event
*/
function processMessage(event)
{
var message;
try {
message = JSON.parse(event.data);
} catch (e) {}
if(!message.type)
return;
switch (message.type)
{
case "command":
processCommand(message);
break;
case "event":
processEvent(message);
break;
default:
console.error("Unknown type of the message");
return;
}
}
function setupListeners() {
APP.xmpp.addListener(XMPPEvents.MUC_ENTER, function (from) {
API.triggerEvent("participantJoined", {jid: from});
});
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, function (from, nick, txt, myjid) {
if (from != myjid)
API.triggerEvent("incomingMessage",
{"from": from, "nick": nick, "message": txt});
});
APP.xmpp.addListener(XMPPEvents.MUC_LEFT, function (jid) {
API.triggerEvent("participantLeft", {jid: jid});
});
APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, function (jid, newDisplayName) {
name = displayName[jid];
if(!name || name != newDisplayName) {
API.triggerEvent("displayNameChange", {jid: jid, displayname: newDisplayName});
displayName[jid] = newDisplayName;
}
});
APP.xmpp.addListener(XMPPEvents.SENDING_CHAT_MESSAGE, function (body) {
APP.API.triggerEvent("outgoingMessage", {"message": body});
});
}
var API = {
/**
* Check whether the API should be enabled or not.
* @returns {boolean}
*/
isEnabled: function () {
var hash = location.hash;
if(hash && hash.indexOf("external") > -1 && window.postMessage)
return true;
return false;
},
/**
* Initializes the APIConnector. Setups message event listeners that will
* receive information from external applications that embed Jitsi Meet.
* It also sends a message to the external application that APIConnector
* is initialized.
*/
init: function () {
if (window.addEventListener)
{
window.addEventListener('message',
processMessage, false);
}
else
{
window.attachEvent('onmessage', processMessage);
}
sendMessage({type: "system", loaded: true});
setupListeners();
},
/**
* Checks whether the event is enabled ot not.
* @param name the name of the event.
* @returns {*}
*/
isEventEnabled: function (name) {
return events[name];
},
/**
* Sends event object to the external application that has been subscribed
* for that event.
* @param name the name event
* @param object data associated with the event
*/
triggerEvent: function (name, object) {
if(this.isEnabled() && this.isEventEnabled(name))
sendMessage({
type: "event", action: "result", event: name, result: object});
},
/**
* Removes the listeners.
*/
dispose: function () {
if(window.removeEventListener)
{
window.removeEventListener("message",
processMessage, false);
}
else
{
window.detachEvent('onmessage', processMessage);
}
}
};
module.exports = API;
},{"../../service/xmpp/XMPPEvents":97}],3:[function(require,module,exports){
/* global Strophe, focusedVideoSrc*/
// cache datachannels to avoid garbage collection
// https://code.google.com/p/chromium/issues/detail?id=405545
var RTCEvents = require("../../service/RTC/RTCEvents");
var _dataChannels = [];
var eventEmitter = null;
var DataChannels =
{
/**
* Callback triggered by PeerConnection when new data channel is opened
* on the bridge.
* @param event the event info object.
*/
onDataChannel: function (event)
{
var dataChannel = event.channel;
dataChannel.onopen = function () {
console.info("Data channel opened by the Videobridge!", dataChannel);
// Code sample for sending string and/or binary data
// Sends String message to the bridge
//dataChannel.send("Hello bridge!");
// Sends 12 bytes binary message to the bridge
//dataChannel.send(new ArrayBuffer(12));
// when the data channel becomes available, tell the bridge about video
// selections so that it can do adaptive simulcast,
// we want the notification to trigger even if userJid is undefined,
// or null.
var userJid = APP.UI.getLargeVideoState().userResourceJid;
// we want the notification to trigger even if userJid is undefined,
// or null.
onSelectedEndpointChanged(userJid);
};
dataChannel.onerror = function (error) {
console.error("Data Channel Error:", error, dataChannel);
};
dataChannel.onmessage = function (event) {
var data = event.data;
// JSON
var obj;
try {
obj = JSON.parse(data);
}
catch (e) {
console.error(
"Failed to parse data channel message as JSON: ",
data,
dataChannel);
}
if (('undefined' !== typeof(obj)) && (null !== obj)) {
var colibriClass = obj.colibriClass;
if ("DominantSpeakerEndpointChangeEvent" === colibriClass) {
// Endpoint ID from the Videobridge.
var dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint;
console.info(
"Data channel new dominant speaker event: ",
dominantSpeakerEndpoint);
eventEmitter.emit(RTCEvents.DOMINANTSPEAKER_CHANGED, dominantSpeakerEndpoint);
}
else if ("InLastNChangeEvent" === colibriClass)
{
var oldValue = obj.oldValue;
var newValue = obj.newValue;
// Make sure that oldValue and newValue are of type boolean.
var type;
if ((type = typeof oldValue) !== 'boolean') {
if (type === 'string') {
oldValue = (oldValue == "true");
} else {
oldValue = new Boolean(oldValue).valueOf();
}
}
if ((type = typeof newValue) !== 'boolean') {
if (type === 'string') {
newValue = (newValue == "true");
} else {
newValue = new Boolean(newValue).valueOf();
}
}
eventEmitter.emit(RTCEvents.LASTN_CHANGED, oldValue, newValue);
}
else if ("LastNEndpointsChangeEvent" === colibriClass)
{
// The new/latest list of last-n endpoint IDs.
var lastNEndpoints = obj.lastNEndpoints;
// The list of endpoint IDs which are entering the list of
// last-n at this time i.e. were not in the old list of last-n
// endpoint IDs.
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
var stream = obj.stream;
console.log(
"Data channel new last-n event: ",
lastNEndpoints, endpointsEnteringLastN, obj);
eventEmitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,
lastNEndpoints, endpointsEnteringLastN, obj);
}
else if ("SimulcastLayersChangedEvent" === colibriClass)
{
eventEmitter.emit(RTCEvents.SIMULCAST_LAYER_CHANGED,
obj.endpointSimulcastLayers);
}
else if ("SimulcastLayersChangingEvent" === colibriClass)
{
eventEmitter.emit(RTCEvents.SIMULCAST_LAYER_CHANGING,
obj.endpointSimulcastLayers);
}
else if ("StartSimulcastLayerEvent" === colibriClass)
{
eventEmitter.emit(RTCEvents.SIMULCAST_START, obj.simulcastLayer);
}
else if ("StopSimulcastLayerEvent" === colibriClass)
{
eventEmitter.emit(RTCEvents.SIMULCAST_STOP, obj.simulcastLayer);
}
else
{
console.debug("Data channel JSON-formatted message: ", obj);
}
}
};
dataChannel.onclose = function ()
{
console.info("The Data Channel closed", dataChannel);
var idx = _dataChannels.indexOf(dataChannel);
if (idx > -1)
_dataChannels = _dataChannels.splice(idx, 1);
};
_dataChannels.push(dataChannel);
},
/**
* Binds "ondatachannel" event listener to given PeerConnection instance.
* @param peerConnection WebRTC peer connection instance.
*/
init: function (peerConnection, emitter) {
if(!config.openSctp)
return;
peerConnection.ondatachannel = this.onDataChannel;
eventEmitter = emitter;
// Sample code for opening new data channel from Jitsi Meet to the bridge.
// Although it's not a requirement to open separate channels from both bridge
// and peer as single channel can be used for sending and receiving data.
// So either channel opened by the bridge or the one opened here is enough
// for communication with the bridge.
/*var dataChannelOptions =
{
reliable: true
};
var dataChannel
= peerConnection.createDataChannel("myChannel", dataChannelOptions);
// Can be used only when is in open state
dataChannel.onopen = function ()
{
dataChannel.send("My channel !!!");
};
dataChannel.onmessage = function (event)
{
var msgData = event.data;
console.info("Got My Data Channel Message:", msgData, dataChannel);
};*/
},
handleSelectedEndpointEvent: onSelectedEndpointChanged,
handlePinnedEndpointEvent: onPinnedEndpointChanged
};
function onSelectedEndpointChanged(userResource)
{
console.log('selected endpoint changed: ', userResource);
if (_dataChannels && _dataChannels.length != 0)
{
_dataChannels.some(function (dataChannel) {
if (dataChannel.readyState == 'open')
{
console.log('sending selected endpoint changed '
+ 'notification to the bridge: ', userResource);
dataChannel.send(JSON.stringify({
'colibriClass': 'SelectedEndpointChangedEvent',
'selectedEndpoint':
(!userResource || userResource === null)?
null : userResource
}));
return true;
}
});
}
}
function onPinnedEndpointChanged(userResource)
{
console.log('pinned endpoint changed: ', userResource);
if (_dataChannels && _dataChannels.length != 0)
{
_dataChannels.some(function (dataChannel) {
if (dataChannel.readyState == 'open')
{
dataChannel.send(JSON.stringify({
'colibriClass': 'PinnedEndpointChangedEvent',
'pinnedEndpoint':
(!userResource || userResource == null)?
null : userResource
}));
return true;
}
});
}
}
module.exports = DataChannels;
},{"../../service/RTC/RTCEvents":89}],4:[function(require,module,exports){
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
function LocalStream(stream, type, eventEmitter, videoType)
{
this.stream = stream;
this.eventEmitter = eventEmitter;
this.type = type;
this.videoType = videoType;
var self = this;
if(type == "audio")
{
this.getTracks = function () {
return self.stream.getAudioTracks();
};
}
else
{
this.getTracks = function () {
return self.stream.getVideoTracks();
};
}
this.stream.onended = function()
{
self.streamEnded();
};
}
LocalStream.prototype.streamEnded = function () {
this.eventEmitter.emit(StreamEventTypes.EVENT_TYPE_LOCAL_ENDED, this);
}
LocalStream.prototype.getOriginalStream = function()
{
return this.stream;
}
LocalStream.prototype.isAudioStream = function () {
return (this.stream.getAudioTracks() && this.stream.getAudioTracks().length > 0);
};
LocalStream.prototype.mute = function()
{
var ismuted = false;
var tracks = this.getTracks();
for (var idx = 0; idx < tracks.length; idx++) {
ismuted = !tracks[idx].enabled;
tracks[idx].enabled = ismuted;
}
return ismuted;
};
LocalStream.prototype.setMute = function(mute)
{
if(window.location.protocol != "https:" ||
this.isAudioStream() || this.videoType === "screen")
{
var tracks = this.getTracks();
for (var idx = 0; idx < tracks.length; idx++) {
tracks[idx].enabled = mute;
}
}
else
{
if(mute === false) {
APP.xmpp.removeStream(this.stream);
this.stream.stop();
}
else
{
APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(["video"],
function (stream) {
APP.RTC.changeLocalVideo(stream, false, function () {});
});
}
}
};
LocalStream.prototype.isMuted = function () {
var tracks = [];
if(this.type == "audio")
{
tracks = this.stream.getAudioTracks();
}
else
{
if(this.stream.ended)
return true;
tracks = this.stream.getVideoTracks();
}
for (var idx = 0; idx < tracks.length; idx++) {
if(tracks[idx].enabled)
return false;
}
return true;
}
LocalStream.prototype.getId = function () {
return this.stream.getTracks()[0].id;
}
module.exports = LocalStream;
},{"../../service/RTC/StreamEventTypes.js":91}],5:[function(require,module,exports){
////These lines should be uncommented when require works in app.js
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
var StreamEventType = require("../../service/RTC/StreamEventTypes");
/**
* Creates a MediaStream object for the given data, session id and ssrc.
* It is a wrapper class for the MediaStream.
*
* @param data the data object from which we obtain the stream,
* the peerjid, etc.
* @param sid the session id
* @param ssrc the ssrc corresponding to this MediaStream
*
* @constructor
*/
function MediaStream(data, sid, ssrc, browser, eventEmitter) {
// XXX(gp) to minimize headaches in the future, we should build our
// abstractions around tracks and not streams. ORTC is track based API.
// Mozilla expects m-lines to represent media tracks.
//
// Practically, what I'm saying is that we should have a MediaTrack class
// and not a MediaStream class.
//
// Also, we should be able to associate multiple SSRCs with a MediaTrack as
// a track might have an associated RTX and FEC sources.
this.sid = sid;
this.stream = data.stream;
this.peerjid = data.peerjid;
this.ssrc = ssrc;
this.type = (this.stream.getVideoTracks().length > 0)?
MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
this.videoType = null;
this.muted = false;
this.eventEmitter = eventEmitter;
}
MediaStream.prototype.getOriginalStream = function()
{
return this.stream;
};
MediaStream.prototype.setMute = function (value)
{
this.stream.muted = value;
this.muted = value;
};
MediaStream.prototype.setVideoType = function (value) {
if(this.videoType === value)
return;
this.videoType = value;
this.eventEmitter.emit(StreamEventType.EVENT_TYPE_REMOTE_CHANGED,
this.peerjid);
};
module.exports = MediaStream;
},{"../../service/RTC/MediaStreamTypes":87,"../../service/RTC/StreamEventTypes":91}],6:[function(require,module,exports){
var EventEmitter = require("events");
var RTCUtils = require("./RTCUtils.js");
var LocalStream = require("./LocalStream.js");
var DataChannels = require("./DataChannels");
var MediaStream = require("./MediaStream.js");
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
var RTCEvents = require("../../service/RTC/RTCEvents.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var UIEvents = require("../../service/UI/UIEvents");
var eventEmitter = new EventEmitter();
var RTC = {
rtcUtils: null,
devices: {
audio: false,
video: false
},
localStreams: [],
remoteStreams: {},
localAudio: null,
localVideo: null,
addStreamListener: function (listener, eventType) {
eventEmitter.on(eventType, listener);
},
addListener: function (type, listener) {
eventEmitter.on(type, listener);
},
removeStreamListener: function (listener, eventType) {
if(!(eventType instanceof StreamEventTypes))
throw "Illegal argument";
eventEmitter.removeListener(eventType, listener);
},
createLocalStream: function (stream, type, change, videoType) {
var localStream = new LocalStream(stream, type, eventEmitter, videoType);
//in firefox we have only one stream object
if(this.localStreams.length == 0 ||
this.localStreams[0].getOriginalStream() != stream)
this.localStreams.push(localStream);
if(type == "audio")
{
this.localAudio = localStream;
}
else
{
this.localVideo = localStream;
}
var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
if(change)
eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
eventEmitter.emit(eventType, localStream);
return localStream;
},
removeLocalStream: function (stream) {
for(var i = 0; i < this.localStreams.length; i++)
{
if(this.localStreams[i].getOriginalStream() === stream) {
delete this.localStreams[i];
return;
}
}
},
createRemoteStream: function (data, sid, thessrc) {
var remoteStream = new MediaStream(data, sid, thessrc,
this.getBrowserType(), eventEmitter);
var jid = data.peerjid || APP.xmpp.myJid();
if(!this.remoteStreams[jid]) {
this.remoteStreams[jid] = {};
}
this.remoteStreams[jid][remoteStream.type]= remoteStream;
eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream);
return remoteStream;
},
getBrowserType: function () {
return this.rtcUtils.browser;
},
getPCConstraints: function () {
return this.rtcUtils.pc_constraints;
},
getUserMediaWithConstraints:function(um, success_callback,
failure_callback, resolution,
bandwidth, fps, desktopStream)
{
return this.rtcUtils.getUserMediaWithConstraints(um, success_callback,
failure_callback, resolution, bandwidth, fps, desktopStream);
},
attachMediaStream: function (element, stream) {
this.rtcUtils.attachMediaStream(element, stream);
},
getStreamID: function (stream) {
return this.rtcUtils.getStreamID(stream);
},
getVideoSrc: function (element) {
return this.rtcUtils.getVideoSrc(element);
},
setVideoSrc: function (element, src) {
this.rtcUtils.setVideoSrc(element, src);
},
dispose: function() {
if (this.rtcUtils) {
this.rtcUtils = null;
}
},
stop: function () {
this.dispose();
},
start: function () {
var self = this;
APP.desktopsharing.addListener(
function (stream, isUsingScreenStream, callback) {
self.changeLocalVideo(stream, isUsingScreenStream, callback);
}, DesktopSharingEventTypes.NEW_STREAM_CREATED);
APP.xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
for(var i = 0; i < changedStreams.length; i++) {
var type = changedStreams[i].type;
if (type != "audio") {
var peerStreams = self.remoteStreams[jid];
if(!peerStreams)
continue;
var videoStream = peerStreams[MediaStreamType.VIDEO_TYPE];
if(!videoStream)
continue;
videoStream.setVideoType(changedStreams[i].type);
}
}
});
APP.xmpp.addListener(XMPPEvents.CALL_INCOMING, function(event) {
DataChannels.init(event.peerconnection, eventEmitter);
});
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT,
DataChannels.handleSelectedEndpointEvent);
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
DataChannels.handlePinnedEndpointEvent);
this.rtcUtils = new RTCUtils(this);
this.rtcUtils.obtainAudioAndVideoPermissions();
},
muteRemoteVideoStream: function (jid, value) {
var stream;
if(this.remoteStreams[jid] &&
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE])
{
stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
}
if(!stream)
return true;
if (value != stream.muted) {
stream.setMute(value);
return true;
}
return false;
},
switchVideoStreams: function (new_stream) {
this.localVideo.stream = new_stream;
this.localStreams = [];
//in firefox we have only one stream object
if (this.localAudio.getOriginalStream() != new_stream)
this.localStreams.push(this.localAudio);
this.localStreams.push(this.localVideo);
},
changeLocalVideo: function (stream, isUsingScreenStream, callback) {
var oldStream = this.localVideo.getOriginalStream();
var type = (isUsingScreenStream? "screen" : "video");
var localCallback = callback;
if(this.localVideo.isMuted() && this.localVideo.videoType !== type)
{
localCallback = function() {
APP.xmpp.setVideoMute(false, APP.UI.setVideoMuteButtonsState);
callback();
};
}
var videoStream = this.rtcUtils.createVideoStream(stream);
this.localVideo = this.createLocalStream(videoStream, "video", true, type);
// Stop the stream to trigger onended event for old stream
oldStream.stop();
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
},
/**
* 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}
*/
isVideoSrcDesktop: function (jid) {
if(!jid)
return false;
var isDesktop = false;
var stream = null;
if (APP.xmpp.myJid() === jid) {
// local video
stream = this.localVideo;
} else {
var peerStreams = this.remoteStreams[jid];
if(!peerStreams)
return false;
stream = peerStreams[MediaStreamType.VIDEO_TYPE];
}
if(stream)
isDesktop = (stream.videoType === "screen");
return isDesktop;
},
setVideoMute: function(mute, callback, options) {
if(!this.localVideo)
return;
if (mute == APP.RTC.localVideo.isMuted())
{
APP.xmpp.sendVideoInfoPresence(mute);
if(callback)
callback();
}
else
{
APP.RTC.localVideo.setMute(!mute);
APP.xmpp.setVideoMute(
mute,
callback,
options);
}
},
setDeviceAvailability: function (devices) {
if(!devices)
return;
if(devices.audio === true || devices.audio === false)
this.devices.audio = devices.audio;
if(devices.video === true || devices.video === false)
this.devices.video = devices.video;
eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices);
}
};
module.exports = RTC;
},{"../../service/RTC/MediaStreamTypes":87,"../../service/RTC/RTCEvents.js":89,"../../service/RTC/StreamEventTypes.js":91,"../../service/UI/UIEvents":92,"../../service/desktopsharing/DesktopSharingEventTypes":95,"../../service/xmpp/XMPPEvents":97,"./DataChannels":3,"./LocalStream.js":4,"./MediaStream.js":5,"./RTCUtils.js":7,"events":98}],7:[function(require,module,exports){
var RTCBrowserType = require("../../service/RTC/RTCBrowserType.js");
var Resolutions = require("../../service/RTC/Resolutions");
var currentResolution = null;
function getPreviousResolution(resolution) {
if(!Resolutions[resolution])
return null;
var order = Resolutions[resolution].order;
var res = null;
var resName = null;
for(var i in Resolutions)
{
var tmp = Resolutions[i];
if(res == null || (res.order < tmp.order && tmp.order < order))
{
resName = i;
res = tmp;
}
}
return resName;
}
function setResolutionConstraints(constraints, resolution, isAndroid)
{
if (resolution && !constraints.video || isAndroid) {
constraints.video = { mandatory: {}, optional: [] };// same behaviour as true
}
if(Resolutions[resolution])
{
constraints.video.mandatory.minWidth = Resolutions[resolution].width;
constraints.video.mandatory.minHeight = Resolutions[resolution].height;
}
else
{
if (isAndroid) {
constraints.video.mandatory.minWidth = 320;
constraints.video.mandatory.minHeight = 240;
constraints.video.mandatory.maxFrameRate = 15;
}
}
if (constraints.video.mandatory.minWidth)
constraints.video.mandatory.maxWidth = constraints.video.mandatory.minWidth;
if (constraints.video.mandatory.minHeight)
constraints.video.mandatory.maxHeight = constraints.video.mandatory.minHeight;
}
function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid)
{
var constraints = {audio: false, video: false};
if (um.indexOf('video') >= 0) {
constraints.video = { mandatory: {}, optional: [] };// same behaviour as true
}
if (um.indexOf('audio') >= 0) {
constraints.audio = { mandatory: {}, optional: []};// same behaviour as true
}
if (um.indexOf('screen') >= 0) {
constraints.video = {
mandatory: {
chromeMediaSource: "screen",
googLeakyBucket: true,
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3
},
optional: []
};
}
if (um.indexOf('desktop') >= 0) {
constraints.video = {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: desktopStream,
googLeakyBucket: true,
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3
},
optional: []
};
}
if (constraints.audio) {
// if it is good enough for hangouts...
constraints.audio.optional.push(
{googEchoCancellation: true},
{googAutoGainControl: true},
{googNoiseSupression: true},
{googHighpassFilter: true},
{googNoisesuppression2: true},
{googEchoCancellation2: true},
{googAutoGainControl2: true}
);
}
if (constraints.video) {
constraints.video.optional.push(
{googNoiseReduction: false} // chrome 37 workaround for issue 3807, reenable in M38
);
if (um.indexOf('video') >= 0) {
constraints.video.optional.push(
{googLeakyBucket: true}
);
}
}
if (um.indexOf('video') >= 0) {
setResolutionConstraints(constraints, resolution, isAndroid);
}
if (bandwidth) { // doesn't work currently, see webrtc issue 1846
if (!constraints.video) constraints.video = {mandatory: {}, optional: []};//same behaviour as true
constraints.video.optional.push({bandwidth: bandwidth});
}
if (fps) { // for some cameras it might be necessary to request 30fps
// so they choose 30fps mjpg over 10fps yuy2
if (!constraints.video) constraints.video = {mandatory: {}, optional: []};// same behaviour as true;
constraints.video.mandatory.minFrameRate = fps;
}
return constraints;
}
function RTCUtils(RTCService)
{
this.service = RTCService;
if (navigator.mozGetUserMedia) {
console.log('This appears to be Firefox');
var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
if (version >= 39) {
this.peerconnection = mozRTCPeerConnection;
this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
this.pc_constraints = {};
this.attachMediaStream = function (element, stream) {
// srcObject is being standardized and FF will eventually
// support that unprefixed. FF also supports the
// "element.src = URL.createObjectURL(...)" combo, but that
// will be deprecated in favour of srcObject.
//
// https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
// https://github.com/webrtc/samples/issues/302
element[0].mozSrcObject = stream;
element[0].play();
};
this.getStreamID = function (stream) {
var tracks = stream.getVideoTracks();
if(!tracks || tracks.length == 0)
{
tracks = stream.getAudioTracks();
}
return tracks[0].id.replace(/[\{,\}]/g,"");
};
this.getVideoSrc = function (element) {
return element.mozSrcObject;
};
this.setVideoSrc = function (element, src) {
element.mozSrcObject = src;
};
RTCSessionDescription = mozRTCSessionDescription;
RTCIceCandidate = mozRTCIceCandidate;
} else {
window.location.href = 'unsupported_browser.html';
return;
}
} else if (navigator.webkitGetUserMedia) {
console.log('This appears to be Chrome');
this.peerconnection = webkitRTCPeerConnection;
this.browser = RTCBrowserType.RTC_BROWSER_CHROME;
this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
this.attachMediaStream = function (element, stream) {
element.attr('src', webkitURL.createObjectURL(stream));
};
this.getStreamID = function (stream) {
// streams from FF endpoints have the characters '{' and '}'
// that make jQuery choke.
return stream.id.replace(/[\{,\}]/g,"");
};
this.getVideoSrc = function (element) {
return element.getAttribute("src");
};
this.setVideoSrc = function (element, src) {
element.setAttribute("src", src);
};
// DTLS should now be enabled by default but..
this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
if (navigator.userAgent.indexOf('Android') != -1) {
this.pc_constraints = {}; // disable DTLS on Android
}
if (!webkitMediaStream.prototype.getVideoTracks) {
webkitMediaStream.prototype.getVideoTracks = function () {
return this.videoTracks;
};
}
if (!webkitMediaStream.prototype.getAudioTracks) {
webkitMediaStream.prototype.getAudioTracks = function () {
return this.audioTracks;
};
}
}
else
{
try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
window.location.href = 'unsupported_browser.html';
return;
}
}
RTCUtils.prototype.getUserMediaWithConstraints = function(
um, success_callback, failure_callback, resolution,bandwidth, fps,
desktopStream)
{
currentResolution = resolution;
// Check if we are running on Android device
var isAndroid = navigator.userAgent.indexOf('Android') != -1;
var constraints = getConstraints(
um, resolution, bandwidth, fps, desktopStream, isAndroid);
var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var self = this;
try {
if (config.enableSimulcast
&& constraints.video
&& constraints.video.chromeMediaSource !== 'screen'
&& constraints.video.chromeMediaSource !== 'desktop'
&& !isAndroid
// We currently do not support FF, as it doesn't have multistream support.
&& !isFF) {
APP.simulcast.getUserMedia(constraints, function (stream) {
console.log('onUserMediaSuccess');
self.setAvailableDevices(um, true);
success_callback(stream);
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
self.setAvailableDevices(um, false);
if (failure_callback) {
failure_callback(error);
}
});
} else {
this.getUserMedia(constraints,
function (stream) {
console.log('onUserMediaSuccess');
self.setAvailableDevices(um, true);
success_callback(stream);
},
function (error) {
self.setAvailableDevices(um, false);
console.warn('Failed to get access to local media. Error ',
error, constraints);
if (failure_callback) {
failure_callback(error);
}
});
}
} catch (e) {
console.error('GUM failed: ', e);
if(failure_callback) {
failure_callback(e);
}
}
};
RTCUtils.prototype.setAvailableDevices = function (um, available) {
var devices = {};
if(um.indexOf("video") != -1)
{
devices.video = available;
}
if(um.indexOf("audio") != -1)
{
devices.audio = available;
}
this.service.setDeviceAvailability(devices);
}
/**
* We ask for audio and video combined stream in order to get permissions and
* not to ask twice.
*/
RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback) {
var self = this;
// Get AV
if(!devices)
devices = ['audio', 'video'];
this.getUserMediaWithConstraints(
devices,
function (stream) {
if(callback)
callback(stream);
else
self.successCallback(stream);
},
function (error) {
self.errorCallback(error);
},
config.resolution || '360');
}
RTCUtils.prototype.successCallback = function (stream) {
if(stream)
console.log('got', stream, stream.getAudioTracks().length,
stream.getVideoTracks().length);
this.handleLocalStream(stream);
};
RTCUtils.prototype.errorCallback = function (error) {
var self = this;
console.error('failed to obtain audio/video stream - trying audio only', error);
var resolution = getPreviousResolution(currentResolution);
if(typeof error == "object" && error.constraintName && error.name
&& (error.name == "ConstraintNotSatisfiedError" ||
error.name == "OverconstrainedError") &&
(error.constraintName == "minWidth" || error.constraintName == "maxWidth" ||
error.constraintName == "minHeight" || error.constraintName == "maxHeight")
&& resolution != null)
{
self.getUserMediaWithConstraints(['audio', 'video'],
function (stream) {
return self.successCallback(stream);
}, function (error) {
return self.errorCallback(error);
}, resolution);
}
else
{
self.getUserMediaWithConstraints(
['audio'],
function (stream) {
return self.successCallback(stream);
},
function (error) {
console.error('failed to obtain audio/video stream - stop',
error);
return self.successCallback(null);
}
);
}
}
RTCUtils.prototype.handleLocalStream = function(stream)
{
if(window.webkitMediaStream)
{
var audioStream = new webkitMediaStream();
var videoStream = new webkitMediaStream();
if(stream) {
var audioTracks = stream.getAudioTracks();
for (var i = 0; i < audioTracks.length; i++) {
audioStream.addTrack(audioTracks[i]);
}
var videoTracks = stream.getVideoTracks();
for (i = 0; i < videoTracks.length; i++) {
videoStream.addTrack(videoTracks[i]);
}
}
this.service.createLocalStream(audioStream, "audio");
this.service.createLocalStream(videoStream, "video");
}
else
{//firefox
this.service.createLocalStream(stream, "stream");
}
};
RTCUtils.prototype.createVideoStream = function(stream)
{
var videoStream = null;
if(window.webkitMediaStream)
{
videoStream = new webkitMediaStream();
if(stream)
{
var videoTracks = stream.getVideoTracks();
for (i = 0; i < videoTracks.length; i++) {
videoStream.addTrack(videoTracks[i]);
}
}
}
else
videoStream = stream;
return videoStream;
};
module.exports = RTCUtils;
},{"../../service/RTC/RTCBrowserType.js":88,"../../service/RTC/Resolutions":90}],8:[function(require,module,exports){
var UI = {};
var VideoLayout = require("./videolayout/VideoLayout.js");
var AudioLevels = require("./audio_levels/AudioLevels.js");
var Prezi = require("./prezi/Prezi.js");
var Etherpad = require("./etherpad/Etherpad.js");
var Chat = require("./side_pannels/chat/Chat.js");
var Toolbar = require("./toolbars/Toolbar");
var ToolbarToggler = require("./toolbars/ToolbarToggler");
var BottomToolbar = require("./toolbars/BottomToolbar");
var ContactList = require("./side_pannels/contactlist/ContactList");
var Avatar = require("./avatar/Avatar");
var EventEmitter = require("events");
var SettingsMenu = require("./side_pannels/settings/SettingsMenu");
var Settings = require("./../settings/Settings");
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 NicknameHandler = require("./util/NicknameHandler");
var CQEvents = require("../../service/connectionquality/CQEvents");
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
var RTCEvents = require("../../service/RTC/RTCEvents");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var eventEmitter = new EventEmitter();
var roomName = null;
function setupPrezi()
{
$("#reloadPresentationLink").click(function()
{
Prezi.reloadPresentation();
});
}
function setupChat()
{
Chat.init();
$("#toggle_smileys").click(function() {
Chat.toggleSmileys();
});
}
function setupToolbars() {
Toolbar.init(UI);
Toolbar.setupButtonsFromConfig();
BottomToolbar.init();
}
function streamHandler(stream) {
switch (stream.type)
{
case "audio":
VideoLayout.changeLocalAudio(stream);
break;
case "video":
VideoLayout.changeLocalVideo(stream);
break;
case "stream":
VideoLayout.changeLocalStream(stream);
break;
}
}
function onDisposeConference(unload) {
Toolbar.showAuthenticateButton(false);
};
function onDisplayNameChanged(jid, displayName) {
ContactList.onDisplayNameChange(jid, displayName);
SettingsMenu.onDisplayNameChange(jid, displayName);
VideoLayout.onDisplayNameChanged(jid, displayName);
}
function registerListeners() {
APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
APP.RTC.addStreamListener(function (stream) {
VideoLayout.onRemoteStreamAdded(stream);
}, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
APP.RTC.addStreamListener(function (jid) {
VideoLayout.onVideoTypeChanged(jid);
}, StreamEventTypes.EVENT_TYPE_REMOTE_CHANGED);
APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged);
APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (resourceJid) {
VideoLayout.onDominantSpeakerChanged(resourceJid);
});
APP.RTC.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
function (lastNEndpoints, endpointsEnteringLastN, stream) {
VideoLayout.onLastNEndpointsChanged(lastNEndpoints,
endpointsEnteringLastN, stream);
});
APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGED,
function (endpointSimulcastLayers) {
VideoLayout.onSimulcastLayersChanged(endpointSimulcastLayers);
});
APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGING,
function (endpointSimulcastLayers) {
VideoLayout.onSimulcastLayersChanging(endpointSimulcastLayers);
});
APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED,
function (devices) {
VideoLayout.setDeviceAvailabilityIcons(null, devices);
})
APP.statistics.addAudioLevelListener(function(jid, audioLevel)
{
var resourceJid;
if(jid === APP.statistics.LOCAL_JID)
{
resourceJid = AudioLevels.LOCAL_LEVEL;
if(APP.RTC.localAudio.isMuted())
{
audioLevel = 0;
}
}
else
{
resourceJid = Strophe.getResourceFromJid(jid);
}
AudioLevels.updateAudioLevel(resourceJid, audioLevel,
UI.getLargeVideoState().userResourceJid);
});
APP.desktopsharing.addListener(function () {
ToolbarToggler.showDesktopSharingButton();
}, DesktopSharingEventTypes.INIT);
APP.desktopsharing.addListener(
Toolbar.changeDesktopSharingButtonState,
DesktopSharingEventTypes.SWITCHING_DONE);
APP.connectionquality.addListener(CQEvents.LOCALSTATS_UPDATED,
VideoLayout.updateLocalConnectionStats);
APP.connectionquality.addListener(CQEvents.REMOTESTATS_UPDATED,
VideoLayout.updateConnectionStats);
APP.connectionquality.addListener(CQEvents.STOP,
VideoLayout.onStatsStop);
APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
APP.xmpp.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () {
messageHandler.openMessageDialog(
'dialog.serviceUnavailable',
'dialog.gracefulShutdown'
);
});
APP.xmpp.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) {
var title = APP.translation.generateTranslatonHTML(
"dialog.reservationError");
var message = APP.translation.generateTranslatonHTML(
"dialog.reservationErrorMsg", {code: code, msg: msg});
messageHandler.openDialog(
title,
message,
true, {},
function (event, value, message, formVals)
{
return false;
}
);
});
APP.xmpp.addListener(XMPPEvents.KICKED, function () {
messageHandler.openMessageDialog("dialog.sessTerminated",
"dialog.kickMessage");
});
APP.xmpp.addListener(XMPPEvents.MUC_DESTROYED, function (reason) {
//FIXME: use Session Terminated from translation, but
// 'reason' text comes from XMPP packet and is not translated
var title = APP.translation.generateTranslatonHTML("dialog.sessTerminated");
messageHandler.openDialog(
title, reason, true, {},
function (event, value, message, formVals)
{
return false;
}
);
});
APP.xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () {
messageHandler.showError("dialog.error",
"dialog.bridgeUnavailable");
});
APP.xmpp.addListener(XMPPEvents.USER_ID_CHANGED, function (from, id) {
Avatar.setUserAvatar(from, id);
});
APP.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;
}
}
}
});
APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged);
APP.xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined);
APP.xmpp.addListener(XMPPEvents.LOCALROLE_CHANGED, onLocalRoleChange);
APP.xmpp.addListener(XMPPEvents.MUC_ENTER, onMucEntered);
APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged);
APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus);
APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject);
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
APP.xmpp.addListener(XMPPEvents.MUC_LEFT, onMucLeft);
APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordReqiured);
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad);
APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED,
onAuthenticationRequired);
APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE,
function (resource, devices) {
VideoLayout.setDeviceAvailabilityIcons(resource, devices);
});
}
/**
* Mutes/unmutes the local video.
*
* @param mute <tt>true</tt> to mute the local video; otherwise, <tt>false</tt>
* @param options an object which specifies optional arguments such as the
* <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> 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) {
APP.RTC.setVideoMute(mute,
UI.setVideoMuteButtonsState,
options);
}
function bindEvents()
{
/**
* Resizes and repositions videos in full screen mode.
*/
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
function () {
VideoLayout.resizeLargeVideoContainer();
VideoLayout.positionLarge();
}
);
$(window).resize(function () {
VideoLayout.resizeLargeVideoContainer();
VideoLayout.positionLarge();
});
}
UI.start = function (init) {
document.title = interfaceConfig.APP_NAME;
if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
{
$("#videoconference_page").hide();
var setupWelcomePage = require("./welcome_page/WelcomePage");
setupWelcomePage();
return;
}
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
var leftWatermarkDiv
= $("#largeVideoContainer div[class='watermark leftwatermark']");
leftWatermarkDiv.css({display: 'block'});
leftWatermarkDiv.parent().get(0).href
= interfaceConfig.JITSI_WATERMARK_LINK;
}
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
var rightWatermarkDiv
= $("#largeVideoContainer div[class='watermark rightwatermark']");
rightWatermarkDiv.css({display: 'block'});
rightWatermarkDiv.parent().get(0).href
= interfaceConfig.BRAND_WATERMARK_LINK;
rightWatermarkDiv.get(0).style.backgroundImage
= "url(images/rightwatermark.png)";
}
if (interfaceConfig.SHOW_POWERED_BY) {
$("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
}
$("#welcome_page").hide();
VideoLayout.resizeLargeVideoContainer();
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
// Set the defaults for prompt dialogs.
jQuery.prompt.setDefaults({persistent: false});
VideoLayout.init(eventEmitter);
AudioLevels.init();
NicknameHandler.init(eventEmitter);
registerListeners();
bindEvents();
setupPrezi();
setupToolbars();
setupChat();
document.title = interfaceConfig.APP_NAME;
$("#downloadlog").click(function (event) {
dump(event.target);
});
if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
{
$("#videoconference_page").hide();
var setupWelcomePage = require("./welcome_page/WelcomePage");
setupWelcomePage();
return;
}
$("#welcome_page").hide();
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
$('#notice').css({display: 'block'});
}
document.getElementById('largeVideo').volume = 0;
if (!$('#settings').is(':visible')) {
console.log('init');
init();
} else {
loginInfo.onsubmit = function (e) {
if (e.preventDefault) e.preventDefault();
$('#settings').hide();
init();
};
}
toastr.options = {
"closeButton": true,
"debug": false,
"positionClass": "notification-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "2000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut",
"reposition": function() {
if(PanelToggler.isVisible()) {
$("#toast-container").addClass("notification-bottom-right-center");
} else {
$("#toast-container").removeClass("notification-bottom-right-center");
}
},
"newestOnTop": false
};
SettingsMenu.init();
};
function chatAddError(errorMessage, originalText)
{
return Chat.chatAddError(errorMessage, originalText);
};
function chatSetSubject(text)
{
return Chat.chatSetSubject(text);
};
function updateChatConversation(from, displayName, message) {
return Chat.updateChatConversation(from, displayName, message);
};
function onMucJoined(jid, info) {
Toolbar.updateRoomUrl(window.location.href);
var meHTML = APP.translation.generateTranslatonHTML("me");
$("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")");
var settings = Settings.getSettings();
// Add myself to the contact list.
ContactList.addContact(jid, settings.email || settings.uid);
// Once we've joined the muc show the toolbar
ToolbarToggler.showToolbar();
var displayName = !config.displayJids
? info.displayName : Strophe.getResourceFromJid(jid);
if (displayName)
onDisplayNameChanged('localVideoContainer', displayName);
}
function initEtherpad(name) {
Etherpad.init(name);
};
function onMucLeft(jid) {
console.log('left.muc', jid);
var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) +
'>.displayname').html();
messageHandler.notify(displayName,'notify.somebody',
'disconnected',
'notify.disconnected');
// Need to call this with a slight delay, otherwise the element couldn't be
// found for some reason.
// XXX(gp) it works fine without the timeout for me (with Chrome 38).
window.setTimeout(function () {
var container = document.getElementById(
'participant_' + Strophe.getResourceFromJid(jid));
if (container) {
ContactList.removeContact(jid);
VideoLayout.removeConnectionIndicator(jid);
// hide here, wait for video to close before removing
$(container).hide();
VideoLayout.resizeThumbnails();
}
}, 10);
VideoLayout.participantLeft(jid);
};
function onLocalRoleChange(jid, info, pres, isModerator)
{
console.info("My role changed, new role: " + info.role);
onModeratorStatusChanged(isModerator);
VideoLayout.showModeratorIndicator();
if (isModerator) {
Authentication.closeAuthenticationWindow();
messageHandler.notify(null, "notify.me",
'connected', "notify.moderator");
}
}
function onModeratorStatusChanged(isModerator) {
Toolbar.showSipCallButton(isModerator);
Toolbar.showRecordingButton(
isModerator); //&&
// FIXME:
// Recording visible if
// there are at least 2(+ 1 focus) participants
//Object.keys(connection.emuc.members).length >= 3);
if (isModerator && config.etherpad_base) {
Etherpad.init();
}
};
function onPasswordReqiured(callback) {
// password is required
Toolbar.lockLockButton();
var message = '<h2 data-i18n="dialog.passwordRequired">';
message += APP.translation.translateString(
"dialog.passwordRequired");
message += '</h2>' +
'<input name="lockKey" type="text" data-i18n=' +
'"[placeholder]dialog.password" placeholder="' +
APP.translation.translateString("dialog.password") +
'" autofocus>';
messageHandler.openTwoButtonDialog(null, null, null, message,
true,
"dialog.Ok",
function (e, v, m, f) {},
null,
function (e, v, m, f) {
if (v) {
var lockKey = f.lockKey;
if (lockKey) {
Toolbar.setSharedKey(lockKey);
callback(lockKey);
}
}
},
':input:first'
);
}
function onMucEntered(jid, id, displayName) {
messageHandler.notify(displayName,'notify.somebody',
'connected',
'notify.connected');
// Add Peer's container
VideoLayout.ensurePeerContainerExists(jid,id);
}
function onMucPresenceStatus( jid, info) {
VideoLayout.setPresenceStatus(
'participant_' + Strophe.getResourceFromJid(jid), info.status);
}
function onMucRoleChanged(role, displayName) {
VideoLayout.showModeratorIndicator();
if (role === 'moderator') {
var messageKey, messageOptions = {};
if (!displayName) {
messageKey = "notify.grantedToUnknown";
}
else
{
messageKey = "notify.grantedTo";
messageOptions = {to: displayName};
}
messageHandler.notify(
displayName,'notify.somebody',
'connected', messageKey,
messageOptions);
}
}
function onAuthenticationRequired(intervalCallback) {
Authentication.openAuthenticationDialog(
roomName, intervalCallback, function () {
Toolbar.authenticateClicked();
});
};
function onLastNChanged(oldValue, newValue) {
if (config.muteLocalVideoIfNotInLastN) {
setVideoMute(!newValue, { 'byUser': false });
}
}
UI.toggleSmileys = function () {
Chat.toggleSmileys();
};
UI.getSettings = function () {
return Settings.getSettings();
};
UI.toggleFilmStrip = function () {
return BottomToolbar.toggleFilmStrip();
};
UI.toggleChat = function () {
return BottomToolbar.toggleChat();
};
UI.toggleContactList = function () {
return BottomToolbar.toggleContactList();
};
UI.inputDisplayNameHandler = function (value) {
VideoLayout.inputDisplayNameHandler(value);
};
UI.getLargeVideoState = function()
{
return VideoLayout.getLargeVideoState();
};
UI.generateRoomName = function() {
if(roomName)
return roomName;
var roomnode = null;
var path = window.location.pathname;
// determinde the room node from the url
// TODO: just the roomnode or the whole bare jid?
if (config.getroomnode && typeof config.getroomnode === 'function') {
// custom function might be responsible for doing the pushstate
roomnode = config.getroomnode(path);
} else {
/* fall back to default strategy
* this is making assumptions about how the URL->room mapping happens.
* It currently assumes deployment at root, with a rewrite like the
* following one (for nginx):
location ~ ^/([a-zA-Z0-9]+)$ {
rewrite ^/(.*)$ / break;
}
*/
if (path.length > 1) {
roomnode = path.substr(1).toLowerCase();
} else {
var word = RoomNameGenerator.generateRoomWithoutSeparator();
roomnode = word.toLowerCase();
window.history.pushState('VideoChat',
'Room: ' + word, window.location.pathname + word);
}
}
roomName = roomnode + '@' + config.hosts.muc;
return roomName;
};
UI.connectionIndicatorShowMore = function(id)
{
return VideoLayout.connectionIndicators[id].showMore();
};
UI.showLoginPopup = function(callback)
{
console.log('password is required');
var message = '<h2 data-i18n="dialog.passwordRequired">';
message += APP.translation.translateString(
"dialog.passwordRequired");
message += '</h2>' +
'<input name="username" type="text" ' +
'placeholder="user@domain.net" autofocus>' +
'<input name="password" ' +
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
' placeholder="user password">';
UI.messageHandler.openTwoButtonDialog(null, null, null, message,
true,
"dialog.Ok",
function (e, v, m, f) {
if (v) {
if (f.username !== null && f.password != null) {
callback(f.username, f.password);
}
}
},
null, null, ':input:first'
);
}
UI.checkForNicknameAndJoin = function () {
Authentication.closeAuthenticationDialog();
Authentication.stopInterval();
var nick = null;
if (config.useNicks) {
nick = window.prompt('Your nickname (optional)');
}
APP.xmpp.joinRoom(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 = APP.xmpp.populateData();
var metadata = {};
metadata.time = new Date();
metadata.url = window.location.href;
metadata.ua = navigator.userAgent;
var log = APP.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.
*/
UI.toggleVideo = function () {
setVideoMute(!APP.RTC.localVideo.isMuted());
};
/**
* Mutes / unmutes audio for the local participant.
*/
UI.toggleAudio = function() {
UI.setAudioMuted(!APP.RTC.localAudio.isMuted());
};
/**
* Sets muted audio state for the local participant.
*/
UI.setAudioMuted = function (mute) {
if(!APP.xmpp.setAudioMute(mute, function () {
VideoLayout.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.addListener = function (type, listener) {
eventEmitter.on(type, listener);
}
UI.clickOnVideo = function (videoNumber) {
var remoteVideos = $(".videocontainer:not(#mixedstream)");
if (remoteVideos.length > videoNumber) {
remoteVideos[videoNumber].click();
}
}
//Used by torture
UI.showToolbar = function () {
return ToolbarToggler.showToolbar();
}
//Used by torture
UI.dockToolbar = function (isDock) {
return ToolbarToggler.dockToolbar(isDock);
}
UI.setVideoMuteButtonsState = 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);
}
}
module.exports = UI;
},{"../../service/RTC/RTCEvents":89,"../../service/RTC/StreamEventTypes":91,"../../service/connectionquality/CQEvents":94,"../../service/desktopsharing/DesktopSharingEventTypes":95,"../../service/xmpp/XMPPEvents":97,"./../settings/Settings":38,"./audio_levels/AudioLevels.js":9,"./authentication/Authentication":11,"./avatar/Avatar":13,"./etherpad/Etherpad.js":14,"./prezi/Prezi.js":15,"./side_pannels/SidePanelToggler":17,"./side_pannels/chat/Chat.js":18,"./side_pannels/contactlist/ContactList":22,"./side_pannels/settings/SettingsMenu":23,"./toolbars/BottomToolbar":24,"./toolbars/Toolbar":25,"./toolbars/ToolbarToggler":26,"./util/MessageHandler":28,"./util/NicknameHandler":29,"./util/UIUtil":30,"./videolayout/VideoLayout.js":32,"./welcome_page/RoomnameGenerator":33,"./welcome_page/WelcomePage":34,"events":98}],9:[function(require,module,exports){
var CanvasUtil = require("./CanvasUtils");
var ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
function initActiveSpeakerAudioLevels() {
var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2;
// Draw a circle.
ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
// Add a shadow around the circle
ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
ASDrawContext.shadowOffsetX = 0;
ASDrawContext.shadowOffsetY = 0;
}
/**
* The audio Levels plugin.
*/
var AudioLevels = (function(my) {
var audioLevelCanvasCache = {};
my.LOCAL_LEVEL = 'local';
my.init = function () {
initActiveSpeakerAudioLevels();
}
/**
* Updates the audio level canvas for the given peerJid. If the canvas
* didn't exist we create it.
*/
my.updateAudioLevelCanvas = function (peerJid, VideoLayout) {
var resourceJid = null;
var videoSpanId = null;
if (!peerJid)
videoSpanId = 'localVideoContainer';
else {
resourceJid = Strophe.getResourceFromJid(peerJid);
videoSpanId = 'participant_' + resourceJid;
}
var videoSpan = document.getElementById(videoSpanId);
if (!videoSpan) {
if (resourceJid)
console.error("No video element for jid", resourceJid);
else
console.error("No video element for local video.");
return;
}
var audioLevelCanvas = $('#' + videoSpanId + '>canvas');
var videoSpaceWidth = $('#remoteVideos').width();
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var thumbnailWidth = thumbnailSize[0];
var thumbnailHeight = thumbnailSize[1];
if (!audioLevelCanvas || audioLevelCanvas.length === 0) {
audioLevelCanvas = document.createElement('canvas');
audioLevelCanvas.className = "audiolevel";
audioLevelCanvas.style.bottom = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
audioLevelCanvas.style.left = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
resizeAudioLevelCanvas( audioLevelCanvas,
thumbnailWidth,
thumbnailHeight);
videoSpan.appendChild(audioLevelCanvas);
} else {
audioLevelCanvas = audioLevelCanvas.get(0);
resizeAudioLevelCanvas( audioLevelCanvas,
thumbnailWidth,
thumbnailHeight);
}
};
/**
* Updates the audio level UI for the given resourceJid.
*
* @param resourceJid the resource jid indicating the video element for
* which we draw the audio level
* @param audioLevel the newAudio level to render
*/
my.updateAudioLevel = function (resourceJid, audioLevel, largeVideoResourceJid) {
drawAudioLevelCanvas(resourceJid, audioLevel);
var videoSpanId = getVideoSpanId(resourceJid);
var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0);
if (!audioLevelCanvas)
return;
var drawContext = audioLevelCanvas.getContext('2d');
var canvasCache = audioLevelCanvasCache[resourceJid];
drawContext.clearRect (0, 0,
audioLevelCanvas.width, audioLevelCanvas.height);
drawContext.drawImage(canvasCache, 0, 0);
if(resourceJid === AudioLevels.LOCAL_LEVEL) {
if(!APP.xmpp.myJid()) {
return;
}
resourceJid = APP.xmpp.myResource();
}
if(resourceJid === largeVideoResourceJid) {
window.requestAnimationFrame(function () {
AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
});
}
};
my.updateActiveSpeakerAudioLevel = function(audioLevel) {
if($("#activeSpeaker").css("visibility") == "hidden")
return;
ASDrawContext.clearRect(0, 0, 300, 300);
if(audioLevel == 0)
return;
ASDrawContext.shadowBlur = getShadowLevel(audioLevel);
// Fill the shape.
ASDrawContext.fill();
};
/**
* Resizes the given audio level canvas to match the given thumbnail size.
*/
function resizeAudioLevelCanvas(audioLevelCanvas,
thumbnailWidth,
thumbnailHeight) {
audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
}
/**
* Draws the audio level canvas into the cached canvas object.
*
* @param resourceJid the resource jid indicating the video element for
* which we draw the audio level
* @param audioLevel the newAudio level to render
*/
function drawAudioLevelCanvas(resourceJid, audioLevel) {
if (!audioLevelCanvasCache[resourceJid]) {
var videoSpanId = getVideoSpanId(resourceJid);
var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0);
/*
* FIXME Testing has shown that audioLevelCanvasOrig may not exist.
* In such a case, the method CanvasUtil.cloneCanvas may throw an
* error. Since audio levels are frequently updated, the errors have
* been observed to pile into the console, strain the CPU.
*/
if (audioLevelCanvasOrig)
{
audioLevelCanvasCache[resourceJid]
= CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
}
}
var canvas = audioLevelCanvasCache[resourceJid];
if (!canvas)
return;
var drawContext = canvas.getContext('2d');
drawContext.clearRect(0, 0, canvas.width, canvas.height);
var shadowLevel = getShadowLevel(audioLevel);
if (shadowLevel > 0)
// drawContext, x, y, w, h, r, shadowColor, shadowLevel
CanvasUtil.drawRoundRectGlow( drawContext,
interfaceConfig.CANVAS_EXTRA/2, interfaceConfig.CANVAS_EXTRA/2,
canvas.width - interfaceConfig.CANVAS_EXTRA,
canvas.height - interfaceConfig.CANVAS_EXTRA,
interfaceConfig.CANVAS_RADIUS,
interfaceConfig.SHADOW_COLOR,
shadowLevel);
}
/**
* Returns the shadow/glow level for the given audio level.
*
* @param audioLevel the audio level from which we determine the shadow
* level
*/
function getShadowLevel (audioLevel) {
var shadowLevel = 0;
if (audioLevel <= 0.3) {
shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3));
}
else if (audioLevel <= 0.6) {
shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3));
}
else {
shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
}
return shadowLevel;
}
/**
* Returns the video span id corresponding to the given resourceJid or local
* user.
*/
function getVideoSpanId(resourceJid) {
var videoSpanId = null;
if (resourceJid === AudioLevels.LOCAL_LEVEL
|| (APP.xmpp.myResource() && resourceJid
=== APP.xmpp.myResource()))
videoSpanId = 'localVideoContainer';
else
videoSpanId = 'participant_' + resourceJid;
return videoSpanId;
}
/**
* Indicates that the remote video has been resized.
*/
$(document).bind('remotevideo.resized', function (event, width, height) {
var resized = false;
$('#remoteVideos>span>canvas').each(function() {
var canvas = $(this).get(0);
if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
canvas.width = width + interfaceConfig.CANVAS_EXTRA;
resized = true;
}
if (canvas.heigh !== height + interfaceConfig.CANVAS_EXTRA) {
canvas.height = height + interfaceConfig.CANVAS_EXTRA;
resized = true;
}
});
if (resized)
Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) {
audioLevelCanvasCache[resourceJid].width
= width + interfaceConfig.CANVAS_EXTRA;
audioLevelCanvasCache[resourceJid].height
= height + interfaceConfig.CANVAS_EXTRA;
});
});
return my;
})(AudioLevels || {});
module.exports = AudioLevels;
},{"./CanvasUtils":10}],10:[function(require,module,exports){
/**
* Utility class for drawing canvas shapes.
*/
var CanvasUtil = (function(my) {
/**
* Draws a round rectangle with a glow. The glowWidth indicates the depth
* of the glow.
*
* @param drawContext the context of the canvas to draw to
* @param x the x coordinate of the round rectangle
* @param y the y coordinate of the round rectangle
* @param w the width of the round rectangle
* @param h the height of the round rectangle
* @param glowColor the color of the glow
* @param glowWidth the width of the glow
*/
my.drawRoundRectGlow
= function(drawContext, x, y, w, h, r, glowColor, glowWidth) {
// Save the previous state of the context.
drawContext.save();
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
// Draw a round rectangle.
drawContext.beginPath();
drawContext.moveTo(x+r, y);
drawContext.arcTo(x+w, y, x+w, y+h, r);
drawContext.arcTo(x+w, y+h, x, y+h, r);
drawContext.arcTo(x, y+h, x, y, r);
drawContext.arcTo(x, y, x+w, y, r);
drawContext.closePath();
// Add a shadow around the rectangle
drawContext.shadowColor = glowColor;
drawContext.shadowBlur = glowWidth;
drawContext.shadowOffsetX = 0;
drawContext.shadowOffsetY = 0;
// Fill the shape.
drawContext.fill();
drawContext.save();
drawContext.restore();
// 1) Uncomment this line to use Composite Operation, which is doing the
// same as the clip function below and is also antialiasing the round
// border, but is said to be less fast performance wise.
// drawContext.globalCompositeOperation='destination-out';
drawContext.beginPath();
drawContext.moveTo(x+r, y);
drawContext.arcTo(x+w, y, x+w, y+h, r);
drawContext.arcTo(x+w, y+h, x, y+h, r);
drawContext.arcTo(x, y+h, x, y, r);
drawContext.arcTo(x, y, x+w, y, r);
drawContext.closePath();
// 2) Uncomment this line to use Composite Operation, which is doing the
// same as the clip function below and is also antialiasing the round
// border, but is said to be less fast performance wise.
// drawContext.fill();
// Comment these two lines if choosing to do the same with composite
// operation above 1 and 2.
drawContext.clip();
drawContext.clearRect(0, 0, 277, 200);
// Restore the previous context state.
drawContext.restore();
};
/**
* Clones the given canvas.
*
* @return the new cloned canvas.
*/
my.cloneCanvas = function (oldCanvas) {
/*
* FIXME Testing has shown that oldCanvas may not exist. In such a case,
* the method CanvasUtil.cloneCanvas may throw an error. Since audio
* levels are frequently updated, the errors have been observed to pile
* into the console, strain the CPU.
*/
if (!oldCanvas)
return oldCanvas;
//create a new canvas
var newCanvas = document.createElement('canvas');
var context = newCanvas.getContext('2d');
//set dimensions
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
//apply the old canvas to the new one
context.drawImage(oldCanvas, 0, 0);
//return the new canvas
return newCanvas;
};
return my;
})(CanvasUtil || {});
module.exports = CanvasUtil;
},{}],11:[function(require,module,exports){
/* global $, APP*/
var LoginDialog = require('./LoginDialog');
var Moderator = require('../../xmpp/moderator');
/* 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('@'));
var title = APP.translation.generateTranslatonHTML("dialog.Stop");
var msg = APP.translation.generateTranslatonHTML("dialog.AuthMsg",
{room: room});
var buttonTxt
= APP.translation.generateTranslatonHTML("dialog.Authenticate");
var buttons = [];
buttons.push({title: buttonTxt, value: "authNow"});
authDialog = APP.UI.messageHandler.openDialog(
title,
msg,
true,
buttons,
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;
}
},
xmppAuthenticate: function () {
var loginDialog = LoginDialog.show(
function (connection, state) {
if (!state) {
// User cancelled
loginDialog.close();
return;
} else if (state == APP.xmpp.Status.CONNECTED) {
loginDialog.close();
Authentication.stopInterval();
Authentication.closeAuthenticationDialog();
// Close the connection as anonymous one will be used
// to create the conference. Session-id will authorize
// the request.
connection.disconnect();
var roomName = APP.UI.generateRoomName();
Moderator.allocateConferenceFocus(roomName, function () {
// If it's not "on the fly" authentication now join
// the conference room
if (!APP.xmpp.getMUCJoined()) {
APP.UI.checkForNicknameAndJoin();
}
});
}
}, true);
},
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) {
authDialog.close();
authDialog = null;
}
},
createAuthenticationWindow: function (callback, url) {
authenticationWindow = APP.UI.messageHandler.openCenteredPopup(
url, 910, 660,
// On closed
function () {
// Close authentication dialog if opened
Authentication.closeAuthenticationDialog();
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;
},{"../../xmpp/moderator":53,"./LoginDialog":12}],12:[function(require,module,exports){
/* global $, APP, config*/
var XMPP = require('../../xmpp/xmpp');
var Moderator = require('../../xmpp/moderator');
//FIXME: use LoginDialog to add retries to XMPP.connect method used when
// anonymous domain is not enabled
/**
* Creates new <tt>Dialog</tt> instance.
* @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt> called
* when we either fail to connect or succeed(check Strophe.Status).
* @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to Jicofo
* in order to create session-id after the connection is established.
* @constructor
*/
function Dialog(callback, obtainSession) {
var self = this;
var stop = false;
var connection = APP.xmpp.createConnection();
var message = '<h2 data-i18n="dialog.passwordRequired">';
message += APP.translation.translateString("dialog.passwordRequired");
message += '</h2>' +
'<input name="username" type="text" ' +
'placeholder="user@domain.net" autofocus>' +
'<input name="password" ' +
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
' placeholder="user password">';
var okButton = APP.translation.generateTranslatonHTML("dialog.Ok");
var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel");
var states = {
login: {
html: message,
buttons: [
{ title: okButton, value: true},
{ title: cancelButton, value: false}
],
focus: ':input:first',
submit: function (e, v, m, f) {
e.preventDefault();
if (v) {
var jid = f.username;
var password = f.password;
if (jid && password) {
stop = false;
connection.reset();
connDialog.goToState('connecting');
connection.connect(jid, password, stateHandler);
}
} else {
// User cancelled
stop = true;
callback();
}
}
},
connecting: {
title: APP.translation.translateString('dialog.connecting'),
html: '<div id="connectionStatus"></div>',
buttons: [],
defaultButton: 0
},
finished: {
title: APP.translation.translateString('dialog.error'),
html: '<div id="errorMessage"></div>',
buttons: [
{
title: APP.translation.translateString('dialog.retry'),
value: 'retry'
},
{
title: APP.translation.translateString('dialog.Cancel'),
value: 'cancel'
},
],
defaultButton: 0,
submit: function (e, v, m, f) {
e.preventDefault();
if (v === 'retry')
connDialog.goToState('login');
else
callback();
}
}
};
var connDialog
= APP.UI.messageHandler.openDialogWithStates(states,
{ persistent: true, closeText: '' }, null);
var stateHandler = function (status, message) {
if (stop) {
return;
}
var translateKey = "connection." + XMPP.getStatusString(status);
var statusStr = APP.translation.translateString(translateKey);
// Display current state
var connectionStatus =
connDialog.getState('connecting').find('#connectionStatus');
connectionStatus.text(statusStr);
switch (status) {
case XMPP.Status.CONNECTED:
stop = true;
if (!obtainSession) {
callback(connection, status);
return;
}
// Obtaining session-id status
connectionStatus.text(
APP.translation.translateString(
'connection.FETCH_SESSION_ID'));
// Authenticate with Jicofo and obtain session-id
var roomName = APP.UI.generateRoomName();
// Jicofo will return new session-id when connected
// from authenticated domain
connection.sendIQ(
Moderator.createConferenceIq(roomName),
function (result) {
connectionStatus.text(
APP.translation.translateString(
'connection.GOT_SESSION_ID'));
stop = true;
// Parse session-id
Moderator.parseSessionId(result);
callback(connection, status);
},
function (error) {
console.error("Auth on the fly failed", error);
stop = true;
var errorMsg =
APP.translation.translateString(
'connection.GET_SESSION_ID_ERROR') +
$(error).find('>error').attr('code');
self.displayError(errorMsg);
connection.disconnect();
});
break;
case XMPP.Status.AUTHFAIL:
case XMPP.Status.CONNFAIL:
case XMPP.Status.DISCONNECTED:
stop = true;
callback(connection, status);
var errorMessage = statusStr;
if (message)
{
errorMessage += ': ' + message;
}
self.displayError(errorMessage);
break;
default:
break;
}
};
/**
* Displays error message in 'finished' state which allows either to cancel
* or retry.
* @param message the final message to be displayed.
*/
this.displayError = function (message) {
var finishedState = connDialog.getState('finished');
var errorMessageElem = finishedState.find('#errorMessage');
errorMessageElem.text(message);
connDialog.goToState('finished');
};
/**
* Closes LoginDialog.
*/
this.close = function () {
stop = true;
connDialog.close();
};
}
var LoginDialog = {
/**
* Displays login prompt used to establish new XMPP connection. Given
* <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
* called when we connect successfully(status === CONNECTED) or when we fail
* to do so. On connection failure program can call Dialog.close() method in
* order to cancel or do nothing to let the user retry.
* @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
* called when we either fail to connect or succeed(check
* Strophe.Status).
* @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
* Jicofo in order to create session-id after the connection is
* established.
* @returns {Dialog}
*/
show: function (callback, obtainSession) {
return new Dialog(callback, obtainSession);
}
};
module.exports = LoginDialog;
},{"../../xmpp/moderator":53,"../../xmpp/xmpp":61}],13:[function(require,module,exports){
var Settings = require("../../settings/Settings");
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
var users = {};
var activeSpeakerJid;
function setVisibility(selector, show) {
if (selector && selector.length > 0) {
selector.css("visibility", show ? "visible" : "hidden");
}
}
function isUserMuted(jid) {
// XXX(gp) we may want to rename this method to something like
// isUserStreaming, for example.
if (jid && jid != APP.xmpp.myJid()) {
var resource = Strophe.getResourceFromJid(jid);
if (!require("../videolayout/VideoLayout").isInLastN(resource)) {
return true;
}
}
if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
return null;
}
return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted;
}
function getGravatarUrl(id, size) {
if(id === APP.xmpp.myJid() || !id) {
id = Settings.getSettings().uid;
}
return 'https://www.gravatar.com/avatar/' +
MD5.hexdigest(id.trim().toLowerCase()) +
"?d=wavatar&size=" + (size || "30");
}
var Avatar = {
/**
* Sets the user's avatar in the settings menu(if local user), contact list
* and thumbnail
* @param jid jid of the user
* @param id email or userID to be used as a hash
*/
setUserAvatar: function (jid, id) {
if (id) {
if (users[jid] === id) {
return;
}
users[jid] = id;
}
var thumbUrl = getGravatarUrl(users[jid] || jid, 100);
var contactListUrl = getGravatarUrl(users[jid] || jid);
var resourceJid = Strophe.getResourceFromJid(jid);
var thumbnail = $('#participant_' + resourceJid);
var avatar = $('#avatar_' + resourceJid);
// set the avatar in the settings menu if it is local user and get the
// local video container
if (jid === APP.xmpp.myJid()) {
$('#avatar').get(0).src = thumbUrl;
thumbnail = $('#localVideoContainer');
}
// set the avatar in the contact list
var contact = $('#' + resourceJid + '>img');
if (contact && contact.length > 0) {
contact.get(0).src = contactListUrl;
}
// set the avatar in the thumbnail
if (avatar && avatar.length > 0) {
avatar[0].src = thumbUrl;
} else {
if (thumbnail && thumbnail.length > 0) {
avatar = document.createElement('img');
avatar.id = 'avatar_' + resourceJid;
avatar.className = 'userAvatar';
avatar.src = thumbUrl;
thumbnail.append(avatar);
}
}
//if the user is the current active speaker - update the active speaker
// avatar
if (jid === activeSpeakerJid) {
this.updateActiveSpeakerAvatarSrc(jid);
}
},
/**
* Hides or shows the user's avatar
* @param jid jid of the user
* @param show whether we should show the avatar or not
* video because there is no dominant speaker and no focused speaker
*/
showUserAvatar: function (jid, show) {
if (users[jid]) {
var resourceJid = Strophe.getResourceFromJid(jid);
var video = $('#participant_' + resourceJid + '>video');
var avatar = $('#avatar_' + resourceJid);
if (jid === APP.xmpp.myJid()) {
video = $('#localVideoWrapper>video');
}
if (show === undefined || show === null) {
show = isUserMuted(jid);
}
//if the user is the currently focused, the dominant speaker or if
//there is no focused and no dominant speaker and the large video is
//currently shown
if (activeSpeakerJid === jid && require("../videolayout/VideoLayout").isLargeVideoOnTop()) {
setVisibility($("#largeVideo"), !show);
setVisibility($('#activeSpeaker'), show);
setVisibility(avatar, false);
setVisibility(video, false);
} else {
if (video && video.length > 0) {
setVisibility(video, !show);
setVisibility(avatar, show);
}
}
}
},
/**
* Updates the src of the active speaker avatar
* @param jid of the current active speaker
*/
updateActiveSpeakerAvatarSrc: function (jid) {
if (!jid) {
jid = APP.xmpp.findJidFromResource(
require("../videolayout/VideoLayout").getLargeVideoState().userResourceJid);
}
var avatar = $("#activeSpeakerAvatar")[0];
var url = getGravatarUrl(users[jid],
interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE);
if (jid === activeSpeakerJid && avatar.src === url) {
return;
}
activeSpeakerJid = jid;
var isMuted = isUserMuted(jid);
if (jid && isMuted !== null) {
avatar.src = url;
setVisibility($("#largeVideo"), !isMuted);
Avatar.showUserAvatar(jid, isMuted);
}
}
};
module.exports = Avatar;
},{"../../../service/RTC/MediaStreamTypes":87,"../../settings/Settings":38,"../videolayout/VideoLayout":32}],14:[function(require,module,exports){
/* global $, config,
setLargeVideoVisible, Util */
var VideoLayout = require("../videolayout/VideoLayout");
var Prezi = require("../prezi/Prezi");
var UIUtil = require("../util/UIUtil");
var etherpadName = null;
var etherpadIFrame = null;
var domain = null;
var options = "?showControls=true&showChat=false&showLineNumbers=true&useMonospaceFont=false";
/**
* Resizes the etherpad.
*/
function resize() {
if ($('#etherpad>iframe').length) {
var remoteVideos = $('#remoteVideos');
var availableHeight
= window.innerHeight - remoteVideos.outerHeight();
var availableWidth = UIUtil.getAvailableVideoWidth();
$('#etherpad>iframe').width(availableWidth);
$('#etherpad>iframe').height(availableHeight);
}
}
/**
* Shares the Etherpad name with other participants.
*/
function shareEtherpad() {
APP.xmpp.addToPresence("etherpad", etherpadName);
}
/**
* Creates the Etherpad button and adds it to the toolbar.
*/
function enableEtherpadButton() {
if (!$('#etherpadButton').is(":visible"))
$('#etherpadButton').css({display: 'inline-block'});
}
/**
* Creates the IFrame for the etherpad.
*/
function createIFrame() {
etherpadIFrame = document.createElement('iframe');
etherpadIFrame.src = domain + etherpadName + options;
etherpadIFrame.frameBorder = 0;
etherpadIFrame.scrolling = "no";
etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
etherpadIFrame.setAttribute('style', 'visibility: hidden;');
document.getElementById('etherpad').appendChild(etherpadIFrame);
etherpadIFrame.onload = function() {
document.domain = document.domain;
bubbleIframeMouseMove(etherpadIFrame);
setTimeout(function() {
// the iframes inside of the etherpad are
// not yet loaded when the etherpad iframe is loaded
var outer = etherpadIFrame.
contentDocument.getElementsByName("ace_outer")[0];
bubbleIframeMouseMove(outer);
var inner = outer.
contentDocument.getElementsByName("ace_inner")[0];
bubbleIframeMouseMove(inner);
}, 2000);
};
}
function bubbleIframeMouseMove(iframe){
var existingOnMouseMove = iframe.contentWindow.onmousemove;
iframe.contentWindow.onmousemove = function(e){
if(existingOnMouseMove) existingOnMouseMove(e);
var evt = document.createEvent("MouseEvents");
var boundingClientRect = iframe.getBoundingClientRect();
evt.initMouseEvent(
"mousemove",
true, // bubbles
false, // not cancelable
window,
e.detail,
e.screenX,
e.screenY,
e.clientX + boundingClientRect.left,
e.clientY + boundingClientRect.top,
e.ctrlKey,
e.altKey,
e.shiftKey,
e.metaKey,
e.button,
null // no related element
);
iframe.dispatchEvent(evt);
};
}
/**
* On video selected event.
*/
$(document).bind('video.selected', function (event, isPresentation) {
if (config.etherpad_base && etherpadIFrame && etherpadIFrame.style.visibility !== 'hidden')
Etherpad.toggleEtherpad(isPresentation);
});
var Etherpad = {
/**
* Initializes the etherpad.
*/
init: function (name) {
if (config.etherpad_base && !etherpadName) {
domain = config.etherpad_base;
if (!name) {
// In case we're the focus we generate the name.
etherpadName = Math.random().toString(36).substring(7) +
'_' + (new Date().getTime()).toString();
shareEtherpad();
}
else
etherpadName = name;
enableEtherpadButton();
/**
* Resizes the etherpad, when the window is resized.
*/
$(window).resize(function () {
resize();
});
}
},
/**
* Opens/hides the Etherpad.
*/
toggleEtherpad: function (isPresentation) {
if (!etherpadIFrame)
createIFrame();
var largeVideo = null;
if (Prezi.isPresentationVisible())
largeVideo = $('#presentation>iframe');
else
largeVideo = $('#largeVideo');
if ($('#etherpad>iframe').css('visibility') === 'hidden') {
$('#activeSpeaker').css('visibility', 'hidden');
largeVideo.fadeOut(300, function () {
if (Prezi.isPresentationVisible()) {
largeVideo.css({opacity: '0'});
} else {
VideoLayout.setLargeVideoVisible(false);
}
});
$('#etherpad>iframe').fadeIn(300, function () {
document.body.style.background = '#eeeeee';
$('#etherpad>iframe').css({visibility: 'visible'});
$('#etherpad').css({zIndex: 2});
});
}
else if ($('#etherpad>iframe')) {
$('#etherpad>iframe').fadeOut(300, function () {
$('#etherpad>iframe').css({visibility: 'hidden'});
$('#etherpad').css({zIndex: 0});
document.body.style.background = 'black';
});
if (!isPresentation) {
$('#largeVideo').fadeIn(300, function () {
VideoLayout.setLargeVideoVisible(true);
});
}
}
resize();
},
isVisible: function() {
var etherpadIframe = $('#etherpad>iframe');
return etherpadIframe && etherpadIframe.is(':visible');
}
};
module.exports = Etherpad;
},{"../prezi/Prezi":15,"../util/UIUtil":30,"../videolayout/VideoLayout":32}],15:[function(require,module,exports){
var ToolbarToggler = require("../toolbars/ToolbarToggler");
var UIUtil = require("../util/UIUtil");
var VideoLayout = require("../videolayout/VideoLayout");
var messageHandler = require("../util/MessageHandler");
var PreziPlayer = require("./PreziPlayer");
var preziPlayer = null;
var Prezi = {
/**
* Reloads the current presentation.
*/
reloadPresentation: function() {
var iframe = document.getElementById(preziPlayer.options.preziId);
iframe.src = iframe.src;
},
/**
* Returns <tt>true</tt> if the presentation is visible, <tt>false</tt> -
* otherwise.
*/
isPresentationVisible: function () {
return ($('#presentation>iframe') != null
&& $('#presentation>iframe').css('opacity') == 1);
},
/**
* Opens the Prezi dialog, from which the user could choose a presentation
* to load.
*/
openPreziDialog: function() {
var myprezi = APP.xmpp.getPrezi();
if (myprezi) {
messageHandler.openTwoButtonDialog("dialog.removePreziTitle",
null,
"dialog.removePreziMsg",
null,
false,
"dialog.Remove",
function(e,v,m,f) {
if(v) {
APP.xmpp.removePreziFromPresence();
}
}
);
}
else if (preziPlayer != null) {
messageHandler.openTwoButtonDialog("dialog.sharePreziTitle",
null, "dialog.sharePreziMsg",
null,
false,
"dialog.Ok",
function(e,v,m,f) {
$.prompt.close();
}
);
}
else {
var html = APP.translation.generateTranslatonHTML(
"dialog.sharePreziTitle");
var cancelButton = APP.translation.generateTranslatonHTML(
"dialog.Cancel");
var shareButton = APP.translation.generateTranslatonHTML(
"dialog.Share");
var backButton = APP.translation.generateTranslatonHTML(
"dialog.Back");
var buttons = [];
var buttons1 = [];
// Cancel button to both states
buttons.push({title: cancelButton, value: false});
buttons1.push({title: cancelButton, value: false});
// Share button
buttons.push({title: shareButton, value: true});
// Back button
buttons1.push({title: backButton, value: true});
var linkError = APP.translation.generateTranslatonHTML(
"dialog.preziLinkError");
var defaultUrl = APP.translation.translateString("defaultPreziLink",
{url: "http://prezi.com/wz7vhjycl7e6/my-prezi"});
var openPreziState = {
state0: {
html: '<h2>' + html + '</h2>' +
'<input name="preziUrl" type="text" ' +
'data-i18n="[placeholder]defaultPreziLink" data-i18n-options=\'' +
JSON.stringify({"url": "http://prezi.com/wz7vhjycl7e6/my-prezi"}) +
'\' placeholder="' + defaultUrl + '" autofocus>',
persistent: false,
buttons: buttons,
focus: ':input:first',
defaultButton: 0,
submit: function (e, v, m, f) {
e.preventDefault();
if(v)
{
var preziUrl = f.preziUrl;
if (preziUrl)
{
var urlValue
= encodeURI(UIUtil.escapeHtml(preziUrl));
if (urlValue.indexOf('http://prezi.com/') != 0
&& urlValue.indexOf('https://prezi.com/') != 0)
{
$.prompt.goToState('state1');
return false;
}
else {
var presIdTmp = urlValue.substring(
urlValue.indexOf("prezi.com/") + 10);
if (!isAlphanumeric(presIdTmp)
|| presIdTmp.indexOf('/') < 2) {
$.prompt.goToState('state1');
return false;
}
else {
APP.xmpp.addToPresence("prezi", urlValue);
$.prompt.close();
}
}
}
}
else
$.prompt.close();
}
},
state1: {
html: '<h2>' + html + '</h2>' +
linkError,
persistent: false,
buttons: buttons1,
focus: ':input:first',
defaultButton: 1,
submit: function (e, v, m, f) {
e.preventDefault();
if (v === 0)
$.prompt.close();
else
$.prompt.goToState('state0');
}
}
};
messageHandler.openDialogWithStates(openPreziState);
}
}
};
/**
* A new presentation has been added.
*
* @param event the event indicating the add of a presentation
* @param jid the jid from which the presentation was added
* @param presUrl url of the presentation
* @param currentSlide the current slide to which we should move
*/
function presentationAdded(event, jid, presUrl, currentSlide) {
console.log("presentation added", presUrl);
var presId = getPresentationId(presUrl);
var elementId = 'participant_'
+ Strophe.getResourceFromJid(jid)
+ '_' + presId;
// We explicitly don't specify the peer jid here, because we don't want
// this video to be dealt with as a peer related one (for example we
// don't want to show a mute/kick menu for this one, etc.).
VideoLayout.addRemoteVideoContainer(null, elementId);
VideoLayout.resizeThumbnails();
var controlsEnabled = false;
if (jid === APP.xmpp.myJid())
controlsEnabled = true;
setPresentationVisible(true);
$('#largeVideoContainer').hover(
function (event) {
if (Prezi.isPresentationVisible()) {
var reloadButtonRight = window.innerWidth
- $('#presentation>iframe').offset().left
- $('#presentation>iframe').width();
$('#reloadPresentation').css({ right: reloadButtonRight,
display:'inline-block'});
}
},
function (event) {
if (!Prezi.isPresentationVisible())
$('#reloadPresentation').css({display:'none'});
else {
var e = event.toElement || event.relatedTarget;
if (e && e.id != 'reloadPresentation' && e.id != 'header')
$('#reloadPresentation').css({display:'none'});
}
});
preziPlayer = new PreziPlayer(
'presentation',
{preziId: presId,
width: getPresentationWidth(),
height: getPresentationHeihgt(),
controls: controlsEnabled,
debug: true
});
$('#presentation>iframe').attr('id', preziPlayer.options.preziId);
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
console.log("prezi status", event.value);
if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
if (jid != APP.xmpp.myJid())
preziPlayer.flyToStep(currentSlide);
}
});
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
console.log("event value", event.value);
APP.xmpp.addToPresence("preziSlide", event.value);
});
$("#" + elementId).css( 'background-image',
'url(../images/avatarprezi.png)');
$("#" + elementId).click(
function () {
setPresentationVisible(true);
}
);
};
/**
* A presentation has been removed.
*
* @param event the event indicating the remove of a presentation
* @param jid the jid for which the presentation was removed
* @param the url of the presentation
*/
function presentationRemoved(event, jid, presUrl) {
console.log('presentation removed', presUrl);
var presId = getPresentationId(presUrl);
setPresentationVisible(false);
$('#participant_'
+ Strophe.getResourceFromJid(jid)
+ '_' + presId).remove();
$('#presentation>iframe').remove();
if (preziPlayer != null) {
preziPlayer.destroy();
preziPlayer = null;
}
};
/**
* Indicates if the given string is an alphanumeric string.
* Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
* purpose of checking URIs.
*/
function isAlphanumeric(unsafeText) {
var regex = /^[a-z0-9-_\/&\?=;]+$/i;
return regex.test(unsafeText);
}
/**
* Returns the presentation id from the given url.
*/
function getPresentationId (presUrl) {
var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
return presIdTmp.substring(0, presIdTmp.indexOf('/'));
}
/**
* Returns the presentation width.
*/
function getPresentationWidth() {
var availableWidth = UIUtil.getAvailableVideoWidth();
var availableHeight = getPresentationHeihgt();
var aspectRatio = 16.0 / 9.0;
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
return availableWidth;
}
/**
* Returns the presentation height.
*/
function getPresentationHeihgt() {
var remoteVideos = $('#remoteVideos');
return window.innerHeight - remoteVideos.outerHeight();
}
/**
* Resizes the presentation iframe.
*/
function resize() {
if ($('#presentation>iframe')) {
$('#presentation>iframe').width(getPresentationWidth());
$('#presentation>iframe').height(getPresentationHeihgt());
}
}
/**
* Shows/hides a presentation.
*/
function setPresentationVisible(visible) {
var prezi = $('#presentation>iframe');
if (visible) {
// Trigger the video.selected event to indicate a change in the
// large video.
$(document).trigger("video.selected", [true]);
$('#largeVideo').fadeOut(300);
prezi.fadeIn(300, function() {
prezi.css({opacity:'1'});
ToolbarToggler.dockToolbar(true);
VideoLayout.setLargeVideoVisible(false);
});
$('#activeSpeaker').css('visibility', 'hidden');
}
else {
if (prezi.css('opacity') == '1') {
prezi.fadeOut(300, function () {
prezi.css({opacity:'0'});
$('#reloadPresentation').css({display:'none'});
$('#largeVideo').fadeIn(300, function() {
VideoLayout.setLargeVideoVisible(true);
ToolbarToggler.dockToolbar(false);
});
});
}
}
}
/**
* Presentation has been removed.
*/
$(document).bind('presentationremoved.muc', presentationRemoved);
/**
* Presentation has been added.
*/
$(document).bind('presentationadded.muc', presentationAdded);
/*
* Indicates presentation slide change.
*/
$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
if (preziPlayer && preziPlayer.getCurrentStep() != current) {
preziPlayer.flyToStep(current);
var animationStepsArray = preziPlayer.getAnimationCountOnSteps();
for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
preziPlayer.flyToStep(current, i);
}
}
});
/**
* On video selected event.
*/
$(document).bind('video.selected', function (event, isPresentation) {
if (!isPresentation && $('#presentation>iframe')) {
setPresentationVisible(false);
}
});
$(window).resize(function () {
resize();
});
module.exports = Prezi;
},{"../toolbars/ToolbarToggler":26,"../util/MessageHandler":28,"../util/UIUtil":30,"../videolayout/VideoLayout":32,"./PreziPlayer":16}],16:[function(require,module,exports){
(function() {
"use strict";
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
window.PreziPlayer = (function() {
PreziPlayer.API_VERSION = 1;
PreziPlayer.CURRENT_STEP = 'currentStep';
PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
PreziPlayer.CURRENT_OBJECT = 'currentObject';
PreziPlayer.STATUS_LOADING = 'loading';
PreziPlayer.STATUS_READY = 'ready';
PreziPlayer.STATUS_CONTENT_READY = 'contentready';
PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
PreziPlayer.EVENT_STATUS = "statusChange";
PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
PreziPlayer.domain = "https://prezi.com";
PreziPlayer.path = "/player/";
PreziPlayer.players = {};
PreziPlayer.binded_methods = ['changesHandler'];
PreziPlayer.createMultiplePlayers = function(optionArray){
for(var i=0; i<optionArray.length; i++) {
var optionSet = optionArray[i];
new PreziPlayer(optionSet.id, optionSet);
};
};
PreziPlayer.messageReceived = function(event){
var message, item, player;
try {
message = JSON.parse(event.data);
if (message.id && (player = PreziPlayer.players[message.id])) {
if (player.options.debug === true) {
if (console && console.log)
console.log('received', message);
}
if (message.type === "changes") {
player.changesHandler(message);
}
for (var i = 0; i < player.callbacks.length; i++) {
item = player.callbacks[i];
if (item && message.type === item.event) {
item.callback(message);
}
}
}
} catch (e) { }
};
function PreziPlayer(id, options) {
var params, paramString = "", _this = this;
if (PreziPlayer.players[id]){
PreziPlayer.players[id].destroy();
}
for(var i=0; i<PreziPlayer.binded_methods.length; i++) {
var method_name = PreziPlayer.binded_methods[i];
_this[method_name] = __bind(_this[method_name], _this);
};
options = options || {};
this.options = options;
this.values = {'status': PreziPlayer.STATUS_LOADING};
this.values[PreziPlayer.CURRENT_STEP] = 0;
this.values[PreziPlayer.CURRENT_ANIMATION_STEP] = 0;
this.values[PreziPlayer.CURRENT_OBJECT] = null;
this.callbacks = [];
this.id = id;
this.embedTo = document.getElementById(id);
if (!this.embedTo) {
throw "The element id is not available.";
}
this.iframe = document.createElement('iframe');
params = [
{ name: 'oid', value: options.preziId },
{ name: 'explorable', value: options.explorable ? 1 : 0 },
{ name: 'controls', value: options.controls ? 1 : 0 }
];
for(var i=0; i<params.length; i++) {
var param = params[i];
paramString += (i===0 ? "?" : "&") + param.name + "=" + param.value;
};
this.iframe.src = PreziPlayer.domain + PreziPlayer.path + paramString;
this.iframe.frameBorder = 0;
this.iframe.scrolling = "no";
this.iframe.width = options.width || 640;
this.iframe.height = options.height || 480;
this.embedTo.innerHTML = '';
// JITSI: IN CASE SOMETHING GOES WRONG.
try {
this.embedTo.appendChild(this.iframe);
}
catch (err) {
console.log("CATCH ERROR");
}
// JITSI: Increase interval from 200 to 500, which fixes prezi
// crashes for us.
this.initPollInterval = setInterval(function(){
_this.sendMessage({'action': 'init'});
}, 500);
PreziPlayer.players[id] = this;
}
PreziPlayer.prototype.changesHandler = function(message) {
var key, value, j, item;
if (this.initPollInterval) {
clearInterval(this.initPollInterval);
this.initPollInterval = false;
}
for (key in message.data) {
if (message.data.hasOwnProperty(key)){
value = message.data[key];
this.values[key] = value;
for (j=0; j<this.callbacks.length; j++) {
item = this.callbacks[j];
if (item && item.event === key + "Change"){
item.callback({type: item.event, value: value});
}
}
}
}
};
PreziPlayer.prototype.destroy = function() {
if (this.initPollInterval) {
clearInterval(this.initPollInterval);
this.initPollInterval = false;
}
this.embedTo.innerHTML = '';
};
PreziPlayer.prototype.sendMessage = function(message) {
if (this.options.debug === true) {
if (console && console.log) console.log('sent', message);
}
message.version = PreziPlayer.API_VERSION;
message.id = this.id;
return this.iframe.contentWindow.postMessage(JSON.stringify(message), '*');
};
PreziPlayer.prototype.nextStep = /* nextStep is DEPRECATED */
PreziPlayer.prototype.flyToNextStep = function() {
return this.sendMessage({
'action': 'present',
'data': ['moveToNextStep']
});
};
PreziPlayer.prototype.previousStep = /* previousStep is DEPRECATED */
PreziPlayer.prototype.flyToPreviousStep = function() {
return this.sendMessage({
'action': 'present',
'data': ['moveToPrevStep']
});
};
PreziPlayer.prototype.toStep = /* toStep is DEPRECATED */
PreziPlayer.prototype.flyToStep = function(step, animation_step) {
var obj = this;
// check animation_step
if (animation_step > 0 &&
obj.values.animationCountOnSteps &&
obj.values.animationCountOnSteps[step] <= animation_step) {
animation_step = obj.values.animationCountOnSteps[step];
}
// jump to animation steps by calling flyToNextStep()
function doAnimationSteps() {
if (obj.values.isMoving == true) {
setTimeout(doAnimationSteps, 100); // wait until the flight ends
return;
}
while (animation_step-- > 0) {
obj.flyToNextStep(); // do the animation steps
}
}
setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time
// jump to the step
return this.sendMessage({
'action': 'present',
'data': ['moveToStep', step]
});
};
PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
PreziPlayer.prototype.flyToObject = function(objectId) {
return this.sendMessage({
'action': 'present',
'data': ['moveToObject', objectId]
});
};
PreziPlayer.prototype.play = function(defaultDelay) {
return this.sendMessage({
'action': 'present',
'data': ['startAutoPlay', defaultDelay]
});
};
PreziPlayer.prototype.stop = function() {
return this.sendMessage({
'action': 'present',
'data': ['stopAutoPlay']
});
};
PreziPlayer.prototype.pause = function(defaultDelay) {
return this.sendMessage({
'action': 'present',
'data': ['pauseAutoPlay', defaultDelay]
});
};
PreziPlayer.prototype.getCurrentStep = function() {
return this.values.currentStep;
};
PreziPlayer.prototype.getCurrentAnimationStep = function() {
return this.values.currentAnimationStep;
};
PreziPlayer.prototype.getCurrentObject = function() {
return this.values.currentObject;
};
PreziPlayer.prototype.getStatus = function() {
return this.values.status;
};
PreziPlayer.prototype.isPlaying = function() {
return this.values.isAutoPlaying;
};
PreziPlayer.prototype.getStepCount = function() {
return this.values.stepCount;
};
PreziPlayer.prototype.getAnimationCountOnSteps = function() {
return this.values.animationCountOnSteps;
};
PreziPlayer.prototype.getTitle = function() {
return this.values.title;
};
PreziPlayer.prototype.setDimensions = function(dims) {
for (var parameter in dims) {
this.iframe[parameter] = dims[parameter];
}
}
PreziPlayer.prototype.getDimensions = function() {
return {
width: parseInt(this.iframe.width, 10),
height: parseInt(this.iframe.height, 10)
}
}
PreziPlayer.prototype.on = function(event, callback) {
this.callbacks.push({
event: event,
callback: callback
});
};
PreziPlayer.prototype.off = function(event, callback) {
var j, item;
if (event === undefined) {
this.callbacks = [];
}
j = this.callbacks.length;
while (j--) {
item = this.callbacks[j];
if (item && item.event === event && (callback === undefined || item.callback === callback)){
this.callbacks.splice(j, 1);
}
}
};
if (window.addEventListener) {
window.addEventListener('message', PreziPlayer.messageReceived, false);
} else {
window.attachEvent('onmessage', PreziPlayer.messageReceived);
}
return PreziPlayer;
})();
})();
module.exports = PreziPlayer;
},{}],17:[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..
*/
var PanelToggler = (function(my) {
var currentlyOpen = null;
var buttons = {
'#chatspace': '#chatBottomButton',
'#contactlist': '#contactListButton',
'#settingsmenu': '#settingsButton'
};
/**
* Resizes the video area
* @param isClosing whether the side panel is going to be closed or is going to open / remain opened
* @param completeFunction a function to be called when the video space is resized
*/
var resizeVideoArea = function(isClosing, completeFunction) {
var videospace = $('#videospace');
var panelSize = isClosing ? [0, 0] : PanelToggler.getPanelSize();
var videospaceWidth = window.innerWidth - panelSize[0];
var videospaceHeight = window.innerHeight;
var videoSize
= VideoLayout.getVideoSize(null, null, videospaceWidth, videospaceHeight);
var videoWidth = videoSize[0];
var videoHeight = videoSize[1];
var videoPosition = VideoLayout.getVideoPosition(videoWidth,
videoHeight,
videospaceWidth,
videospaceHeight);
var horizontalIndent = videoPosition[0];
var verticalIndent = videoPosition[1];
var thumbnailSize = VideoLayout.calculateThumbnailSize(videospaceWidth);
var thumbnailsWidth = thumbnailSize[0];
var thumbnailsHeight = thumbnailSize[1];
//for chat
videospace.animate({
right: panelSize[0],
width: videospaceWidth,
height: videospaceHeight
},
{
queue: false,
duration: 500,
complete: completeFunction
});
$('#remoteVideos').animate({
height: thumbnailsHeight
},
{
queue: false,
duration: 500
});
$('#remoteVideos>span').animate({
height: thumbnailsHeight,
width: thumbnailsWidth
},
{
queue: false,
duration: 500,
complete: function () {
$(document).trigger(
"remotevideo.resized",
[thumbnailsWidth,
thumbnailsHeight]);
}
});
$('#largeVideoContainer').animate({
width: videospaceWidth,
height: videospaceHeight
},
{
queue: false,
duration: 500
});
$('#largeVideo').animate({
width: videoWidth,
height: videoHeight,
top: verticalIndent,
bottom: verticalIndent,
left: horizontalIndent,
right: horizontalIndent
},
{
queue: false,
duration: 500
});
};
/**
* Toggles the windows in the side panel
* @param object the window that should be shown
* @param selector the selector for the element containing the panel
* @param onOpenComplete function to be called when the panel is opened
* @param onOpen function to be called if the window is going to be opened
* @param onClose function to be called if the window is going to be closed
*/
var toggle = function(object, selector, onOpenComplete, onOpen, onClose) {
UIUtil.buttonClick(buttons[selector], "active");
if (object.isVisible()) {
$("#toast-container").animate({
right: '5px'
},
{
queue: false,
duration: 500
});
$(selector).hide("slide", {
direction: "right",
queue: false,
duration: 500
});
if(typeof onClose === "function") {
onClose();
}
currentlyOpen = null;
}
else {
// Undock the toolbar when the chat is shown and if we're in a
// video mode.
if (VideoLayout.isLargeVideoVisible()) {
ToolbarToggler.dockToolbar(false);
}
if(currentlyOpen) {
var current = $(currentlyOpen);
UIUtil.buttonClick(buttons[currentlyOpen], "active");
current.css('z-index', 4);
setTimeout(function () {
current.css('display', 'none');
current.css('z-index', 5);
}, 500);
}
$("#toast-container").animate({
right: (PanelToggler.getPanelSize()[0] + 5) + 'px'
},
{
queue: false,
duration: 500
});
$(selector).show("slide", {
direction: "right",
queue: false,
duration: 500,
complete: onOpenComplete
});
if(typeof onOpen === "function") {
onOpen();
}
currentlyOpen = selector;
}
};
/**
* Opens / closes the chat area.
*/
my.toggleChat = function() {
var chatCompleteFunction = Chat.isVisible() ?
function() {} : function () {
Chat.scrollChatToBottom();
$('#chatspace').trigger('shown');
};
resizeVideoArea(Chat.isVisible(), chatCompleteFunction);
toggle(Chat,
'#chatspace',
function () {
// Request the focus in the nickname field or the chat input field.
if ($('#nickname').css('visibility') === 'visible') {
$('#nickinput').focus();
} else {
$('#usermsg').focus();
}
},
null,
Chat.resizeChat,
null);
};
/**
* Opens / closes the contact list area.
*/
my.toggleContactList = function () {
var completeFunction = ContactList.isVisible() ?
function() {} : function () { $('#contactlist').trigger('shown');};
resizeVideoArea(ContactList.isVisible(), completeFunction);
toggle(ContactList,
'#contactlist',
null,
function() {
ContactList.setVisualNotification(false);
},
null);
};
/**
* Opens / closes the settings menu
*/
my.toggleSettingsMenu = function() {
resizeVideoArea(SettingsMenu.isVisible(), function (){});
toggle(SettingsMenu,
'#settingsmenu',
null,
function() {
var settings = Settings.getSettings();
$('#setDisplayName').get(0).value = settings.displayName;
$('#setEmail').get(0).value = settings.email;
},
null);
};
/**
* Returns the size of the side panel.
*/
my.getPanelSize = function () {
var availableHeight = window.innerHeight;
var availableWidth = window.innerWidth;
var panelWidth = 200;
if (availableWidth * 0.2 < 200) {
panelWidth = availableWidth * 0.2;
}
return [panelWidth, availableHeight];
};
my.isVisible = function() {
return (Chat.isVisible() || ContactList.isVisible() || SettingsMenu.isVisible());
};
return my;
}(PanelToggler || {}));
module.exports = PanelToggler;
},{"../toolbars/ToolbarToggler":26,"../util/UIUtil":30,"../videolayout/VideoLayout":32,"./../../settings/Settings":38,"./chat/Chat":18,"./contactlist/ContactList":22,"./settings/SettingsMenu":23}],18:[function(require,module,exports){
/* global $, Util, nickname:true */
var Replacement = require("./Replacement");
var CommandsProcessor = require("./Commands");
var ToolbarToggler = require("../../toolbars/ToolbarToggler");
var smileys = require("./smileys.json").smileys;
var NicknameHandler = require("../../util/NicknameHandler");
var UIUtil = require("../../util/UIUtil");
var UIEvents = require("../../../../service/UI/UIEvents");
var notificationInterval = false;
var unreadMessages = 0;
/**
* Shows/hides a visual notification, indicating that a message has arrived.
*/
function setVisualNotification(show) {
var unreadMsgElement = document.getElementById('unreadMessages');
var unreadMsgBottomElement
= document.getElementById('bottomUnreadMessages');
var glower = $('#chatButton');
var bottomGlower = $('#chatBottomButton');
if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString();
unreadMsgBottomElement.innerHTML = unreadMessages.toString();
ToolbarToggler.dockToolbar(true);
var chatButtonElement
= document.getElementById('chatButton').parentNode;
var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
UIUtil.getTextWidth(unreadMsgElement)) / 2;
var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
UIUtil.getTextHeight(unreadMsgElement)) / 2 - 3;
unreadMsgElement.setAttribute(
'style',
'top:' + topIndent +
'; left:' + leftIndent + ';');
var chatBottomButtonElement
= document.getElementById('chatBottomButton').parentNode;
var bottomLeftIndent = (UIUtil.getTextWidth(chatBottomButtonElement) -
UIUtil.getTextWidth(unreadMsgBottomElement)) / 2;
var bottomTopIndent = (UIUtil.getTextHeight(chatBottomButtonElement) -
UIUtil.getTextHeight(unreadMsgBottomElement)) / 2 - 2;
unreadMsgBottomElement.setAttribute(
'style',
'top:' + bottomTopIndent +
'; left:' + bottomLeftIndent + ';');
if (!glower.hasClass('icon-chat-simple')) {
glower.removeClass('icon-chat');
glower.addClass('icon-chat-simple');
}
}
else {
unreadMsgElement.innerHTML = '';
unreadMsgBottomElement.innerHTML = '';
glower.removeClass('icon-chat-simple');
glower.addClass('icon-chat');
}
if (show && !notificationInterval) {
notificationInterval = window.setInterval(function () {
glower.toggleClass('active');
bottomGlower.toggleClass('active glowing');
}, 800);
}
else if (!show && notificationInterval) {
window.clearInterval(notificationInterval);
notificationInterval = false;
glower.removeClass('active');
bottomGlower.removeClass('glowing');
bottomGlower.addClass('active');
}
}
/**
* Returns the current time in the format it is shown to the user
* @returns {string}
*/
function getCurrentTime() {
var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();
if(hour.toString().length === 1) {
hour = '0'+hour;
}
if(minute.toString().length === 1) {
minute = '0'+minute;
}
if(second.toString().length === 1) {
second = '0'+second;
}
return hour+':'+minute+':'+second;
}
function toggleSmileys()
{
var smileys = $('#smileysContainer');
if(!smileys.is(':visible')) {
smileys.show("slide", { direction: "down", duration: 300});
} else {
smileys.hide("slide", { direction: "down", duration: 300});
}
$('#usermsg').focus();
}
function addClickFunction(smiley, number) {
smiley.onclick = function addSmileyToMessage() {
var usermsg = $('#usermsg');
var message = usermsg.val();
message += smileys['smiley' + number];
usermsg.val(message);
usermsg.get(0).setSelectionRange(message.length, message.length);
toggleSmileys();
usermsg.focus();
};
}
/**
* Adds the smileys container to the chat
*/
function addSmileys() {
var smileysContainer = document.createElement('div');
smileysContainer.id = 'smileysContainer';
for(var i = 1; i <= 21; i++) {
var smileyContainer = document.createElement('div');
smileyContainer.id = 'smiley' + i;
smileyContainer.className = 'smileyContainer';
var smiley = document.createElement('img');
smiley.src = 'images/smileys/smiley' + i + '.svg';
smiley.className = 'smiley';
addClickFunction(smiley, i);
smileyContainer.appendChild(smiley);
smileysContainer.appendChild(smileyContainer);
}
$("#chatspace").append(smileysContainer);
}
/**
* Resizes the chat conversation.
*/
function resizeChatConversation() {
var msgareaHeight = $('#usermsg').outerHeight();
var chatspace = $('#chatspace');
var width = chatspace.width();
var chat = $('#chatconversation');
var smileys = $('#smileysarea');
smileys.height(msgareaHeight);
$("#smileys").css('bottom', (msgareaHeight - 26) / 2);
$('#smileysContainer').css('bottom', msgareaHeight);
chat.width(width - 10);
chat.height(window.innerHeight - 15 - msgareaHeight);
}
/**
* Chat related user interface.
*/
var Chat = (function (my) {
/**
* Initializes chat related interface.
*/
my.init = function () {
if(NicknameHandler.getNickname())
Chat.setChatConversationMode(true);
NicknameHandler.addListener(UIEvents.NICKNAME_CHANGED,
function (nickname) {
Chat.setChatConversationMode(true);
});
$('#nickinput').keydown(function (event) {
if (event.keyCode === 13) {
event.preventDefault();
var val = UIUtil.escapeHtml(this.value);
this.value = '';
if (!NicknameHandler.getNickname()) {
NicknameHandler.setNickname(val);
return;
}
}
});
$('#usermsg').keydown(function (event) {
if (event.keyCode === 13) {
event.preventDefault();
var value = this.value;
$('#usermsg').val('').trigger('autosize.resize');
this.focus();
var command = new CommandsProcessor(value);
if(command.isCommand())
{
command.processCommand();
}
else
{
var message = UIUtil.escapeHtml(value);
APP.xmpp.sendChatMessage(message, NicknameHandler.getNickname());
}
}
});
var onTextAreaResize = function () {
resizeChatConversation();
Chat.scrollChatToBottom();
};
$('#usermsg').autosize({callback: onTextAreaResize});
$("#chatspace").bind("shown",
function () {
unreadMessages = 0;
setVisualNotification(false);
});
addSmileys();
};
/**
* Appends the given message to the chat conversation.
*/
my.updateChatConversation = function (from, displayName, message) {
var divClassName = '';
if (APP.xmpp.myJid() === from) {
divClassName = "localuser";
}
else {
divClassName = "remoteuser";
if (!Chat.isVisible()) {
unreadMessages++;
UIUtil.playSoundNotification('chatNotification');
setVisualNotification(true);
}
}
// replace links and smileys
// Strophe already escapes special symbols on sending,
// so we escape here only tags to avoid double &amp;
var escMessage = message.replace(/</g, '&lt;').
replace(/>/g, '&gt;').replace(/\n/g, '<br/>');
var escDisplayName = UIUtil.escapeHtml(displayName);
message = Replacement.processReplacements(escMessage);
var messageContainer =
'<div class="chatmessage">'+
'<img src="../images/chatArrow.svg" class="chatArrow">' +
'<div class="username ' + divClassName +'">' + escDisplayName +
'</div>' + '<div class="timestamp">' + getCurrentTime() +
'</div>' + '<div class="usermessage">' + message + '</div>' +
'</div>';
$('#chatconversation').append(messageContainer);
$('#chatconversation').animate(
{ scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
};
/**
* Appends error message to the conversation
* @param errorMessage the received error message.
* @param originalText the original message.
*/
my.chatAddError = function(errorMessage, originalText)
{
errorMessage = UIUtil.escapeHtml(errorMessage);
originalText = UIUtil.escapeHtml(originalText);
$('#chatconversation').append(
'<div class="errorMessage"><b>Error: </b>' + 'Your message' +
(originalText? (' \"'+ originalText + '\"') : "") +
' was not sent.' +
(errorMessage? (' Reason: ' + errorMessage) : '') + '</div>');
$('#chatconversation').animate(
{ scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
};
/**
* Sets the subject to the UI
* @param subject the subject
*/
my.chatSetSubject = function(subject)
{
if(subject)
subject = subject.trim();
$('#subject').html(Replacement.linkify(UIUtil.escapeHtml(subject)));
if(subject === "")
{
$("#subject").css({display: "none"});
}
else
{
$("#subject").css({display: "block"});
}
};
/**
* Sets the chat conversation mode.
*/
my.setChatConversationMode = function (isConversationMode) {
if (isConversationMode) {
$('#nickname').css({visibility: 'hidden'});
$('#chatconversation').css({visibility: 'visible'});
$('#usermsg').css({visibility: 'visible'});
$('#smileysarea').css({visibility: 'visible'});
$('#usermsg').focus();
}
};
/**
* Resizes the chat area.
*/
my.resizeChat = function () {
var chatSize = require("../SidePanelToggler").getPanelSize();
$('#chatspace').width(chatSize[0]);
$('#chatspace').height(chatSize[1]);
resizeChatConversation();
};
/**
* Indicates if the chat is currently visible.
*/
my.isVisible = function () {
return $('#chatspace').is(":visible");
};
/**
* Shows and hides the window with the smileys
*/
my.toggleSmileys = toggleSmileys;
/**
* Scrolls chat to the bottom.
*/
my.scrollChatToBottom = function() {
setTimeout(function () {
$('#chatconversation').scrollTop(
$('#chatconversation')[0].scrollHeight);
}, 5);
};
return my;
}(Chat || {}));
module.exports = Chat;
},{"../../../../service/UI/UIEvents":92,"../../toolbars/ToolbarToggler":26,"../../util/NicknameHandler":29,"../../util/UIUtil":30,"../SidePanelToggler":17,"./Commands":19,"./Replacement":20,"./smileys.json":21}],19:[function(require,module,exports){
var UIUtil = require("../../util/UIUtil");
/**
* List with supported commands. The keys are the names of the commands and
* the value is the function that processes the message.
* @type {{String: function}}
*/
var commands = {
"topic" : processTopic
};
/**
* Extracts the command from the message.
* @param message the received message
* @returns {string} the command
*/
function getCommand(message)
{
if(message)
{
for(var command in commands)
{
if(message.indexOf("/" + command) == 0)
return command;
}
}
return "";
};
/**
* Processes the data for topic command.
* @param commandArguments the arguments of the topic command.
*/
function processTopic(commandArguments)
{
var topic = UIUtil.escapeHtml(commandArguments);
APP.xmpp.setSubject(topic);
}
/**
* Constructs new CommandProccessor instance from a message that
* handles commands received via chat messages.
* @param message the message
* @constructor
*/
function CommandsProcessor(message)
{
var command = getCommand(message);
/**
* Returns the name of the command.
* @returns {String} the command
*/
this.getCommand = function()
{
return command;
};
var messageArgument = message.substr(command.length + 2);
/**
* Returns the arguments of the command.
* @returns {string}
*/
this.getArgument = function()
{
return messageArgument;
};
}
/**
* Checks whether this instance is valid command or not.
* @returns {boolean}
*/
CommandsProcessor.prototype.isCommand = function()
{
if(this.getCommand())
return true;
return false;
};
/**
* Processes the command.
*/
CommandsProcessor.prototype.processCommand = function()
{
if(!this.isCommand())
return;
commands[this.getCommand()](this.getArgument());
};
module.exports = CommandsProcessor;
},{"../../util/UIUtil":30}],20:[function(require,module,exports){
var Smileys = require("./smileys.json");
/**
* Processes links and smileys in "body"
*/
function processReplacements(body)
{
//make links clickable
body = linkify(body);
//add smileys
body = smilify(body);
return body;
}
/**
* Finds and replaces all links in the links in "body"
* with their <a href=""></a>
*/
function linkify(inputText)
{
var replacedText, replacePattern1, replacePattern2, replacePattern3;
//URLs starting with http://, https://, or ftp://
replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
return replacedText;
}
/**
* Replaces common smiley strings with images
*/
function smilify(body)
{
if(!body) {
return body;
}
var regexs = Smileys["regexs"];
for(var smiley in regexs) {
if(regexs.hasOwnProperty(smiley)) {
body = body.replace(regexs[smiley],
'<img class="smiley" src="images/smileys/' + smiley + '.svg">');
}
}
return body;
}
module.exports = {
processReplacements: processReplacements,
linkify: linkify
};
},{"./smileys.json":21}],21:[function(require,module,exports){
module.exports={
"smileys": {
"smiley1": ":)",
"smiley2": ":(",
"smiley3": ":D",
"smiley4": "(y)",
"smiley5": " :P",
"smiley6": "(wave)",
"smiley7": "(blush)",
"smiley8": "(chuckle)",
"smiley9": "(shocked)",
"smiley10": ":*",
"smiley11": "(n)",
"smiley12": "(search)",
"smiley13": " <3",
"smiley14": "(oops)",
"smiley15": "(angry)",
"smiley16": "(angel)",
"smiley17": "(sick)",
"smiley18": ";(",
"smiley19": "(bomb)",
"smiley20": "(clap)",
"smiley21": " ;)"
},
"regexs": {
"smiley2": /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi,
"smiley3": /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi,
"smiley1": /(:-\)|:\))/gi,
"smiley4": /(\(y\)|\(Y\)|\(ok\))/gi,
"smiley5": /(:-P|:P|:-p|:p)/gi,
"smiley6": /(\(wave\))/gi,
"smiley7": /(\(blush\))/gi,
"smiley8": /(\(chuckle\))/gi,
"smiley9": /(:-0|\(shocked\))/gi,
"smiley10": /(:-\*|:\*|\(kiss\))/gi,
"smiley11": /(\(n\))/gi,
"smiley12": /(\(search\))/g,
"smiley13": /(<3|&lt;3|&amp;lt;3|\(L\)|\(l\)|\(H\)|\(h\))/gi,
"smiley14": /(\(oops\))/gi,
"smiley15": /(\(angry\))/gi,
"smiley16": /(\(angel\))/gi,
"smiley17": /(\(sick\))/gi,
"smiley18": /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi,
"smiley19": /(\(bomb\))/gi,
"smiley20": /(\(clap\))/gi,
"smiley21": /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi
}
}
},{}],22:[function(require,module,exports){
var numberOfContacts = 0;
var notificationInterval;
/**
* Updates the number of participants in the contact list button and sets
* the glow
* @param delta indicates whether a new user has joined (1) or someone has
* left(-1)
*/
function updateNumberOfParticipants(delta) {
//when the user is alone we don't show the number of participants
if(numberOfContacts === 0) {
$("#numberOfParticipants").text('');
numberOfContacts += delta;
} else if(numberOfContacts !== 0 && !ContactList.isVisible()) {
ContactList.setVisualNotification(true);
numberOfContacts += delta;
$("#numberOfParticipants").text(numberOfContacts);
}
}
/**
* Creates the avatar element.
*
* @return the newly created avatar element
*/
function createAvatar(id) {
var avatar = document.createElement('img');
avatar.className = "icon-avatar avatar";
avatar.src = "https://www.gravatar.com/avatar/" + id + "?d=wavatar&size=30";
return avatar;
}
/**
* Creates the display name paragraph.
*
* @param displayName the display name to set
*/
function createDisplayNameParagraph(key, displayName) {
var p = document.createElement('p');
if(displayName)
p.innerText = displayName;
else if(key)
{
p.setAttribute("data-i18n",key);
p.innerText = APP.translation.translateString(key);
}
return p;
}
function stopGlowing(glower) {
window.clearInterval(notificationInterval);
notificationInterval = false;
glower.removeClass('glowing');
if (!ContactList.isVisible()) {
glower.removeClass('active');
}
}
/**
* Contact list.
*/
var ContactList = {
/**
* Indicates if the chat is currently visible.
*
* @return <tt>true</tt> if the chat is currently visible, <tt>false</tt> -
* otherwise
*/
isVisible: function () {
return $('#contactlist').is(":visible");
},
/**
* Adds a contact for the given peerJid if such doesn't yet exist.
*
* @param peerJid the peerJid corresponding to the contact
* @param id the user's email or userId used to get the user's avatar
*/
ensureAddContact: function (peerJid, id) {
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contact = $('#contactlist>ul>li[id="' + resourceJid + '"]');
if (!contact || contact.length <= 0)
ContactList.addContact(peerJid, id);
},
/**
* Adds a contact for the given peer jid.
*
* @param peerJid the jid of the contact to add
* @param id the email or userId of the user
*/
addContact: function (peerJid, id) {
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contactlist = $('#contactlist>ul');
var newContact = document.createElement('li');
newContact.id = resourceJid;
newContact.className = "clickable";
newContact.onclick = function (event) {
if (event.currentTarget.className === "clickable") {
$(ContactList).trigger('contactclicked', [peerJid]);
}
};
newContact.appendChild(createAvatar(id));
newContact.appendChild(createDisplayNameParagraph("participant"));
var clElement = contactlist.get(0);
if (resourceJid === APP.xmpp.myResource()
&& $('#contactlist>ul .title')[0].nextSibling.nextSibling) {
clElement.insertBefore(newContact,
$('#contactlist>ul .title')[0].nextSibling.nextSibling);
}
else {
clElement.appendChild(newContact);
}
updateNumberOfParticipants(1);
},
/**
* Removes a contact for the given peer jid.
*
* @param peerJid the peerJid corresponding to the contact to remove
*/
removeContact: function (peerJid) {
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contact = $('#contactlist>ul>li[id="' + resourceJid + '"]');
if (contact && contact.length > 0) {
var contactlist = $('#contactlist>ul');
contactlist.get(0).removeChild(contact.get(0));
updateNumberOfParticipants(-1);
}
},
setVisualNotification: function (show, stopGlowingIn) {
var glower = $('#contactListButton');
if (show && !notificationInterval) {
notificationInterval = window.setInterval(function () {
glower.toggleClass('active glowing');
}, 800);
}
else if (!show && notificationInterval) {
stopGlowing(glower);
}
if (stopGlowingIn) {
setTimeout(function () {
stopGlowing(glower);
}, stopGlowingIn);
}
},
setClickable: function (resourceJid, isClickable) {
var contact = $('#contactlist>ul>li[id="' + resourceJid + '"]');
if (isClickable) {
contact.addClass('clickable');
} else {
contact.removeClass('clickable');
}
},
onDisplayNameChange: function (peerJid, displayName) {
if (peerJid === 'localVideoContainer')
peerJid = APP.xmpp.myJid();
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contactName = $('#contactlist #' + resourceJid + '>p');
if (contactName && displayName && displayName.length > 0)
contactName.html(displayName);
}
};
module.exports = ContactList;
},{}],23:[function(require,module,exports){
var Avatar = require("../../avatar/Avatar");
var Settings = require("./../../../settings/Settings");
var UIUtil = require("../../util/UIUtil");
var languages = require("../../../../service/translation/languages");
function generateLanguagesSelectBox()
{
var currentLang = APP.translation.getCurrentLanguage();
var html = "<select id=\"languages_selectbox\">";
var langArray = languages.getLanguages();
for(var i = 0; i < langArray.length; i++)
{
var lang = langArray[i];
html += "<option ";
if(lang === currentLang)
html += "selected ";
html += "value=\"" + lang + "\" data-i18n='languages:" + lang + "'>";
html += "</option>";
}
return html + "</select>";
}
var SettingsMenu = {
init: function () {
$("#updateSettings").before(generateLanguagesSelectBox());
APP.translation.translateElement($("#languages_selectbox"));
$('#settingsmenu>input').keyup(function(event){
if(event.keyCode === 13) {//enter
SettingsMenu.update();
}
});
$("#updateSettings").click(function () {
SettingsMenu.update();
});
},
update: function() {
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
if(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
APP.xmpp.addToPresence("displayName", displayName, true);
}
var language = $("#languages_selectbox").val();
APP.translation.setLanguage(language);
Settings.setLanguage(language);
APP.xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(newEmail);
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
},
isVisible: function() {
return $('#settingsmenu').is(':visible');
},
setDisplayName: function(newDisplayName) {
var displayName = Settings.setDisplayName(newDisplayName);
$('#setDisplayName').get(0).value = displayName;
},
onDisplayNameChange: function(peerJid, newDisplayName) {
if(peerJid === 'localVideoContainer' ||
peerJid === APP.xmpp.myJid()) {
this.setDisplayName(newDisplayName);
}
}
};
module.exports = SettingsMenu;
},{"../../../../service/translation/languages":96,"../../avatar/Avatar":13,"../../util/UIUtil":30,"./../../../settings/Settings":38}],24:[function(require,module,exports){
var PanelToggler = require("../side_pannels/SidePanelToggler");
var buttonHandlers = {
"bottom_toolbar_contact_list": function () {
BottomToolbar.toggleContactList();
},
"bottom_toolbar_film_strip": function () {
BottomToolbar.toggleFilmStrip();
},
"bottom_toolbar_chat": function () {
BottomToolbar.toggleChat();
}
};
var BottomToolbar = (function (my) {
my.init = function () {
for(var k in buttonHandlers)
$("#" + k).click(buttonHandlers[k]);
};
my.toggleChat = function() {
PanelToggler.toggleChat();
};
my.toggleContactList = function() {
PanelToggler.toggleContactList();
};
my.toggleFilmStrip = function() {
var filmstrip = $("#remoteVideos");
filmstrip.toggleClass("hidden");
};
$(document).bind("remotevideo.resized", function (event, width, height) {
var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;
$('#bottomToolbar').css({bottom: bottom + 'px'});
});
return my;
}(BottomToolbar || {}));
module.exports = BottomToolbar;
},{"../side_pannels/SidePanelToggler":17}],25:[function(require,module,exports){
/* global APP,$, 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 AuthenticationEvents
= require("../../../service/authentication/AuthenticationEvents");
var roomUrl = null;
var sharedKey = '';
var UI = null;
var buttonHandlers =
{
"toolbar_button_mute": function () {
return APP.UI.toggleAudio();
},
"toolbar_button_camera": function () {
return APP.UI.toggleVideo();
},
/*"toolbar_button_authentication": function () {
return Toolbar.authenticateClicked();
},*/
"toolbar_button_record": function () {
return toggleRecording();
},
"toolbar_button_security": function () {
return Toolbar.openLockDialog();
},
"toolbar_button_link": function () {
return Toolbar.openLinkDialog();
},
"toolbar_button_chat": function () {
return BottomToolbar.toggleChat();
},
"toolbar_button_prezi": function () {
return Prezi.openPreziDialog();
},
"toolbar_button_etherpad": function () {
return Etherpad.toggleEtherpad(0);
},
"toolbar_button_desktopsharing": function () {
return APP.desktopsharing.toggleScreenSharing();
},
"toolbar_button_fullScreen": function()
{
UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
return Toolbar.toggleFullScreen();
},
"toolbar_button_sip": function () {
return callSipButtonClicked();
},
"toolbar_button_settings": function () {
PanelToggler.toggleSettingsMenu();
},
"toolbar_button_hangup": function () {
return hangup();
},
"toolbar_button_login": function () {
Toolbar.authenticateClicked();
},
"toolbar_button_logout": function () {
// Ask for confirmation
messageHandler.openTwoButtonDialog(
"dialog.logoutTitle",
null,
"dialog.logoutQuestion",
null,
false,
"dialog.Yes",
function (evt, yes) {
if (yes) {
APP.xmpp.logout(function (url) {
if (url) {
window.location.href = url;
} else {
hangup();
}
});
}
});
}
};
function hangup() {
APP.xmpp.disposeConference();
if(config.enableWelcomePage)
{
setTimeout(function()
{
window.localStorage.welcomePageDisabled = false;
window.location.pathname = "/";
}, 10000);
}
var title = APP.translation.generateTranslatonHTML(
"dialog.sessTerminated");
var msg = APP.translation.generateTranslatonHTML(
"dialog.hungUp");
var button = APP.translation.generateTranslatonHTML(
"dialog.joinAgain");
var buttons = [];
buttons.push({title: button, value: true});
UI.messageHandler.openDialog(
title,
msg,
true,
buttons,
function(event, value, message, formVals)
{
window.location.reload();
return false;
}
);
}
/**
* Starts or stops the recording for the conference.
*/
function toggleRecording() {
APP.xmpp.toggleRecording(function (callback) {
var msg = APP.translation.generateTranslatonHTML(
"dialog.recordingToken");
var token = APP.translation.translateString("dialog.token");
APP.UI.messageHandler.openTwoButtonDialog(null, null, null,
'<h2>' + msg + '</h2>' +
'<input name="recordingToken" type="text" ' +
' data-i18n="[placeholder]dialog.token" ' +
'placeholder="' + token + '" autofocus>',
false,
"dialog.Save",
function (e, v, m, f) {
if (v) {
var token = f.recordingToken;
if (token) {
callback(UIUtil.escapeHtml(token));
}
}
},
null,
function () { },
':input:first'
);
}, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
}
/**
* Locks / unlocks the room.
*/
function lockRoom(lock) {
var currentSharedKey = '';
if (lock)
currentSharedKey = sharedKey;
APP.xmpp.lockRoom(currentSharedKey, function (res) {
// password is required
if (sharedKey)
{
console.log('set room password');
Toolbar.lockLockButton();
}
else
{
console.log('removed room password');
Toolbar.unlockLockButton();
}
}, function (err) {
console.warn('setting password failed', err);
messageHandler.showError("dialog.lockTitle",
"dialog.lockMessage");
Toolbar.setSharedKey('');
}, function () {
console.warn('room passwords not supported');
messageHandler.showError("dialog.warning",
"dialog.passwordNotSupported");
Toolbar.setSharedKey('');
});
};
/**
* Invite participants to conference.
*/
function inviteParticipants() {
if (roomUrl === null)
return;
var sharedKeyText = "";
if (sharedKey && sharedKey.length > 0) {
sharedKeyText =
APP.translation.translateString("email.sharedKey",
{sharedKey: sharedKey});
sharedKeyText = sharedKeyText.replace(/\n/g, "%0D%0A");
}
var supportedBrowsers = "Chromium, Google Chrome " +
APP.translation.translateString("email.and") + " Opera";
var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1);
var subject = APP.translation.translateString("email.subject",
{appName:interfaceConfig.APP_NAME, conferenceName: conferenceName});
var body = APP.translation.translateString("email.body",
{appName:interfaceConfig.APP_NAME, sharedKeyText: sharedKeyText,
roomUrl: roomUrl, supportedBrowsers: supportedBrowsers});
body = body.replace(/\n/g, "%0D%0A");
if (window.localStorage.displayname) {
body += "%0D%0A%0D%0A" + window.localStorage.displayname;
}
if (interfaceConfig.INVITATION_POWERED_BY) {
body += "%0D%0A%0D%0A--%0D%0Apowered by jitsi.org";
}
window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
}
function callSipButtonClicked()
{
var defaultNumber
= config.defaultSipNumber ? config.defaultSipNumber : '';
var sipMsg = APP.translation.generateTranslatonHTML(
"dialog.sipMsg");
messageHandler.openTwoButtonDialog(null, null, null,
'<h2>' + sipMsg + '</h2>' +
'<input name="sipNumber" type="text"' +
' value="' + defaultNumber + '" autofocus>',
false,
"dialog.Dial",
function (e, v, m, f) {
if (v) {
var numberInput = f.sipNumber;
if (numberInput) {
APP.xmpp.dial(
numberInput, 'fromnumber', UI.getRoomName(), sharedKey);
}
}
},
null, null, ':input:first'
);
}
var Toolbar = (function (my) {
my.init = function (ui) {
for(var k in buttonHandlers)
$("#" + k).click(buttonHandlers[k]);
UI = ui;
// Update login info
APP.xmpp.addListener(
AuthenticationEvents.IDENTITY_UPDATED,
function (authenticationEnabled, userIdentity) {
var loggedIn = false;
if (userIdentity) {
loggedIn = true;
}
Toolbar.showAuthenticateButton(authenticationEnabled);
if (authenticationEnabled) {
Toolbar.setAuthenticatedIdentity(userIdentity);
Toolbar.showLoginButton(!loggedIn);
Toolbar.showLogoutButton(loggedIn);
}
}
);
},
/**
* Sets shared key
* @param sKey the shared key
*/
my.setSharedKey = function (sKey) {
sharedKey = sKey;
};
my.authenticateClicked = function () {
Authentication.focusAuthenticationWindow();
if (!APP.xmpp.isExternalAuthEnabled()) {
Authentication.xmppAuthenticate();
return;
}
// Get authentication URL
if (!APP.xmpp.getMUCJoined()) {
APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {
// If conference has not been started yet - redirect to login page
window.location.href = url;
});
} else {
APP.xmpp.getPopupLoginUrl(UI.getRoomName(), function (url) {
// Otherwise - open popup with authentication URL
var authenticationWindow = Authentication.createAuthenticationWindow(
function () {
// On popup closed - retry room allocation
APP.xmpp.allocateConferenceFocus(
APP.UI.getRoomName(),
function () { console.info("AUTH DONE"); }
);
}, url);
if (!authenticationWindow) {
messageHandler.openMessageDialog(
null, "dialog.popupError");
}
});
}
};
/**
* Updates the room invite url.
*/
my.updateRoomUrl = function (newRoomUrl) {
roomUrl = newRoomUrl;
// If the invite dialog has been already opened we update the information.
var inviteLink = document.getElementById('inviteLinkRef');
if (inviteLink) {
inviteLink.value = roomUrl;
inviteLink.select();
document.getElementById('jqi_state0_buttonInvite').disabled = false;
}
};
/**
* Disables and enables some of the buttons.
*/
my.setupButtonsFromConfig = function () {
if (config.disablePrezi)
{
$("#prezi_button").css({display: "none"});
}
};
/**
* Opens the lock room dialog.
*/
my.openLockDialog = function () {
// Only the focus is able to set a shared key.
if (!APP.xmpp.isModerator()) {
if (sharedKey) {
messageHandler.openMessageDialog(null,
"dialog.passwordError");
} else {
messageHandler.openMessageDialog(null, "dialog.passwordError2");
}
} else {
if (sharedKey) {
messageHandler.openTwoButtonDialog(null, null,
"dialog.passwordCheck",
null,
false,
"dialog.Remove",
function (e, v) {
if (v) {
Toolbar.setSharedKey('');
lockRoom(false);
}
});
} else {
var msg = APP.translation.generateTranslatonHTML(
"dialog.passwordMsg");
var yourPassword = APP.translation.translateString(
"dialog.yourPassword");
messageHandler.openTwoButtonDialog(null, null, null,
'<h2>' + msg + '</h2>' +
'<input name="lockKey" type="text"' +
' data-i18n="[placeholder]dialog.yourPassword" ' +
'placeholder="' + yourPassword + '" autofocus>',
false,
"dialog.Save",
function (e, v, m, f) {
if (v) {
var lockKey = f.lockKey;
if (lockKey) {
Toolbar.setSharedKey(
UIUtil.escapeHtml(lockKey));
lockRoom(true);
}
}
},
null, null, 'input:first'
);
}
}
};
/**
* Opens the invite link dialog.
*/
my.openLinkDialog = function () {
var inviteAttreibutes;
if (roomUrl === null) {
inviteAttreibutes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
APP.translation.translateString("roomUrlDefaultMsg") + '"';
} else {
inviteAttreibutes = "value=\"" + encodeURI(roomUrl) + "\"";
}
messageHandler.openTwoButtonDialog("dialog.shareLink",
null, null,
'<input id="inviteLinkRef" type="text" ' +
inviteAttreibutes + ' onclick="this.select();" readonly>',
false,
"dialog.Invite",
function (e, v) {
if (v) {
if (roomUrl) {
inviteParticipants();
}
}
},
function () {
if (roomUrl) {
document.getElementById('inviteLinkRef').select();
} else {
document.getElementById('jqi_state0_buttonInvite')
.disabled = true;
}
}
);
};
/**
* Opens the settings dialog.
* FIXME: not used ?
*/
my.openSettingsDialog = function () {
var settings1 = APP.translation.generateTranslatonHTML(
"dialog.settings1");
var settings2 = APP.translation.generateTranslatonHTML(
"dialog.settings2");
var settings3 = APP.translation.generateTranslatonHTML(
"dialog.settings3");
var yourPassword = APP.translation.translateString(
"dialog.yourPassword");
messageHandler.openTwoButtonDialog(null,
'<h2>' + settings1 + '</h2>' +
'<input type="checkbox" id="initMuted">' +
settings2 + '<br/>' +
'<input type="checkbox" id="requireNicknames">' +
settings3 +
'<input id="lockKey" type="text" placeholder="' + yourPassword +
'" data-i18n="[placeholder]dialog.yourPassword" autofocus>',
null,
null,
false,
"dialog.Save",
function () {
document.getElementById('lockKey').focus();
},
function (e, v) {
if (v) {
if ($('#initMuted').is(":checked")) {
// it is checked
}
if ($('#requireNicknames').is(":checked")) {
// it is checked
}
/*
var lockKey = document.getElementById('lockKey');
if (lockKey.value) {
setSharedKey(lockKey.value);
lockRoom(true);
}
*/
}
}
);
};
/**
* Toggles the application in and out of full screen mode
* (a.k.a. presentation mode in Chrome).
*/
my.toggleFullScreen = function () {
var fsElement = document.documentElement;
if (!document.mozFullScreen && !document.webkitIsFullScreen) {
//Enter Full Screen
if (fsElement.mozRequestFullScreen) {
fsElement.mozRequestFullScreen();
}
else {
fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
//Exit Full Screen
if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else {
document.webkitCancelFullScreen();
}
}
};
/**
* Unlocks the lock button state.
*/
my.unlockLockButton = function () {
if ($("#lockIcon").hasClass("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"))
UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
* Shows or hides authentication button
* @param show <tt>true</tt> to show or <tt>false</tt> to hide
*/
my.showAuthenticateButton = function (show) {
if (show) {
$('#authentication').css({display: "inline"});
}
else {
$('#authentication').css({display: "none"});
}
};
// Shows or hides the 'recording' button.
my.showRecordingButton = function (show) {
if (!config.enableRecording) {
return;
}
if (show) {
$('#recording').css({display: "inline"});
}
else {
$('#recording').css({display: "none"});
}
};
// Sets the state of the recording button
my.setRecordingButtonState = function (isRecording) {
if (isRecording) {
$('#recordButton').removeClass("icon-recEnable");
$('#recordButton').addClass("icon-recEnable active");
} else {
$('#recordButton').removeClass("icon-recEnable active");
$('#recordButton').addClass("icon-recEnable");
}
};
// Shows or hides SIP calls button
my.showSipCallButton = function (show) {
if (APP.xmpp.isSipGatewayEnabled() && show) {
$('#sipCallButton').css({display: "inline-block"});
} else {
$('#sipCallButton').css({display: "none"});
}
};
/**
* Displays user authenticated identity name(login).
* @param authIdentity identity name to be displayed.
*/
my.setAuthenticatedIdentity = function (authIdentity) {
if (authIdentity) {
$('#toolbar_auth_identity').css({display: "list-item"});
$('#toolbar_auth_identity').text(authIdentity);
} else {
$('#toolbar_auth_identity').css({display: "none"});
}
};
/**
* Shows/hides login button.
* @param show <tt>true</tt> to show
*/
my.showLoginButton = function (show) {
if (show) {
$('#toolbar_button_login').css({display: "list-item"});
} else {
$('#toolbar_button_login').css({display: "none"});
}
};
/**
* Shows/hides logout button.
* @param show <tt>true</tt> to show
*/
my.showLogoutButton = function (show) {
if (show) {
$('#toolbar_button_logout').css({display: "list-item"});
} else {
$('#toolbar_button_logout').css({display: "none"});
}
};
/**
* Sets the state of the button. The button has blue glow if desktop
* streaming is active.
* @param active the state of the desktop streaming.
*/
my.changeDesktopSharingButtonState = function (active) {
var button = $("#desktopsharing > a");
if (active)
{
button.addClass("glow");
}
else
{
button.removeClass("glow");
}
};
return my;
}(Toolbar || {}));
module.exports = Toolbar;
},{"../../../service/authentication/AuthenticationEvents":93,"../authentication/Authentication":11,"../etherpad/Etherpad":14,"../prezi/Prezi":15,"../side_pannels/SidePanelToggler":17,"../util/MessageHandler":28,"../util/UIUtil":30,"./BottomToolbar":24}],26:[function(require,module,exports){
/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
var toolbarTimeoutObject,
toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
function showDesktopSharingButton() {
if (APP.desktopsharing.isDesktopSharingEnabled()) {
$('#desktopsharing').css({display: "inline"});
} else {
$('#desktopsharing').css({display: "none"});
}
}
/**
* Hides the toolbar.
*/
function hideToolbar() {
var header = $("#header"),
bottomToolbar = $("#bottomToolbar");
var isToolbarHover = false;
header.find('*').each(function () {
var id = $(this).attr('id');
if ($("#" + id + ":hover").length > 0) {
isToolbarHover = true;
}
});
if ($("#bottomToolbar:hover").length > 0) {
isToolbarHover = true;
}
clearTimeout(toolbarTimeoutObject);
toolbarTimeoutObject = null;
if (!isToolbarHover) {
header.hide("slide", { direction: "up", duration: 300});
$('#subject').animate({top: "-=40"}, 300);
if ($("#remoteVideos").hasClass("hidden")) {
bottomToolbar.hide(
"slide", {direction: "right", duration: 300});
}
}
else {
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
}
}
var ToolbarToggler = {
/**
* Shows the main toolbar.
*/
showToolbar: function () {
var header = $("#header"),
bottomToolbar = $("#bottomToolbar");
if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
header.show("slide", { direction: "up", duration: 300});
$('#subject').animate({top: "+=40"}, 300);
if (!bottomToolbar.is(":visible")) {
bottomToolbar.show(
"slide", {direction: "right", duration: 300});
}
if (toolbarTimeoutObject) {
clearTimeout(toolbarTimeoutObject);
toolbarTimeoutObject = null;
}
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
}
if (APP.xmpp.isModerator())
{
// TODO: Enable settings functionality.
// Need to uncomment the settings button in index.html.
// $('#settingsButton').css({visibility:"visible"});
}
// Show/hide desktop sharing button
showDesktopSharingButton();
},
/**
* Docks/undocks the toolbar.
*
* @param isDock indicates what operation to perform
*/
dockToolbar: function (isDock) {
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {
this.showToolbar();
}
// Then clear the time out, to dock the toolbar.
if (toolbarTimeoutObject) {
clearTimeout(toolbarTimeoutObject);
toolbarTimeoutObject = null;
}
}
else {
if (!$('#header').is(':visible')) {
this.showToolbar();
}
else {
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
}
}
},
showDesktopSharingButton: showDesktopSharingButton
};
module.exports = ToolbarToggler;
},{}],27:[function(require,module,exports){
var JitsiPopover = (function () {
/**
* Constructs new JitsiPopover and attaches it to the element
* @param element jquery selector
* @param options the options for the popover.
* @constructor
*/
function JitsiPopover(element, options)
{
this.options = {
skin: "white",
content: ""
};
if(options)
{
if(options.skin)
this.options.skin = options.skin;
if(options.content)
this.options.content = options.content;
}
this.elementIsHovered = false;
this.popoverIsHovered = false;
this.popoverShown = false;
element.data("jitsi_popover", this);
this.element = element;
this.template = ' <div class="jitsipopover ' + this.options.skin +
'"><div class="arrow"></div><div class="jitsipopover-content"></div>' +
'<div class="jitsiPopupmenuPadding"></div></div>';
var self = this;
this.element.on("mouseenter", function () {
self.elementIsHovered = true;
self.show();
}).on("mouseleave", function () {
self.elementIsHovered = false;
setTimeout(function () {
self.hide();
}, 10);
});
}
/**
* Shows the popover
*/
JitsiPopover.prototype.show = function () {
this.createPopover();
this.popoverShown = true;
};
/**
* Hides the popover
*/
JitsiPopover.prototype.hide = function () {
if(!this.elementIsHovered && !this.popoverIsHovered && this.popoverShown)
{
this.forceHide();
}
};
/**
* Hides the popover
*/
JitsiPopover.prototype.forceHide = function () {
$(".jitsipopover").remove();
this.popoverShown = false;
};
/**
* Creates the popover html
*/
JitsiPopover.prototype.createPopover = function () {
$("body").append(this.template);
$(".jitsipopover > .jitsipopover-content").html(this.options.content);
var self = this;
$(".jitsipopover").on("mouseenter", function () {
self.popoverIsHovered = true;
}).on("mouseleave", function () {
self.popoverIsHovered = false;
self.hide();
});
this.refreshPosition();
};
/**
* Refreshes the position of the popover
*/
JitsiPopover.prototype.refreshPosition = function () {
$(".jitsipopover").position({
my: "bottom",
at: "top",
collision: "fit",
of: this.element,
using: function (position, elements) {
var calcLeft = elements.target.left - elements.element.left + elements.target.width/2;
$(".jitsipopover").css({top: position.top, left: position.left, display: "table"});
$(".jitsipopover > .arrow").css({left: calcLeft});
$(".jitsipopover > .jitsiPopupmenuPadding").css({left: calcLeft - 50});
}
});
};
/**
* Updates the content of popover.
* @param content new content
*/
JitsiPopover.prototype.updateContent = function (content) {
this.options.content = content;
if(!this.popoverShown)
return;
$(".jitsipopover").remove();
this.createPopover();
};
return JitsiPopover;
})();
module.exports = JitsiPopover;
},{}],28:[function(require,module,exports){
/* global $, APP, jQuery, toastr */
var messageHandler = (function(my) {
/**
* Shows a message to the user.
*
* @param titleString the title of the message
* @param messageString the text of the message
*/
my.openMessageDialog = function(titleKey, messageKey) {
var title = null;
if(titleKey)
{
title = APP.translation.generateTranslatonHTML(titleKey);
}
var message = APP.translation.generateTranslatonHTML(messageKey);
$.prompt(message,
{
title: title,
persistent: false
}
);
};
/**
* Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
*
* @param titleString the title of the message
* @param msgString the text of the message
* @param persistent boolean value which determines whether the message is persistent or not
* @param leftButton the fist button's text
* @param submitFunction function to be called on submit
* @param loadedFunction function to be called after the prompt is fully loaded
* @param closeFunction function to be called after the prompt is closed
* @param focus optional focus selector or button index to be focused after
* the dialog is opened
* @param defaultButton index of default button which will be activated when
* the user press 'enter'. Indexed from 0.
*/
my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString,
persistent, leftButtonKey, submitFunction, loadedFunction,
closeFunction, focus, defaultButton)
{
var buttons = [];
var leftButton = APP.translation.generateTranslatonHTML(leftButtonKey);
buttons.push({ title: leftButton, value: true});
var cancelButton
= APP.translation.generateTranslatonHTML("dialog.Cancel");
buttons.push({title: cancelButton, value: false});
var message = msgString, title = titleString;
if (titleKey)
{
title = APP.translation.generateTranslatonHTML(titleKey);
}
if (msgKey) {
message = APP.translation.generateTranslatonHTML(msgKey);
}
$.prompt(message, {
title: title,
persistent: false,
buttons: buttons,
defaultButton: defaultButton,
focus: focus,
loaded: loadedFunction,
submit: submitFunction,
close: closeFunction
});
};
/**
* Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
*
* @param titleString the title of the message
* @param msgString the text of the message
* @param persistent boolean value which determines whether the message is persistent or not
* @param buttons object with the buttons. The keys must be the name of the button and value is the value
* that will be passed to submitFunction
* @param submitFunction function to be called on submit
* @param loadedFunction function to be called after the prompt is fully loaded
*/
my.openDialog = function (titleString, msgString, persistent, buttons,
submitFunction, loadedFunction) {
var args = {
title: titleString,
persistent: persistent,
buttons: buttons,
defaultButton: 1,
loaded: loadedFunction,
submit: submitFunction
};
if (persistent) {
args.closeText = '';
}
return new Impromptu(msgString, args);
};
/**
* Closes currently opened dialog.
*/
my.closeDialog = function () {
$.prompt.close();
};
/**
* Shows a dialog with different states to the user.
*
* @param statesObject object containing all the states of the dialog
*/
my.openDialogWithStates = function (statesObject, options) {
return new Impromptu(statesObject, options);
};
/**
* Opens new popup window for given <tt>url</tt> centered over current
* window.
*
* @param url the URL to be displayed in the popup window
* @param w the width of the popup window
* @param h the height of the popup window
* @param onPopupClosed optional callback function called when popup window
* has been closed.
*
* @returns popup window object if opened successfully or undefined
* in case we failed to open it(popup blocked)
*/
my.openCenteredPopup = function (url, w, h, onPopupClosed) {
var l = window.screenX + (window.innerWidth / 2) - (w / 2);
var t = window.screenY + (window.innerHeight / 2) - (h / 2);
var popup = window.open(
url, '_blank',
'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + '');
if (popup && onPopupClosed) {
var pollTimer = window.setInterval(function () {
if (popup.closed !== false) {
window.clearInterval(pollTimer);
onPopupClosed();
}
}, 200);
}
return popup;
};
/**
* Shows a dialog prompting the user to send an error report.
*
* @param titleString the title of the message
* @param msgString the text of the message
* @param error the error that is being reported
*/
my.openReportDialog = function(titleKey, msgKey, error) {
my.openMessageDialog(titleKey, msgKey);
console.log(error);
//FIXME send the error to the server
};
/**
* Shows an error dialog to the user.
* @param title the title of the message
* @param message the text of the messafe
*/
my.showError = function(titleKey, msgKey) {
if(!titleKey) {
titleKey = "dialog.oops";
}
if(!msgKey)
{
msgKey = "dialog.defaultError";
}
messageHandler.openMessageDialog(titleKey, msgKey);
};
my.notify = function(displayName, displayNameKey,
cls, messageKey, messageArguments) {
var displayNameSpan = '<span class="nickname" ';
if(displayName)
{
displayNameSpan += ">" + displayName;
}
else
{
displayNameSpan += "data-i18n='" + displayNameKey +
"'>" + APP.translation.translateString(displayNameKey);
}
displayNameSpan += "</span>";
toastr.info(
displayNameSpan + '<br>' +
'<span class=' + cls + ' data-i18n="' + messageKey + '"' +
(messageArguments?
" data-i18n-options='" + JSON.stringify(messageArguments) + "'"
: "") + ">" +
APP.translation.translateString(messageKey,
messageArguments) +
'</span>');
};
return my;
}(messageHandler || {}));
module.exports = messageHandler;
},{}],29:[function(require,module,exports){
var UIEvents = require("../../../service/UI/UIEvents");
var nickname = null;
var eventEmitter = null;
var NickanameHandler = {
init: function (emitter) {
eventEmitter = emitter;
var storedDisplayName = window.localStorage.displayname;
if (storedDisplayName) {
nickname = storedDisplayName;
}
},
setNickname: function (newNickname) {
if (!newNickname || nickname === newNickname)
return;
nickname = newNickname;
window.localStorage.displayname = nickname;
eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newNickname);
},
getNickname: function () {
return nickname;
},
addListener: function (type, listener) {
eventEmitter.on(type, listener);
}
};
module.exports = NickanameHandler;
},{"../../../service/UI/UIEvents":92}],30:[function(require,module,exports){
/**
* Created by hristo on 12/22/14.
*/
module.exports = {
/**
* Returns the available video width.
*/
getAvailableVideoWidth: function () {
var PanelToggler = require("../side_pannels/SidePanelToggler");
var rightPanelWidth
= 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
},
/**
* Returns the text width for the given element.
*
* @param el the element
*/
getTextWidth: function (el) {
return (el.clientWidth + 1);
},
/**
* Returns the text height for the given element.
*
* @param el the element
*/
getTextHeight: function (el) {
return (el.clientHeight + 1);
},
/**
* Plays the sound given by id.
*
* @param id the identifier of the audio element.
*/
playSoundNotification: function (id) {
document.getElementById(id).play();
},
/**
* Escapes the given text.
*/
escapeHtml: function (unsafeText) {
return $('<div/>').text(unsafeText).html();
},
imageToGrayScale: function (canvas) {
var context = canvas.getContext('2d');
var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
var pixels = imgData.data;
for (var i = 0, n = pixels.length; i < n; i += 4) {
var grayscale
= pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
pixels[i ] = grayscale; // red
pixels[i+1] = grayscale; // green
pixels[i+2] = grayscale; // blue
// pixels[i+3] is alpha
}
// redraw the image in black & white
context.putImageData(imgData, 0, 0);
},
setTooltip: function (element, key, position) {
element.setAttribute("data-i18n", "[data-content]" + key);
element.setAttribute("data-toggle", "popover");
element.setAttribute("data-placement", position);
element.setAttribute("data-html", true);
element.setAttribute("data-container", "body");
}
};
},{"../side_pannels/SidePanelToggler":17}],31:[function(require,module,exports){
var JitsiPopover = require("../util/JitsiPopover");
/**
* Constructs new connection indicator.
* @param videoContainer the video container associated with the indicator.
* @constructor
*/
function ConnectionIndicator(videoContainer, jid, VideoLayout)
{
this.videoContainer = videoContainer;
this.bandwidth = null;
this.packetLoss = null;
this.bitrate = null;
this.showMoreValue = false;
this.resolution = null;
this.transport = [];
this.popover = null;
this.jid = jid;
this.create();
this.videoLayout = VideoLayout;
}
/**
* Values for the connection quality
* @type {{98: string,
* 81: string,
* 64: string,
* 47: string,
* 30: string,
* 0: string}}
*/
ConnectionIndicator.connectionQualityValues = {
98: "18px", //full
81: "15px",//4 bars
64: "11px",//3 bars
47: "7px",//2 bars
30: "3px",//1 bar
0: "0px"//empty
};
ConnectionIndicator.getIP = function(value)
{
return value.substring(0, value.lastIndexOf(":"));
};
ConnectionIndicator.getPort = function(value)
{
return value.substring(value.lastIndexOf(":") + 1, value.length);
};
ConnectionIndicator.getStringFromArray = function (array) {
var res = "";
for(var i = 0; i < array.length; i++)
{
res += (i === 0? "" : ", ") + array[i];
}
return res;
};
/**
* Generates the html content.
* @returns {string} the html content.
*/
ConnectionIndicator.prototype.generateText = function () {
var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
var translate = APP.translation.translateString;
if(this.bitrate === null)
{
downloadBitrate = "N/A";
uploadBitrate = "N/A";
}
else
{
downloadBitrate =
this.bitrate.download? this.bitrate.download + " Kbps" : "N/A";
uploadBitrate =
this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A";
}
if(this.packetLoss === null)
{
packetLoss = "N/A";
}
else
{
packetLoss = "<span class='jitsipopover_green'>&darr;</span>" +
(this.packetLoss.download !== null? this.packetLoss.download : "N/A") +
"% <span class='jitsipopover_orange'>&uarr;</span>" +
(this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") + "%";
}
var resolutionValue = null;
if(this.resolution && this.jid != null)
{
var keys = Object.keys(this.resolution);
if(keys.length == 1)
{
for(var ssrc in this.resolution)
{
resolutionValue = this.resolution[ssrc];
}
}
else if(keys.length > 1)
{
var displayedSsrc = APP.simulcast.getReceivingSSRC(this.jid);
resolutionValue = this.resolution[displayedSsrc];
}
}
if(this.jid === null)
{
resolution = "";
if(this.resolution === null || !Object.keys(this.resolution) ||
Object.keys(this.resolution).length === 0)
{
resolution = "N/A";
}
else
for(i in this.resolution)
{
resolutionValue = this.resolution[i];
if(resolutionValue)
{
if(resolutionValue.height &&
resolutionValue.width)
{
resolution += (resolution === ""? "" : ", ") +
resolutionValue.width + "x" +
resolutionValue.height;
}
}
}
}
else if(!resolutionValue ||
!resolutionValue.height ||
!resolutionValue.width)
{
resolution = "N/A";
}
else
{
resolution = resolutionValue.width + "x" + resolutionValue.height;
}
var result = "<table style='width:100%'>" +
"<tr>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.bitrate'>" +
translate("connectionindicator.bitrate") + "</span></td>" +
"<td><span class='jitsipopover_green'>&darr;</span>" +
downloadBitrate + " <span class='jitsipopover_orange'>&uarr;</span>" +
uploadBitrate + "</td>" +
"</tr><tr>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.packetloss'>" +
translate("connectionindicator.packetloss") + "</span></td>" +
"<td>" + packetLoss + "</td>" +
"</tr><tr>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.resolution'>" +
translate("connectionindicator.resolution") + "</span></td>" +
"<td>" + resolution + "</td></tr></table>";
if(this.videoContainer.id == "localVideoContainer") {
result += "<div class=\"jitsipopover_showmore\" " +
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
this.videoContainer.id + "')\" data-i18n='connectionindicator." +
(this.showMoreValue ? "less" : "more") + "'>" +
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
"</div><br />";
}
if(this.showMoreValue)
{
var downloadBandwidth, uploadBandwidth, transport;
if(this.bandwidth === null)
{
downloadBandwidth = "N/A";
uploadBandwidth = "N/A";
}
else
{
downloadBandwidth = this.bandwidth.download?
this.bandwidth.download + " Kbps" :
"N/A";
uploadBandwidth = this.bandwidth.upload?
this.bandwidth.upload + " Kbps" :
"N/A";
}
if(!this.transport || this.transport.length === 0)
{
transport = "<tr>" +
"<td><span class='jitsipopover_blue' " +
"data-i18n='connectionindicator.address'>" +
translate("connectionindicator.address") + "</span></td>" +
"<td> N/A</td></tr>";
}
else
{
var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
for(i = 0; i < this.transport.length; i++)
{
var ip = ConnectionIndicator.getIP(this.transport[i].ip);
var port = ConnectionIndicator.getPort(this.transport[i].ip);
var localIP =
ConnectionIndicator.getIP(this.transport[i].localip);
var localPort =
ConnectionIndicator.getPort(this.transport[i].localip);
if(data.remoteIP.indexOf(ip) == -1)
{
data.remoteIP.push(ip);
}
if(data.remotePort.indexOf(port) == -1)
{
data.remotePort.push(port);
}
if(data.localIP.indexOf(localIP) == -1)
{
data.localIP.push(localIP);
}
if(data.localPort.indexOf(localPort) == -1)
{
data.localPort.push(localPort);
}
}
var local_address_key = "connectionindicator.localaddress";
var remote_address_key = "connectionindicator.remoteaddress";
var localTransport =
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
local_address_key +"' data-i18n-options='" +
JSON.stringify({count: data.localIP.length}) + "'>" +
translate(local_address_key, {count: data.localIP.length}) +
"</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.localIP) +
"</td></tr>";
transport =
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
remote_address_key + "' data-i18n-options='" +
JSON.stringify({count: data.remoteIP.length}) + "'>" +
translate(remote_address_key,
{count: data.remoteIP.length}) +
"</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.remoteIP) +
"</td></tr>";
var key_remote = "connectionindicator.remoteport",
key_local = "connectionindicator.localport";
transport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue' data-i18n='" + key_remote +
"' data-i18n-options='" +
JSON.stringify({count: this.transport.length}) + "'>" +
translate(key_remote, {count: this.transport.length}) +
"</span></td><td>";
localTransport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue' data-i18n='" + key_local +
"' data-i18n-options='" +
JSON.stringify({count: this.transport.length}) + "'>" +
translate(key_local, {count: this.transport.length}) +
"</span></td><td>";
transport +=
ConnectionIndicator.getStringFromArray(data.remotePort);
localTransport +=
ConnectionIndicator.getStringFromArray(data.localPort);
transport += "</td></tr>";
transport += localTransport + "</td></tr>";
transport +="<tr>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.transport'>" +
translate("connectionindicator.transport") + "</span></td>" +
"<td>" + this.transport[0].type + "</td></tr>";
}
result += "<table style='width:100%'>" +
"<tr>" +
"<td>" +
"<span class='jitsipopover_blue' data-i18n='connectionindicator.bandwidth'>" +
translate("connectionindicator.bandwidth") + "</span>" +
"</td><td>" +
"<span class='jitsipopover_green'>&darr;</span>" +
downloadBandwidth +
" <span class='jitsipopover_orange'>&uarr;</span>" +
uploadBandwidth + "</td></tr>";
result += transport + "</table>";
}
return result;
};
/**
* Shows or hide the additional information.
*/
ConnectionIndicator.prototype.showMore = function () {
this.showMoreValue = !this.showMoreValue;
this.updatePopoverData();
};
function createIcon(classes)
{
var icon = document.createElement("span");
for(var i in classes)
{
icon.classList.add(classes[i]);
}
icon.appendChild(
document.createElement("i")).classList.add("icon-connection");
return icon;
}
/**
* Creates the indicator
*/
ConnectionIndicator.prototype.create = function () {
this.connectionIndicatorContainer = document.createElement("div");
this.connectionIndicatorContainer.className = "connectionindicator";
this.connectionIndicatorContainer.style.display = "none";
this.videoContainer.appendChild(this.connectionIndicatorContainer);
this.popover = new JitsiPopover(
$("#" + this.videoContainer.id + " > .connectionindicator"),
{content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
APP.translation.translateString("connectionindicator.na") + "</div>",
skin: "black"});
this.emptyIcon = this.connectionIndicatorContainer.appendChild(
createIcon(["connection", "connection_empty"]));
this.fullIcon = this.connectionIndicatorContainer.appendChild(
createIcon(["connection", "connection_full"]));
};
/**
* Removes the indicator
*/
ConnectionIndicator.prototype.remove = function()
{
this.connectionIndicatorContainer.remove();
this.popover.forceHide();
};
/**
* Updates the data of the indicator
* @param percent the percent of connection quality
* @param object the statistics data.
*/
ConnectionIndicator.prototype.updateConnectionQuality =
function (percent, object) {
if(percent === null)
{
this.connectionIndicatorContainer.style.display = "none";
this.popover.forceHide();
return;
}
else
{
if(this.connectionIndicatorContainer.style.display == "none") {
this.connectionIndicatorContainer.style.display = "block";
this.videoLayout.updateMutePosition(this.videoContainer.id);
}
}
this.bandwidth = object.bandwidth;
this.bitrate = object.bitrate;
this.packetLoss = object.packetLoss;
this.transport = object.transport;
if(object.resolution)
{
this.resolution = object.resolution;
}
for(var quality in ConnectionIndicator.connectionQualityValues)
{
if(percent >= quality)
{
this.fullIcon.style.width =
ConnectionIndicator.connectionQualityValues[quality];
}
}
this.updatePopoverData();
};
/**
* Updates the resolution
* @param resolution the new resolution
*/
ConnectionIndicator.prototype.updateResolution = function (resolution) {
this.resolution = resolution;
this.updatePopoverData();
};
/**
* Updates the content of the popover
*/
ConnectionIndicator.prototype.updatePopoverData = function () {
this.popover.updateContent(
"<div class=\"connection_info\">" + this.generateText() + "</div>");
APP.translation.translateElement($(".connection_info"));
};
/**
* Hides the popover
*/
ConnectionIndicator.prototype.hide = function () {
this.popover.forceHide();
};
/**
* Hides the indicator
*/
ConnectionIndicator.prototype.hideIndicator = function () {
this.connectionIndicatorContainer.style.display = "none";
if(this.popover)
this.popover.forceHide();
};
module.exports = ConnectionIndicator;
},{"../util/JitsiPopover":27}],32:[function(require,module,exports){
var AudioLevels = require("../audio_levels/AudioLevels");
var Avatar = require("../avatar/Avatar");
var Chat = require("../side_pannels/chat/Chat");
var ContactList = require("../side_pannels/contactlist/ContactList");
var UIUtil = require("../util/UIUtil");
var ConnectionIndicator = require("./ConnectionIndicator");
var NicknameHandler = require("../util/NicknameHandler");
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
var UIEvents = require("../../../service/UI/UIEvents");
var currentDominantSpeaker = null;
var lastNCount = config.channelLastN;
var localLastNCount = config.channelLastN;
var localLastNSet = [];
var lastNEndpointsCache = [];
var lastNPickupJid = null;
var largeVideoState = {
updateInProgress: false,
newSrc: ''
};
var eventEmitter = null;
/**
* Currently focused video "src"(displayed in large video).
* @type {String}
*/
var focusedVideoInfo = null;
/**
* 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;
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(
APP.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 = APP.simulcast.getReceivingVideoStream(stream);
APP.RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
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.
*
* @return an array with 2 elements, the horizontal indent and the vertical
* indent
*/
function getCameraVideoPosition(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
// Parent height isn't completely calculated when we position the video in
// full screen mode and this is why we use the screen height in this case.
// Need to think it further at some point and implement it properly.
var isFullScreen = document.fullScreen ||
document.mozFullScreen ||
document.webkitIsFullScreen;
if (isFullScreen)
videoSpaceHeight = window.innerHeight;
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
return [horizontalIndent, verticalIndent];
}
/**
* Returns an array of the video horizontal and vertical indents.
* Centers horizontally and top aligns vertically.
*
* @return an array with 2 elements, the horizontal indent and the vertical
* indent
*/
function getDesktopVideoPosition(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
var verticalIndent = 0;// Top aligned
return [horizontalIndent, verticalIndent];
}
/**
* Returns an array of the video dimensions, so that it covers the screen.
* It leaves no empty areas, but some parts of the video might not be visible.
*
* @return an array with 2 elements, the video width and the video height
*/
function getCameraVideoSize(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
if (!videoWidth)
videoWidth = currentVideoWidth;
if (!videoHeight)
videoHeight = currentVideoHeight;
var aspectRatio = videoWidth / videoHeight;
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
if (availableWidth / aspectRatio < videoSpaceHeight) {
availableHeight = videoSpaceHeight;
availableWidth = availableHeight * aspectRatio;
}
if (availableHeight * aspectRatio < videoSpaceWidth) {
availableWidth = videoSpaceWidth;
availableHeight = availableWidth / aspectRatio;
}
return [availableWidth, availableHeight];
}
/**
* Sets the display name for the given video span id.
*/
function setDisplayName(videoSpanId, displayName, key) {
var nameSpan = $('#' + videoSpanId + '>span.displayname');
var defaultLocalDisplayName = APP.translation.generateTranslatonHTML(
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
// If we already have a display name for this video.
if (nameSpan.length > 0) {
var nameSpanElement = nameSpan.get(0);
if (nameSpanElement.id === 'localDisplayName' &&
$('#localDisplayName').text() !== displayName) {
if (displayName && displayName.length > 0)
{
var meHTML = APP.translation.generateTranslatonHTML("me");
$('#localDisplayName').html(displayName + ' (' + meHTML + ')');
}
else
$('#localDisplayName').html(defaultLocalDisplayName);
} else {
if (displayName && displayName.length > 0)
{
$('#' + videoSpanId + '_name').html(displayName);
}
else if (key && key.length > 0)
{
var nameHtml = APP.translation.generateTranslatonHTML(key);
$('#' + videoSpanId + '_name').html(nameHtml);
}
else
$('#' + videoSpanId + '_name').text(
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
}
} else {
var editButton = null;
nameSpan = document.createElement('span');
nameSpan.className = 'displayname';
$('#' + videoSpanId)[0].appendChild(nameSpan);
if (videoSpanId === 'localVideoContainer') {
editButton = createEditDisplayNameButton();
if (displayName && displayName.length > 0) {
var meHTML = APP.translation.generateTranslatonHTML("me");
nameSpan.innerHTML = displayName + meHTML;
}
else
nameSpan.innerHTML = defaultLocalDisplayName;
}
else {
if (displayName && displayName.length > 0) {
nameSpan.innerText = displayName;
}
else
nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
}
if (!editButton) {
nameSpan.id = videoSpanId + '_name';
} else {
nameSpan.id = 'localDisplayName';
$('#' + videoSpanId)[0].appendChild(editButton);
//translates popover of edit button
APP.translation.translateElement($("a.displayname"));
var editableText = document.createElement('input');
editableText.className = 'displayname';
editableText.type = 'text';
editableText.id = 'editDisplayName';
if (displayName && displayName.length) {
editableText.value
= displayName;
}
var defaultNickname = APP.translation.translateString(
"defaultNickname", {name: "Jane Pink"});
editableText.setAttribute('style', 'display:none;');
editableText.setAttribute('data-18n',
'[placeholder]defaultNickname');
editableText.setAttribute("data-i18n-options",
JSON.stringify({name: "Jane Pink"}));
editableText.setAttribute("placeholder", defaultNickname);
$('#' + videoSpanId)[0].appendChild(editableText);
$('#localVideoContainer .displayname')
.bind("click", function (e) {
e.preventDefault();
e.stopPropagation();
$('#localDisplayName').hide();
$('#editDisplayName').show();
$('#editDisplayName').focus();
$('#editDisplayName').select();
$('#editDisplayName').one("focusout", function (e) {
VideoLayout.inputDisplayNameHandler(this.value);
});
$('#editDisplayName').on('keydown', function (e) {
if (e.keyCode === 13) {
e.preventDefault();
VideoLayout.inputDisplayNameHandler(this.value);
}
});
});
}
}
}
/**
* Gets the selector of video thumbnail container for the user identified by
* given <tt>userJid</tt>
* @param resourceJid user's Jid for whom we want to get the video container.
*/
function getParticipantContainer(resourceJid)
{
if (!resourceJid)
return null;
if (resourceJid === APP.xmpp.myResource())
return $("#localVideoContainer");
else
return $("#participant_" + resourceJid);
}
/**
* Sets the size and position of the given video element.
*
* @param video the video element to position
* @param width the desired video width
* @param height the desired video height
* @param horizontalIndent the left and right indent
* @param verticalIndent the top and bottom indent
*/
function positionVideo(video,
width,
height,
horizontalIndent,
verticalIndent) {
video.width(width);
video.height(height);
video.css({ top: verticalIndent + 'px',
bottom: verticalIndent + 'px',
left: horizontalIndent + 'px',
right: horizontalIndent + 'px'});
}
/**
* Adds the remote video menu element for the given <tt>jid</tt> in the
* given <tt>parentElement</tt>.
*
* @param jid the jid indicating the video for which we're adding a menu.
* @param parentElement the parent element where this menu will be added
*/
function addRemoteVideoMenu(jid, parentElement) {
var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu';
parentElement.appendChild(spanElement);
var menuElement = document.createElement('i');
menuElement.className = 'fa fa-angle-down';
menuElement.title = 'Remote user controls';
spanElement.appendChild(menuElement);
// <ul class="popupmenu">
// <li><a href="#">Mute</a></li>
// <li><a href="#">Eject</a></li>
// </ul>
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id
= 'remote_popupmenu_' + Strophe.getResourceFromJid(jid);
spanElement.appendChild(popupmenuElement);
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
if (!mutedAudios[jid]) {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
muteLinkItem.onclick = function(){
if ($(this).attr('disabled') != undefined) {
event.preventDefault();
}
var isMute = mutedAudios[jid] == true;
APP.xmpp.setMute(jid, !isMute);
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
}
else {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
}
};
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(jid);
popupmenuElement.setAttribute('style', 'display:none;');
};
ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem);
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement($("#" + popupmenuElement.id + " > li > a > div"));
}
/**
* Removes remote video menu element from video element identified by
* given <tt>videoElementId</tt>.
*
* @param videoElementId the id of local or remote video element.
*/
function removeRemoteVideoMenu(videoElementId) {
var menuSpan = $('#' + videoElementId + '>span.remotevideomenu');
if (menuSpan.length) {
menuSpan.remove();
}
}
/**
* Updates the data for the indicator
* @param id the id of the indicator
* @param percent the percent for connection quality
* @param object the data
*/
function updateStatsIndicator(id, percent, object) {
if(VideoLayout.connectionIndicators[id])
VideoLayout.connectionIndicators[id].updateConnectionQuality(percent, object);
}
/**
* Returns an array of the video dimensions, so that it keeps it's aspect
* ratio and fits available area with it's larger dimension. This method
* ensures that whole video will be visible and can leave empty areas.
*
* @return an array with 2 elements, the video width and the video height
*/
function getDesktopVideoSize(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
if (!videoWidth)
videoWidth = currentVideoWidth;
if (!videoHeight)
videoHeight = currentVideoHeight;
var aspectRatio = videoWidth / videoHeight;
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
videoSpaceHeight -= $('#remoteVideos').outerHeight();
if (availableWidth / aspectRatio >= videoSpaceHeight)
{
availableHeight = videoSpaceHeight;
availableWidth = availableHeight * aspectRatio;
}
if (availableHeight * aspectRatio >= videoSpaceWidth)
{
availableWidth = videoSpaceWidth;
availableHeight = availableWidth / aspectRatio;
}
return [availableWidth, availableHeight];
}
/**
* Creates the edit display name button.
*
* @returns the edit button
*/
function createEditDisplayNameButton() {
var editButton = document.createElement('a');
editButton.className = 'displayname';
UIUtil.setTooltip(editButton,
"videothumbnail.editnickname",
"top");
editButton.innerHTML = '<i class="fa fa-pencil"></i>';
return editButton;
}
/**
* Creates the element indicating the moderator(owner) of the conference.
*
* @param parentElement the parent element where the owner indicator will
* be added
*/
function createModeratorIndicatorElement(parentElement) {
var moderatorIndicator = document.createElement('i');
moderatorIndicator.className = 'fa fa-star';
parentElement.appendChild(moderatorIndicator);
UIUtil.setTooltip(parentElement,
"videothumbnail.moderator",
"top");
}
var VideoLayout = (function (my) {
my.connectionIndicators = {};
// By default we use camera
my.getVideoSize = getCameraVideoSize;
my.getVideoPosition = getCameraVideoPosition;
my.init = function (emitter) {
// Listen for large video size updates
document.getElementById('largeVideo')
.addEventListener('loadedmetadata', function (e) {
currentVideoWidth = this.videoWidth;
currentVideoHeight = this.videoHeight;
VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
});
eventEmitter = emitter;
};
my.isInLastN = function(resource) {
return lastNCount < 0 // lastN is disabled, return true
|| (lastNCount > 0 && lastNEndpointsCache.length == 0) // lastNEndpoints cache not built yet, return true
|| (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
};
my.changeLocalStream = function (stream) {
VideoLayout.changeLocalVideo(stream);
};
my.changeLocalAudio = function(stream) {
APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
document.getElementById('localAudio').autoplay = true;
document.getElementById('localAudio').volume = 0;
if (preMuted) {
if(!APP.UI.setAudioMuted(true))
{
preMuted = mute;
}
preMuted = false;
}
};
my.changeLocalVideo = function(stream) {
var flipX = true;
if(stream.videoType == "screen")
flipX = false;
var localVideo = document.createElement('video');
localVideo.id = 'localVideo_' +
APP.RTC.getStreamID(stream.getOriginalStream());
localVideo.autoplay = true;
localVideo.volume = 0; // is it required if audio is separated ?
localVideo.oncontextmenu = function () { return false; };
var localVideoContainer = document.getElementById('localVideoWrapper');
localVideoContainer.appendChild(localVideo);
// Set default display name.
setDisplayName('localVideoContainer');
if(!VideoLayout.connectionIndicators["localVideoContainer"]) {
VideoLayout.connectionIndicators["localVideoContainer"]
= new ConnectionIndicator($("#localVideoContainer")[0], null, VideoLayout);
}
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
var localVideoSelector = $('#' + localVideo.id);
function localVideoClick(event) {
event.stopPropagation();
VideoLayout.handleVideoThumbClicked(
APP.RTC.getVideoSrc(localVideo),
false,
APP.xmpp.myResource());
}
// Add click handler to both video and video wrapper elements in case
// there's no video.
localVideoSelector.click(localVideoClick);
$('#localVideoContainer').click(localVideoClick);
// Add hover handler
$('#localVideoContainer').hover(
function() {
VideoLayout.showDisplayName('localVideoContainer', true);
},
function() {
if (!VideoLayout.isLargeVideoVisible()
|| APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
VideoLayout.showDisplayName('localVideoContainer', false);
}
);
// Add stream ended handler
stream.getOriginalStream().onended = function () {
localVideoContainer.removeChild(localVideo);
VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
};
// Flip video x axis if needed
flipXLocalVideo = flipX;
if (flipX) {
localVideoSelector.addClass("flipVideoX");
}
// Attach WebRTC stream
var videoStream = APP.simulcast.getLocalVideoStream();
APP.RTC.attachMediaStream(localVideoSelector, videoStream);
localVideoSrc = APP.RTC.getVideoSrc(localVideo);
var myResourceJid = APP.xmpp.myResource();
VideoLayout.updateLargeVideo(localVideoSrc, 0,
myResourceJid);
};
/**
* Adds or removes icons for not available camera and microphone.
* @param resourceJid the jid of user
* @param devices available devices
*/
my.setDeviceAvailabilityIcons = function (resourceJid, devices) {
if(!devices)
return;
var container = null
if(!resourceJid)
{
container = $("#localVideoContainer")[0];
}
else
{
container = $("#participant_" + resourceJid)[0];
}
if(!container)
return;
$("#" + container.id + " > .noMic").remove();
$("#" + container.id + " > .noVideo").remove();
if(!devices.audio)
{
container.appendChild(document.createElement("div")).setAttribute("class","noMic");
}
if(!devices.video)
{
container.appendChild(document.createElement("div")).setAttribute("class","noVideo");
}
if(!devices.audio && !devices.video)
{
$("#" + container.id + " > .noMic").css("background-position", "75%");
$("#" + container.id + " > .noVideo").css("background-position", "25%");
$("#" + container.id + " > .noVideo").css("background-color", "transparent");
}
}
/**
* Checks if removed video is currently displayed and tries to display
* another one instead.
* @param removedVideoSrc src stream identifier of the video.
*/
my.updateRemovedVideo = function(removedVideoSrc) {
if (removedVideoSrc === APP.RTC.getVideoSrc($('#largeVideo')[0])) {
// this is currently displayed as large
// pick the last visible video in the row
// if nobody else is left, this picks the local video
var pick
= $('#remoteVideos>span[id!="mixedstream"]:visible:last>video')
.get(0);
if (!pick) {
console.info("Last visible video no longer exists");
pick = $('#remoteVideos>span[id!="mixedstream"]>video').get(0);
if (!pick || !APP.RTC.getVideoSrc(pick)) {
// Try local video
console.info("Fallback to local video...");
pick = $('#remoteVideos>span>span>video').get(0);
}
}
// mute if localvideo
if (pick) {
var container = pick.parentNode;
var jid = null;
if(container)
{
if(container.id == "localVideoWrapper")
{
jid = APP.xmpp.myResource();
}
else
{
jid = VideoLayout.getPeerContainerResourceJid(container);
}
}
VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(pick), pick.volume, jid);
} else {
console.warn("Failed to elect large video");
}
}
};
my.onRemoteStreamAdded = function (stream) {
var container;
var remotes = document.getElementById('remoteVideos');
if (stream.peerjid) {
VideoLayout.ensurePeerContainerExists(stream.peerjid);
container = document.getElementById(
'participant_' + Strophe.getResourceFromJid(stream.peerjid));
} else {
var id = stream.getOriginalStream().id;
if (id !== 'mixedmslabel'
// FIXME: default stream is added always with new focus
// (to be investigated)
&& id !== 'default') {
console.error('can not associate stream',
id,
'with a participant');
// We don't want to add it here since it will cause troubles
return;
}
// FIXME: for the mixed ms we dont need a video -- currently
container = document.createElement('span');
container.id = 'mixedstream';
container.className = 'videocontainer';
remotes.appendChild(container);
UIUtil.playSoundNotification('userJoined');
}
if (container) {
VideoLayout.addRemoteStreamElement( container,
stream.sid,
stream.getOriginalStream(),
stream.peerjid,
stream.ssrc);
}
}
my.getLargeVideoState = function () {
return largeVideoState;
};
/**
* Updates the large video with the given new video source.
*/
my.updateLargeVideo = function(newSrc, vol, resourceJid) {
console.log('hover in', newSrc);
if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc) {
$('#activeSpeaker').css('visibility', 'hidden');
// Due to the simulcast the localVideoSrc may have changed when the
// fadeOut event triggers. In that case the getJidFromVideoSrc and
// isVideoSrcDesktop methods will not function correctly.
//
// Also, again due to the simulcast, the updateLargeVideo method can
// be called multiple times almost simultaneously. Therefore, we
// store the state here and update only once.
largeVideoState.newSrc = newSrc;
largeVideoState.isVisible = $('#largeVideo').is(':visible');
largeVideoState.isDesktop = APP.RTC.isVideoSrcDesktop(
APP.xmpp.findJidFromResource(resourceJid));
if(largeVideoState.userResourceJid) {
largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
} else {
largeVideoState.oldResourceJid = null;
}
largeVideoState.userResourceJid = resourceJid;
// Screen stream is already rotated
largeVideoState.flipX = (newSrc === localVideoSrc) && flipXLocalVideo;
var userChanged = false;
if (largeVideoState.oldResourceJid !== largeVideoState.userResourceJid) {
userChanged = true;
// we want the notification to trigger even if userJid is undefined,
// or null.
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
largeVideoState.userResourceJid);
}
if (!largeVideoState.updateInProgress) {
largeVideoState.updateInProgress = true;
var doUpdate = function () {
Avatar.updateActiveSpeakerAvatarSrc(
APP.xmpp.findJidFromResource(
largeVideoState.userResourceJid));
if (!userChanged && largeVideoState.preload &&
largeVideoState.preload !== null &&
APP.RTC.getVideoSrc($(largeVideoState.preload)[0]) === newSrc)
{
console.info('Switching to preloaded video');
var attributes = $('#largeVideo').prop("attributes");
// loop through largeVideo attributes and apply them on
// preload.
$.each(attributes, function () {
if (this.name !== 'id' && this.name !== 'src') {
largeVideoState.preload.attr(this.name, this.value);
}
});
largeVideoState.preload.appendTo($('#largeVideoContainer'));
$('#largeVideo').attr('id', 'previousLargeVideo');
largeVideoState.preload.attr('id', 'largeVideo');
$('#previousLargeVideo').remove();
largeVideoState.preload.on('loadedmetadata', function (e) {
currentVideoWidth = this.videoWidth;
currentVideoHeight = this.videoHeight;
VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
});
largeVideoState.preload = null;
largeVideoState.preload_ssrc = 0;
} else {
APP.RTC.setVideoSrc($('#largeVideo')[0], largeVideoState.newSrc);
}
var videoTransform = document.getElementById('largeVideo')
.style.webkitTransform;
if (largeVideoState.flipX && videoTransform !== 'scaleX(-1)') {
document.getElementById('largeVideo').style.webkitTransform
= "scaleX(-1)";
}
else if (!largeVideoState.flipX && videoTransform === 'scaleX(-1)') {
document.getElementById('largeVideo').style.webkitTransform
= "none";
}
// Change the way we'll be measuring and positioning large video
VideoLayout.getVideoSize = largeVideoState.isDesktop
? getDesktopVideoSize
: getCameraVideoSize;
VideoLayout.getVideoPosition = largeVideoState.isDesktop
? getDesktopVideoPosition
: getCameraVideoPosition;
// Only if the large video is currently visible.
// Disable previous dominant speaker video.
if (largeVideoState.oldResourceJid) {
VideoLayout.enableDominantSpeaker(
largeVideoState.oldResourceJid,
false);
}
// Enable new dominant speaker in the remote videos section.
if (largeVideoState.userResourceJid) {
VideoLayout.enableDominantSpeaker(
largeVideoState.userResourceJid,
true);
}
if (userChanged && largeVideoState.isVisible) {
// using "this" should be ok because we're called
// from within the fadeOut event.
$(this).fadeIn(300);
}
if(userChanged) {
Avatar.showUserAvatar(
APP.xmpp.findJidFromResource(
largeVideoState.oldResourceJid));
}
largeVideoState.updateInProgress = false;
};
if (userChanged) {
$('#largeVideo').fadeOut(300, doUpdate);
} else {
doUpdate();
}
}
} else {
Avatar.showUserAvatar(
APP.xmpp.findJidFromResource(
largeVideoState.userResourceJid));
}
};
my.handleVideoThumbClicked = function(videoSrc,
noPinnedEndpointChangedEvent,
resourceJid) {
// Restore style for previously focused video
var oldContainer = null;
if(focusedVideoInfo) {
var focusResourceJid = focusedVideoInfo.resourceJid;
oldContainer = getParticipantContainer(focusResourceJid);
}
if (oldContainer) {
oldContainer.removeClass("videoContainerFocused");
}
// Unlock current focused.
if (focusedVideoInfo && focusedVideoInfo.src === videoSrc)
{
focusedVideoInfo = null;
var dominantSpeakerVideo = null;
// Enable the currently set dominant speaker.
if (currentDominantSpeaker) {
dominantSpeakerVideo
= $('#participant_' + currentDominantSpeaker + '>video')
.get(0);
if (dominantSpeakerVideo) {
VideoLayout.updateLargeVideo(
APP.RTC.getVideoSrc(dominantSpeakerVideo),
1,
currentDominantSpeaker);
}
}
if (!noPinnedEndpointChangedEvent) {
eventEmitter.emit(UIEvents.PINNED_ENDPOINT);
}
return;
}
// Lock new video
focusedVideoInfo = {
src: videoSrc,
resourceJid: resourceJid
};
// Update focused/pinned interface.
if (resourceJid)
{
var container = getParticipantContainer(resourceJid);
container.addClass("videoContainerFocused");
if (!noPinnedEndpointChangedEvent) {
eventEmitter.emit(UIEvents.PINNED_ENDPOINT, resourceJid);
}
}
if ($('#largeVideo').attr('src') === videoSrc &&
VideoLayout.isLargeVideoOnTop()) {
return;
}
// Triggers a "video.selected" event. The "false" parameter indicates
// this isn't a prezi.
$(document).trigger("video.selected", [false]);
VideoLayout.updateLargeVideo(videoSrc, 1, resourceJid);
$('audio').each(function (idx, el) {
if (el.id.indexOf('mixedmslabel') !== -1) {
el.volume = 0;
el.volume = 1;
}
});
};
/**
* Positions the large video.
*
* @param videoWidth the stream video width
* @param videoHeight the stream video height
*/
my.positionLarge = function (videoWidth, videoHeight) {
var videoSpaceWidth = $('#videospace').width();
var videoSpaceHeight = window.innerHeight;
var videoSize = VideoLayout.getVideoSize(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight);
var largeVideoWidth = videoSize[0];
var largeVideoHeight = videoSize[1];
var videoPosition = VideoLayout.getVideoPosition(largeVideoWidth,
largeVideoHeight,
videoSpaceWidth,
videoSpaceHeight);
var horizontalIndent = videoPosition[0];
var verticalIndent = videoPosition[1];
positionVideo($('#largeVideo'),
largeVideoWidth,
largeVideoHeight,
horizontalIndent, verticalIndent);
};
/**
* Shows/hides the large video.
*/
my.setLargeVideoVisible = function(isVisible) {
var resourceJid = largeVideoState.userResourceJid;
if (isVisible) {
$('#largeVideo').css({visibility: 'visible'});
$('.watermark').css({visibility: 'visible'});
VideoLayout.enableDominantSpeaker(resourceJid, true);
}
else {
$('#largeVideo').css({visibility: 'hidden'});
$('#activeSpeaker').css('visibility', 'hidden');
$('.watermark').css({visibility: 'hidden'});
VideoLayout.enableDominantSpeaker(resourceJid, false);
if(focusedVideoInfo) {
var focusResourceJid = focusedVideoInfo.resourceJid;
var oldContainer = getParticipantContainer(focusResourceJid);
if (oldContainer && oldContainer.length > 0) {
oldContainer.removeClass("videoContainerFocused");
}
focusedVideoInfo = null;
if(focusResourceJid) {
Avatar.showUserAvatar(
APP.xmpp.findJidFromResource(focusResourceJid));
}
}
}
};
/**
* Indicates if the large video is currently visible.
*
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
*/
my.isLargeVideoVisible = function() {
return $('#largeVideo').is(':visible');
};
my.isLargeVideoOnTop = function () {
var Etherpad = require("../etherpad/Etherpad");
var Prezi = require("../prezi/Prezi");
return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
};
/**
* Checks if container for participant identified by given peerJid exists
* in the document and creates it eventually.
*
* @param peerJid peer Jid to check.
* @param userId user email or id for setting the avatar
*
* @return Returns <tt>true</tt> if the peer container exists,
* <tt>false</tt> - otherwise
*/
my.ensurePeerContainerExists = function(peerJid, userId) {
ContactList.ensureAddContact(peerJid, userId);
var resourceJid = Strophe.getResourceFromJid(peerJid);
var videoSpanId = 'participant_' + resourceJid;
if (!$('#' + videoSpanId).length) {
var container =
VideoLayout.addRemoteVideoContainer(peerJid, videoSpanId, userId);
Avatar.setUserAvatar(peerJid, userId);
// Set default display name.
setDisplayName(videoSpanId);
VideoLayout.connectionIndicators[videoSpanId] =
new ConnectionIndicator(container, peerJid, VideoLayout);
var nickfield = document.createElement('span');
nickfield.className = "nick";
nickfield.appendChild(document.createTextNode(resourceJid));
container.appendChild(nickfield);
// In case this is not currently in the last n we don't show it.
if (localLastNCount
&& localLastNCount > 0
&& $('#remoteVideos>span').length >= localLastNCount + 2) {
showPeerContainer(resourceJid, 'hide');
}
else
VideoLayout.resizeThumbnails();
}
};
my.addRemoteVideoContainer = function(peerJid, spanId) {
var container = document.createElement('span');
container.id = spanId;
container.className = 'videocontainer';
var remotes = document.getElementById('remoteVideos');
remotes.appendChild(container);
// 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 (APP.xmpp.isModerator() && peerJid !== null)
addRemoteVideoMenu(peerJid, container);
AudioLevels.updateAudioLevelCanvas(peerJid, VideoLayout);
return container;
};
/**
* Creates an audio or video stream element.
*/
my.createStreamElement = function (sid, stream) {
var isVideo = stream.getVideoTracks().length > 0;
var element = isVideo
? document.createElement('video')
: document.createElement('audio');
var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_')
+ sid + '_' + APP.RTC.getStreamID(stream);
element.id = id;
element.autoplay = true;
element.oncontextmenu = function () { return false; };
return element;
};
my.addRemoteStreamElement
= function (container, sid, stream, peerJid, thessrc) {
var newElementId = null;
var isVideo = stream.getVideoTracks().length > 0;
if (container) {
var streamElement = VideoLayout.createStreamElement(sid, stream);
newElementId = streamElement.id;
container.appendChild(streamElement);
var sel = $('#' + newElementId);
sel.hide();
// If the container is currently visible we attach the stream.
if (!isVideo
|| (container.offsetParent !== null && isVideo)) {
var videoStream = APP.simulcast.getReceivingVideoStream(stream);
APP.RTC.attachMediaStream(sel, videoStream);
if (isVideo)
waitForRemoteVideo(sel, thessrc, stream, peerJid);
}
stream.onended = function () {
console.log('stream ended', this);
VideoLayout.removeRemoteStreamElement(
stream, isVideo, container);
// NOTE(gp) it seems that under certain circumstances, the
// onended event is not fired and thus the contact list is not
// updated.
//
// The onended event of a stream should be fired when the SSRCs
// corresponding to that stream are removed from the SDP; but
// this doesn't seem to always be the case, resulting in ghost
// contacts.
//
// In an attempt to fix the ghost contacts problem, I'm moving
// the removeContact() method call in app.js, inside the
// 'muc.left' event handler.
//if (peerJid)
// ContactList.removeContact(peerJid);
};
// Add click handler.
container.onclick = function (event) {
/*
* FIXME It turns out that videoThumb may not exist (if there is
* no actual video).
*/
var videoThumb = $('#' + container.id + '>video').get(0);
if (videoThumb) {
VideoLayout.handleVideoThumbClicked(
APP.RTC.getVideoSrc(videoThumb),
false,
Strophe.getResourceFromJid(peerJid));
}
event.stopPropagation();
event.preventDefault();
return false;
};
// Add hover handler
$(container).hover(
function() {
VideoLayout.showDisplayName(container.id, true);
},
function() {
var videoSrc = null;
if ($('#' + container.id + '>video')
&& $('#' + container.id + '>video').length > 0) {
videoSrc = APP.RTC.getVideoSrc($('#' + container.id + '>video').get(0));
}
// If the video has been "pinned" by the user we want to
// keep the display name on place.
if (!VideoLayout.isLargeVideoVisible()
|| videoSrc !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
VideoLayout.showDisplayName(container.id, false);
}
);
}
return newElementId;
};
/**
* Removes the remote stream element corresponding to the given stream and
* parent container.
*
* @param stream the stream
* @param isVideo <tt>true</tt> if given <tt>stream</tt> is a video one.
* @param container
*/
my.removeRemoteStreamElement = function (stream, isVideo, container) {
if (!container)
return;
var select = null;
var removedVideoSrc = null;
if (isVideo) {
select = $('#' + container.id + '>video');
removedVideoSrc = APP.RTC.getVideoSrc(select.get(0));
}
else
select = $('#' + container.id + '>audio');
// Mark video as removed to cancel waiting loop(if video is removed
// before has started)
select.removed = true;
select.remove();
var audioCount = $('#' + container.id + '>audio').length;
var videoCount = $('#' + container.id + '>video').length;
if (!audioCount && !videoCount) {
console.log("Remove whole user", container.id);
if(VideoLayout.connectionIndicators[container.id])
VideoLayout.connectionIndicators[container.id].remove();
// Remove whole container
container.remove();
UIUtil.playSoundNotification('userLeft');
VideoLayout.resizeThumbnails();
}
if (removedVideoSrc)
VideoLayout.updateRemovedVideo(removedVideoSrc);
};
/**
* Show/hide peer container for the given resourceJid.
*/
function showPeerContainer(resourceJid, state) {
var peerContainer = $('#participant_' + resourceJid);
if (!peerContainer)
return;
var isHide = state === 'hide';
var resizeThumbnails = false;
if (!isHide) {
if (!peerContainer.is(':visible')) {
resizeThumbnails = true;
peerContainer.show();
}
var jid = APP.xmpp.findJidFromResource(resourceJid);
if (state == 'show')
{
// peerContainer.css('-webkit-filter', '');
Avatar.showUserAvatar(jid, false);
}
else // if (state == 'avatar')
{
// peerContainer.css('-webkit-filter', 'grayscale(100%)');
Avatar.showUserAvatar(jid, true);
}
}
else if (peerContainer.is(':visible') && isHide)
{
resizeThumbnails = true;
peerContainer.hide();
if(VideoLayout.connectionIndicators['participant_' + resourceJid])
VideoLayout.connectionIndicators['participant_' + resourceJid].hide();
}
if (resizeThumbnails) {
VideoLayout.resizeThumbnails();
}
// We want to be able to pin a participant from the contact list, even
// if he's not in the lastN set!
// ContactList.setClickable(resourceJid, !isHide);
};
my.inputDisplayNameHandler = function (name) {
NicknameHandler.setNickname(name);
if (!$('#localDisplayName').is(":visible")) {
if (NicknameHandler.getNickname())
{
var meHTML = APP.translation.generateTranslatonHTML("me");
$('#localDisplayName').html(NicknameHandler.getNickname() + " (" + meHTML + ")");
}
else
{
var defaultHTML = APP.translation.generateTranslatonHTML(
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
$('#localDisplayName')
.html(defaultHTML);
}
$('#localDisplayName').show();
}
$('#editDisplayName').hide();
};
/**
* Shows/hides the display name on the remote video.
* @param videoSpanId the identifier of the video span element
* @param isShow indicates if the display name should be shown or hidden
*/
my.showDisplayName = function(videoSpanId, isShow) {
var nameSpan = $('#' + videoSpanId + '>span.displayname').get(0);
if (isShow) {
if (nameSpan && nameSpan.innerHTML && nameSpan.innerHTML.length)
nameSpan.setAttribute("style", "display:inline-block;");
}
else {
if (nameSpan)
nameSpan.setAttribute("style", "display:none;");
}
};
/**
* Shows the presence status message for the given video.
*/
my.setPresenceStatus = function (videoSpanId, statusMsg) {
if (!$('#' + videoSpanId).length) {
// No container
return;
}
var statusSpan = $('#' + videoSpanId + '>span.status');
if (!statusSpan.length) {
//Add status span
statusSpan = document.createElement('span');
statusSpan.className = 'status';
statusSpan.id = videoSpanId + '_status';
$('#' + videoSpanId)[0].appendChild(statusSpan);
statusSpan = $('#' + videoSpanId + '>span.status');
}
// Display status
if (statusMsg && statusMsg.length) {
$('#' + videoSpanId + '_status').text(statusMsg);
statusSpan.get(0).setAttribute("style", "display:inline-block;");
}
else {
// Hide
statusSpan.get(0).setAttribute("style", "display:none;");
}
};
/**
* Shows a visual indicator for the moderator of the conference.
*/
my.showModeratorIndicator = function () {
var isModerator = APP.xmpp.isModerator();
if (isModerator) {
var indicatorSpan = $('#localVideoContainer .focusindicator');
if (indicatorSpan.children().length === 0)
{
createModeratorIndicatorElement(indicatorSpan[0]);
//translates text in focus indicator
APP.translation.translateElement($('#localVideoContainer .focusindicator'));
}
}
var members = APP.xmpp.getMembers();
Object.keys(members).forEach(function (jid) {
if (Strophe.getResourceFromJid(jid) === 'focus') {
// Skip server side focus
return;
}
var resourceJid = Strophe.getResourceFromJid(jid);
var videoSpanId = 'participant_' + resourceJid;
var videoContainer = document.getElementById(videoSpanId);
if (!videoContainer) {
console.error("No video container for " + jid);
return;
}
var member = members[jid];
if (member.role === 'moderator') {
// Remove menu if peer is moderator
var menuSpan = $('#' + videoSpanId + '>span.remotevideomenu');
if (menuSpan.length) {
removeRemoteVideoMenu(videoSpanId);
}
// Show moderator indicator
var indicatorSpan
= $('#' + videoSpanId + ' .focusindicator');
if (!indicatorSpan || indicatorSpan.length === 0) {
indicatorSpan = document.createElement('span');
indicatorSpan.className = 'focusindicator';
videoContainer.appendChild(indicatorSpan);
createModeratorIndicatorElement(indicatorSpan);
//translates text in focus indicators
APP.translation.translateElement($('#' + videoSpanId + ' .focusindicator'));
}
} else if (isModerator) {
// We are moderator, but user is not - add menu
if ($('#remote_popupmenu_' + resourceJid).length <= 0) {
addRemoteVideoMenu(
jid,
document.getElementById('participant_' + resourceJid));
}
}
});
};
/**
* Shows video muted indicator over small videos.
*/
my.showVideoIndicator = function(videoSpanId, isMuted) {
var videoMutedSpan = $('#' + videoSpanId + '>span.videoMuted');
if (isMuted === 'false') {
if (videoMutedSpan.length > 0) {
videoMutedSpan.remove();
}
}
else {
if(videoMutedSpan.length == 0) {
videoMutedSpan = document.createElement('span');
videoMutedSpan.className = 'videoMuted';
$('#' + videoSpanId)[0].appendChild(videoMutedSpan);
var mutedIndicator = document.createElement('i');
mutedIndicator.className = 'icon-camera-disabled';
UIUtil.setTooltip(mutedIndicator,
"videothumbnail.videomute",
"top");
videoMutedSpan.appendChild(mutedIndicator);
//translate texts for muted indicator
APP.translation.translateElement($('#' + videoSpanId + " > span > i"));
}
VideoLayout.updateMutePosition(videoSpanId);
}
};
my.updateMutePosition = function (videoSpanId) {
var audioMutedSpan = $('#' + videoSpanId + '>span.audioMuted');
var connectionIndicator = $('#' + videoSpanId + '>div.connectionindicator');
var videoMutedSpan = $('#' + videoSpanId + '>span.videoMuted');
if(connectionIndicator.length > 0
&& connectionIndicator[0].style.display != "none") {
audioMutedSpan.css({right: "23px"});
videoMutedSpan.css({right: ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"});
}
else
{
audioMutedSpan.css({right: "0px"});
videoMutedSpan.css({right: (audioMutedSpan.length > 0? 30 : 0) + "px"});
}
}
/**
* Shows audio muted indicator over small videos.
* @param {string} isMuted
*/
my.showAudioIndicator = function(videoSpanId, isMuted) {
var audioMutedSpan = $('#' + videoSpanId + '>span.audioMuted');
if (isMuted === 'false') {
if (audioMutedSpan.length > 0) {
audioMutedSpan.popover('hide');
audioMutedSpan.remove();
}
}
else {
if(audioMutedSpan.length == 0 ) {
audioMutedSpan = document.createElement('span');
audioMutedSpan.className = 'audioMuted';
UIUtil.setTooltip(audioMutedSpan,
"videothumbnail.mute",
"top");
$('#' + videoSpanId)[0].appendChild(audioMutedSpan);
APP.translation.translateElement($('#' + videoSpanId + " > span"));
var mutedIndicator = document.createElement('i');
mutedIndicator.className = 'icon-mic-disabled';
audioMutedSpan.appendChild(mutedIndicator);
}
VideoLayout.updateMutePosition(videoSpanId);
}
};
/*
* Shows or hides the audio muted indicator over the local thumbnail video.
* @param {boolean} isMuted
*/
my.showLocalAudioIndicator = function(isMuted) {
VideoLayout.showAudioIndicator('localVideoContainer', isMuted.toString());
};
/**
* Resizes the large video container.
*/
my.resizeLargeVideoContainer = function () {
Chat.resizeChat();
var availableHeight = window.innerHeight;
var availableWidth = UIUtil.getAvailableVideoWidth();
if (availableWidth < 0 || availableHeight < 0) return;
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
$('#largeVideoContainer').width(availableWidth);
$('#largeVideoContainer').height(availableHeight);
var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
var top = availableHeight / 2 - avatarSize / 4 * 3;
$('#activeSpeaker').css('top', top);
VideoLayout.resizeThumbnails();
};
/**
* Resizes thumbnails.
*/
my.resizeThumbnails = function() {
var videoSpaceWidth = $('#remoteVideos').width();
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var width = thumbnailSize[0];
var height = thumbnailSize[1];
// size videos so that while keeping AR and max height, we have a
// nice fit
$('#remoteVideos').height(height);
$('#remoteVideos>span').width(width);
$('#remoteVideos>span').height(height);
$('.userAvatar').css('left', (width - height) / 2);
$(document).trigger("remotevideo.resized", [width, height]);
};
/**
* Enables the dominant speaker UI.
*
* @param resourceJid the jid indicating the video element to
* activate/deactivate
* @param isEnable indicates if the dominant speaker should be enabled or
* disabled
*/
my.enableDominantSpeaker = function(resourceJid, isEnable) {
var videoSpanId = null;
var videoContainerId = null;
if (resourceJid
=== APP.xmpp.myResource()) {
videoSpanId = 'localVideoWrapper';
videoContainerId = 'localVideoContainer';
}
else {
videoSpanId = 'participant_' + resourceJid;
videoContainerId = videoSpanId;
}
var displayName = resourceJid;
var nameSpan = $('#' + videoContainerId + '>span.displayname');
if (nameSpan.length > 0)
displayName = nameSpan.html();
console.log("UI enable dominant speaker",
displayName,
resourceJid,
isEnable);
videoSpan = document.getElementById(videoContainerId);
if (!videoSpan) {
return;
}
var video = $('#' + videoSpanId + '>video');
if (video && video.length > 0) {
if (isEnable) {
var isLargeVideoVisible = VideoLayout.isLargeVideoOnTop();
VideoLayout.showDisplayName(videoContainerId, isLargeVideoVisible);
if (!videoSpan.classList.contains("dominantspeaker"))
videoSpan.classList.add("dominantspeaker");
}
else {
VideoLayout.showDisplayName(videoContainerId, false);
if (videoSpan.classList.contains("dominantspeaker"))
videoSpan.classList.remove("dominantspeaker");
}
Avatar.showUserAvatar(
APP.xmpp.findJidFromResource(resourceJid));
}
};
/**
* Calculates the thumbnail size.
*
* @param videoSpaceWidth the width of the video space
*/
my.calculateThumbnailSize = function (videoSpaceWidth) {
// Calculate the available height, which is the inner window height minus
// 39px for the header minus 2px for the delimiter lines on the top and
// bottom of the large video, minus the 36px space inside the remoteVideos
// container used for highlighting shadow.
var availableHeight = 100;
var numvids = $('#remoteVideos>span:visible').length;
if (localLastNCount && localLastNCount > 0) {
numvids = Math.min(localLastNCount + 1, numvids);
}
// Remove the 3px borders arround videos and border around the remote
// videos area and the 4 pixels between the local video and the others
//TODO: Find out where the 4 pixels come from and remove them
var availableWinWidth = videoSpaceWidth - 2 * 3 * numvids - 70 - 4;
var availableWidth = availableWinWidth / numvids;
var aspectRatio = 16.0 / 9.0;
var maxHeight = Math.min(160, availableHeight);
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio);
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
return [availableWidth, availableHeight];
};
/**
* Updates the remote video menu.
*
* @param jid the jid indicating the video for which we're adding a menu.
* @param isMuted indicates the current mute state
*/
my.updateRemoteVideoMenu = function(jid, isMuted) {
var muteMenuItem
= $('#remote_popupmenu_'
+ Strophe.getResourceFromJid(jid)
+ '>li>a.mutelink');
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
if (muteMenuItem.length) {
var muteLink = muteMenuItem.get(0);
if (isMuted === 'true') {
muteLink.innerHTML = mutedIndicator + ' Muted';
muteLink.className = 'mutelink disabled';
}
else {
muteLink.innerHTML = mutedIndicator + ' Mute';
muteLink.className = 'mutelink';
}
}
};
/**
* Returns the current dominant speaker resource jid.
*/
my.getDominantSpeakerResourceJid = function () {
return currentDominantSpeaker;
};
/**
* Returns the corresponding resource jid to the given peer container
* DOM element.
*
* @return the corresponding resource jid to the given peer container
* DOM element
*/
my.getPeerContainerResourceJid = function (containerElement) {
var i = containerElement.id.indexOf('participant_');
if (i >= 0)
return containerElement.id.substring(i + 12);
};
/**
* On contact list item clicked.
*/
$(ContactList).bind('contactclicked', function(event, jid) {
if (!jid) {
return;
}
var resource = Strophe.getResourceFromJid(jid);
var videoContainer = $("#participant_" + resource);
if (videoContainer.length > 0) {
var videoThumb = $('video', videoContainer).get(0);
// It is not always the case that a videoThumb exists (if there is
// no actual video).
if (videoThumb) {
if (videoThumb.src && videoThumb.src != '') {
// We have a video src, great! Let's update the large video
// now.
VideoLayout.handleVideoThumbClicked(
videoThumb.src,
false,
Strophe.getResourceFromJid(jid));
} else {
// If we don't have a video src for jid, there's absolutely
// no point in calling handleVideoThumbClicked; Quite
// simply, it won't work because it needs an src to attach
// to the large video.
//
// Instead, we trigger the pinned endpoint changed event to
// let the bridge adjust its lastN set for myjid and store
// the pinned user in the lastNPickupJid variable to be
// picked up later by the lastN changed event handler.
lastNPickupJid = jid;
eventEmitter.emit(UIEvents.PINNED_ENDPOINT,
Strophe.getResourceFromJid(jid));
}
} else if (jid == APP.xmpp.myJid()) {
$("#localVideoContainer").click();
}
}
});
/**
* On audio muted event.
*/
$(document).bind('audiomuted.muc', function (event, jid, isMuted) {
/*
// FIXME: but focus can not mute in this case ? - check
if (jid === xmpp.myJid()) {
// The local mute indicator is controlled locally
return;
}*/
var videoSpanId = null;
if (jid === APP.xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid);
}
mutedAudios[jid] = isMuted;
if (APP.xmpp.isModerator()) {
VideoLayout.updateRemoteVideoMenu(jid, isMuted);
}
if (videoSpanId)
VideoLayout.showAudioIndicator(videoSpanId, isMuted);
});
/**
* On video muted event.
*/
$(document).bind('videomuted.muc', function (event, jid, value) {
var isMuted = (value === "true");
if(jid !== APP.xmpp.myJid() && !APP.RTC.muteRemoteVideoStream(jid, isMuted))
return;
Avatar.showUserAvatar(jid, isMuted);
var videoSpanId = null;
if (jid === APP.xmpp.myJid()) {
videoSpanId = 'localVideoContainer';
} else {
VideoLayout.ensurePeerContainerExists(jid);
videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid);
}
if (videoSpanId)
VideoLayout.showVideoIndicator(videoSpanId, value);
});
/**
* Display name changed.
*/
my.onDisplayNameChanged =
function (jid, displayName, status) {
if (jid === 'localVideoContainer'
|| jid === APP.xmpp.myJid()) {
setDisplayName('localVideoContainer',
displayName);
} else {
VideoLayout.ensurePeerContainerExists(jid);
setDisplayName(
'participant_' + Strophe.getResourceFromJid(jid),
displayName,
status);
}
};
/**
* On dominant speaker changed event.
*/
my.onDominantSpeakerChanged = function (resourceJid) {
// We ignore local user events.
if (resourceJid
=== APP.xmpp.myResource())
return;
var members = APP.xmpp.getMembers();
// Update the current dominant speaker.
if (resourceJid !== currentDominantSpeaker) {
var oldSpeakerVideoSpanId = "participant_" + currentDominantSpeaker,
newSpeakerVideoSpanId = "participant_" + resourceJid;
var currentJID = APP.xmpp.findJidFromResource(currentDominantSpeaker);
var newJID = APP.xmpp.findJidFromResource(resourceJid);
if(currentDominantSpeaker && (!members || !members[currentJID] ||
!members[currentJID].displayName)) {
setDisplayName(oldSpeakerVideoSpanId, null);
}
if(resourceJid && (!members || !members[newJID] ||
!members[newJID].displayName)) {
setDisplayName(newSpeakerVideoSpanId, null,
interfaceConfig.DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME);
}
currentDominantSpeaker = resourceJid;
} else {
return;
}
// Obtain container for new dominant speaker.
var container = document.getElementById(
'participant_' + resourceJid);
// Local video will not have container found, but that's ok
// since we don't want to switch to local video.
if (container && !focusedVideoInfo)
{
var video = container.getElementsByTagName("video");
// Update the large video if the video source is already available,
// otherwise wait for the "videoactive.jingle" event.
if (video.length && video[0].currentTime > 0)
VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(video[0]), resourceJid);
}
};
/**
* On last N change event.
*
* @param lastNEndpoints the list of last N endpoints
* @param endpointsEnteringLastN the list currently entering last N
* endpoints
*/
my.onLastNEndpointsChanged = function ( lastNEndpoints,
endpointsEnteringLastN,
stream) {
if (lastNCount !== lastNEndpoints.length)
lastNCount = lastNEndpoints.length;
lastNEndpointsCache = lastNEndpoints;
// Say A, B, C, D, E, and F are in a conference and LastN = 3.
//
// If LastN drops to, say, 2, because of adaptivity, then E should see
// thumbnails for A, B and C. A and B are in E's server side LastN set,
// so E sees them. C is only in E's local LastN set.
//
// If F starts talking and LastN = 3, then E should see thumbnails for
// F, A, B. B gets "ejected" from E's server side LastN set, but it
// enters E's local LastN ejecting C.
// Increase the local LastN set size, if necessary.
if (lastNCount > localLastNCount) {
localLastNCount = lastNCount;
}
// Update the local LastN set preserving the order in which the
// endpoints appeared in the LastN/local LastN set.
var nextLocalLastNSet = lastNEndpoints.slice(0);
for (var i = 0; i < localLastNSet.length; i++) {
if (nextLocalLastNSet.length >= localLastNCount) {
break;
}
var resourceJid = localLastNSet[i];
if (nextLocalLastNSet.indexOf(resourceJid) === -1) {
nextLocalLastNSet.push(resourceJid);
}
}
localLastNSet = nextLocalLastNSet;
var updateLargeVideo = false;
// Handle LastN/local LastN changes.
$('#remoteVideos>span').each(function( index, element ) {
var resourceJid = VideoLayout.getPeerContainerResourceJid(element);
var isReceived = true;
if (resourceJid
&& lastNEndpoints.indexOf(resourceJid) < 0
&& localLastNSet.indexOf(resourceJid) < 0) {
console.log("Remove from last N", resourceJid);
showPeerContainer(resourceJid, 'hide');
isReceived = false;
} else if (resourceJid
&& $('#participant_' + resourceJid).is(':visible')
&& lastNEndpoints.indexOf(resourceJid) < 0
&& localLastNSet.indexOf(resourceJid) >= 0) {
showPeerContainer(resourceJid, 'avatar');
isReceived = false;
}
if (!isReceived) {
// resourceJid has dropped out of the server side lastN set, so
// it is no longer being received. If resourceJid was being
// displayed in the large video we have to switch to another
// user.
var largeVideoResource = largeVideoState.userResourceJid;
if (!updateLargeVideo && resourceJid === largeVideoResource) {
updateLargeVideo = true;
}
}
});
if (!endpointsEnteringLastN || endpointsEnteringLastN.length < 0)
endpointsEnteringLastN = lastNEndpoints;
if (endpointsEnteringLastN && endpointsEnteringLastN.length > 0) {
endpointsEnteringLastN.forEach(function (resourceJid) {
var isVisible = $('#participant_' + resourceJid).is(':visible');
showPeerContainer(resourceJid, 'show');
if (!isVisible) {
console.log("Add to last N", resourceJid);
var jid = APP.xmpp.findJidFromResource(resourceJid);
var mediaStream = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
var sel = $('#participant_' + resourceJid + '>video');
var videoStream = APP.simulcast.getReceivingVideoStream(
mediaStream.stream);
APP.RTC.attachMediaStream(sel, videoStream);
if (lastNPickupJid == mediaStream.peerjid) {
// Clean up the lastN pickup jid.
lastNPickupJid = null;
// Don't fire the events again, they've already
// been fired in the contact list click handler.
VideoLayout.handleVideoThumbClicked(
$(sel).attr('src'),
false,
Strophe.getResourceFromJid(mediaStream.peerjid));
updateLargeVideo = false;
}
waitForRemoteVideo(sel, mediaStream.ssrc, mediaStream.stream, resourceJid);
}
})
}
// The endpoint that was being shown in the large video has dropped out
// of the lastN set and there was no lastN pickup jid. We need to update
// the large video now.
if (updateLargeVideo) {
var resource, container, src;
var myResource
= APP.xmpp.myResource();
// Find out which endpoint to show in the large video.
for (var i = 0; i < lastNEndpoints.length; i++) {
resource = lastNEndpoints[i];
if (!resource || resource === myResource)
continue;
container = $("#participant_" + resource);
if (container.length == 0)
continue;
src = $('video', container).attr('src');
if (!src)
continue;
// videoSrcToSsrc needs to be update for this call to succeed.
VideoLayout.updateLargeVideo(src);
break;
}
}
};
my.onSimulcastLayersChanging = function (endpointSimulcastLayers) {
endpointSimulcastLayers.forEach(function (esl) {
var resource = esl.endpoint;
// if lastN is enabled *and* the endpoint is *not* in the lastN set,
// then ignore the event (= do not preload anything).
//
// The bridge could probably stop sending this message if it's for
// an endpoint that's not in lastN.
if (lastNCount != -1
&& (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
return;
}
var primarySSRC = esl.simulcastLayer.primarySSRC;
// Get session and stream from primary ssrc.
var res = APP.simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
var sid = res.sid;
var electedStream = res.stream;
if (sid && electedStream) {
var msid = APP.simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
console.info([esl, primarySSRC, msid, sid, electedStream]);
var preload = (Strophe.getResourceFromJid(APP.xmpp.getJidFromSSRC(primarySSRC)) == largeVideoState.userResourceJid);
if (preload) {
if (largeVideoState.preload)
{
$(largeVideoState.preload).remove();
}
console.info('Preloading remote video');
largeVideoState.preload = $('<video autoplay></video>');
// ssrcs are unique in an rtp session
largeVideoState.preload_ssrc = primarySSRC;
APP.RTC.attachMediaStream(largeVideoState.preload, electedStream)
}
} else {
console.error('Could not find a stream or a session.', sid, electedStream);
}
});
};
/**
* On simulcast layers changed event.
*/
my.onSimulcastLayersChanged = function (endpointSimulcastLayers) {
endpointSimulcastLayers.forEach(function (esl) {
var resource = esl.endpoint;
// if lastN is enabled *and* the endpoint is *not* in the lastN set,
// then ignore the event (= do not change large video/thumbnail
// SRCs).
//
// Note that even if we ignore the "changed" event in this event
// handler, the bridge must continue sending these events because
// the simulcast code in simulcast.js uses it to know what's going
// to be streamed by the bridge when/if the endpoint gets back into
// the lastN set.
if (lastNCount != -1
&& (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
return;
}
var primarySSRC = esl.simulcastLayer.primarySSRC;
// Get session and stream from primary ssrc.
var res = APP.simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
var sid = res.sid;
var electedStream = res.stream;
if (sid && electedStream) {
var msid = APP.simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
console.info('Switching simulcast substream.');
console.info([esl, primarySSRC, msid, sid, electedStream]);
var msidParts = msid.split(' ');
var selRemoteVideo = $(['#', 'remoteVideo_', sid, '_', msidParts[0]].join(''));
var updateLargeVideo = (Strophe.getResourceFromJid(APP.xmpp.getJidFromSSRC(primarySSRC))
== largeVideoState.userResourceJid);
var updateFocusedVideoSrc = (focusedVideoInfo && focusedVideoInfo.src && focusedVideoInfo.src != '' &&
(APP.RTC.getVideoSrc(selRemoteVideo[0]) == focusedVideoInfo.src));
var electedStreamUrl;
if (largeVideoState.preload_ssrc == primarySSRC)
{
APP.RTC.setVideoSrc(selRemoteVideo[0], APP.RTC.getVideoSrc(largeVideoState.preload[0]));
}
else
{
if (largeVideoState.preload
&& largeVideoState.preload != null) {
$(largeVideoState.preload).remove();
}
largeVideoState.preload_ssrc = 0;
APP.RTC.attachMediaStream(selRemoteVideo, electedStream);
}
var jid = APP.xmpp.getJidFromSSRC(primarySSRC);
if (updateLargeVideo) {
VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(selRemoteVideo[0]), null,
Strophe.getResourceFromJid(jid));
}
if (updateFocusedVideoSrc) {
focusedVideoInfo.src = APP.RTC.getVideoSrc(selRemoteVideo[0]);
}
var videoId;
if(resource == APP.xmpp.myResource())
{
videoId = "localVideoContainer";
}
else
{
videoId = "participant_" + resource;
}
var connectionIndicator = VideoLayout.connectionIndicators[videoId];
if(connectionIndicator)
connectionIndicator.updatePopoverData();
} else {
console.error('Could not find a stream or a sid.', sid, electedStream);
}
});
};
/**
* Updates local stats
* @param percent
* @param object
*/
my.updateLocalConnectionStats = function (percent, object) {
var resolution = null;
if(object.resolution !== null)
{
resolution = object.resolution;
object.resolution = resolution[APP.xmpp.myJid()];
delete resolution[APP.xmpp.myJid()];
}
updateStatsIndicator("localVideoContainer", percent, object);
for(var jid in resolution)
{
if(resolution[jid] === null)
continue;
var id = 'participant_' + Strophe.getResourceFromJid(jid);
if(VideoLayout.connectionIndicators[id])
{
VideoLayout.connectionIndicators[id].updateResolution(resolution[jid]);
}
}
};
/**
* Updates remote stats.
* @param jid the jid associated with the stats
* @param percent the connection quality percent
* @param object the stats data
*/
my.updateConnectionStats = function (jid, percent, object) {
var resourceJid = Strophe.getResourceFromJid(jid);
var videoSpanId = 'participant_' + resourceJid;
updateStatsIndicator(videoSpanId, percent, object);
};
/**
* Removes the connection
* @param jid
*/
my.removeConnectionIndicator = function (jid) {
if(VideoLayout.connectionIndicators['participant_' + Strophe.getResourceFromJid(jid)])
VideoLayout.connectionIndicators['participant_' + Strophe.getResourceFromJid(jid)].remove();
};
/**
* Hides the connection indicator
* @param jid
*/
my.hideConnectionIndicator = function (jid) {
if(VideoLayout.connectionIndicators['participant_' + Strophe.getResourceFromJid(jid)])
VideoLayout.connectionIndicators['participant_' + Strophe.getResourceFromJid(jid)].hide();
};
/**
* Hides all the indicators
*/
my.onStatsStop = function () {
for(var indicator in VideoLayout.connectionIndicators)
{
VideoLayout.connectionIndicators[indicator].hideIndicator();
}
};
my.participantLeft = function (jid) {
// Unlock large video
if (focusedVideoInfo && focusedVideoInfo.jid === jid)
{
console.info("Focused video owner has left the conference");
focusedVideoInfo = null;
}
}
my.onVideoTypeChanged = function (jid) {
if(jid &&
Strophe.getResourceFromJid(jid) === largeVideoState.userResourceJid)
{
largeVideoState.isDesktop = APP.RTC.isVideoSrcDesktop(jid);
VideoLayout.getVideoSize = largeVideoState.isDesktop
? getDesktopVideoSize
: getCameraVideoSize;
VideoLayout.getVideoPosition = largeVideoState.isDesktop
? getDesktopVideoPosition
: getCameraVideoPosition;
VideoLayout.positionLarge(null, null);
}
}
return my;
}(VideoLayout || {}));
module.exports = VideoLayout;
},{"../../../service/RTC/MediaStreamTypes":87,"../../../service/UI/UIEvents":92,"../audio_levels/AudioLevels":9,"../avatar/Avatar":13,"../etherpad/Etherpad":14,"../prezi/Prezi":15,"../side_pannels/chat/Chat":18,"../side_pannels/contactlist/ContactList":22,"../util/NicknameHandler":29,"../util/UIUtil":30,"./ConnectionIndicator":31}],33:[function(require,module,exports){
//var nouns = [
//];
var pluralNouns = [
"Aliens", "Animals", "Antelopes", "Ants", "Apes", "Apples", "Baboons", "Bacteria", "Badgers", "Bananas", "Bats",
"Bears", "Birds", "Bonobos", "Brides", "Bugs", "Bulls", "Butterflies", "Cheetahs",
"Cherries", "Chicken", "Children", "Chimps", "Clowns", "Cows", "Creatures", "Dinosaurs", "Dogs", "Dolphins",
"Donkeys", "Dragons", "Ducks", "Dwarfs", "Eagles", "Elephants", "Elves", "FAIL", "Fathers",
"Fish", "Flowers", "Frogs", "Fruit", "Fungi", "Galaxies", "Geese", "Goats",
"Gorillas", "Hedgehogs", "Hippos", "Horses", "Hunters", "Insects", "Kids", "Knights",
"Lemons", "Lemurs", "Leopards", "LifeForms", "Lions", "Lizards", "Mice", "Monkeys", "Monsters",
"Mushrooms", "Octopodes", "Oranges", "Orangutans", "Organisms", "Pants", "Parrots", "Penguins",
"People", "Pigeons", "Pigs", "Pineapples", "Plants", "Potatoes", "Priests", "Rats", "Reptiles", "Reptilians",
"Rhinos", "Seagulls", "Sheep", "Siblings", "Snakes", "Spaghetti", "Spiders", "Squid", "Squirrels",
"Stars", "Students", "Teachers", "Tigers", "Tomatoes", "Trees", "Vampires", "Vegetables", "Viruses", "Vulcans",
"Warewolves", "Weasels", "Whales", "Witches", "Wizards", "Wolves", "Workers", "Worms", "Zebras"
];
//var places = [
//"Pub", "University", "Airport", "Library", "Mall", "Theater", "Stadium", "Office", "Show", "Gallows", "Beach",
// "Cemetery", "Hospital", "Reception", "Restaurant", "Bar", "Church", "House", "School", "Square", "Village",
// "Cinema", "Movies", "Party", "Restroom", "End", "Jail", "PostOffice", "Station", "Circus", "Gates", "Entrance",
// "Bridge"
//];
var verbs = [
"Abandon", "Adapt", "Advertise", "Answer", "Anticipate", "Appreciate",
"Approach", "Argue", "Ask", "Bite", "Blossom", "Blush", "Breathe", "Breed", "Bribe", "Burn", "Calculate",
"Clean", "Code", "Communicate", "Compute", "Confess", "Confiscate", "Conjugate", "Conjure", "Consume",
"Contemplate", "Crawl", "Dance", "Delegate", "Devour", "Develop", "Differ", "Discuss",
"Dissolve", "Drink", "Eat", "Elaborate", "Emancipate", "Estimate", "Expire", "Extinguish",
"Extract", "FAIL", "Facilitate", "Fall", "Feed", "Finish", "Floss", "Fly", "Follow", "Fragment", "Freeze",
"Gather", "Glow", "Grow", "Hex", "Hide", "Hug", "Hurry", "Improve", "Intersect", "Investigate", "Jinx",
"Joke", "Jubilate", "Kiss", "Laugh", "Manage", "Meet", "Merge", "Move", "Object", "Observe", "Offer",
"Paint", "Participate", "Party", "Perform", "Plan", "Pursue", "Pierce", "Play", "Postpone", "Pray", "Proclaim",
"Question", "Read", "Reckon", "Rejoice", "Represent", "Resize", "Rhyme", "Scream", "Search", "Select", "Share", "Shoot",
"Shout", "Signal", "Sing", "Skate", "Sleep", "Smile", "Smoke", "Solve", "Spell", "Steer", "Stink",
"Substitute", "Swim", "Taste", "Teach", "Terminate", "Think", "Type", "Unite", "Vanish", "Worship"
];
var adverbs = [
"Absently", "Accurately", "Accusingly", "Adorably", "AllTheTime", "Alone", "Always", "Amazingly", "Angrily",
"Anxiously", "Anywhere", "Appallingly", "Apparently", "Articulately", "Astonishingly", "Badly", "Barely",
"Beautifully", "Blindly", "Bravely", "Brightly", "Briskly", "Brutally", "Calmly", "Carefully", "Casually",
"Cautiously", "Cleverly", "Constantly", "Correctly", "Crazily", "Curiously", "Cynically", "Daily",
"Dangerously", "Deliberately", "Delicately", "Desperately", "Discreetly", "Eagerly", "Easily", "Euphoricly",
"Evenly", "Everywhere", "Exactly", "Expectantly", "Extensively", "FAIL", "Ferociously", "Fiercely", "Finely",
"Flatly", "Frequently", "Frighteningly", "Gently", "Gloriously", "Grimly", "Guiltily", "Happily",
"Hard", "Hastily", "Heroically", "High", "Highly", "Hourly", "Humbly", "Hysterically", "Immensely",
"Impartially", "Impolitely", "Indifferently", "Intensely", "Jealously", "Jovially", "Kindly", "Lazily",
"Lightly", "Loudly", "Lovingly", "Loyally", "Magnificently", "Malevolently", "Merrily", "Mightily", "Miserably",
"Mysteriously", "NOT", "Nervously", "Nicely", "Nowhere", "Objectively", "Obnoxiously", "Obsessively",
"Obviously", "Often", "Painfully", "Patiently", "Playfully", "Politely", "Poorly", "Precisely", "Promptly",
"Quickly", "Quietly", "Randomly", "Rapidly", "Rarely", "Recklessly", "Regularly", "Remorsefully", "Responsibly",
"Rudely", "Ruthlessly", "Sadly", "Scornfully", "Seamlessly", "Seldom", "Selfishly", "Seriously", "Shakily",
"Sharply", "Sideways", "Silently", "Sleepily", "Slightly", "Slowly", "Slyly", "Smoothly", "Softly", "Solemnly", "Steadily", "Sternly", "Strangely", "Strongly", "Stunningly", "Surely", "Tenderly", "Thoughtfully",
"Tightly", "Uneasily", "Vanishingly", "Violently", "Warmly", "Weakly", "Wearily", "Weekly", "Weirdly", "Well",
"Well", "Wickedly", "Wildly", "Wisely", "Wonderfully", "Yearly"
];
var adjectives = [
"Abominable", "Accurate", "Adorable", "All", "Alleged", "Ancient", "Angry", "Angry", "Anxious", "Appalling",
"Apparent", "Astonishing", "Attractive", "Awesome", "Baby", "Bad", "Beautiful", "Benign", "Big", "Bitter",
"Blind", "Blue", "Bold", "Brave", "Bright", "Brisk", "Calm", "Camouflaged", "Casual", "Cautious",
"Choppy", "Chosen", "Clever", "Cold", "Cool", "Crawly", "Crazy", "Creepy", "Cruel", "Curious", "Cynical",
"Dangerous", "Dark", "Delicate", "Desperate", "Difficult", "Discreet", "Disguised", "Dizzy",
"Dumb", "Eager", "Easy", "Edgy", "Electric", "Elegant", "Emancipated", "Enormous", "Euphoric", "Evil",
"FAIL", "Fast", "Ferocious", "Fierce", "Fine", "Flawed", "Flying", "Foolish", "Foxy",
"Freezing", "Funny", "Furious", "Gentle", "Glorious", "Golden", "Good", "Green", "Green", "Guilty",
"Hairy", "Happy", "Hard", "Hasty", "Hazy", "Heroic", "Hostile", "Hot", "Humble", "Humongous",
"Humorous", "Hysterical", "Idealistic", "Ignorant", "Immense", "Impartial", "Impolite", "Indifferent",
"Infuriated", "Insightful", "Intense", "Interesting", "Intimidated", "Intriguing", "Jealous", "Jolly", "Jovial",
"Jumpy", "Kind", "Laughing", "Lazy", "Liquid", "Lonely", "Longing", "Loud", "Loving", "Loyal", "Macabre", "Mad",
"Magical", "Magnificent", "Malevolent", "Medieval", "Memorable", "Mere", "Merry", "Mighty",
"Mischievous", "Miserable", "Modified", "Moody", "Most", "Mysterious", "Mystical", "Needy",
"Nervous", "Nice", "Objective", "Obnoxious", "Obsessive", "Obvious", "Opinionated", "Orange",
"Painful", "Passionate", "Perfect", "Pink", "Playful", "Poisonous", "Polite", "Poor", "Popular", "Powerful",
"Precise", "Preserved", "Pretty", "Purple", "Quick", "Quiet", "Random", "Rapid", "Rare", "Real",
"Reassuring", "Reckless", "Red", "Regular", "Remorseful", "Responsible", "Rich", "Rude", "Ruthless",
"Sad", "Scared", "Scary", "Scornful", "Screaming", "Selfish", "Serious", "Shady", "Shaky", "Sharp",
"Shiny", "Shy", "Simple", "Sleepy", "Slow", "Sly", "Small", "Smart", "Smelly", "Smiling", "Smooth",
"Smug", "Sober", "Soft", "Solemn", "Square", "Square", "Steady", "Strange", "Strong",
"Stunning", "Subjective", "Successful", "Surly", "Sweet", "Tactful", "Tense",
"Thoughtful", "Tight", "Tiny", "Tolerant", "Uneasy", "Unique", "Unseen", "Warm", "Weak",
"Weird", "WellCooked", "Wild", "Wise", "Witty", "Wonderful", "Worried", "Yellow", "Young",
"Zealous"
];
//var pronouns = [
//];
//var conjunctions = [
//"And", "Or", "For", "Above", "Before", "Against", "Between"
//];
/*
* Maps a string (category name) to the array of words from that category.
*/
var CATEGORIES =
{
//"_NOUN_": nouns,
"_PLURALNOUN_": pluralNouns,
//"_PLACE_": places,
"_VERB_": verbs,
"_ADVERB_": adverbs,
"_ADJECTIVE_": adjectives
//"_PRONOUN_": pronouns,
//"_CONJUNCTION_": conjunctions,
};
var PATTERNS = [
"_ADJECTIVE__PLURALNOUN__VERB__ADVERB_"
// BeautifulFungiOrSpaghetti
//"_ADJECTIVE__PLURALNOUN__CONJUNCTION__PLURALNOUN_",
// AmazinglyScaryToy
//"_ADVERB__ADJECTIVE__NOUN_",
// NeitherTrashNorRifle
//"Neither_NOUN_Nor_NOUN_",
//"Either_NOUN_Or_NOUN_",
// EitherCopulateOrInvestigate
//"Either_VERB_Or_VERB_",
//"Neither_VERB_Nor_VERB_",
//"The_ADJECTIVE__ADJECTIVE__NOUN_",
//"The_ADVERB__ADJECTIVE__NOUN_",
//"The_ADVERB__ADJECTIVE__NOUN_s",
//"The_ADVERB__ADJECTIVE__PLURALNOUN__VERB_",
// WolvesComputeBadly
//"_PLURALNOUN__VERB__ADVERB_",
// UniteFacilitateAndMerge
//"_VERB__VERB_And_VERB_",
//NastyWitchesAtThePub
//"_ADJECTIVE__PLURALNOUN_AtThe_PLACE_",
];
/*
* Returns a random element from the array 'arr'
*/
function randomElement(arr)
{
return arr[Math.floor(Math.random() * arr.length)];
}
/*
* Returns true if the string 's' contains one of the
* template strings.
*/
function hasTemplate(s)
{
for (var template in CATEGORIES){
if (s.indexOf(template) >= 0){
return true;
}
}
}
/**
* Generates new room name.
*/
var RoomNameGenerator = {
generateRoomWithoutSeparator: function()
{
// Note that if more than one pattern is available, the choice of 'name' won't be random (names from patterns
// with fewer options will have higher probability of being chosen that names from patterns with more options).
var name = randomElement(PATTERNS);
var word;
while (hasTemplate(name)){
for (var template in CATEGORIES){
word = randomElement(CATEGORIES[template]);
name = name.replace(template, word);
}
}
return name;
}
}
module.exports = RoomNameGenerator;
},{}],34:[function(require,module,exports){
var animateTimeout, updateTimeout;
var RoomNameGenerator = require("./RoomnameGenerator");
function enter_room()
{
var val = $("#enter_room_field").val();
if(!val) {
val = $("#enter_room_field").attr("room_name");
}
if (val) {
window.location.pathname = "/" + val;
}
}
function animate(word) {
var currentVal = $("#enter_room_field").attr("placeholder");
$("#enter_room_field").attr("placeholder", currentVal + word.substr(0, 1));
animateTimeout = setTimeout(function() {
animate(word.substring(1, word.length))
}, 70);
}
function update_roomname()
{
var word = RoomNameGenerator.generateRoomWithoutSeparator();
$("#enter_room_field").attr("room_name", word);
$("#enter_room_field").attr("placeholder", "");
clearTimeout(animateTimeout);
animate(word);
updateTimeout = setTimeout(update_roomname, 10000);
}
function setupWelcomePage()
{
$("#videoconference_page").hide();
$("#domain_name").text(
window.location.protocol + "//" + window.location.host + "/");
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
var leftWatermarkDiv
= $("#welcome_page_header div[class='watermark leftwatermark']");
if(leftWatermarkDiv && leftWatermarkDiv.length > 0)
{
leftWatermarkDiv.css({display: 'block'});
leftWatermarkDiv.parent().get(0).href
= interfaceConfig.JITSI_WATERMARK_LINK;
}
}
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
var rightWatermarkDiv
= $("#welcome_page_header div[class='watermark rightwatermark']");
if(rightWatermarkDiv && rightWatermarkDiv.length > 0) {
rightWatermarkDiv.css({display: 'block'});
rightWatermarkDiv.parent().get(0).href
= interfaceConfig.BRAND_WATERMARK_LINK;
rightWatermarkDiv.get(0).style.backgroundImage
= "url(images/rightwatermark.png)";
}
}
if (interfaceConfig.SHOW_POWERED_BY) {
$("#welcome_page_header>a[class='poweredby']")
.css({display: 'block'});
}
$("#enter_room_button").click(function()
{
enter_room();
});
$("#enter_room_field").keydown(function (event) {
if (event.keyCode === 13 /* enter */) {
enter_room();
}
});
if (!(interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE === false)){
var updateTimeout;
var animateTimeout;
$("#reload_roomname").click(function () {
clearTimeout(updateTimeout);
clearTimeout(animateTimeout);
update_roomname();
});
$("#reload_roomname").show();
update_roomname();
}
$("#disable_welcome").click(function () {
window.localStorage.welcomePageDisabled
= $("#disable_welcome").is(":checked");
});
}
module.exports = setupWelcomePage;
},{"./RoomnameGenerator":33}],35:[function(require,module,exports){
var EventEmitter = require("events");
var eventEmitter = new EventEmitter();
var CQEvents = require("../../service/connectionquality/CQEvents");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
/**
* local stats
* @type {{}}
*/
var stats = {};
/**
* remote stats
* @type {{}}
*/
var remoteStats = {};
/**
* Interval for sending statistics to other participants
* @type {null}
*/
var sendIntervalId = null;
/**
* Start statistics sending.
*/
function startSendingStats() {
sendStats();
sendIntervalId = setInterval(sendStats, 10000);
}
/**
* Sends statistics to other participants
*/
function sendStats() {
APP.xmpp.addToPresence("connectionQuality", convertToMUCStats(stats));
}
/**
* Converts statistics to format for sending through XMPP
* @param stats the statistics
* @returns {{bitrate_donwload: *, bitrate_uplpoad: *, packetLoss_total: *, packetLoss_download: *, packetLoss_upload: *}}
*/
function convertToMUCStats(stats) {
return {
"bitrate_download": stats.bitrate.download,
"bitrate_upload": stats.bitrate.upload,
"packetLoss_total": stats.packetLoss.total,
"packetLoss_download": stats.packetLoss.download,
"packetLoss_upload": stats.packetLoss.upload
};
}
/**
* Converts statitistics to format used by VideoLayout
* @param stats
* @returns {{bitrate: {download: *, upload: *}, packetLoss: {total: *, download: *, upload: *}}}
*/
function parseMUCStats(stats) {
return {
bitrate: {
download: stats.bitrate_download,
upload: stats.bitrate_upload
},
packetLoss: {
total: stats.packetLoss_total,
download: stats.packetLoss_download,
upload: stats.packetLoss_upload
}
};
}
var ConnectionQuality = {
init: function () {
APP.xmpp.addListener(XMPPEvents.REMOTE_STATS, this.updateRemoteStats);
APP.statistics.addConnectionStatsListener(this.updateLocalStats);
APP.statistics.addRemoteStatsStopListener(this.stopSendingStats);
},
/**
* Updates the local statistics
* @param data new statistics
*/
updateLocalStats: function (data) {
stats = data;
eventEmitter.emit(CQEvents.LOCALSTATS_UPDATED, 100 - stats.packetLoss.total, stats);
if (sendIntervalId == null) {
startSendingStats();
}
},
/**
* Updates remote statistics
* @param jid the jid associated with the statistics
* @param data the statistics
*/
updateRemoteStats: function (jid, data) {
if (data == null || data.packetLoss_total == null) {
eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, jid, null, null);
return;
}
remoteStats[jid] = parseMUCStats(data);
eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED,
jid, 100 - data.packetLoss_total, remoteStats[jid]);
},
/**
* Stops statistics sending.
*/
stopSendingStats: function () {
clearInterval(sendIntervalId);
sendIntervalId = null;
//notify UI about stopping statistics gathering
eventEmitter.emit(CQEvents.STOP);
},
/**
* Returns the local statistics.
*/
getStats: function () {
return stats;
},
addListener: function (type, listener) {
eventEmitter.on(type, listener);
}
};
module.exports = ConnectionQuality;
},{"../../service/connectionquality/CQEvents":94,"../../service/xmpp/XMPPEvents":97,"events":98}],36:[function(require,module,exports){
/* global $, alert, APP, changeLocalVideo, chrome, config, getConferenceHandler,
getUserMediaWithConstraints */
/**
* Indicates that desktop stream is currently in use(for toggle purpose).
* @type {boolean}
*/
var isUsingScreenStream = false;
/**
* Indicates that switch stream operation is in progress and prevent from
* triggering new events.
* @type {boolean}
*/
var switchInProgress = false;
/**
* Method used to get screen sharing stream.
*
* @type {function (stream_callback, failure_callback}
*/
var obtainDesktopStream = null;
/**
* Indicates whether desktop sharing extension is installed.
* @type {boolean}
*/
var extInstalled = false;
/**
* Indicates whether update of desktop sharing extension is required.
* @type {boolean}
*/
var extUpdateRequired = false;
/**
* Flag used to cache desktop sharing enabled state. Do not use directly as
* it can be <tt>null</tt>.
*
* @type {null|boolean}
*/
var _desktopSharingEnabled = null;
var EventEmitter = require("events");
var eventEmitter = new EventEmitter();
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
/**
* Method obtains desktop stream from WebRTC 'screen' source.
* Flag 'chrome://flags/#enable-usermedia-screen-capture' must be enabled.
*/
function obtainWebRTCScreen(streamCallback, failCallback) {
APP.RTC.getUserMediaWithConstraints(
['screen'],
streamCallback,
failCallback
);
}
/**
* Constructs inline install URL for Chrome desktop streaming extension.
* The 'chromeExtensionId' must be defined in config.js.
* @returns {string}
*/
function getWebStoreInstallUrl()
{
return "https://chrome.google.com/webstore/detail/" +
config.chromeExtensionId;
}
/**
* Checks whether extension update is required.
* @param minVersion minimal required version
* @param extVersion current extension version
* @returns {boolean}
*/
function isUpdateRequired(minVersion, extVersion)
{
try
{
var s1 = minVersion.split('.');
var s2 = extVersion.split('.');
var len = Math.max(s1.length, s2.length);
for (var i = 0; i < len; i++)
{
var n1 = 0,
n2 = 0;
if (i < s1.length)
n1 = parseInt(s1[i]);
if (i < s2.length)
n2 = parseInt(s2[i]);
if (isNaN(n1) || isNaN(n2))
{
return true;
}
else if (n1 !== n2)
{
return n1 > n2;
}
}
// will happen if boths version has identical numbers in
// their components (even if one of them is longer, has more components)
return false;
}
catch (e)
{
console.error("Failed to parse extension version", e);
APP.UI.messageHandler.showError("dialog.error",
"dialog.detectext");
return true;
}
}
function checkExtInstalled(callback) {
if (!chrome.runtime) {
// No API, so no extension for sure
callback(false, false);
return;
}
chrome.runtime.sendMessage(
config.chromeExtensionId,
{ getVersion: true },
function (response) {
if (!response || !response.version) {
// Communication failure - assume that no endpoint exists
console.warn(
"Extension not installed?: ", chrome.runtime.lastError);
callback(false, false);
return;
}
// Check installed extension version
var extVersion = response.version;
console.log('Extension version is: ' + extVersion);
var updateRequired
= isUpdateRequired(config.minChromeExtVersion, extVersion);
callback(!updateRequired, updateRequired);
}
);
}
function doGetStreamFromExtension(streamCallback, failCallback) {
// Sends 'getStream' msg to the extension.
// Extension id must be defined in the config.
chrome.runtime.sendMessage(
config.chromeExtensionId,
{ getStream: true, sources: config.desktopSharingSources },
function (response) {
if (!response) {
failCallback(chrome.runtime.lastError);
return;
}
console.log("Response from extension: " + response);
if (response.streamId) {
APP.RTC.getUserMediaWithConstraints(
['desktop'],
function (stream) {
streamCallback(stream);
},
failCallback,
null, null, null,
response.streamId);
} else {
failCallback("Extension failed to get the stream");
}
}
);
}
/**
* Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop'
* stream for returned stream token.
*/
function obtainScreenFromExtension(streamCallback, failCallback) {
if (extInstalled) {
doGetStreamFromExtension(streamCallback, failCallback);
} else {
if (extUpdateRequired) {
alert(
'Jitsi Desktop Streamer requires update. ' +
'Changes will take effect after next Chrome restart.');
}
chrome.webstore.install(
getWebStoreInstallUrl(),
function (arg) {
console.log("Extension installed successfully", arg);
// We need to reload the page in order to get the access to
// chrome.runtime
window.location.reload(false);
},
function (arg) {
console.log("Failed to install the extension", arg);
failCallback(arg);
APP.UI.messageHandler.showError("dialog.error",
"dialog.failtoinstall");
}
);
}
}
/**
* Call this method to toggle desktop sharing feature.
* @param method pass "ext" to use chrome extension for desktop capture(chrome
* extension required), pass "webrtc" to use WebRTC "screen" desktop
* source('chrome://flags/#enable-usermedia-screen-capture' must be
* enabled), pass any other string or nothing in order to disable this
* feature completely.
*/
function setDesktopSharing(method) {
// Check if we are running chrome
if (!navigator.webkitGetUserMedia) {
obtainDesktopStream = null;
console.info("Desktop sharing disabled");
} else if (method == "ext") {
obtainDesktopStream = obtainScreenFromExtension;
console.info("Using Chrome extension for desktop sharing");
} else if (method == "webrtc") {
obtainDesktopStream = obtainWebRTCScreen;
console.info("Using Chrome WebRTC for desktop sharing");
}
// Reset enabled cache
_desktopSharingEnabled = null;
}
/**
* Initializes <link rel=chrome-webstore-item /> with extension id set in
* config.js to support inline installs. Host site must be selected as main
* website of published extension.
*/
function initInlineInstalls()
{
$("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
}
function getVideoStreamFailed(error) {
console.error("Failed to obtain the stream to switch to", error);
switchInProgress = false;
isUsingScreenStream = false;
newStreamCreated(null);
}
function getDesktopStreamFailed(error) {
console.error("Failed to obtain the stream to switch to", error);
switchInProgress = false;
}
function streamSwitchDone() {
switchInProgress = false;
eventEmitter.emit(
DesktopSharingEventTypes.SWITCHING_DONE,
isUsingScreenStream);
}
function newStreamCreated(stream)
{
eventEmitter.emit(DesktopSharingEventTypes.NEW_STREAM_CREATED,
stream, isUsingScreenStream, streamSwitchDone);
}
module.exports = {
isUsingScreenStream: function () {
return isUsingScreenStream;
},
/**
* @returns {boolean} <tt>true</tt> if desktop sharing feature is available
* and enabled.
*/
isDesktopSharingEnabled: function () {
if (_desktopSharingEnabled === null) {
if (obtainDesktopStream === obtainScreenFromExtension) {
// Parse chrome version
var userAgent = navigator.userAgent.toLowerCase();
// We can assume that user agent is chrome, because it's
// enforced when 'ext' streaming method is set
var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
console.log("Chrome version" + userAgent, ver);
_desktopSharingEnabled = ver >= 34;
} else {
_desktopSharingEnabled =
obtainDesktopStream === obtainWebRTCScreen;
}
}
return _desktopSharingEnabled;
},
init: function () {
setDesktopSharing(config.desktopSharing);
// Initialize Chrome extension inline installs
if (config.chromeExtensionId) {
initInlineInstalls();
// Check if extension is installed
checkExtInstalled(function (installed, updateRequired) {
extInstalled = installed;
extUpdateRequired = updateRequired;
console.info(
"Chrome extension installed: " + extInstalled +
" updateRequired: " + extUpdateRequired);
});
}
eventEmitter.emit(DesktopSharingEventTypes.INIT);
},
addListener: function (listener, type)
{
eventEmitter.on(type, listener);
},
removeListener: function (listener, type) {
eventEmitter.removeListener(type, listener);
},
/*
* Toggles screen sharing.
*/
toggleScreenSharing: function () {
if (switchInProgress || !obtainDesktopStream) {
console.warn("Switch in progress or no method defined");
return;
}
switchInProgress = true;
if (!isUsingScreenStream)
{
// Switch to desktop stream
obtainDesktopStream(
function (stream) {
// We now use screen stream
isUsingScreenStream = true;
// Hook 'ended' event to restore camera
// when screen stream stops
stream.addEventListener('ended',
function (e) {
if (!switchInProgress && isUsingScreenStream) {
APP.desktopsharing.toggleScreenSharing();
}
}
);
newStreamCreated(stream);
},
getDesktopStreamFailed);
} else {
// Disable screen stream
APP.RTC.getUserMediaWithConstraints(
['video'],
function (stream) {
// We are now using camera stream
isUsingScreenStream = false;
newStreamCreated(stream);
},
getVideoStreamFailed, config.resolution || '360'
);
}
}
};
},{"../../service/desktopsharing/DesktopSharingEventTypes":95,"events":98}],37:[function(require,module,exports){
//maps keycode to character, id of popover for given function and function
var shortcuts = {
67: {
character: "C",
id: "toggleChatPopover",
function: APP.UI.toggleChat
},
70: {
character: "F",
id: "filmstripPopover",
function: APP.UI.toggleFilmStrip
},
77: {
character: "M",
id: "mutePopover",
function: APP.UI.toggleAudio
},
84: {
character: "T",
function: function() {
if(!APP.RTC.localAudio.isMuted()) {
APP.UI.toggleAudio();
}
}
},
86: {
character: "V",
id: "toggleVideoPopover",
function: APP.UI.toggleVideo
}
};
var KeyboardShortcut = {
init: function () {
window.onkeyup = function(e) {
var keycode = e.which;
if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) {
if (typeof shortcuts[keycode] === "object") {
shortcuts[keycode].function();
}
else if (keycode >= "0".charCodeAt(0) &&
keycode <= "9".charCodeAt(0)) {
APP.UI.clickOnVideo(keycode - "0".charCodeAt(0) + 1);
}
//esc while the smileys are visible hides them
} else if (keycode === 27 && $('#smileysContainer').is(':visible')) {
APP.UI.toggleSmileys();
}
};
window.onkeydown = function(e) {
if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) {
if(e.which === "T".charCodeAt(0)) {
if(APP.RTC.localAudio.isMuted()) {
APP.UI.toggleAudio();
}
}
}
};
var self = this;
$('body').popover({ selector: '[data-toggle=popover]',
trigger: 'click hover',
content: function() {
return this.getAttribute("content") +
self.getShortcut(this.getAttribute("shortcut"));
}
});
},
/**
*
* @param id indicates the popover associated with the shortcut
* @returns {string} the keyboard shortcut used for the id given
*/
getShortcut: function (id) {
for (var keycode in shortcuts) {
if (shortcuts.hasOwnProperty(keycode)) {
if (shortcuts[keycode].id === id) {
return " (" + shortcuts[keycode].character + ")";
}
}
}
return "";
}
};
module.exports = KeyboardShortcut;
},{}],38:[function(require,module,exports){
var email = '';
var displayName = '';
var userId;
var language = null;
function supportsLocalStorage() {
try {
return 'localStorage' in window && window.localStorage !== null;
} catch (e) {
console.log("localstorage is not supported");
return false;
}
}
function generateUniqueId() {
function _p8() {
return (Math.random().toString(16) + "000000000").substr(2, 8);
}
return _p8() + _p8() + _p8() + _p8();
}
if (supportsLocalStorage()) {
if (!window.localStorage.jitsiMeetId) {
window.localStorage.jitsiMeetId = generateUniqueId();
console.log("generated id", window.localStorage.jitsiMeetId);
}
userId = window.localStorage.jitsiMeetId || '';
email = window.localStorage.email || '';
displayName = window.localStorage.displayname || '';
language = window.localStorage.language;
} else {
console.log("local storage is not supported");
userId = generateUniqueId();
}
var Settings =
{
setDisplayName: function (newDisplayName) {
displayName = newDisplayName;
window.localStorage.displayname = displayName;
return displayName;
},
setEmail: function (newEmail)
{
email = newEmail;
window.localStorage.email = newEmail;
return email;
},
getSettings: function () {
return {
email: email,
displayName: displayName,
uid: userId,
language: language
};
},
setLanguage: function (lang) {
language = lang;
window.localStorage.language = lang;
}
};
module.exports = Settings;
},{}],39:[function(require,module,exports){
/**
*
* @constructor
*/
function SimulcastLogger(name, lvl) {
this.name = name;
this.lvl = lvl;
}
SimulcastLogger.prototype.log = function (text) {
if (this.lvl) {
console.log(text);
}
};
SimulcastLogger.prototype.info = function (text) {
if (this.lvl > 1) {
console.info(text);
}
};
SimulcastLogger.prototype.fine = function (text) {
if (this.lvl > 2) {
console.log(text);
}
};
SimulcastLogger.prototype.error = function (text) {
console.error(text);
};
module.exports = SimulcastLogger;
},{}],40:[function(require,module,exports){
var SimulcastLogger = require("./SimulcastLogger");
var SimulcastUtils = require("./SimulcastUtils");
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
function SimulcastReceiver() {
this.simulcastUtils = new SimulcastUtils();
this.logger = new SimulcastLogger('SimulcastReceiver', 1);
}
SimulcastReceiver.prototype._remoteVideoSourceCache = '';
SimulcastReceiver.prototype._remoteMaps = {
msid2Quality: {},
ssrc2Msid: {},
msid2ssrc: {},
receivingVideoStreams: {}
};
SimulcastReceiver.prototype._cacheRemoteVideoSources = function (lines) {
this._remoteVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
};
SimulcastReceiver.prototype._restoreRemoteVideoSources = function (lines) {
this.simulcastUtils._replaceVideoSources(lines, this._remoteVideoSourceCache);
};
SimulcastReceiver.prototype._ensureGoogConference = function (lines) {
var sb;
this.logger.info('Ensuring x-google-conference flag...')
if (this.simulcastUtils._indexOfArray('a=x-google-flag:conference', lines) === this.simulcastUtils._emptyCompoundIndex) {
// TODO(gp) do that for the audio as well as suggested by fippo.
// Add the google conference flag
sb = this.simulcastUtils._getVideoSources(lines);
sb = ['a=x-google-flag:conference'].concat(sb);
this.simulcastUtils._replaceVideoSources(lines, sb);
}
};
SimulcastReceiver.prototype._restoreSimulcastGroups = function (sb) {
this._restoreRemoteVideoSources(sb);
};
/**
* Restores the simulcast groups of the remote description. In
* transformRemoteDescription we remove those in order for the set remote
* description to succeed. The focus needs the signal the groups to new
* participants.
*
* @param desc
* @returns {*}
*/
SimulcastReceiver.prototype.reverseTransformRemoteDescription = function (desc) {
var sb;
if (!this.simulcastUtils.isValidDescription(desc)) {
return desc;
}
if (config.enableSimulcast) {
sb = desc.sdp.split('\r\n');
this._restoreSimulcastGroups(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
}
return desc;
};
SimulcastUtils.prototype._ensureOrder = function (lines) {
var videoSources, sb;
videoSources = this.parseMedia(lines, ['video'])[0];
sb = this._compileVideoSources(videoSources);
this._replaceVideoSources(lines, sb);
};
SimulcastReceiver.prototype._updateRemoteMaps = function (lines) {
var remoteVideoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0],
videoSource, quality;
// (re) initialize the remote maps.
this._remoteMaps.msid2Quality = {};
this._remoteMaps.ssrc2Msid = {};
this._remoteMaps.msid2ssrc = {};
var self = this;
if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
remoteVideoSources.groups.forEach(function (group) {
if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
quality = 0;
group.ssrcs.forEach(function (ssrc) {
videoSource = remoteVideoSources.sources[ssrc];
self._remoteMaps.msid2Quality[videoSource.msid] = quality++;
self._remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
self._remoteMaps.msid2ssrc[videoSource.msid] = videoSource.ssrc;
});
}
});
}
};
SimulcastReceiver.prototype._setReceivingVideoStream = function (resource, ssrc) {
this._remoteMaps.receivingVideoStreams[resource] = ssrc;
};
/**
* Returns a stream with single video track, the one currently being
* received by this endpoint.
*
* @param stream the remote simulcast stream.
* @returns {webkitMediaStream}
*/
SimulcastReceiver.prototype.getReceivingVideoStream = function (stream) {
var tracks, i, electedTrack, msid, quality = 0, receivingTrackId;
var self = this;
if (config.enableSimulcast) {
stream.getVideoTracks().some(function (track) {
return Object.keys(self._remoteMaps.receivingVideoStreams).some(function (resource) {
var ssrc = self._remoteMaps.receivingVideoStreams[resource];
var msid = self._remoteMaps.ssrc2Msid[ssrc];
if (msid == [stream.id, track.id].join(' ')) {
electedTrack = track;
return true;
}
});
});
if (!electedTrack) {
// we don't have an elected track, choose by initial quality.
tracks = stream.getVideoTracks();
for (i = 0; i < tracks.length; i++) {
msid = [stream.id, tracks[i].id].join(' ');
if (this._remoteMaps.msid2Quality[msid] === quality) {
electedTrack = tracks[i];
break;
}
}
// TODO(gp) if the initialQuality could not be satisfied, lower
// the requirement and try again.
}
}
return (electedTrack)
? new webkitMediaStream([electedTrack])
: stream;
};
SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
var resource = Strophe.getResourceFromJid(jid);
var ssrc = this._remoteMaps.receivingVideoStreams[resource];
// If we haven't receiving a "changed" event yet, then we must be receiving
// low quality (that the sender always streams).
if(!ssrc)
{
var remoteStreamObject = APP.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;
}
}
}
}
return ssrc;
};
SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
{
var sid, electedStream;
var i, j, k;
var jid = APP.xmpp.getJidFromSSRC(ssrc);
if(jid && APP.RTC.remoteStreams[jid])
{
var remoteStreamObject = APP.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;
}
}
}
}
else
{
console.debug(APP.RTC.remoteStreams, jid, ssrc);
}
return {
sid: sid,
stream: electedStream
};
};
/**
* Gets the fully qualified msid (stream.id + track.id) associated to the
* SSRC.
*
* @param ssrc
* @returns {*}
*/
SimulcastReceiver.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
return this._remoteMaps.ssrc2Msid[ssrc];
};
/**
* Removes the ssrc-group:SIM from the remote description bacause Chrome
* either gets confused and thinks this is an FID group or, if an FID group
* is already present, it fails to set the remote description.
*
* @param desc
* @returns {*}
*/
SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
if (desc && desc.sdp) {
var sb = desc.sdp.split('\r\n');
this._updateRemoteMaps(sb);
this._cacheRemoteVideoSources(sb);
// NOTE(gp) this needs to be called after updateRemoteMaps because we
// need the simulcast group in the _updateRemoteMaps() method.
this.simulcastUtils._removeSimulcastGroup(sb);
if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
// We don't need the goog conference flag if we're not doing
// simulcast.
this._ensureGoogConference(sb);
}
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
this.logger.fine(['Transformed remote description', desc.sdp].join(' '));
}
return desc;
};
module.exports = SimulcastReceiver;
},{"../../service/RTC/MediaStreamTypes":87,"./SimulcastLogger":39,"./SimulcastUtils":42}],41:[function(require,module,exports){
var SimulcastLogger = require("./SimulcastLogger");
var SimulcastUtils = require("./SimulcastUtils");
function SimulcastSender() {
this.simulcastUtils = new SimulcastUtils();
this.logger = new SimulcastLogger('SimulcastSender', 1);
}
SimulcastSender.prototype.displayedLocalVideoStream = null;
SimulcastSender.prototype._generateGuid = (function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
};
}());
// Returns a random integer between min (included) and max (excluded)
// Using Math.round() gives a non-uniform distribution!
SimulcastSender.prototype._generateRandomSSRC = function () {
var min = 0, max = 0xffffffff;
return Math.floor(Math.random() * (max - min)) + min;
};
SimulcastSender.prototype.getLocalVideoStream = function () {
return (this.displayedLocalVideoStream != null)
? this.displayedLocalVideoStream
// in case we have no simulcast at all, i.e. we didn't perform the GUM
: APP.RTC.localVideo.getOriginalStream();
};
function NativeSimulcastSender() {
SimulcastSender.call(this); // call the super constructor.
}
NativeSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
NativeSimulcastSender.prototype._localExplosionMap = {};
NativeSimulcastSender.prototype._isUsingScreenStream = false;
NativeSimulcastSender.prototype._localVideoSourceCache = '';
NativeSimulcastSender.prototype.reset = function () {
this._localExplosionMap = {};
this._isUsingScreenStream = APP.desktopsharing.isUsingScreenStream();
};
NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {
this._localVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
};
NativeSimulcastSender.prototype._restoreLocalVideoSources = function (lines) {
this.simulcastUtils._replaceVideoSources(lines, this._localVideoSourceCache);
};
NativeSimulcastSender.prototype._appendSimulcastGroup = function (lines) {
var videoSources, ssrcGroup, simSSRC, numOfSubs = 2, i, sb, msid;
this.logger.info('Appending simulcast group...');
// Get the primary SSRC information.
videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
// Start building the SIM SSRC group.
ssrcGroup = ['a=ssrc-group:SIM'];
// The video source buffer.
sb = [];
// Create the simulcast sub-streams.
for (i = 0; i < numOfSubs; i++) {
// TODO(gp) prevent SSRC collision.
simSSRC = this._generateRandomSSRC();
ssrcGroup.push(simSSRC);
if (videoSources.base) {
sb.splice.apply(sb, [sb.length, 0].concat(
[["a=ssrc:", simSSRC, " cname:", videoSources.base.cname].join(''),
["a=ssrc:", simSSRC, " msid:", videoSources.base.msid].join('')]
));
}
this.logger.info(['Generated substream ', i, ' with SSRC ', simSSRC, '.'].join(''));
}
// Add the group sim layers.
sb.splice(0, 0, ssrcGroup.join(' '))
this.simulcastUtils._replaceVideoSources(lines, sb);
};
// Does the actual patching.
NativeSimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
this.logger.info('Ensuring simulcast group...');
if (this.simulcastUtils._indexOfArray('a=ssrc-group:SIM', lines) === this.simulcastUtils._emptyCompoundIndex) {
this._appendSimulcastGroup(lines);
this._cacheLocalVideoSources(lines);
} else {
// verify that the ssrcs participating in the SIM group are present
// in the SDP (needed for presence).
this._restoreLocalVideoSources(lines);
}
};
/**
* Produces a single stream with multiple tracks for local video sources.
*
* @param lines
* @private
*/
NativeSimulcastSender.prototype._explodeSimulcastSenderSources = function (lines) {
var sb, msid, sid, tid, videoSources, self;
this.logger.info('Exploding local video sources...');
videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
self = this;
if (videoSources.groups && videoSources.groups.length !== 0) {
videoSources.groups.forEach(function (group) {
if (group.semantics === 'SIM') {
group.ssrcs.forEach(function (ssrc) {
// Get the msid for this ssrc..
if (self._localExplosionMap[ssrc]) {
// .. either from the explosion map..
msid = self._localExplosionMap[ssrc];
} else {
// .. or generate a new one (msid).
sid = videoSources.sources[ssrc].msid
.substring(0, videoSources.sources[ssrc].msid.indexOf(' '));
tid = self._generateGuid();
msid = [sid, tid].join(' ');
self._localExplosionMap[ssrc] = msid;
}
// Assign it to the source object.
videoSources.sources[ssrc].msid = msid;
// TODO(gp) Change the msid of associated sources.
});
}
});
}
sb = this.simulcastUtils._compileVideoSources(videoSources);
this.simulcastUtils._replaceVideoSources(lines, sb);
};
/**
* GUM for simulcast.
*
* @param constraints
* @param success
* @param err
*/
NativeSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
// There's nothing special to do for native simulcast, so just do a normal GUM.
navigator.webkitGetUserMedia(constraints, function (hqStream) {
success(hqStream);
}, err);
};
/**
* Prepares the local description for public usage (i.e. to be signaled
* through Jingle to the focus).
*
* @param desc
* @returns {RTCSessionDescription}
*/
NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
var sb;
if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
return desc;
}
sb = desc.sdp.split('\r\n');
this._explodeSimulcastSenderSources(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
this.logger.fine(['Exploded local video sources', desc.sdp].join(' '));
return desc;
};
/**
* Ensures that the simulcast group is present in the answer, _if_ native
* simulcast is enabled,
*
* @param desc
* @returns {*}
*/
NativeSimulcastSender.prototype.transformAnswer = function (desc) {
if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
return desc;
}
var sb = desc.sdp.split('\r\n');
// Even if we have enabled native simulcasting previously
// (with a call to SLD with an appropriate SDP, for example),
// createAnswer seems to consistently generate incomplete SDP
// with missing SSRCS.
//
// So, subsequent calls to SLD will have missing SSRCS and presence
// won't have the complete list of SRCs.
this._ensureSimulcastGroup(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
this.logger.fine(['Transformed answer', desc.sdp].join(' '));
return desc;
};
/**
*
*
* @param desc
* @returns {*}
*/
NativeSimulcastSender.prototype.transformLocalDescription = function (desc) {
return desc;
};
NativeSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
// Nothing to do here, native simulcast does that auto-magically.
};
NativeSimulcastSender.prototype.constructor = NativeSimulcastSender;
function SimpleSimulcastSender() {
SimulcastSender.call(this);
}
SimpleSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
SimpleSimulcastSender.prototype.localStream = null;
SimpleSimulcastSender.prototype._localMaps = {
msids: [],
msid2ssrc: {}
};
/**
* Groups local video sources together in the ssrc-group:SIM group.
*
* @param lines
* @private
*/
SimpleSimulcastSender.prototype._groupLocalVideoSources = function (lines) {
var sb, videoSources, ssrcs = [], ssrc;
this.logger.info('Grouping local video sources...');
videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
for (ssrc in videoSources.sources) {
// jitsi-meet destroys/creates streams at various places causing
// the original local stream ids to change. The only thing that
// remains unchanged is the trackid.
this._localMaps.msid2ssrc[videoSources.sources[ssrc].msid.split(' ')[1]] = ssrc;
}
var self = this;
// TODO(gp) add only "free" sources.
this._localMaps.msids.forEach(function (msid) {
ssrcs.push(self._localMaps.msid2ssrc[msid]);
});
if (!videoSources.groups) {
videoSources.groups = [];
}
videoSources.groups.push({
'semantics': 'SIM',
'ssrcs': ssrcs
});
sb = this.simulcastUtils._compileVideoSources(videoSources);
this.simulcastUtils._replaceVideoSources(lines, sb);
};
/**
* GUM for simulcast.
*
* @param constraints
* @param success
* @param err
*/
SimpleSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
// TODO(gp) what if we request a resolution not supported by the hardware?
// TODO(gp) make the lq stream configurable; although this wouldn't work with native simulcast
var lqConstraints = {
audio: false,
video: {
mandatory: {
maxWidth: 320,
maxHeight: 180,
maxFrameRate: 15
}
}
};
this.logger.info('HQ constraints: ', constraints);
this.logger.info('LQ constraints: ', lqConstraints);
// NOTE(gp) if we request the lq stream first webkitGetUserMedia
// fails randomly. Tested with Chrome 37. As fippo suggested, the
// reason appears to be that Chrome only acquires the cam once and
// then downscales the picture (https://code.google.com/p/chromium/issues/detail?id=346616#c11)
var self = this;
navigator.webkitGetUserMedia(constraints, function (hqStream) {
self.localStream = hqStream;
// reset local maps.
self._localMaps.msids = [];
self._localMaps.msid2ssrc = {};
// add hq trackid to local map
self._localMaps.msids.push(hqStream.getVideoTracks()[0].id);
navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
self.displayedLocalVideoStream = lqStream;
// NOTE(gp) The specification says Array.forEach() will visit
// the array elements in numeric order, and that it doesn't
// visit elements that don't exist.
// add lq trackid to local map
self._localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
self.localStream.addTrack(lqStream.getVideoTracks()[0]);
success(self.localStream);
}, err);
}, err);
};
/**
* Prepares the local description for public usage (i.e. to be signaled
* through Jingle to the focus).
*
* @param desc
* @returns {RTCSessionDescription}
*/
SimpleSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
var sb;
if (!this.simulcastUtils.isValidDescription(desc)) {
return desc;
}
sb = desc.sdp.split('\r\n');
this._groupLocalVideoSources(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
this.logger.fine('Grouped local video sources');
this.logger.fine(desc.sdp);
return desc;
};
/**
* Ensures that the simulcast group is present in the answer, _if_ native
* simulcast is enabled,
*
* @param desc
* @returns {*}
*/
SimpleSimulcastSender.prototype.transformAnswer = function (desc) {
return desc;
};
/**
*
*
* @param desc
* @returns {*}
*/
SimpleSimulcastSender.prototype.transformLocalDescription = function (desc) {
var sb = desc.sdp.split('\r\n');
this.simulcastUtils._removeSimulcastGroup(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
this.logger.fine('Transformed local description');
this.logger.fine(desc.sdp);
return desc;
};
SimpleSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
var trackid;
var self = this;
this.logger.log(['Requested to', enabled ? 'enable' : 'disable', ssrc].join(' '));
if (Object.keys(this._localMaps.msid2ssrc).some(function (tid) {
// Search for the track id that corresponds to the ssrc
if (self._localMaps.msid2ssrc[tid] == ssrc) {
trackid = tid;
return true;
}
}) && self.localStream.getVideoTracks().some(function (track) {
// Start/stop the track that corresponds to the track id
if (track.id === trackid) {
track.enabled = enabled;
return true;
}
})) {
this.logger.log([trackid, enabled ? 'enabled' : 'disabled'].join(' '));
$(document).trigger(enabled
? 'simulcastlayerstarted'
: 'simulcastlayerstopped');
} else {
this.logger.error("I don't have a local stream with SSRC " + ssrc);
}
};
SimpleSimulcastSender.prototype.constructor = SimpleSimulcastSender;
function NoSimulcastSender() {
SimulcastSender.call(this);
}
NoSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
/**
* GUM for simulcast.
*
* @param constraints
* @param success
* @param err
*/
NoSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
navigator.webkitGetUserMedia(constraints, function (hqStream) {
success(hqStream);
}, err);
};
/**
* Prepares the local description for public usage (i.e. to be signaled
* through Jingle to the focus).
*
* @param desc
* @returns {RTCSessionDescription}
*/
NoSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
return desc;
};
/**
* Ensures that the simulcast group is present in the answer, _if_ native
* simulcast is enabled,
*
* @param desc
* @returns {*}
*/
NoSimulcastSender.prototype.transformAnswer = function (desc) {
return desc;
};
/**
*
*
* @param desc
* @returns {*}
*/
NoSimulcastSender.prototype.transformLocalDescription = function (desc) {
return desc;
};
NoSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
};
NoSimulcastSender.prototype.constructor = NoSimulcastSender;
module.exports = {
"native": NativeSimulcastSender,
"no": NoSimulcastSender
}
},{"./SimulcastLogger":39,"./SimulcastUtils":42}],42:[function(require,module,exports){
var SimulcastLogger = require("./SimulcastLogger");
/**
*
* @constructor
*/
function SimulcastUtils() {
this.logger = new SimulcastLogger("SimulcastUtils", 1);
}
/**
*
* @type {{}}
* @private
*/
SimulcastUtils.prototype._emptyCompoundIndex = {};
/**
*
* @param lines
* @param videoSources
* @private
*/
SimulcastUtils.prototype._replaceVideoSources = function (lines, videoSources) {
var i, inVideo = false, index = -1, howMany = 0;
this.logger.info('Replacing video sources...');
for (i = 0; i < lines.length; i++) {
if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
// Out of video.
break;
}
if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
// In video.
inVideo = true;
}
if (inVideo && (lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:'
|| lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:')) {
if (index === -1) {
index = i;
}
howMany++;
}
}
// efficiency baby ;)
lines.splice.apply(lines,
[index, howMany].concat(videoSources));
};
SimulcastUtils.prototype.isValidDescription = function (desc)
{
return desc && desc != null
&& desc.type && desc.type != ''
&& desc.sdp && desc.sdp != '';
};
SimulcastUtils.prototype._getVideoSources = function (lines) {
var i, inVideo = false, sb = [];
this.logger.info('Getting video sources...');
for (i = 0; i < lines.length; i++) {
if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
// Out of video.
break;
}
if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
// In video.
inVideo = true;
}
if (inVideo && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
// In SSRC.
sb.push(lines[i]);
}
if (inVideo && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
sb.push(lines[i]);
}
}
return sb;
};
SimulcastUtils.prototype.parseMedia = function (lines, mediatypes) {
var i, res = [], type, cur_media, idx, ssrcs, cur_ssrc, ssrc,
ssrc_attribute, group, semantics, skip = true;
this.logger.info('Parsing media sources...');
for (i = 0; i < lines.length; i++) {
if (lines[i].substring(0, 'm='.length) === 'm=') {
type = lines[i]
.substr('m='.length, lines[i].indexOf(' ') - 'm='.length);
skip = mediatypes !== undefined && mediatypes.indexOf(type) === -1;
if (!skip) {
cur_media = {
'type': type,
'sources': {},
'groups': []
};
res.push(cur_media);
}
} else if (!skip && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
idx = lines[i].indexOf(' ');
ssrc = lines[i].substring('a=ssrc:'.length, idx);
if (cur_media.sources[ssrc] === undefined) {
cur_ssrc = {'ssrc': ssrc};
cur_media.sources[ssrc] = cur_ssrc;
}
ssrc_attribute = lines[i].substr(idx + 1).split(':', 2)[0];
cur_ssrc[ssrc_attribute] = lines[i].substr(idx + 1).split(':', 2)[1];
if (cur_media.base === undefined) {
cur_media.base = cur_ssrc;
}
} else if (!skip && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
idx = lines[i].indexOf(' ');
semantics = lines[i].substr(0, idx).substr('a=ssrc-group:'.length);
ssrcs = lines[i].substr(idx).trim().split(' ');
group = {
'semantics': semantics,
'ssrcs': ssrcs
};
cur_media.groups.push(group);
} else if (!skip && (lines[i].substring(0, 'a=sendrecv'.length) === 'a=sendrecv' ||
lines[i].substring(0, 'a=recvonly'.length) === 'a=recvonly' ||
lines[i].substring(0, 'a=sendonly'.length) === 'a=sendonly' ||
lines[i].substring(0, 'a=inactive'.length) === 'a=inactive')) {
cur_media.direction = lines[i].substring('a='.length);
}
}
return res;
};
/**
* The _indexOfArray() method returns the first a CompoundIndex at which a
* given element can be found in the array, or _emptyCompoundIndex if it is
* not present.
*
* Example:
*
* _indexOfArray('3', [ 'this is line 1', 'this is line 2', 'this is line 3' ])
*
* returns {row: 2, column: 14}
*
* @param needle
* @param haystack
* @param start
* @returns {}
* @private
*/
SimulcastUtils.prototype._indexOfArray = function (needle, haystack, start) {
var length = haystack.length, idx, i;
if (!start) {
start = 0;
}
for (i = start; i < length; i++) {
idx = haystack[i].indexOf(needle);
if (idx !== -1) {
return {row: i, column: idx};
}
}
return this._emptyCompoundIndex;
};
SimulcastUtils.prototype._removeSimulcastGroup = function (lines) {
var i;
for (i = lines.length - 1; i >= 0; i--) {
if (lines[i].indexOf('a=ssrc-group:SIM') !== -1) {
lines.splice(i, 1);
}
}
};
SimulcastUtils.prototype._compileVideoSources = function (videoSources) {
var sb = [], ssrc, addedSSRCs = [];
this.logger.info('Compiling video sources...');
// Add the groups
if (videoSources.groups && videoSources.groups.length !== 0) {
videoSources.groups.forEach(function (group) {
if (group.ssrcs && group.ssrcs.length !== 0) {
sb.push([['a=ssrc-group:', group.semantics].join(''), group.ssrcs.join(' ')].join(' '));
// if (group.semantics !== 'SIM') {
group.ssrcs.forEach(function (ssrc) {
addedSSRCs.push(ssrc);
sb.splice.apply(sb, [sb.length, 0].concat([
["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
});
//}
}
});
}
// Then add any free sources.
if (videoSources.sources) {
for (ssrc in videoSources.sources) {
if (addedSSRCs.indexOf(ssrc) === -1) {
sb.splice.apply(sb, [sb.length, 0].concat([
["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
}
}
}
return sb;
};
module.exports = SimulcastUtils;
},{"./SimulcastLogger":39}],43:[function(require,module,exports){
/*jslint plusplus: true */
/*jslint nomen: true*/
var SimulcastSender = require("./SimulcastSender");
var NoSimulcastSender = SimulcastSender["no"];
var NativeSimulcastSender = SimulcastSender["native"];
var SimulcastReceiver = require("./SimulcastReceiver");
var SimulcastUtils = require("./SimulcastUtils");
var RTCEvents = require("../../service/RTC/RTCEvents");
/**
*
* @constructor
*/
function SimulcastManager() {
// Create the simulcast utilities.
this.simulcastUtils = new SimulcastUtils();
// Create remote simulcast.
this.simulcastReceiver = new SimulcastReceiver();
// Initialize local simulcast.
// TODO(gp) move into SimulcastManager.prototype.getUserMedia and take into
// account constraints.
if (!config.enableSimulcast) {
this.simulcastSender = new NoSimulcastSender();
} else {
var isChromium = window.chrome,
vendorName = window.navigator.vendor;
if(isChromium !== null && isChromium !== undefined
/* skip opera */
&& vendorName === "Google Inc."
/* skip Chromium as suggested by fippo */
&& !window.navigator.appVersion.match(/Chromium\//) ) {
var ver = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
if (ver > 37) {
this.simulcastSender = new NativeSimulcastSender();
} else {
this.simulcastSender = new NoSimulcastSender();
}
} else {
this.simulcastSender = new NoSimulcastSender();
}
}
APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGED,
function (endpointSimulcastLayers) {
endpointSimulcastLayers.forEach(function (esl) {
var ssrc = esl.simulcastLayer.primarySSRC;
simulcast._setReceivingVideoStream(esl.endpoint, ssrc);
});
});
APP.RTC.addListener(RTCEvents.SIMULCAST_START, function (simulcastLayer) {
var ssrc = simulcastLayer.primarySSRC;
simulcast._setLocalVideoStreamEnabled(ssrc, true);
});
APP.RTC.addListener(RTCEvents.SIMULCAST_STOP, function (simulcastLayer) {
var ssrc = simulcastLayer.primarySSRC;
simulcast._setLocalVideoStreamEnabled(ssrc, false);
});
}
/**
* Restores the simulcast groups of the remote description. In
* transformRemoteDescription we remove those in order for the set remote
* description to succeed. The focus needs the signal the groups to new
* participants.
*
* @param desc
* @returns {*}
*/
SimulcastManager.prototype.reverseTransformRemoteDescription = function (desc) {
return this.simulcastReceiver.reverseTransformRemoteDescription(desc);
};
/**
* Removes the ssrc-group:SIM from the remote description bacause Chrome
* either gets confused and thinks this is an FID group or, if an FID group
* is already present, it fails to set the remote description.
*
* @param desc
* @returns {*}
*/
SimulcastManager.prototype.transformRemoteDescription = function (desc) {
return this.simulcastReceiver.transformRemoteDescription(desc);
};
/**
* Gets the fully qualified msid (stream.id + track.id) associated to the
* SSRC.
*
* @param ssrc
* @returns {*}
*/
SimulcastManager.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
return this.simulcastReceiver.getRemoteVideoStreamIdBySSRC(ssrc);
};
/**
* Returns a stream with single video track, the one currently being
* received by this endpoint.
*
* @param stream the remote simulcast stream.
* @returns {webkitMediaStream}
*/
SimulcastManager.prototype.getReceivingVideoStream = function (stream) {
return this.simulcastReceiver.getReceivingVideoStream(stream);
};
/**
*
*
* @param desc
* @returns {*}
*/
SimulcastManager.prototype.transformLocalDescription = function (desc) {
return this.simulcastSender.transformLocalDescription(desc);
};
/**
*
* @returns {*}
*/
SimulcastManager.prototype.getLocalVideoStream = function() {
return this.simulcastSender.getLocalVideoStream();
};
/**
* GUM for simulcast.
*
* @param constraints
* @param success
* @param err
*/
SimulcastManager.prototype.getUserMedia = function (constraints, success, err) {
this.simulcastSender.getUserMedia(constraints, success, err);
};
/**
* Prepares the local description for public usage (i.e. to be signaled
* through Jingle to the focus).
*
* @param desc
* @returns {RTCSessionDescription}
*/
SimulcastManager.prototype.reverseTransformLocalDescription = function (desc) {
return this.simulcastSender.reverseTransformLocalDescription(desc);
};
/**
* Ensures that the simulcast group is present in the answer, _if_ native
* simulcast is enabled,
*
* @param desc
* @returns {*}
*/
SimulcastManager.prototype.transformAnswer = function (desc) {
return this.simulcastSender.transformAnswer(desc);
};
SimulcastManager.prototype.getReceivingSSRC = function (jid) {
return this.simulcastReceiver.getReceivingSSRC(jid);
};
SimulcastManager.prototype.getReceivingVideoStreamBySSRC = function (msid) {
return this.simulcastReceiver.getReceivingVideoStreamBySSRC(msid);
};
/**
*
* @param lines
* @param mediatypes
* @returns {*}
*/
SimulcastManager.prototype.parseMedia = function(lines, mediatypes) {
var sb = lines.sdp.split('\r\n');
return this.simulcastUtils.parseMedia(sb, mediatypes);
};
SimulcastManager.prototype._setReceivingVideoStream = function(resource, ssrc) {
this.simulcastReceiver._setReceivingVideoStream(resource, ssrc);
};
SimulcastManager.prototype._setLocalVideoStreamEnabled = function(ssrc, enabled) {
this.simulcastSender._setLocalVideoStreamEnabled(ssrc, enabled);
};
SimulcastManager.prototype.resetSender = function() {
if (typeof this.simulcastSender.reset === 'function'){
this.simulcastSender.reset();
}
};
var simulcast = new SimulcastManager();
module.exports = simulcast;
},{"../../service/RTC/RTCEvents":89,"./SimulcastReceiver":40,"./SimulcastSender":41,"./SimulcastUtils":42}],44:[function(require,module,exports){
/**
* Provides statistics for the local stream.
*/
/**
* Size of the webaudio analizer buffer.
* @type {number}
*/
var WEBAUDIO_ANALIZER_FFT_SIZE = 2048;
/**
* Value of the webaudio analizer smoothing time parameter.
* @type {number}
*/
var WEBAUDIO_ANALIZER_SMOOTING_TIME = 0.8;
/**
* Converts time domain data array to audio level.
* @param array the time domain data array.
* @returns {number} the audio level
*/
function timeDomainDataToAudioLevel(samples) {
var maxVolume = 0;
var length = samples.length;
for (var i = 0; i < length; i++) {
if (maxVolume < samples[i])
maxVolume = samples[i];
}
return parseFloat(((maxVolume - 127) / 128).toFixed(3));
};
/**
* Animates audio level change
* @param newLevel the new audio level
* @param lastLevel the last audio level
* @returns {Number} the audio level to be set
*/
function animateLevel(newLevel, lastLevel)
{
var value = 0;
var diff = lastLevel - newLevel;
if(diff > 0.2)
{
value = lastLevel - 0.2;
}
else if(diff < -0.4)
{
value = lastLevel + 0.4;
}
else
{
value = newLevel;
}
return parseFloat(value.toFixed(3));
}
/**
* <tt>LocalStatsCollector</tt> calculates statistics for the local stream.
*
* @param stream the local stream
* @param interval stats refresh interval given in ms.
* @param {function(LocalStatsCollector)} updateCallback the callback called on stats
* update.
* @constructor
*/
function LocalStatsCollector(stream, interval, statisticsService, eventEmitter) {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
this.stream = stream;
this.intervalId = null;
this.intervalMilis = interval;
this.eventEmitter = eventEmitter;
this.audioLevel = 0;
this.statisticsService = statisticsService;
}
/**
* Starts the collecting the statistics.
*/
LocalStatsCollector.prototype.start = function () {
if (config.disableAudioLevels || !window.AudioContext)
return;
var context = new AudioContext();
var analyser = context.createAnalyser();
analyser.smoothingTimeConstant = WEBAUDIO_ANALIZER_SMOOTING_TIME;
analyser.fftSize = WEBAUDIO_ANALIZER_FFT_SIZE;
var source = context.createMediaStreamSource(this.stream);
source.connect(analyser);
var self = this;
this.intervalId = setInterval(
function () {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteTimeDomainData(array);
var audioLevel = timeDomainDataToAudioLevel(array);
if(audioLevel != self.audioLevel) {
self.audioLevel = animateLevel(audioLevel, self.audioLevel);
self.eventEmitter.emit(
"statistics.audioLevel",
self.statisticsService.LOCAL_JID,
self.audioLevel);
}
},
this.intervalMilis
);
};
/**
* Stops collecting the statistics.
*/
LocalStatsCollector.prototype.stop = function () {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
};
module.exports = LocalStatsCollector;
},{}],45:[function(require,module,exports){
/* global ssrc2jid */
/* jshint -W117 */
var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
/**
* Calculates packet lost percent using the number of lost packets and the
* number of all packet.
* @param lostPackets the number of lost packets
* @param totalPackets the number of all packets.
* @returns {number} packet loss percent
*/
function calculatePacketLoss(lostPackets, totalPackets) {
if(!totalPackets || totalPackets <= 0 || !lostPackets || lostPackets <= 0)
return 0;
return Math.round((lostPackets/totalPackets)*100);
}
function getStatValue(item, name) {
if(!keyMap[APP.RTC.getBrowserType()][name])
throw "The property isn't supported!";
var key = keyMap[APP.RTC.getBrowserType()][name];
return APP.RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_CHROME? item.stat(key) : item[key];
}
/**
* Peer statistics data holder.
* @constructor
*/
function PeerStats()
{
this.ssrc2Loss = {};
this.ssrc2AudioLevel = {};
this.ssrc2bitrate = {};
this.ssrc2resolution = {};
}
/**
* The bandwidth
* @type {{}}
*/
PeerStats.bandwidth = {};
/**
* The bit rate
* @type {{}}
*/
PeerStats.bitrate = {};
/**
* The packet loss rate
* @type {{}}
*/
PeerStats.packetLoss = null;
/**
* Sets packets loss rate for given <tt>ssrc</tt> that blong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param lossRate new packet loss rate value to be set.
*/
PeerStats.prototype.setSsrcLoss = function (ssrc, lossRate)
{
this.ssrc2Loss[ssrc] = lossRate;
};
/**
* Sets resolution for given <tt>ssrc</tt> that belong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param resolution new resolution value to be set.
*/
PeerStats.prototype.setSsrcResolution = function (ssrc, resolution)
{
if(resolution === null && this.ssrc2resolution[ssrc])
{
delete this.ssrc2resolution[ssrc];
}
else if(resolution !== null)
this.ssrc2resolution[ssrc] = resolution;
};
/**
* Sets the bit rate for given <tt>ssrc</tt> that blong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param bitrate new bitrate value to be set.
*/
PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate)
{
if(this.ssrc2bitrate[ssrc])
{
this.ssrc2bitrate[ssrc].download += bitrate.download;
this.ssrc2bitrate[ssrc].upload += bitrate.upload;
}
else {
this.ssrc2bitrate[ssrc] = bitrate;
}
};
/**
* Sets new audio level(input or output) for given <tt>ssrc</tt> that identifies
* the stream which belongs to the peer represented by this instance.
* @param ssrc RTP stream SSRC for which current audio level value will be
* updated.
* @param audioLevel the new audio level value to be set. Value is truncated to
* fit the range from 0 to 1.
*/
PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel)
{
// Range limit 0 - 1
this.ssrc2AudioLevel[ssrc] = formatAudioLevel(audioLevel);
};
function formatAudioLevel(audioLevel) {
return Math.min(Math.max(audioLevel, 0), 1);
}
/**
* Array with the transport information.
* @type {Array}
*/
PeerStats.transport = [];
/**
* <tt>StatsCollector</tt> registers for stats updates of given
* <tt>peerconnection</tt> in given <tt>interval</tt>. On each update particular
* stats are extracted and put in {@link PeerStats} objects. Once the processing
* is done <tt>audioLevelsUpdateCallback</tt> is called with <tt>this</tt>
* instance as an event source.
*
* @param peerconnection webRTC peer connection object.
* @param interval stats refresh interval given in ms.
* @param {function(StatsCollector)} audioLevelsUpdateCallback the callback
* called on stats update.
* @constructor
*/
function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, eventEmitter)
{
this.peerconnection = peerconnection;
this.baselineAudioLevelsReport = null;
this.currentAudioLevelsReport = null;
this.currentStatsReport = null;
this.baselineStatsReport = null;
this.audioLevelsIntervalId = null;
this.eventEmitter = eventEmitter;
/**
* Gather PeerConnection stats once every this many milliseconds.
*/
this.GATHER_INTERVAL = 15000;
/**
* Log stats via the focus once every this many milliseconds.
*/
this.LOG_INTERVAL = 60000;
/**
* Gather stats and store them in this.statsToBeLogged.
*/
this.gatherStatsIntervalId = null;
/**
* Send the stats already saved in this.statsToBeLogged to be logged via
* the focus.
*/
this.logStatsIntervalId = null;
/**
* Stores the statistics which will be send to the focus to be logged.
*/
this.statsToBeLogged =
{
timestamps: [],
stats: {}
};
// Updates stats interval
this.audioLevelsIntervalMilis = audioLevelsInterval;
this.statsIntervalId = null;
this.statsIntervalMilis = statsInterval;
// Map of jids to PeerStats
this.jid2stats = {};
}
module.exports = StatsCollector;
/**
* Stops stats updates.
*/
StatsCollector.prototype.stop = function () {
if (this.audioLevelsIntervalId) {
clearInterval(this.audioLevelsIntervalId);
this.audioLevelsIntervalId = null;
}
if (this.statsIntervalId)
{
clearInterval(this.statsIntervalId);
this.statsIntervalId = null;
}
if(this.logStatsIntervalId)
{
clearInterval(this.logStatsIntervalId);
this.logStatsIntervalId = null;
}
if(this.gatherStatsIntervalId)
{
clearInterval(this.gatherStatsIntervalId);
this.gatherStatsIntervalId = null;
}
};
/**
* Callback passed to <tt>getStats</tt> method.
* @param error an error that occurred on <tt>getStats</tt> call.
*/
StatsCollector.prototype.errorCallback = function (error)
{
console.error("Get stats error", error);
this.stop();
};
/**
* Starts stats updates.
*/
StatsCollector.prototype.start = function ()
{
var self = this;
if(!config.disableAudioLevels) {
this.audioLevelsIntervalId = setInterval(
function () {
// Interval updates
self.peerconnection.getStats(
function (report) {
var results = null;
if (!report || !report.result ||
typeof report.result != 'function') {
results = report;
}
else {
results = report.result();
}
//console.error("Got interval report", results);
self.currentAudioLevelsReport = results;
self.processAudioLevelReport();
self.baselineAudioLevelsReport =
self.currentAudioLevelsReport;
},
self.errorCallback
);
},
self.audioLevelsIntervalMilis
);
}
if(!config.disableStats) {
this.statsIntervalId = setInterval(
function () {
// Interval updates
self.peerconnection.getStats(
function (report) {
var results = null;
if (!report || !report.result ||
typeof report.result != 'function') {
//firefox
results = report;
}
else {
//chrome
results = report.result();
}
//console.error("Got interval report", results);
self.currentStatsReport = results;
try {
self.processStatsReport();
}
catch (e) {
console.error("Unsupported key:" + e, e);
}
self.baselineStatsReport = self.currentStatsReport;
},
self.errorCallback
);
},
self.statsIntervalMilis
);
}
if (config.logStats) {
this.gatherStatsIntervalId = setInterval(
function () {
self.peerconnection.getStats(
function (report) {
self.addStatsToBeLogged(report.result());
},
function () {
}
);
},
this.GATHER_INTERVAL
);
this.logStatsIntervalId = setInterval(
function() { self.logStats(); },
this.LOG_INTERVAL);
}
};
/**
* Checks whether a certain record should be included in the logged statistics.
*/
function acceptStat(reportId, reportType, statName) {
if (reportType == "googCandidatePair" && statName == "googChannelId")
return false;
if (reportType == "ssrc") {
if (statName == "googTrackId" ||
statName == "transportId" ||
statName == "ssrc")
return false;
}
return true;
}
/**
* Checks whether a certain record should be included in the logged statistics.
*/
function acceptReport(id, type) {
if (id.substring(0, 15) == "googCertificate" ||
id.substring(0, 9) == "googTrack" ||
id.substring(0, 20) == "googLibjingleSession")
return false;
if (type == "googComponent")
return false;
return true;
}
/**
* Converts the stats to the format used for logging, and saves the data in
* this.statsToBeLogged.
* @param reports Reports as given by webkitRTCPerConnection.getStats.
*/
StatsCollector.prototype.addStatsToBeLogged = function (reports) {
var self = this;
var num_records = this.statsToBeLogged.timestamps.length;
this.statsToBeLogged.timestamps.push(new Date().getTime());
reports.map(function (report) {
if (!acceptReport(report.id, report.type))
return;
var stat = self.statsToBeLogged.stats[report.id];
if (!stat) {
stat = self.statsToBeLogged.stats[report.id] = {};
}
stat.type = report.type;
report.names().map(function (name) {
if (!acceptStat(report.id, report.type, name))
return;
var values = stat[name];
if (!values) {
values = stat[name] = [];
}
while (values.length < num_records) {
values.push(null);
}
values.push(report.stat(name));
});
});
};
StatsCollector.prototype.logStats = function () {
if(!APP.xmpp.sendLogs(this.statsToBeLogged))
return;
// Reset the stats
this.statsToBeLogged.stats = {};
this.statsToBeLogged.timestamps = [];
};
var keyMap = {};
keyMap[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
"ssrc": "ssrc",
"packetsReceived": "packetsReceived",
"packetsLost": "packetsLost",
"packetsSent": "packetsSent",
"bytesReceived": "bytesReceived",
"bytesSent": "bytesSent"
};
keyMap[RTCBrowserType.RTC_BROWSER_CHROME] = {
"receiveBandwidth": "googAvailableReceiveBandwidth",
"sendBandwidth": "googAvailableSendBandwidth",
"remoteAddress": "googRemoteAddress",
"transportType": "googTransportType",
"localAddress": "googLocalAddress",
"activeConnection": "googActiveConnection",
"ssrc": "ssrc",
"packetsReceived": "packetsReceived",
"packetsSent": "packetsSent",
"packetsLost": "packetsLost",
"bytesReceived": "bytesReceived",
"bytesSent": "bytesSent",
"googFrameHeightReceived": "googFrameHeightReceived",
"googFrameWidthReceived": "googFrameWidthReceived",
"googFrameHeightSent": "googFrameHeightSent",
"googFrameWidthSent": "googFrameWidthSent",
"audioInputLevel": "audioInputLevel",
"audioOutputLevel": "audioOutputLevel"
};
/**
* Stats processing logic.
*/
StatsCollector.prototype.processStatsReport = function () {
if (!this.baselineStatsReport) {
return;
}
for (var idx in this.currentStatsReport) {
var now = this.currentStatsReport[idx];
try {
if (getStatValue(now, 'receiveBandwidth') ||
getStatValue(now, 'sendBandwidth')) {
PeerStats.bandwidth = {
"download": Math.round(
(getStatValue(now, 'receiveBandwidth')) / 1000),
"upload": Math.round(
(getStatValue(now, 'sendBandwidth')) / 1000)
};
}
}
catch(e){/*not supported*/}
if(now.type == 'googCandidatePair')
{
var ip, type, localIP, active;
try {
ip = getStatValue(now, 'remoteAddress');
type = getStatValue(now, "transportType");
localIP = getStatValue(now, "localAddress");
active = getStatValue(now, "activeConnection");
}
catch(e){/*not supported*/}
if(!ip || !type || !localIP || active != "true")
continue;
var addressSaved = false;
for(var i = 0; i < PeerStats.transport.length; i++)
{
if(PeerStats.transport[i].ip == ip &&
PeerStats.transport[i].type == type &&
PeerStats.transport[i].localip == localIP)
{
addressSaved = true;
}
}
if(addressSaved)
continue;
PeerStats.transport.push({localip: localIP, ip: ip, type: type});
continue;
}
if(now.type == "candidatepair")
{
if(now.state == "succeeded")
continue;
var local = this.currentStatsReport[now.localCandidateId];
var remote = this.currentStatsReport[now.remoteCandidateId];
PeerStats.transport.push({localip: local.ipAddress + ":" + local.portNumber,
ip: remote.ipAddress + ":" + remote.portNumber, type: local.transport});
}
if (now.type != 'ssrc' && now.type != "outboundrtp" &&
now.type != "inboundrtp") {
continue;
}
var before = this.baselineStatsReport[idx];
if (!before) {
console.warn(getStatValue(now, 'ssrc') + ' not enough data');
continue;
}
var ssrc = getStatValue(now, 'ssrc');
if(!ssrc)
continue;
var jid = APP.xmpp.getJidFromSSRC(ssrc);
if (!jid && (Date.now() - now.timestamp) < 3000) {
console.warn("No jid for ssrc: " + ssrc);
continue;
}
var jidStats = this.jid2stats[jid];
if (!jidStats) {
jidStats = new PeerStats();
this.jid2stats[jid] = jidStats;
}
var isDownloadStream = true;
var key = 'packetsReceived';
if (!getStatValue(now, key))
{
isDownloadStream = false;
key = 'packetsSent';
if (!getStatValue(now, key))
{
console.warn("No packetsReceived nor packetSent stat found");
continue;
}
}
var packetsNow = getStatValue(now, key);
if(!packetsNow || packetsNow < 0)
packetsNow = 0;
var packetsBefore = getStatValue(before, key);
if(!packetsBefore || packetsBefore < 0)
packetsBefore = 0;
var packetRate = packetsNow - packetsBefore;
if(!packetRate || packetRate < 0)
packetRate = 0;
var currentLoss = getStatValue(now, 'packetsLost');
if(!currentLoss || currentLoss < 0)
currentLoss = 0;
var previousLoss = getStatValue(before, 'packetsLost');
if(!previousLoss || previousLoss < 0)
previousLoss = 0;
var lossRate = currentLoss - previousLoss;
if(!lossRate || lossRate < 0)
lossRate = 0;
var packetsTotal = (packetRate + lossRate);
jidStats.setSsrcLoss(ssrc,
{"packetsTotal": packetsTotal,
"packetsLost": lossRate,
"isDownloadStream": isDownloadStream});
var bytesReceived = 0, bytesSent = 0;
if(getStatValue(now, "bytesReceived"))
{
bytesReceived = getStatValue(now, "bytesReceived") -
getStatValue(before, "bytesReceived");
}
if(getStatValue(now, "bytesSent"))
{
bytesSent = getStatValue(now, "bytesSent") -
getStatValue(before, "bytesSent");
}
var time = Math.round((now.timestamp - before.timestamp) / 1000);
if(bytesReceived <= 0 || time <= 0)
{
bytesReceived = 0;
}
else
{
bytesReceived = Math.round(((bytesReceived * 8) / time) / 1000);
}
if(bytesSent <= 0 || time <= 0)
{
bytesSent = 0;
}
else
{
bytesSent = Math.round(((bytesSent * 8) / time) / 1000);
}
jidStats.setSsrcBitrate(ssrc, {
"download": bytesReceived,
"upload": bytesSent});
var resolution = {height: null, width: null};
try {
if (getStatValue(now, "googFrameHeightReceived") &&
getStatValue(now, "googFrameWidthReceived")) {
resolution.height = getStatValue(now, "googFrameHeightReceived");
resolution.width = getStatValue(now, "googFrameWidthReceived");
}
else if (getStatValue(now, "googFrameHeightSent") &&
getStatValue(now, "googFrameWidthSent")) {
resolution.height = getStatValue(now, "googFrameHeightSent");
resolution.width = getStatValue(now, "googFrameWidthSent");
}
}
catch(e){/*not supported*/}
if(resolution.height && resolution.width)
{
jidStats.setSsrcResolution(ssrc, resolution);
}
else
{
jidStats.setSsrcResolution(ssrc, null);
}
}
var self = this;
// Jid stats
var totalPackets = {download: 0, upload: 0};
var lostPackets = {download: 0, upload: 0};
var bitrateDownload = 0;
var bitrateUpload = 0;
var resolutions = {};
Object.keys(this.jid2stats).forEach(
function (jid)
{
Object.keys(self.jid2stats[jid].ssrc2Loss).forEach(
function (ssrc)
{
var type = "upload";
if(self.jid2stats[jid].ssrc2Loss[ssrc].isDownloadStream)
type = "download";
totalPackets[type] +=
self.jid2stats[jid].ssrc2Loss[ssrc].packetsTotal;
lostPackets[type] +=
self.jid2stats[jid].ssrc2Loss[ssrc].packetsLost;
}
);
Object.keys(self.jid2stats[jid].ssrc2bitrate).forEach(
function (ssrc) {
bitrateDownload +=
self.jid2stats[jid].ssrc2bitrate[ssrc].download;
bitrateUpload +=
self.jid2stats[jid].ssrc2bitrate[ssrc].upload;
delete self.jid2stats[jid].ssrc2bitrate[ssrc];
}
);
resolutions[jid] = self.jid2stats[jid].ssrc2resolution;
}
);
PeerStats.bitrate = {"upload": bitrateUpload, "download": bitrateDownload};
PeerStats.packetLoss = {
total:
calculatePacketLoss(lostPackets.download + lostPackets.upload,
totalPackets.download + totalPackets.upload),
download:
calculatePacketLoss(lostPackets.download, totalPackets.download),
upload:
calculatePacketLoss(lostPackets.upload, totalPackets.upload)
};
this.eventEmitter.emit("statistics.connectionstats",
{
"bitrate": PeerStats.bitrate,
"packetLoss": PeerStats.packetLoss,
"bandwidth": PeerStats.bandwidth,
"resolution": resolutions,
"transport": PeerStats.transport
});
PeerStats.transport = [];
};
/**
* Stats processing logic.
*/
StatsCollector.prototype.processAudioLevelReport = function ()
{
if (!this.baselineAudioLevelsReport)
{
return;
}
for (var idx in this.currentAudioLevelsReport)
{
var now = this.currentAudioLevelsReport[idx];
if (now.type != 'ssrc')
{
continue;
}
var before = this.baselineAudioLevelsReport[idx];
if (!before)
{
console.warn(getStatValue(now, 'ssrc') + ' not enough data');
continue;
}
var ssrc = getStatValue(now, 'ssrc');
var jid = APP.xmpp.getJidFromSSRC(ssrc);
if (!jid && (Date.now() - now.timestamp) < 3000)
{
console.warn("No jid for ssrc: " + ssrc);
continue;
}
var jidStats = this.jid2stats[jid];
if (!jidStats)
{
jidStats = new PeerStats();
this.jid2stats[jid] = jidStats;
}
// Audio level
var audioLevel = null;
try {
audioLevel = getStatValue(now, 'audioInputLevel');
if (!audioLevel)
audioLevel = getStatValue(now, 'audioOutputLevel');
}
catch(e) {/*not supported*/
console.warn("Audio Levels are not available in the statistics.");
clearInterval(this.audioLevelsIntervalId);
return;
}
if (audioLevel)
{
// TODO: can't find specs about what this value really is,
// but it seems to vary between 0 and around 32k.
audioLevel = audioLevel / 32767;
jidStats.setSsrcAudioLevel(ssrc, audioLevel);
if(jid != APP.xmpp.myJid())
this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel);
}
}
};
},{"../../service/RTC/RTCBrowserType":88}],46:[function(require,module,exports){
/**
* Created by hristo on 8/4/14.
*/
var LocalStats = require("./LocalStatsCollector.js");
var RTPStats = require("./RTPStatsCollector.js");
var EventEmitter = require("events");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var eventEmitter = new EventEmitter();
var localStats = null;
var rtpStats = null;
function stopLocal()
{
if(localStats)
{
localStats.stop();
localStats = null;
}
}
function stopRemote()
{
if(rtpStats)
{
rtpStats.stop();
eventEmitter.emit("statistics.stop");
rtpStats = null;
}
}
function startRemoteStats (peerconnection) {
if(rtpStats)
{
rtpStats.stop();
rtpStats = null;
}
rtpStats = new RTPStats(peerconnection, 200, 2000, eventEmitter);
rtpStats.start();
}
function onStreamCreated(stream)
{
if(stream.getOriginalStream().getAudioTracks().length === 0)
return;
localStats = new LocalStats(stream.getOriginalStream(), 200, statistics,
eventEmitter);
localStats.start();
}
function onDisposeConference(onUnload) {
stopRemote();
if(onUnload) {
stopLocal();
eventEmitter.removeAllListeners();
}
}
var statistics =
{
/**
* Indicates that this audio level is for local jid.
* @type {string}
*/
LOCAL_JID: 'local',
addAudioLevelListener: function(listener)
{
eventEmitter.on("statistics.audioLevel", listener);
},
removeAudioLevelListener: function(listener)
{
eventEmitter.removeListener("statistics.audioLevel", listener);
},
addConnectionStatsListener: function(listener)
{
eventEmitter.on("statistics.connectionstats", listener);
},
removeConnectionStatsListener: function(listener)
{
eventEmitter.removeListener("statistics.connectionstats", listener);
},
addRemoteStatsStopListener: function(listener)
{
eventEmitter.on("statistics.stop", listener);
},
removeRemoteStatsStopListener: function(listener)
{
eventEmitter.removeListener("statistics.stop", listener);
},
stop: function () {
stopLocal();
stopRemote();
if(eventEmitter)
{
eventEmitter.removeAllListeners();
}
},
stopRemoteStatistics: function()
{
stopRemote();
},
start: function () {
APP.RTC.addStreamListener(onStreamCreated,
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
APP.xmpp.addListener(XMPPEvents.CALL_INCOMING, function (event) {
startRemoteStats(event.peerconnection);
});
}
};
module.exports = statistics;
},{"../../service/RTC/StreamEventTypes.js":91,"../../service/xmpp/XMPPEvents":97,"./LocalStatsCollector.js":44,"./RTPStatsCollector.js":45,"events":98}],47:[function(require,module,exports){
var i18n = require("i18next-client");
var languages = require("../../service/translation/languages");
var Settings = require("../settings/Settings");
var DEFAULT_LANG = languages.EN;
i18n.addPostProcessor("resolveAppName", function(value, key, options) {
return value.replace("__app__", interfaceConfig.APP_NAME);
});
var defaultOptions = {
detectLngQS: "lang",
useCookie: false,
fallbackLng: DEFAULT_LANG,
load: "unspecific",
resGetPath: 'lang/__ns__-__lng__.json',
ns: {
namespaces: ['main', 'languages'],
defaultNs: 'main'
},
lngWhitelist : languages.getLanguages(),
fallbackOnNull: true,
fallbackOnEmpty: true,
useDataAttrOptions: true,
defaultValueFromContent: false,
app: interfaceConfig.APP_NAME,
getAsync: false,
defaultValueFromContent: false,
customLoad: function(lng, ns, options, done) {
var resPath = "lang/__ns__-__lng__.json";
if(lng === languages.EN)
resPath = "lang/__ns__.json";
var url = i18n.functions.applyReplacement(resPath, { lng: lng, ns: ns });
i18n.functions.ajax({
url: url,
success: function(data, status, xhr) {
i18n.functions.log('loaded: ' + url);
done(null, data);
},
error : function(xhr, status, error) {
if ((status && status == 200) ||
(xhr && xhr.status && xhr.status == 200)) {
// file loaded but invalid json, stop waste time !
i18n.functions.error('There is a typo in: ' + url);
} else if ((status && status == 404) ||
(xhr && xhr.status && xhr.status == 404)) {
i18n.functions.log('Does not exist: ' + url);
} else {
var theStatus = status ? status :
((xhr && xhr.status) ? xhr.status : null);
i18n.functions.log(theStatus + ' when loading ' + url);
}
done(error, {});
},
dataType: "json",
async : options.getAsync
});
}
// options for caching
// useLocalStorage: true,
// localStorageExpirationTime: 86400000 // in ms, default 1 week
};
function initCompleted(t)
{
$("[data-i18n]").i18n();
}
function checkForParameter() {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == "lang")
{
return pair[1];
}
}
return null;
}
module.exports = {
init: function (lang) {
var options = defaultOptions;
if(!lang)
{
lang = checkForParameter();
if(!lang)
{
var settings = Settings.getSettings();
if(settings)
lang = settings.language;
}
}
if(lang) {
options.lng = lang;
}
i18n.init(options, initCompleted);
},
translateString: function (key, options) {
return i18n.t(key, options);
},
setLanguage: function (lang) {
if(!lang)
lang = DEFAULT_LANG;
i18n.setLng(lang, defaultOptions, initCompleted);
},
getCurrentLanguage: function () {
return i18n.lng();
},
translateElement: function (selector) {
selector.i18n();
},
generateTranslatonHTML: function (key, options) {
var str = "<span data-i18n=\"" + key + "\"";
if(options)
{
str += " data-i18n-options=\"" + JSON.stringify(options) + "\"";
}
str += ">";
str += this.translateString(key, options);
str += "</span>";
return str;
}
};
},{"../../service/translation/languages":96,"../settings/Settings":38,"i18next-client":62}],48:[function(require,module,exports){
/* jshint -W117 */
var TraceablePeerConnection = require("./TraceablePeerConnection");
var SDPDiffer = require("./SDPDiffer");
var SDPUtil = require("./SDPUtil");
var SDP = require("./SDP");
var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
// Jingle stuff
function JingleSession(me, sid, connection, service) {
this.me = me;
this.sid = sid;
this.connection = connection;
this.initiator = null;
this.responder = null;
this.isInitiator = null;
this.peerjid = null;
this.state = null;
this.localSDP = null;
this.remoteSDP = null;
this.relayedStreams = [];
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
this.usedrip = false; // dripping is sending trickle candidates not one-by-one
this.hadstuncandidate = false;
this.hadturncandidate = false;
this.lasticecandidate = false;
this.statsinterval = null;
this.reason = null;
this.addssrc = [];
this.removessrc = [];
this.pendingop = null;
this.switchstreams = false;
this.wait = true;
this.localStreamsSSRC = null;
/**
* The indicator which determines whether the (local) video has been muted
* in response to a user command in contrast to an automatic decision made
* by the application logic.
*/
this.videoMuteByUser = false;
}
//TODO: this array must be removed when firefox implement multistream support
JingleSession.notReceivedSSRCs = [];
JingleSession.prototype.initiate = function (peerjid, isInitiator) {
var self = this;
if (this.state !== null) {
console.error('attempt to initiate on session ' + this.sid +
'in state ' + this.state);
return;
}
this.isInitiator = isInitiator;
this.state = 'pending';
this.initiator = isInitiator ? this.me : peerjid;
this.responder = !isInitiator ? this.me : peerjid;
this.peerjid = peerjid;
this.hadstuncandidate = false;
this.hadturncandidate = false;
this.lasticecandidate = false;
this.peerconnection
= new TraceablePeerConnection(
this.connection.jingle.ice_config,
this.connection.jingle.pc_constraints );
this.peerconnection.onicecandidate = function (event) {
self.sendIceCandidate(event.candidate);
};
this.peerconnection.onaddstream = function (event) {
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
self.remoteStreamAdded(event);
};
this.peerconnection.onremovestream = function (event) {
// Remove the stream from remoteStreams
// FIXME: remotestreamremoved.jingle not defined anywhere(unused)
$(document).trigger('remotestreamremoved.jingle', [event, self.sid]);
};
this.peerconnection.onsignalingstatechange = function (event) {
if (!(self && self.peerconnection)) return;
};
this.peerconnection.oniceconnectionstatechange = function (event) {
if (!(self && self.peerconnection)) return;
switch (self.peerconnection.iceConnectionState) {
case 'connected':
this.startTime = new Date();
break;
case 'disconnected':
this.stopTime = new Date();
break;
}
onIceConnectionStateChange(self.sid, self);
};
// add any local and relayed stream
APP.RTC.localStreams.forEach(function(stream) {
self.peerconnection.addStream(stream.getOriginalStream());
});
this.relayedStreams.forEach(function(stream) {
self.peerconnection.addStream(stream);
});
};
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';
var pranswer = this.peerconnection.localDescription;
if (!pranswer || pranswer.type != 'pranswer') {
return;
}
console.log('going from pranswer to answer');
if (this.usetrickle) {
// remove candidates already sent from session-accept
var lines = SDPUtil.find_lines(pranswer.sdp, 'a=candidate:');
for (var i = 0; i < lines.length; i++) {
pranswer.sdp = pranswer.sdp.replace(lines[i] + '\r\n', '');
}
}
while (SDPUtil.find_line(pranswer.sdp, 'a=inactive')) {
// FIXME: change any inactive to sendrecv or whatever they were originally
pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
}
pranswer = APP.simulcast.reverseTransformLocalDescription(pranswer);
var prsdp = new SDP(pranswer.sdp);
var accept = $iq({to: this.peerjid,
type: 'set'})
.c('jingle', {xmlns: 'urn:xmpp:jingle:1',
action: 'session-accept',
initiator: this.initiator,
responder: this.responder,
sid: this.sid });
prsdp.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
var sdp = this.peerconnection.localDescription.sdp;
while (SDPUtil.find_line(sdp, 'a=inactive')) {
// 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');
self.setLocalDescription();
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);
},
function (e) {
console.error('setLocalDescription failed', e);
}
);
};
JingleSession.prototype.terminate = function (reason) {
this.state = 'ended';
this.reason = reason;
this.peerconnection.close();
if (this.statsinterval !== null) {
window.clearInterval(this.statsinterval);
this.statsinterval = null;
}
};
JingleSession.prototype.active = function () {
return this.state == 'active';
};
JingleSession.prototype.sendIceCandidate = function (candidate) {
var self = this;
if (candidate && !this.lasticecandidate) {
var ice = SDPUtil.iceparams(this.localSDP.media[candidate.sdpMLineIndex], this.localSDP.session);
var jcand = SDPUtil.candidateToJingle(candidate.candidate);
if (!(ice && jcand)) {
console.error('failed to get ice && jcand');
return;
}
ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
if (jcand.type === 'srflx') {
this.hadstuncandidate = true;
} else if (jcand.type === 'relay') {
this.hadturncandidate = true;
}
if (this.usetrickle) {
if (this.usedrip) {
if (this.drip_container.length === 0) {
// start 20ms callout
window.setTimeout(function () {
if (self.drip_container.length === 0) return;
self.sendIceCandidates(self.drip_container);
self.drip_container = [];
}, 20);
}
this.drip_container.push(candidate);
return;
} else {
self.sendIceCandidate([candidate]);
}
}
} else {
//console.log('sendIceCandidate: last candidate.');
if (!this.usetrickle) {
//console.log('should send full offer now...');
var init = $iq({to: this.peerjid,
type: 'set'})
.c('jingle', {xmlns: 'urn:xmpp:jingle:1',
action: this.peerconnection.localDescription.type == 'offer' ? 'session-initiate' : 'session-accept',
initiator: this.initiator,
sid: this.sid});
this.localSDP = new SDP(this.peerconnection.localDescription.sdp);
var self = this;
var sendJingle = function (ssrc) {
if(!ssrc)
ssrc = {};
self.localSDP.toJingle(init, self.initiator == self.me ? 'initiator' : 'responder', ssrc);
self.connection.sendIQ(init,
function () {
//console.log('session initiate ack');
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);
}
sendJingle();
}
this.lasticecandidate = true;
console.log('Have we encountered any srflx candidates? ' + this.hadstuncandidate);
console.log('Have we encountered any relay candidates? ' + this.hadturncandidate);
if (!(this.hadstuncandidate || this.hadturncandidate) && this.peerconnection.signalingState != 'closed') {
$(document).trigger('nostuncandidates.jingle', [this.sid]);
}
}
};
JingleSession.prototype.sendIceCandidates = function (candidates) {
console.log('sendIceCandidates', candidates);
var cand = $iq({to: this.peerjid, type: 'set'})
.c('jingle', {xmlns: 'urn:xmpp:jingle:1',
action: 'transport-info',
initiator: this.initiator,
sid: this.sid});
for (var mid = 0; mid < this.localSDP.media.length; mid++) {
var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
var mline = SDPUtil.parse_mline(this.localSDP.media[mid].split('\r\n')[0]);
if (cands.length > 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 = APP.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 = '';
$(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';
}
});
var 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 = '';
$(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';
}
});
var 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);
if(new_stream)
self.peerconnection.addStream(new_stream);
}
APP.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');
}
};
/**
* Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
*
* @param mute <tt>true</tt> to mute the (local) video i.e. to disable all video
* tracks; otherwise, <tt>false</tt>
* @param callback a function to be invoked with <tt>mute</tt> 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
* <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> 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;
}
this.hardMuteVideo(mute);
this.modifySources(callback(mute));
};
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;
this.connection.emuc.doLeave();
APP.UI.messageHandler.showError("dialog.sorry",
"dialog.internalError");
}
JingleSession.prototype.setLocalDescription = function () {
// put our ssrcs into presence so other clients can identify our stream
var newssrcs = [];
var media = APP.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' && APP.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);
APP.UI.messageHandler.showError();
}
);
},
function (error) {
console.log('triggerKeyframe createAnswer failed', error);
APP.UI.messageHandler.showError();
}
);
},
function (error) {
console.log('triggerKeyframe setRemoteDescription failed', error);
APP.UI.messageHandler.showError();
}
);
}
JingleSession.prototype.remoteStreamAdded = function (data, times) {
var self = this;
var thessrc;
var ssrc2jid = this.connection.emuc.ssrc2jid;
// 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]) {
if (typeof times === 'undefined')
{
times = 0;
}
if (times > 10)
{
console.warning('Waiting for jid timed out', thessrc);
}
else
{
setTimeout(function(d) {
return function() {
self.remoteStreamAdded(d, times++);
}
}(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];
}
}
}
APP.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 &&
APP.RTC.localVideo.getTracks().length > 0) {
window.setTimeout(function () {
sendKeyframe(self.peerconnection);
}, 3000);
}
}
module.exports = JingleSession;
},{"../../service/RTC/RTCBrowserType":88,"./SDP":49,"./SDPDiffer":50,"./SDPUtil":51,"./TraceablePeerConnection":52}],49:[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 <tt>true</tt> if this SDP contains given SSRC.
* @param ssrc the ssrc to check.
* @returns {boolean} <tt>true</tt> 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 <param name=foo value=bar/>
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 = APP.RTC.localAudio.getId();
}
else
{
msid = APP.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';
// <description><rtcp-mux/></description>
// 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":51}],50:[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 <tt>otherSdp</tt>. 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;
},{}],51:[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
// <candidate component=... foundation=... generation=... id=... ip=... network=... port=... priority=... protocol=... type=.../>
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;
},{}],52:[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
var Interop = require('sdp-interop').Interop;
this.interop = new Interop();
// 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) {
if (typeof description === 'undefined' || description == null) {
return '';
}
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() {
this.trace('getLocalDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.localDescription));
// if we're running on FF, transform to Plan B first.
var desc = this.peerconnection.localDescription;
if (navigator.mozGetUserMedia) {
desc = this.interop.toPlanB(desc);
} else {
desc = APP.simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
}
this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
return desc;
});
TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
this.trace('getRemoteDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.remoteDescription));
// if we're running on FF, transform to Plan B first.
var desc = this.peerconnection.remoteDescription;
if (navigator.mozGetUserMedia) {
desc = this.interop.toPlanB(desc);
} else {
desc = APP.simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
}
this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
return desc;
});
}
TraceablePeerConnection.prototype.addStream = function (stream) {
this.trace('addStream', stream.id);
APP.simulcast.resetSender();
try
{
this.peerconnection.addStream(stream);
}
catch (e)
{
console.error(e);
return;
}
};
TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
this.trace('removeStream', stream.id);
APP.simulcast.resetSender();
if(stopStreams) {
stream.getAudioTracks().forEach(function (track) {
track.stop();
});
stream.getVideoTracks().forEach(function (track) {
track.stop();
});
}
try {
// FF doesn't support this yet.
this.peerconnection.removeStream(stream);
} catch (e) {
console.error(e);
}
};
TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
this.trace('createDataChannel', label, opts);
return this.peerconnection.createDataChannel(label, opts);
};
TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
this.trace('setLocalDescription::preTransform (Plan B)', dumpSDP(description));
// if we're running on FF, transform to Plan A first.
if (navigator.mozGetUserMedia) {
description = this.interop.toPlanA(description);
} else {
description = APP.simulcast.transformLocalDescription(description);
}
this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
var self = this;
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) {
this.trace('setRemoteDescription::preTransform (Plan B)', dumpSDP(description));
// if we're running on FF, transform to Plan A first.
if (navigator.mozGetUserMedia) {
description = this.interop.toPlanA(description);
}
else {
description = APP.simulcast.transformRemoteDescription(description);
}
this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
var self = this;
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::preTransform (Plan A)', dumpSDP(offer));
// if we're running on FF, transform to Plan B first.
if (navigator.mozGetUserMedia) {
offer = self.interop.toPlanB(offer);
}
self.trace('createOfferOnSuccess::postTransform (Plan B)', 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) {
self.trace('createAnswerOnSuccess::preTransfom (Plan A)', dumpSDP(answer));
// if we're running on FF, transform to Plan A first.
if (navigator.mozGetUserMedia) {
answer = self.interop.toPlanB(answer);
} else {
answer = APP.simulcast.transformAnswer(answer);
}
self.trace('createAnswerOnSuccess::postTransfom (Plan B)', 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;
},{"sdp-interop":80}],53:[function(require,module,exports){
/* global $, $iq, APP, config, connection, UI, messageHandler,
roomName, sessionTerminated, Strophe, Util */
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var Settings = require("../settings/Settings");
var AuthenticationEvents
= require("../../service/authentication/AuthenticationEvents");
/**
* Contains logic responsible for enabling/disabling functionality available
* only to moderator users.
*/
var connection = null;
var focusUserJid;
function createExpBackoffTimer(step) {
var count = 1;
return function (reset) {
// Reset call
if (reset) {
count = 1;
return;
}
// Calculate next timeout
var timeout = Math.pow(2, count - 1);
count += 1;
return timeout * step;
};
}
var getNextTimeout = createExpBackoffTimer(1000);
var getNextErrorTimeout = 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 eventEmitter = null;
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, emitter) {
this.xmppService = xmpp;
eventEmitter = emitter;
// Message listener that talks to POPUP window
function listener(event) {
if (event.data && event.data.sessionId) {
if (event.origin !== window.location.origin) {
console.warn(
"Ignoring sessionId from different origin: " + event.origin);
return;
}
localStorage.setItem('sessionId', event.data.sessionId);
// After popup is closed we will authenticate
}
}
// Register
if (window.addEventListener) {
window.addEventListener("message", listener, false);
} else {
window.attachEvent("onmessage", listener);
}
},
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'});
// Session Id used for authentication
var sessionId = localStorage.getItem('sessionId');
var machineUID = Settings.getSettings().uid;
console.info(
"Session ID: " + sessionId + " machine UID: " + machineUID);
elem.c('conference', {
xmlns: 'http://jitsi.org/protocol/focus',
room: roomName,
'machine-uid': machineUID
});
if (sessionId) {
elem.attrs({ 'session-id': sessionId});
}
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();
}
var roomName = APP.UI.generateRoomName();
if (typeof roomName !== 'string') roomName = '';
if (config.enableFirefoxSupport !== undefined && roomName.indexOf('rembson@') === -1) {
elem.c(
'property',
{ name: 'enableFirefoxHacks',
value: config.enableFirefoxSupport})
.up();
}
elem.up();
return elem;
},
parseSessionId: function (resultIq) {
var sessionId = $(resultIq).find('conference').attr('session-id');
if (sessionId) {
console.info('Received sessionId: ' + sessionId);
localStorage.setItem('sessionId', sessionId);
}
},
parseConfigOptions: function (resultIq) {
Moderator.setFocusUserJid(
$(resultIq).find('conference').attr('focusjid'));
var authenticationEnabled
= $(resultIq).find(
'>conference>property' +
'[name=\'authentication\'][value=\'true\']').length > 0;
console.info("Authentication enabled: " + authenticationEnabled);
externalAuthEnabled
= $(resultIq).find(
'>conference>property' +
'[name=\'externalAuth\'][value=\'true\']').length > 0;
console.info('External authentication enabled: ' + externalAuthEnabled);
if (!externalAuthEnabled) {
// We expect to receive sessionId in 'internal' authentication mode
Moderator.parseSessionId(resultIq);
}
var authIdentity = $(resultIq).find('>conference').attr('identity');
eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,
authenticationEnabled, authIdentity);
// 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\'][value=\'true\']').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);
var self = this;
connection.sendIQ(
iq,
function (result) {
// Setup config options
Moderator.parseConfigOptions(result);
if ('true' === $(result).find('conference').attr('ready')) {
// Reset both timers
getNextTimeout(true);
getNextErrorTimeout(true);
// 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) {
// Invalid session ? remove and try again
// without session ID to get a new one
var invalidSession
= $(error).find('>error>session-invalid').length;
if (invalidSession) {
console.info("Session expired! - removing");
localStorage.removeItem("sessionId");
}
if ($(error).find('>error>graceful-shutdown').length) {
eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
return;
}
// Check for error returned by the reservation system
var reservationErr = $(error).find('>error>reservation-error');
if (reservationErr.length) {
// Trigger error event
var errorCode = reservationErr.attr('error-code');
var errorMsg;
if ($(error).find('>error>text')) {
errorMsg = $(error).find('>error>text').text();
}
eventEmitter.emit(
XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
return;
}
// Not authorized to create new room
if ($(error).find('>error>not-authorized').length) {
console.warn("Unauthorized to start the conference", error);
var toDomain
= Strophe.getDomainFromJid(error.getAttribute('to'));
if (toDomain !== config.hosts.anonymousdomain) {
// FIXME: "is external" should come either from
// the focus or config.js
externalAuthEnabled = true;
}
eventEmitter.emit(
XMPPEvents.AUTHENTICATION_REQUIRED,
function () {
Moderator.allocateConferenceFocus(
roomName, callback);
});
return;
}
var waitMs = getNextErrorTimeout();
console.error("Focus error, retry after " + waitMs, error);
// Show message
var focusComponent = Moderator.getFocusComponent();
var retrySec = waitMs / 1000;
// FIXME: message is duplicated ?
// Do not show in case of session invalid
// which means just a retry
if (!invalidSession) {
APP.UI.messageHandler.notify(
null, "notify.focus",
'disconnected', "notify.focusFail",
{component: focusComponent, ms: retrySec});
}
// Reset response timeout
getNextTimeout(true);
window.setTimeout(
function () {
Moderator.allocateConferenceFocus(roomName, callback);
}, waitMs);
}
);
},
getLoginUrl: function (roomName, urlCallback) {
var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
iq.c('login-url', {
xmlns: 'http://jitsi.org/protocol/focus',
room: roomName,
'machine-uid': Settings.getSettings().uid
});
connection.sendIQ(
iq,
function (result) {
var url = $(result).find('login-url').attr('url');
url = url = decodeURIComponent(url);
if (url) {
console.info("Got auth url: " + url);
urlCallback(url);
} else {
console.error(
"Failed to get auth url from the focus", result);
}
},
function (error) {
console.error("Get auth url error", error);
}
);
},
getPopupLoginUrl: function (roomName, urlCallback) {
var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
iq.c('login-url', {
xmlns: 'http://jitsi.org/protocol/focus',
room: roomName,
'machine-uid': Settings.getSettings().uid,
popup: true
});
connection.sendIQ(
iq,
function (result) {
var url = $(result).find('login-url').attr('url');
url = url = decodeURIComponent(url);
if (url) {
console.info("Got POPUP auth url: " + url);
urlCallback(url);
} else {
console.error(
"Failed to get POPUP auth url from the focus", result);
}
},
function (error) {
console.error('Get POPUP auth url error', error);
}
);
},
logout: function (callback) {
var iq = $iq({to: Moderator.getFocusComponent(), type: 'set'});
var sessionId = localStorage.getItem('sessionId');
if (!sessionId) {
callback();
return;
}
iq.c('logout', {
xmlns: 'http://jitsi.org/protocol/focus',
'session-id': sessionId
});
connection.sendIQ(
iq,
function (result) {
var logoutUrl = $(result).find('logout').attr('logout-url');
if (logoutUrl) {
logoutUrl = decodeURIComponent(logoutUrl);
}
console.info("Log out OK, url: " + logoutUrl, result);
localStorage.removeItem('sessionId');
callback(logoutUrl);
},
function (error) {
console.error("Logout error", error);
}
);
}
};
module.exports = Moderator;
},{"../../service/authentication/AuthenticationEvents":93,"../../service/xmpp/XMPPEvents":97,"../settings/Settings":38}],54:[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, connection) {
if (useJirecon){
setRecordingJirecon(state, token, callback, connection);
} else {
setRecordingColibri(state, token, callback, connection);
}
}
function setRecordingJirecon(state, token, callback, connection) {
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, connection) {
var elem = $iq({to: connection.emuc.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, connection) {
if (!Moderator.isModerator()) {
console.log(
'non-focus, or conference not yet organized:' +
' not enabling recording');
return;
}
var self = this;
// Jirecon does not (currently) support a token.
if (!recordingToken && !useJirecon) {
tokenEmptyCallback(function (value) {
setRecordingToken(value);
self.toggleRecording(tokenEmptyCallback,
startingCallback, startedCallback, connection);
});
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);
},
connection
);
}
}
module.exports = Recording;
},{"./moderator":53}],55:[function(require,module,exports){
/* jshint -W117 */
/* a simple MUC connection plugin
* can only handle a single MUC room
*/
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var Moderator = require("./moderator");
var JingleSession = require("./JingleSession");
var bridgeIsDown = false;
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,
focusMucJid: null,
ssrc2jid: {},
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'});
var self = this;
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();
self.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()) {
eventEmitter.emit(XMPPEvents.ETHERPAD, 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 devices = $(pres).find('>devices');
if(devices.length)
{
var audio = devices.find('>audio');
var video = devices.find('>video');
var devicesValues = {audio: false, video: false};
if(audio.length && audio.text() === "true")
{
devicesValues.audio = true;
}
if(video.length && video.text() === "true")
{
devicesValues.video = true;
}
eventEmitter.emit(XMPPEvents.DEVICE_AVAILABLE,
Strophe.getResourceFromJid(from), devicesValues);
}
var stats = $(pres).find('>stats');
if (stats.length) {
var statsObj = {};
Strophe.forEachChild(stats[0], "stat", function (el) {
statsObj[el.getAttribute("name")] = el.getAttribute("value");
});
eventEmitter.emit(XMPPEvents.REMOTE_STATS, 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;
eventEmitter.emit(XMPPEvents.LOCALROLE_CHANGED,
from, member, pres, Moderator.isModerator());
}
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) {
this.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();
}
eventEmitter.emit(XMPPEvents.MUC_ENTER, from, id, member.displayName);
}
} else {
// Presence update for existing participant
// Watch role change:
if (this.members[from].role != member.role) {
this.members[from].role = member.role;
eventEmitter.emit(XMPPEvents.MUC_ROLE_CHANGED,
member.role, member.displayName);
}
}
// Always trigger presence to update bindings
this.parsePresence(from, member, pres);
// Trigger status message update
if (member.status) {
eventEmitter.emit(XMPPEvents.PRESENCE_STATUS, from, member);
}
return true;
},
onPresenceUnavailable: function (pres) {
var from = pres.getAttribute('from');
// room destroyed ?
if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]' +
'>destroy').length) {
var reason;
var reasonSelect = $(pres).find(
'>x[xmlns="http://jabber.org/protocol/muc#user"]' +
'>destroy>reason');
if (reasonSelect.length) {
reason = reasonSelect.text();
}
XMPP.disposeConference(false);
eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);
return true;
}
// 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;
eventEmitter.emit(XMPPEvents.PASSWORD_REQUIRED, 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) {
// enter the room by replying with 'not-authorized'. This would
// result in reconnection from authorized domain.
// We're either missing Jicofo/Prosody config for anonymous
// domains or something is wrong.
// XMPP.promptLogin();
APP.UI.messageHandler.openReportDialog(null,
"dialog.joinError", pres);
} else {
console.warn('onPresError ', pres);
APP.UI.messageHandler.openReportDialog(null,
"dialog.connectError",
pres);
}
} else {
console.warn('onPresError ', pres);
APP.UI.messageHandler.openReportDialog(null,
"dialog.connectError",
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);
eventEmitter.emit(XMPPEvents.SENDING_CHAT_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") {
eventEmitter.emit(XMPPEvents.CHAT_ERROR_RECEIVED,
$(msg).find('>text').text(), txt);
return true;
}
var subject = $(msg).find('>subject');
if (subject.length) {
var subjectText = subject.text();
if (subjectText || subjectText == "") {
eventEmitter.emit(XMPPEvents.SUBJECT_CHANGED, subjectText);
console.log("Subject is changed to " + subjectText);
}
}
if (txt) {
console.log('chat', nick, txt);
eventEmitter.emit(XMPPEvents.MESSAGE_RECEIVED,
from, nick, txt, this.myroomjid);
}
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?
ob.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["devices"])
{
pres.c('devices').c('audio').t(this.presMap['devices'].audio).up()
.c('video').t(this.presMap['devices'].video).up().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();
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;
},
addDevicesToPresence: function (devices) {
this.presMap['devices'] = devices;
},
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) {
eventEmitter.emit(XMPPEvents.MUC_LEFT, 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;
var self = this;
// Remove old ssrcs coming from the jid
Object.keys(this.ssrc2jid).forEach(function (ssrc) {
if (self.ssrc2jid[ssrc] == from) {
delete self.ssrc2jid[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');
self.ssrc2jid[ssrcV] = from;
JingleSession.notReceivedSSRCs.push(ssrcV);
var type = ssrc.getAttribute('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)
{
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);
}
});
};
},{"../../service/xmpp/XMPPEvents":97,"./JingleSession":48,"./moderator":53}],56:[function(require,module,exports){
/* jshint -W117 */
var JingleSession = require("./JingleSession");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
module.exports = function(XMPP, eventEmitter)
{
function CallIncomingJingle(sid, connection) {
var sess = connection.jingle.sessions[sid];
// TODO: do we check activecall == null?
connection.jingle.activecall = sess;
eventEmitter.emit(XMPPEvents.CALL_INCOMING, 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: {},
activecall: null,
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:transports:dtls-sctp: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;
}
});
};
},{"../../service/xmpp/XMPPEvents":97,"./JingleSession":48}],57:[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]);
}
});
};
},{}],58:[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: this.connection.emuc.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 !== this.connection.emuc.focusMucJid) {
console.warn("Ignored mute from non focus peer");
return false;
}
var mute = $(iq).find('mute');
if (mute.length) {
var doMuteAudio = mute.text() === "true";
APP.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);
}
});
}
},{}],59:[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: this.connection.emuc.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;
}
);
}
}
);
};
},{}],60:[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";
}
};
};
},{}],61:[function(require,module,exports){
/* global $, APP, config, Strophe*/
var Moderator = require("./moderator");
var EventEmitter = require("events");
var Recording = require("./recording");
var SDP = require("./SDP");
var Settings = require("../settings/Settings");
var Pako = require("pako");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
var RTCEvents = require("../../service/RTC/RTCEvents");
var UIEvents = require("../../service/UI/UIEvents");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var eventEmitter = new EventEmitter();
var connection = null;
var authenticatedUser = false;
function connect(jid, password) {
connection = XMPP.createConnection();
Moderator.setConnection(connection);
if (connection.disco) {
// for chrome, add multistream cap
}
connection.jingle.pc_constraints = APP.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});
}
// Include user info in MUC presence
var settings = Settings.getSettings();
if (settings.email) {
connection.emuc.addEmailToPresence(settings.email);
}
if (settings.uid) {
connection.emuc.addUserIdToPresence(settings.uid);
}
if (settings.displayName) {
connection.emuc.addDisplayNameToPresence(settings.displayName);
}
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();
}
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)
&& (APP.RTC.localAudio || APP.RTC.localVideo)) {
// .connected is true while connecting?
doJoin();
}
}
function doJoin() {
var roomName = APP.UI.generateRoomName();
Moderator.allocateConferenceFocus(
roomName, APP.UI.checkForNicknameAndJoin);
}
function initStrophePlugins()
{
require("./strophe.emuc")(XMPP, eventEmitter);
require("./strophe.jingle")(XMPP, eventEmitter);
require("./strophe.moderate")(XMPP);
require("./strophe.util")();
require("./strophe.rayo")();
require("./strophe.logger")();
}
function registerListeners() {
APP.RTC.addStreamListener(maybeDoJoin,
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
XMPP.addToPresence("devices", devices);
})
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
XMPP.addToPresence("displayName", nickname);
});
}
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: "<body rid='" + (connection.rid || connection._proto.rid)
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='"
+ (connection.sid || connection._proto.sid)
+ "' type='terminate'>" +
"<presence xmlns='jabber:client' type='unavailable'/>" +
"</body>",
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,
/**
* XMPP connection status
*/
Status: Strophe.Status,
/**
* Remembers if we were muted by the focus.
* @type {boolean}
*/
forceMuted: false,
start: function () {
setupEvents();
initStrophePlugins();
registerListeners();
Moderator.init(this, eventEmitter);
var configDomain = config.hosts.anonymousdomain || config.hosts.domain;
// Force authenticated domain if room is appended with '?login=true'
if (config.hosts.anonymousdomain &&
window.location.search.indexOf("login=true") !== -1) {
configDomain = config.hosts.domain;
}
var jid = configDomain || window.location.hostname;
connect(jid, null);
},
createConnection: function () {
var bosh = config.bosh || '/http-bind';
return new Strophe.Connection(bosh);
},
getStatusString: function (status) {
return Strophe.getStatusString(status);
},
promptLogin: function () {
// FIXME: re-use LoginDialog which supports retries
APP.UI.showLoginPopup(connect);
},
joinRoom: 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 = connection.jingle.activecall;
if (handler && handler.peerconnection) {
// FIXME: probably removing streams is not required and close() should
// be enough
if (APP.RTC.localAudio) {
handler.peerconnection.removeStream(
APP.RTC.localAudio.getOriginalStream(), onUnload);
}
if (APP.RTC.localVideo) {
handler.peerconnection.removeStream(
APP.RTC.localVideo.getOriginalStream(), onUnload);
}
handler.peerconnection.close();
}
connection.jingle.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);
},
getLoginUrl: function (roomName, callback) {
Moderator.getLoginUrl(roomName, callback);
},
getPopupLoginUrl: function (roomName, callback) {
Moderator.getPopupLoginUrl(roomName, callback);
},
isModerator: function () {
return Moderator.isModerator();
},
isSipGatewayEnabled: function () {
return Moderator.isSipGatewayEnabled();
},
isExternalAuthEnabled: function () {
return Moderator.isExternalAuthEnabled();
},
switchStreams: function (stream, oldStream, callback) {
if (connection && connection.jingle.activecall) {
// FIXME: will block switchInProgress on true value in case of exception
connection.jingle.activecall.switchStreams(stream, oldStream, callback);
} else {
// We are done immediately
console.warn("No conference handler or conference not started yet");
callback();
}
},
sendVideoInfoPresence: function (mute) {
connection.emuc.addVideoInfoToPresence(mute);
connection.emuc.sendPresence();
},
setVideoMute: function (mute, callback, options) {
if(!connection)
return;
var self = this;
var localCallback = function (mute) {
self.sendVideoInfoPresence(mute);
return callback(mute);
};
if(connection.jingle.activecall)
{
connection.jingle.activecall.setVideoMute(
mute, localCallback, options);
}
else {
localCallback(mute);
}
},
setAudioMute: function (mute, callback) {
if (!(connection && APP.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 == APP.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.
APP.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');
APP.UI.messageHandler.showError("dialog.error",
"dialog.SLDFailure");
}
);
},
function (error) {
console.log(error);
APP.UI.messageHandler.showError();
}
);
},
function (error) {
console.log('muteVideo SRD error');
APP.UI.messageHandler.showError("dialog.error",
"dialog.SRDFailure");
}
);
},
toggleRecording: function (tokenEmptyCallback,
startingCallback, startedCallback) {
Recording.toggleRecording(tokenEmptyCallback,
startingCallback, startedCallback, connection);
},
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);
break;
case "devices":
connection.emuc.addDevicesToPresence(value);
break;
default :
console.log("Unknown tag for presence: " + name);
return;
}
if (!dontSend)
connection.emuc.sendPresence();
},
/**
* Sends 'data' as a log message to the focus. Returns true iff a message
* was sent.
* @param data
* @returns {boolean} true iff a message was sent.
*/
sendLogs: function (data) {
if(!connection.emuc.focusMucJid)
return false;
var deflate = true;
var content = JSON.stringify(data);
if (deflate) {
content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
}
content = Base64.encode(content);
// XEP-0337-ish
var message = $msg({to: connection.emuc.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);
return true;
},
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);
},
logout: function (callback) {
Moderator.logout(callback);
},
findJidFromResource: function (resource) {
return connection.emuc.findJidFromResource(resource);
},
getMembers: function () {
return connection.emuc.members;
},
getJidFromSSRC: function (ssrc) {
if(!connection)
return null;
return connection.emuc.ssrc2jid[ssrc];
},
getMUCJoined: function () {
return connection.emuc.joined;
},
getSessions: function () {
return connection.jingle.sessions;
},
removeStream: function (stream) {
if(!connection || !connection.jingle.activecall ||
!connection.jingle.activecall.peerconnection)
return;
connection.jingle.activecall.peerconnection.removeStream(stream);
}
};
module.exports = XMPP;
},{"../../service/RTC/RTCEvents":89,"../../service/RTC/StreamEventTypes":91,"../../service/UI/UIEvents":92,"../../service/xmpp/XMPPEvents":97,"../settings/Settings":38,"./SDP":49,"./moderator":53,"./recording":54,"./strophe.emuc":55,"./strophe.jingle":56,"./strophe.logger":57,"./strophe.moderate":58,"./strophe.rayo":59,"./strophe.util":60,"events":98,"pako":63}],62:[function(require,module,exports){
// i18next, v1.7.7
// Copyright (c)2014 Jan Mühlemann (jamuhl).
// Distributed under MIT license
// http://i18next.com
(function() {
// add indexOf to non ECMA-262 standard compliant browsers
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
// add lastIndexOf to non ECMA-262 standard compliant browsers
if (!Array.prototype.lastIndexOf) {
Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = len;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) {
n = 0;
} else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
for (; k >= 0; k--) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
// Add string trim for IE8.
if (typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
}
}
var root = this
, $ = root.jQuery || root.Zepto
, i18n = {}
, resStore = {}
, currentLng
, replacementCounter = 0
, languages = []
, initialized = false
, sync = {};
// Export the i18next object for **CommonJS**.
// If we're not in CommonJS, add `i18n` to the
// global object or to jquery.
if (typeof module !== 'undefined' && module.exports) {
if (!$) {
try {
$ = require('jquery');
} catch(e) {
// just ignore
}
}
if ($) {
$.i18n = $.i18n || i18n;
}
module.exports = i18n;
} else {
if ($) {
$.i18n = $.i18n || i18n;
}
root.i18n = root.i18n || i18n;
}
sync = {
load: function(lngs, options, cb) {
if (options.useLocalStorage) {
sync._loadLocal(lngs, options, function(err, store) {
var missingLngs = [];
for (var i = 0, len = lngs.length; i < len; i++) {
if (!store[lngs[i]]) missingLngs.push(lngs[i]);
}
if (missingLngs.length > 0) {
sync._fetch(missingLngs, options, function(err, fetched) {
f.extend(store, fetched);
sync._storeLocal(fetched);
cb(null, store);
});
} else {
cb(null, store);
}
});
} else {
sync._fetch(lngs, options, function(err, store){
cb(null, store);
});
}
},
_loadLocal: function(lngs, options, cb) {
var store = {}
, nowMS = new Date().getTime();
if(window.localStorage) {
var todo = lngs.length;
f.each(lngs, function(key, lng) {
var local = window.localStorage.getItem('res_' + lng);
if (local) {
local = JSON.parse(local);
if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) {
store[lng] = local;
}
}
todo--; // wait for all done befor callback
if (todo === 0) cb(null, store);
});
}
},
_storeLocal: function(store) {
if(window.localStorage) {
for (var m in store) {
store[m].i18nStamp = new Date().getTime();
f.localStorage.setItem('res_' + m, JSON.stringify(store[m]));
}
}
return;
},
_fetch: function(lngs, options, cb) {
var ns = options.ns
, store = {};
if (!options.dynamicLoad) {
var todo = ns.namespaces.length * lngs.length
, errors;
// load each file individual
f.each(ns.namespaces, function(nsIndex, nsValue) {
f.each(lngs, function(lngIndex, lngValue) {
// Call this once our translation has returned.
var loadComplete = function(err, data) {
if (err) {
errors = errors || [];
errors.push(err);
}
store[lngValue] = store[lngValue] || {};
store[lngValue][nsValue] = data;
todo--; // wait for all done befor callback
if (todo === 0) cb(errors, store);
};
if(typeof options.customLoad == 'function'){
// Use the specified custom callback.
options.customLoad(lngValue, nsValue, options, loadComplete);
} else {
//~ // Use our inbuilt sync.
sync._fetchOne(lngValue, nsValue, options, loadComplete);
}
});
});
} else {
// Call this once our translation has returned.
var loadComplete = function(err, data) {
cb(null, data);
};
if(typeof options.customLoad == 'function'){
// Use the specified custom callback.
options.customLoad(lngs, ns.namespaces, options, loadComplete);
} else {
var url = applyReplacement(options.resGetPath, { lng: lngs.join('+'), ns: ns.namespaces.join('+') });
// load all needed stuff once
f.ajax({
url: url,
success: function(data, status, xhr) {
f.log('loaded: ' + url);
loadComplete(null, data);
},
error : function(xhr, status, error) {
f.log('failed loading: ' + url);
loadComplete('failed loading resource.json error: ' + error);
},
dataType: "json",
async : options.getAsync
});
}
}
},
_fetchOne: function(lng, ns, options, done) {
var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
f.ajax({
url: url,
success: function(data, status, xhr) {
f.log('loaded: ' + url);
done(null, data);
},
error : function(xhr, status, error) {
if ((status && status == 200) || (xhr && xhr.status && xhr.status == 200)) {
// file loaded but invalid json, stop waste time !
f.error('There is a typo in: ' + url);
} else if ((status && status == 404) || (xhr && xhr.status && xhr.status == 404)) {
f.log('Does not exist: ' + url);
} else {
var theStatus = status ? status : ((xhr && xhr.status) ? xhr.status : null);
f.log(theStatus + ' when loading ' + url);
}
done(error, {});
},
dataType: "json",
async : options.getAsync
});
},
postMissing: function(lng, ns, key, defaultValue, lngs) {
var payload = {};
payload[key] = defaultValue;
var urls = [];
if (o.sendMissingTo === 'fallback' && o.fallbackLng[0] !== false) {
for (var i = 0; i < o.fallbackLng.length; i++) {
urls.push({lng: o.fallbackLng[i], url: applyReplacement(o.resPostPath, { lng: o.fallbackLng[i], ns: ns })});
}
} else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng[0] === false) ) {
urls.push({lng: lng, url: applyReplacement(o.resPostPath, { lng: lng, ns: ns })});
} else if (o.sendMissingTo === 'all') {
for (var i = 0, l = lngs.length; i < l; i++) {
urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })});
}
}
for (var y = 0, len = urls.length; y < len; y++) {
var item = urls[y];
f.ajax({
url: item.url,
type: o.sendType,
data: payload,
success: function(data, status, xhr) {
f.log('posted missing key \'' + key + '\' to: ' + item.url);
// add key to resStore
var keys = key.split('.');
var x = 0;
var value = resStore[item.lng][ns];
while (keys[x]) {
if (x === keys.length - 1) {
value = value[keys[x]] = defaultValue;
} else {
value = value[keys[x]] = value[keys[x]] || {};
}
x++;
}
},
error : function(xhr, status, error) {
f.log('failed posting missing key \'' + key + '\' to: ' + item.url);
},
dataType: "json",
async : o.postAsync
});
}
},
reload: reload
};
// defaults
var o = {
lng: undefined,
load: 'all',
preload: [],
lowerCaseLng: false,
returnObjectTrees: false,
fallbackLng: ['dev'],
fallbackNS: [],
detectLngQS: 'setLng',
detectLngFromLocalStorage: false,
ns: 'translation',
fallbackOnNull: true,
fallbackOnEmpty: false,
fallbackToDefaultNS: false,
nsseparator: ':',
keyseparator: '.',
selectorAttr: 'data-i18n',
debug: false,
resGetPath: 'locales/__lng__/__ns__.json',
resPostPath: 'locales/add/__lng__/__ns__',
getAsync: true,
postAsync: true,
resStore: undefined,
useLocalStorage: false,
localStorageExpirationTime: 7*24*60*60*1000,
dynamicLoad: false,
sendMissing: false,
sendMissingTo: 'fallback', // current | all
sendType: 'POST',
interpolationPrefix: '__',
interpolationSuffix: '__',
defaultVariables: false,
reusePrefix: '$t(',
reuseSuffix: ')',
pluralSuffix: '_plural',
pluralNotFound: ['plural_not_found', Math.random()].join(''),
contextNotFound: ['context_not_found', Math.random()].join(''),
escapeInterpolation: false,
indefiniteSuffix: '_indefinite',
indefiniteNotFound: ['indefinite_not_found', Math.random()].join(''),
setJqueryExt: true,
defaultValueFromContent: true,
useDataAttrOptions: false,
cookieExpirationTime: undefined,
useCookie: true,
cookieName: 'i18next',
cookieDomain: undefined,
objectTreeKeyHandler: undefined,
postProcess: undefined,
parseMissingKey: undefined,
missingKeyHandler: sync.postMissing,
shortcutFunction: 'sprintf' // or: defaultValue
};
function _extend(target, source) {
if (!source || typeof source === 'function') {
return target;
}
for (var attr in source) { target[attr] = source[attr]; }
return target;
}
function _deepExtend(target, source) {
for (var prop in source)
if (prop in target)
_deepExtend(target[prop], source[prop]);
else
target[prop] = source[prop];
return target;
}
function _each(object, callback, args) {
var name, i = 0,
length = object.length,
isObj = length === undefined || Object.prototype.toString.apply(object) !== '[object Array]' || typeof object === "function";
if (args) {
if (isObj) {
for (name in object) {
if (callback.apply(object[name], args) === false) {
break;
}
}
} else {
for ( ; i < length; ) {
if (callback.apply(object[i++], args) === false) {
break;
}
}
}
// A special, fast, case for the most common use of each
} else {
if (isObj) {
for (name in object) {
if (callback.call(object[name], name, object[name]) === false) {
break;
}
}
} else {
for ( ; i < length; ) {
if (callback.call(object[i], i, object[i++]) === false) {
break;
}
}
}
}
return object;
}
var _entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
function _escape(data) {
if (typeof data === 'string') {
return data.replace(/[&<>"'\/]/g, function (s) {
return _entityMap[s];
});
}else{
return data;
}
}
function _ajax(options) {
// v0.5.0 of https://github.com/goloroden/http.js
var getXhr = function (callback) {
// Use the native XHR object if the browser supports it.
if (window.XMLHttpRequest) {
return callback(null, new XMLHttpRequest());
} else if (window.ActiveXObject) {
// In Internet Explorer check for ActiveX versions of the XHR object.
try {
return callback(null, new ActiveXObject("Msxml2.XMLHTTP"));
} catch (e) {
return callback(null, new ActiveXObject("Microsoft.XMLHTTP"));
}
}
// If no XHR support was found, throw an error.
return callback(new Error());
};
var encodeUsingUrlEncoding = function (data) {
if(typeof data === 'string') {
return data;
}
var result = [];
for(var dataItem in data) {
if(data.hasOwnProperty(dataItem)) {
result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem]));
}
}
return result.join('&');
};
var utf8 = function (text) {
text = text.replace(/\r\n/g, '\n');
var result = '';
for(var i = 0; i < text.length; i++) {
var c = text.charCodeAt(i);
if(c < 128) {
result += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
result += String.fromCharCode((c >> 6) | 192);
result += String.fromCharCode((c & 63) | 128);
} else {
result += String.fromCharCode((c >> 12) | 224);
result += String.fromCharCode(((c >> 6) & 63) | 128);
result += String.fromCharCode((c & 63) | 128);
}
}
return result;
};
var base64 = function (text) {
var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
text = utf8(text);
var result = '',
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
do {
chr1 = text.charCodeAt(i++);
chr2 = text.charCodeAt(i++);
chr3 = text.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if(isNaN(chr2)) {
enc3 = enc4 = 64;
} else if(isNaN(chr3)) {
enc4 = 64;
}
result +=
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = '';
enc1 = enc2 = enc3 = enc4 = '';
} while(i < text.length);
return result;
};
var mergeHeaders = function () {
// Use the first header object as base.
var result = arguments[0];
// Iterate through the remaining header objects and add them.
for(var i = 1; i < arguments.length; i++) {
var currentHeaders = arguments[i];
for(var header in currentHeaders) {
if(currentHeaders.hasOwnProperty(header)) {
result[header] = currentHeaders[header];
}
}
}
// Return the merged headers.
return result;
};
var ajax = function (method, url, options, callback) {
// Adjust parameters.
if(typeof options === 'function') {
callback = options;
options = {};
}
// Set default parameter values.
options.cache = options.cache || false;
options.data = options.data || {};
options.headers = options.headers || {};
options.jsonp = options.jsonp || false;
options.async = options.async === undefined ? true : options.async;
// Merge the various header objects.
var headers = mergeHeaders({
'accept': '*/*',
'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
}, ajax.headers, options.headers);
// Encode the data according to the content-type.
var payload;
if (headers['content-type'] === 'application/json') {
payload = JSON.stringify(options.data);
} else {
payload = encodeUsingUrlEncoding(options.data);
}
// Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call
// if neccessary.
if(method === 'GET') {
// Setup the query string.
var queryString = [];
if(payload) {
queryString.push(payload);
payload = null;
}
// Handle caching.
if(!options.cache) {
queryString.push('_=' + (new Date()).getTime());
}
// If neccessary prepare the query string for a JSONP call.
if(options.jsonp) {
queryString.push('callback=' + options.jsonp);
queryString.push('jsonp=' + options.jsonp);
}
// Merge the query string and attach it to the url.
queryString = queryString.join('&');
if (queryString.length > 1) {
if (url.indexOf('?') > -1) {
url += '&' + queryString;
} else {
url += '?' + queryString;
}
}
// Make a JSONP call if neccessary.
if(options.jsonp) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
head.appendChild(script);
return;
}
}
// Since we got here, it is no JSONP request, so make a normal XHR request.
getXhr(function (err, xhr) {
if(err) return callback(err);
// Open the request.
xhr.open(method, url, options.async);
// Set the request headers.
for(var header in headers) {
if(headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header, headers[header]);
}
}
// Handle the request events.
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
var data = xhr.responseText || '';
// If no callback is given, return.
if(!callback) {
return;
}
// Return an object that provides access to the data as text and JSON.
callback(xhr.status, {
text: function () {
return data;
},
json: function () {
try {
return JSON.parse(data)
} catch (e) {
f.error('Can not parse JSON. URL: ' + url);
return {};
}
}
});
}
};
// Actually send the XHR request.
xhr.send(payload);
});
};
// Define the external interface.
var http = {
authBasic: function (username, password) {
ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password);
},
connect: function (url, options, callback) {
return ajax('CONNECT', url, options, callback);
},
del: function (url, options, callback) {
return ajax('DELETE', url, options, callback);
},
get: function (url, options, callback) {
return ajax('GET', url, options, callback);
},
head: function (url, options, callback) {
return ajax('HEAD', url, options, callback);
},
headers: function (headers) {
ajax.headers = headers || {};
},
isAllowed: function (url, verb, callback) {
this.options(url, function (status, data) {
callback(data.text().indexOf(verb) !== -1);
});
},
options: function (url, options, callback) {
return ajax('OPTIONS', url, options, callback);
},
patch: function (url, options, callback) {
return ajax('PATCH', url, options, callback);
},
post: function (url, options, callback) {
return ajax('POST', url, options, callback);
},
put: function (url, options, callback) {
return ajax('PUT', url, options, callback);
},
trace: function (url, options, callback) {
return ajax('TRACE', url, options, callback);
}
};
var methode = options.type ? options.type.toLowerCase() : 'get';
http[methode](options.url, options, function (status, data) {
// file: protocol always gives status code 0, so check for data
if (status === 200 || (status === 0 && data.text())) {
options.success(data.json(), status, null);
} else {
options.error(data.text(), status, null);
}
});
}
var _cookie = {
create: function(name,value,minutes,domain) {
var expires;
if (minutes) {
var date = new Date();
date.setTime(date.getTime()+(minutes*60*1000));
expires = "; expires="+date.toGMTString();
}
else expires = "";
domain = (domain)? "domain="+domain+";" : "";
document.cookie = name+"="+value+expires+";"+domain+"path=/";
},
read: function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
}
return null;
},
remove: function(name) {
this.create(name,"",-1);
}
};
var cookie_noop = {
create: function(name,value,minutes,domain) {},
read: function(name) { return null; },
remove: function(name) {}
};
// move dependent functions to a container so that
// they can be overriden easier in no jquery environment (node.js)
var f = {
extend: $ ? $.extend : _extend,
deepExtend: _deepExtend,
each: $ ? $.each : _each,
ajax: $ ? $.ajax : (typeof document !== 'undefined' ? _ajax : function() {}),
cookie: typeof document !== 'undefined' ? _cookie : cookie_noop,
detectLanguage: detectLanguage,
escape: _escape,
log: function(str) {
if (o.debug && typeof console !== "undefined") console.log(str);
},
error: function(str) {
if (typeof console !== "undefined") console.error(str);
},
getCountyIndexOfLng: function(lng) {
var lng_index = 0;
if (lng === 'nb-NO' || lng === 'nn-NO' || lng === 'nb-no' || lng === 'nn-no') lng_index = 1;
return lng_index;
},
toLanguages: function(lng) {
var log = this.log;
function applyCase(l) {
var ret = l;
if (typeof l === 'string' && l.indexOf('-') > -1) {
var parts = l.split('-');
ret = o.lowerCaseLng ?
parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
} else {
ret = o.lowerCaseLng ? l.toLowerCase() : l;
}
return ret;
}
var languages = [];
var whitelist = o.lngWhitelist || false;
var addLanguage = function(language){
//reject langs not whitelisted
if(!whitelist || whitelist.indexOf(language) > -1){
languages.push(language);
}else{
log('rejecting non-whitelisted language: ' + language);
}
};
if (typeof lng === 'string' && lng.indexOf('-') > -1) {
var parts = lng.split('-');
if (o.load !== 'unspecific') addLanguage(applyCase(lng));
if (o.load !== 'current') addLanguage(applyCase(parts[this.getCountyIndexOfLng(lng)]));
} else {
addLanguage(applyCase(lng));
}
for (var i = 0; i < o.fallbackLng.length; i++) {
if (languages.indexOf(o.fallbackLng[i]) === -1 && o.fallbackLng[i]) languages.push(applyCase(o.fallbackLng[i]));
}
return languages;
},
regexEscape: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
regexReplacementEscape: function(strOrFn) {
if (typeof strOrFn === 'string') {
return strOrFn.replace(/\$/g, "$$$$");
} else {
return strOrFn;
}
},
localStorage: {
setItem: function(key, value) {
if (window.localStorage) {
try {
window.localStorage.setItem(key, value);
} catch (e) {
f.log('failed to set value for key "' + key + '" to localStorage.');
}
}
}
}
};
function init(options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
// override defaults with passed in options
f.extend(o, options);
delete o.fixLng; /* passed in each time */
// override functions: .log(), .detectLanguage(), etc
if (o.functions) {
delete o.functions;
f.extend(f, options.functions);
}
// create namespace object if namespace is passed in as string
if (typeof o.ns == 'string') {
o.ns = { namespaces: [o.ns], defaultNs: o.ns};
}
// fallback namespaces
if (typeof o.fallbackNS == 'string') {
o.fallbackNS = [o.fallbackNS];
}
// fallback languages
if (typeof o.fallbackLng == 'string' || typeof o.fallbackLng == 'boolean') {
o.fallbackLng = [o.fallbackLng];
}
// escape prefix/suffix
o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);
if (!o.lng) o.lng = f.detectLanguage();
languages = f.toLanguages(o.lng);
currentLng = languages[0];
f.log('currentLng set to: ' + currentLng);
if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng){ //cookie is unset or invalid
f.cookie.create(o.cookieName, currentLng, o.cookieExpirationTime, o.cookieDomain);
}
if (o.detectLngFromLocalStorage && typeof document !== 'undefined' && window.localStorage) {
f.localStorage.setItem('i18next_lng', currentLng);
}
var lngTranslate = translate;
if (options.fixLng) {
lngTranslate = function(key, options) {
options = options || {};
options.lng = options.lng || lngTranslate.lng;
return translate(key, options);
};
lngTranslate.lng = currentLng;
}
pluralExtensions.setCurrentLng(currentLng);
// add JQuery extensions
if ($ && o.setJqueryExt) addJqueryFunct();
// jQuery deferred
var deferred;
if ($ && $.Deferred) {
deferred = $.Deferred();
}
// return immidiatly if res are passed in
if (o.resStore) {
resStore = o.resStore;
initialized = true;
if (cb) cb(lngTranslate);
if (deferred) deferred.resolve(lngTranslate);
if (deferred) return deferred.promise();
return;
}
// languages to load
var lngsToLoad = f.toLanguages(o.lng);
if (typeof o.preload === 'string') o.preload = [o.preload];
for (var i = 0, l = o.preload.length; i < l; i++) {
var pres = f.toLanguages(o.preload[i]);
for (var y = 0, len = pres.length; y < len; y++) {
if (lngsToLoad.indexOf(pres[y]) < 0) {
lngsToLoad.push(pres[y]);
}
}
}
// else load them
i18n.sync.load(lngsToLoad, o, function(err, store) {
resStore = store;
initialized = true;
if (cb) cb(lngTranslate);
if (deferred) deferred.resolve(lngTranslate);
});
if (deferred) return deferred.promise();
}
function preload(lngs, cb) {
if (typeof lngs === 'string') lngs = [lngs];
for (var i = 0, l = lngs.length; i < l; i++) {
if (o.preload.indexOf(lngs[i]) < 0) {
o.preload.push(lngs[i]);
}
}
return init(cb);
}
function addResourceBundle(lng, ns, resources, deep) {
if (typeof ns !== 'string') {
resources = ns;
ns = o.ns.defaultNs;
} else if (o.ns.namespaces.indexOf(ns) < 0) {
o.ns.namespaces.push(ns);
}
resStore[lng] = resStore[lng] || {};
resStore[lng][ns] = resStore[lng][ns] || {};
if (deep) {
f.deepExtend(resStore[lng][ns], resources);
} else {
f.extend(resStore[lng][ns], resources);
}
}
function hasResourceBundle(lng, ns) {
if (typeof ns !== 'string') {
ns = o.ns.defaultNs;
}
resStore[lng] = resStore[lng] || {};
var res = resStore[lng][ns] || {};
var hasValues = false;
for(var prop in res) {
if (res.hasOwnProperty(prop)) {
hasValues = true;
}
}
return hasValues;
}
function removeResourceBundle(lng, ns) {
if (typeof ns !== 'string') {
ns = o.ns.defaultNs;
}
resStore[lng] = resStore[lng] || {};
resStore[lng][ns] = {};
}
function addResource(lng, ns, key, value) {
if (typeof ns !== 'string') {
resource = ns;
ns = o.ns.defaultNs;
} else if (o.ns.namespaces.indexOf(ns) < 0) {
o.ns.namespaces.push(ns);
}
resStore[lng] = resStore[lng] || {};
resStore[lng][ns] = resStore[lng][ns] || {};
var keys = key.split(o.keyseparator);
var x = 0;
var node = resStore[lng][ns];
var origRef = node;
while (keys[x]) {
if (x == keys.length - 1)
node[keys[x]] = value;
else {
if (node[keys[x]] == null)
node[keys[x]] = {};
node = node[keys[x]];
}
x++;
}
}
function addResources(lng, ns, resources) {
if (typeof ns !== 'string') {
resource = ns;
ns = o.ns.defaultNs;
} else if (o.ns.namespaces.indexOf(ns) < 0) {
o.ns.namespaces.push(ns);
}
for (var m in resources) {
if (typeof resources[m] === 'string') addResource(lng, ns, m, resources[m]);
}
}
function setDefaultNamespace(ns) {
o.ns.defaultNs = ns;
}
function loadNamespace(namespace, cb) {
loadNamespaces([namespace], cb);
}
function loadNamespaces(namespaces, cb) {
var opts = {
dynamicLoad: o.dynamicLoad,
resGetPath: o.resGetPath,
getAsync: o.getAsync,
customLoad: o.customLoad,
ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */
};
// languages to load
var lngsToLoad = f.toLanguages(o.lng);
if (typeof o.preload === 'string') o.preload = [o.preload];
for (var i = 0, l = o.preload.length; i < l; i++) {
var pres = f.toLanguages(o.preload[i]);
for (var y = 0, len = pres.length; y < len; y++) {
if (lngsToLoad.indexOf(pres[y]) < 0) {
lngsToLoad.push(pres[y]);
}
}
}
// check if we have to load
var lngNeedLoad = [];
for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) {
var needLoad = false;
var resSet = resStore[lngsToLoad[a]];
if (resSet) {
for (var b = 0, lenB = namespaces.length; b < lenB; b++) {
if (!resSet[namespaces[b]]) needLoad = true;
}
} else {
needLoad = true;
}
if (needLoad) lngNeedLoad.push(lngsToLoad[a]);
}
if (lngNeedLoad.length) {
i18n.sync._fetch(lngNeedLoad, opts, function(err, store) {
var todo = namespaces.length * lngNeedLoad.length;
// load each file individual
f.each(namespaces, function(nsIndex, nsValue) {
// append namespace to namespace array
if (o.ns.namespaces.indexOf(nsValue) < 0) {
o.ns.namespaces.push(nsValue);
}
f.each(lngNeedLoad, function(lngIndex, lngValue) {
resStore[lngValue] = resStore[lngValue] || {};
resStore[lngValue][nsValue] = store[lngValue][nsValue];
todo--; // wait for all done befor callback
if (todo === 0 && cb) {
if (o.useLocalStorage) i18n.sync._storeLocal(resStore);
cb();
}
});
});
});
} else {
if (cb) cb();
}
}
function setLng(lng, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
} else if (!options) {
options = {};
}
options.lng = lng;
return init(options, cb);
}
function lng() {
return currentLng;
}
function reload(cb) {
resStore = {};
setLng(currentLng, cb);
}
function addJqueryFunct() {
// $.t shortcut
$.t = $.t || translate;
function parse(ele, key, options) {
if (key.length === 0) return;
var attr = 'text';
if (key.indexOf('[') === 0) {
var parts = key.split(']');
key = parts[1];
attr = parts[0].substr(1, parts[0].length-1);
}
if (key.indexOf(';') === key.length-1) {
key = key.substr(0, key.length-2);
}
var optionsToUse;
if (attr === 'html') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.html($.t(key, optionsToUse));
} else if (attr === 'text') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options;
ele.text($.t(key, optionsToUse));
} else if (attr === 'prepend') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.prepend($.t(key, optionsToUse));
} else if (attr === 'append') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.append($.t(key, optionsToUse));
} else if (attr.indexOf("data-") === 0) {
var dataAttr = attr.substr(("data-").length);
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.data(dataAttr) }, options) : options;
var translated = $.t(key, optionsToUse);
//we change into the data cache
ele.data(dataAttr, translated);
//we change into the dom
ele.attr(attr, translated);
} else {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options;
ele.attr(attr, $.t(key, optionsToUse));
}
}
function localize(ele, options) {
var key = ele.attr(o.selectorAttr);
if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val();
if (!key) return;
var target = ele
, targetSelector = ele.data("i18n-target");
if (targetSelector) {
target = ele.find(targetSelector) || ele;
}
if (!options && o.useDataAttrOptions === true) {
options = ele.data("i18n-options");
}
options = options || {};
if (key.indexOf(';') >= 0) {
var keys = key.split(';');
$.each(keys, function(m, k) {
if (k !== '') parse(target, k, options);
});
} else {
parse(target, key, options);
}
if (o.useDataAttrOptions === true) ele.data("i18n-options", options);
}
// fn
$.fn.i18n = function (options) {
return this.each(function() {
// localize element itself
localize($(this), options);
// localize childs
var elements = $(this).find('[' + o.selectorAttr + ']');
elements.each(function() {
localize($(this), options);
});
});
};
}
function applyReplacement(str, replacementHash, nestedKey, options) {
if (!str) return str;
options = options || replacementHash; // first call uses replacement hash combined with options
if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str;
var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped
, suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped
, unEscapingSuffix = 'HTML'+suffix;
var hash = replacementHash.replace && typeof replacementHash.replace === 'object' ? replacementHash.replace : replacementHash;
f.each(hash, function(key, value) {
var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key;
if (typeof value === 'object' && value !== null) {
str = applyReplacement(str, value, nextKey, options);
} else {
if (options.escapeInterpolation || o.escapeInterpolation) {
str = str.replace(new RegExp([prefix, nextKey, unEscapingSuffix].join(''), 'g'), f.regexReplacementEscape(value));
str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.regexReplacementEscape(f.escape(value)));
} else {
str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.regexReplacementEscape(value));
}
// str = options.escapeInterpolation;
}
});
return str;
}
// append it to functions
f.applyReplacement = applyReplacement;
function applyReuse(translated, options) {
var comma = ',';
var options_open = '{';
var options_close = '}';
var opts = f.extend({}, options);
delete opts.postProcess;
while (translated.indexOf(o.reusePrefix) != -1) {
replacementCounter++;
if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion
var index_of_opening = translated.lastIndexOf(o.reusePrefix);
var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length;
var token = translated.substring(index_of_opening, index_of_end_of_closing);
var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, '');
if (index_of_end_of_closing <= index_of_opening) {
f.error('there is an missing closing in following translation value', translated);
return '';
}
if (token_without_symbols.indexOf(comma) != -1) {
var index_of_token_end_of_closing = token_without_symbols.indexOf(comma);
if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) {
var index_of_opts_opening = token_without_symbols.indexOf(options_open, index_of_token_end_of_closing);
var index_of_opts_end_of_closing = token_without_symbols.indexOf(options_close, index_of_opts_opening) + options_close.length;
try {
opts = f.extend(opts, JSON.parse(token_without_symbols.substring(index_of_opts_opening, index_of_opts_end_of_closing)));
token_without_symbols = token_without_symbols.substring(0, index_of_token_end_of_closing);
} catch (e) {
}
}
}
var translated_token = _translate(token_without_symbols, opts);
translated = translated.replace(token, f.regexReplacementEscape(translated_token));
}
return translated;
}
function hasContext(options) {
return (options.context && (typeof options.context == 'string' || typeof options.context == 'number'));
}
function needsPlural(options, lng) {
return (options.count !== undefined && typeof options.count != 'string'/* && pluralExtensions.needsPlural(lng, options.count)*/);
}
function needsIndefiniteArticle(options) {
return (options.indefinite_article !== undefined && typeof options.indefinite_article != 'string' && options.indefinite_article);
}
function exists(key, options) {
options = options || {};
var notFound = _getDefaultValue(key, options)
, found = _find(key, options);
return found !== undefined || found === notFound;
}
function translate(key, options) {
options = options || {};
if (!initialized) {
f.log('i18next not finished initialization. you might have called t function before loading resources finished.')
return options.defaultValue || '';
};
replacementCounter = 0;
return _translate.apply(null, arguments);
}
function _getDefaultValue(key, options) {
return (options.defaultValue !== undefined) ? options.defaultValue : key;
}
function _injectSprintfProcessor() {
var values = [];
// mh: build array from second argument onwards
for (var i = 1; i < arguments.length; i++) {
values.push(arguments[i]);
}
return {
postProcess: 'sprintf',
sprintf: values
};
}
function _translate(potentialKeys, options) {
if (options && typeof options !== 'object') {
if (o.shortcutFunction === 'sprintf') {
// mh: gettext like sprintf syntax found, automatically create sprintf processor
options = _injectSprintfProcessor.apply(null, arguments);
} else if (o.shortcutFunction === 'defaultValue') {
options = {
defaultValue: options
}
}
} else {
options = options || {};
}
if (typeof o.defaultVariables === 'object') {
options = f.extend({}, o.defaultVariables, options);
}
if (potentialKeys === undefined || potentialKeys === null || potentialKeys === '') return '';
if (typeof potentialKeys === 'string') {
potentialKeys = [potentialKeys];
}
var key = potentialKeys[0];
if (potentialKeys.length > 1) {
for (var i = 0; i < potentialKeys.length; i++) {
key = potentialKeys[i];
if (exists(key, options)) {
break;
}
}
}
var notFound = _getDefaultValue(key, options)
, found = _find(key, options)
, lngs = options.lng ? f.toLanguages(options.lng, options.fallbackLng) : languages
, ns = options.ns || o.ns.defaultNs
, parts;
// split ns and key
if (key.indexOf(o.nsseparator) > -1) {
parts = key.split(o.nsseparator);
ns = parts[0];
key = parts[1];
}
if (found === undefined && o.sendMissing && typeof o.missingKeyHandler === 'function') {
if (options.lng) {
o.missingKeyHandler(lngs[0], ns, key, notFound, lngs);
} else {
o.missingKeyHandler(o.lng, ns, key, notFound, lngs);
}
}
var postProcessor = options.postProcess || o.postProcess;
if (found !== undefined && postProcessor) {
if (postProcessors[postProcessor]) {
found = postProcessors[postProcessor](found, key, options);
}
}
// process notFound if function exists
var splitNotFound = notFound;
if (notFound.indexOf(o.nsseparator) > -1) {
parts = notFound.split(o.nsseparator);
splitNotFound = parts[1];
}
if (splitNotFound === key && o.parseMissingKey) {
notFound = o.parseMissingKey(notFound);
}
if (found === undefined) {
notFound = applyReplacement(notFound, options);
notFound = applyReuse(notFound, options);
if (postProcessor && postProcessors[postProcessor]) {
var val = _getDefaultValue(key, options);
found = postProcessors[postProcessor](val, key, options);
}
}
return (found !== undefined) ? found : notFound;
}
function _find(key, options) {
options = options || {};
var optionWithoutCount, translated
, notFound = _getDefaultValue(key, options)
, lngs = languages;
if (!resStore) { return notFound; } // no resStore to translate from
// CI mode
if (lngs[0].toLowerCase() === 'cimode') return notFound;
// passed in lng
if (options.lngs) lngs = options.lngs;
if (options.lng) {
lngs = f.toLanguages(options.lng, options.fallbackLng);
if (!resStore[lngs[0]]) {
var oldAsync = o.getAsync;
o.getAsync = false;
i18n.sync.load(lngs, o, function(err, store) {
f.extend(resStore, store);
o.getAsync = oldAsync;
});
}
}
var ns = options.ns || o.ns.defaultNs;
if (key.indexOf(o.nsseparator) > -1) {
var parts = key.split(o.nsseparator);
ns = parts[0];
key = parts[1];
}
if (hasContext(options)) {
optionWithoutCount = f.extend({}, options);
delete optionWithoutCount.context;
optionWithoutCount.defaultValue = o.contextNotFound;
var contextKey = ns + o.nsseparator + key + '_' + options.context;
translated = translate(contextKey, optionWithoutCount);
if (translated != o.contextNotFound) {
return applyReplacement(translated, { context: options.context }); // apply replacement for context only
} // else continue translation with original/nonContext key
}
if (needsPlural(options, lngs[0])) {
optionWithoutCount = f.extend({ lngs: [lngs[0]]}, options);
delete optionWithoutCount.count;
delete optionWithoutCount.lng;
optionWithoutCount.defaultValue = o.pluralNotFound;
var pluralKey;
if (!pluralExtensions.needsPlural(lngs[0], options.count)) {
pluralKey = ns + o.nsseparator + key;
} else {
pluralKey = ns + o.nsseparator + key + o.pluralSuffix;
var pluralExtension = pluralExtensions.get(lngs[0], options.count);
if (pluralExtension >= 0) {
pluralKey = pluralKey + '_' + pluralExtension;
} else if (pluralExtension === 1) {
pluralKey = ns + o.nsseparator + key; // singular
}
}
translated = translate(pluralKey, optionWithoutCount);
if (translated != o.pluralNotFound) {
return applyReplacement(translated, {
count: options.count,
interpolationPrefix: options.interpolationPrefix,
interpolationSuffix: options.interpolationSuffix
}); // apply replacement for count only
} else if (lngs.length > 1) {
// remove failed lng
var clone = lngs.slice();
clone.shift();
options = f.extend(options, { lngs: clone });
delete options.lng;
// retry with fallbacks
translated = translate(ns + o.nsseparator + key, options);
if (translated != o.pluralNotFound) return translated;
} else {
return translated;
}
}
if (needsIndefiniteArticle(options)) {
var optionsWithoutIndef = f.extend({}, options);
delete optionsWithoutIndef.indefinite_article;
optionsWithoutIndef.defaultValue = o.indefiniteNotFound;
// If we don't have a count, we want the indefinite, if we do have a count, and needsPlural is false
var indefiniteKey = ns + o.nsseparator + key + (((options.count && !needsPlural(options, lngs[0])) || !options.count) ? o.indefiniteSuffix : "");
translated = translate(indefiniteKey, optionsWithoutIndef);
if (translated != o.indefiniteNotFound) {
return translated;
}
}
var found;
var keys = key.split(o.keyseparator);
for (var i = 0, len = lngs.length; i < len; i++ ) {
if (found !== undefined) break;
var l = lngs[i];
var x = 0;
var value = resStore[l] && resStore[l][ns];
while (keys[x]) {
value = value && value[keys[x]];
x++;
}
if (value !== undefined) {
var valueType = Object.prototype.toString.apply(value);
if (typeof value === 'string') {
value = applyReplacement(value, options);
value = applyReuse(value, options);
} else if (valueType === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) {
value = value.join('\n');
value = applyReplacement(value, options);
value = applyReuse(value, options);
} else if (value === null && o.fallbackOnNull === true) {
value = undefined;
} else if (value !== null) {
if (!o.returnObjectTrees && !options.returnObjectTrees) {
if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') {
value = o.objectTreeKeyHandler(key, value, l, ns, options);
} else {
value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
'returned an object instead of string.';
f.log(value);
}
} else if (valueType !== '[object Number]' && valueType !== '[object Function]' && valueType !== '[object RegExp]') {
var copy = (valueType === '[object Array]') ? [] : {}; // apply child translation on a copy
f.each(value, function(m) {
copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options);
});
value = copy;
}
}
if (typeof value === 'string' && value.trim() === '' && o.fallbackOnEmpty === true)
value = undefined;
found = value;
}
}
if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) {
// set flag for fallback lookup - avoid recursion
options.isFallbackLookup = true;
if (o.fallbackNS.length) {
for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) {
found = _find(o.fallbackNS[y] + o.nsseparator + key, options);
if (found || (found==="" && o.fallbackOnEmpty === false)) {
/* compare value without namespace */
var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found
, notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound;
if (foundValue !== notFoundValue) break;
}
}
} else {
found = _find(key, options); // fallback to default NS
}
options.isFallbackLookup = false;
}
return found;
}
function detectLanguage() {
var detectedLng;
var whitelist = o.lngWhitelist || [];
var userLngChoices = [];
// get from qs
var qsParm = [];
if (typeof window !== 'undefined') {
(function() {
var query = window.location.search.substring(1);
var params = query.split('&');
for (var i=0; i<params.length; i++) {
var pos = params[i].indexOf('=');
if (pos > 0) {
var key = params[i].substring(0,pos);
if (key == o.detectLngQS) {
userLngChoices.push(params[i].substring(pos+1));
}
}
}
})();
}
// get from cookie
if (o.useCookie && typeof document !== 'undefined') {
var c = f.cookie.read(o.cookieName);
if (c) userLngChoices.push(c);
}
// get from localStorage
if (o.detectLngFromLocalStorage && typeof window !== 'undefined' && window.localStorage) {
userLngChoices.push(window.localStorage.getItem('i18next_lng'));
}
// get from navigator
if (typeof navigator !== 'undefined') {
if (navigator.languages) { // chrome only; not an array, so can't use .push.apply instead of iterating
for (var i=0;i<navigator.languages.length;i++) {
userLngChoices.push(navigator.languages[i]);
}
}
if (navigator.userLanguage) {
userLngChoices.push(navigator.userLanguage);
}
if (navigator.language) {
userLngChoices.push(navigator.language);
}
}
(function() {
for (var i=0;i<userLngChoices.length;i++) {
var lng = userLngChoices[i];
if (lng.indexOf('-') > -1) {
var parts = lng.split('-');
lng = o.lowerCaseLng ?
parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
}
if (whitelist.length === 0 || whitelist.indexOf(lng) > -1) {
detectedLng = lng;
break;
}
}
})();
//fallback
if (!detectedLng){
detectedLng = o.fallbackLng[0];
}
return detectedLng;
}
// definition http://translate.sourceforge.net/wiki/l10n/pluralforms
/* [code, name, numbers, pluralsType] */
var _rules = [
["ach", "Acholi", [1,2], 1],
["af", "Afrikaans",[1,2], 2],
["ak", "Akan", [1,2], 1],
["am", "Amharic", [1,2], 1],
["an", "Aragonese",[1,2], 2],
["ar", "Arabic", [0,1,2,3,11,100],5],
["arn", "Mapudungun",[1,2], 1],
["ast", "Asturian", [1,2], 2],
["ay", "Aymará", [1], 3],
["az", "Azerbaijani",[1,2],2],
["be", "Belarusian",[1,2,5],4],
["bg", "Bulgarian",[1,2], 2],
["bn", "Bengali", [1,2], 2],
["bo", "Tibetan", [1], 3],
["br", "Breton", [1,2], 1],
["bs", "Bosnian", [1,2,5],4],
["ca", "Catalan", [1,2], 2],
["cgg", "Chiga", [1], 3],
["cs", "Czech", [1,2,5],6],
["csb", "Kashubian",[1,2,5],7],
["cy", "Welsh", [1,2,3,8],8],
["da", "Danish", [1,2], 2],
["de", "German", [1,2], 2],
["dev", "Development Fallback", [1,2], 2],
["dz", "Dzongkha", [1], 3],
["el", "Greek", [1,2], 2],
["en", "English", [1,2], 2],
["eo", "Esperanto",[1,2], 2],
["es", "Spanish", [1,2], 2],
["es_ar","Argentinean Spanish", [1,2], 2],
["et", "Estonian", [1,2], 2],
["eu", "Basque", [1,2], 2],
["fa", "Persian", [1], 3],
["fi", "Finnish", [1,2], 2],
["fil", "Filipino", [1,2], 1],
["fo", "Faroese", [1,2], 2],
["fr", "French", [1,2], 9],
["fur", "Friulian", [1,2], 2],
["fy", "Frisian", [1,2], 2],
["ga", "Irish", [1,2,3,7,11],10],
["gd", "Scottish Gaelic",[1,2,3,20],11],
["gl", "Galician", [1,2], 2],
["gu", "Gujarati", [1,2], 2],
["gun", "Gun", [1,2], 1],
["ha", "Hausa", [1,2], 2],
["he", "Hebrew", [1,2], 2],
["hi", "Hindi", [1,2], 2],
["hr", "Croatian", [1,2,5],4],
["hu", "Hungarian",[1,2], 2],
["hy", "Armenian", [1,2], 2],
["ia", "Interlingua",[1,2],2],
["id", "Indonesian",[1], 3],
["is", "Icelandic",[1,2], 12],
["it", "Italian", [1,2], 2],
["ja", "Japanese", [1], 3],
["jbo", "Lojban", [1], 3],
["jv", "Javanese", [0,1], 13],
["ka", "Georgian", [1], 3],
["kk", "Kazakh", [1], 3],
["km", "Khmer", [1], 3],
["kn", "Kannada", [1,2], 2],
["ko", "Korean", [1], 3],
["ku", "Kurdish", [1,2], 2],
["kw", "Cornish", [1,2,3,4],14],
["ky", "Kyrgyz", [1], 3],
["lb", "Letzeburgesch",[1,2],2],
["ln", "Lingala", [1,2], 1],
["lo", "Lao", [1], 3],
["lt", "Lithuanian",[1,2,10],15],
["lv", "Latvian", [1,2,0],16],
["mai", "Maithili", [1,2], 2],
["mfe", "Mauritian Creole",[1,2],1],
["mg", "Malagasy", [1,2], 1],
["mi", "Maori", [1,2], 1],
["mk", "Macedonian",[1,2],17],
["ml", "Malayalam",[1,2], 2],
["mn", "Mongolian",[1,2], 2],
["mnk", "Mandinka", [0,1,2],18],
["mr", "Marathi", [1,2], 2],
["ms", "Malay", [1], 3],
["mt", "Maltese", [1,2,11,20],19],
["nah", "Nahuatl", [1,2], 2],
["nap", "Neapolitan",[1,2], 2],
["nb", "Norwegian Bokmal",[1,2],2],
["ne", "Nepali", [1,2], 2],
["nl", "Dutch", [1,2], 2],
["nn", "Norwegian Nynorsk",[1,2],2],
["no", "Norwegian",[1,2], 2],
["nso", "Northern Sotho",[1,2],2],
["oc", "Occitan", [1,2], 1],
["or", "Oriya", [2,1], 2],
["pa", "Punjabi", [1,2], 2],
["pap", "Papiamento",[1,2], 2],
["pl", "Polish", [1,2,5],7],
["pms", "Piemontese",[1,2], 2],
["ps", "Pashto", [1,2], 2],
["pt", "Portuguese",[1,2], 2],
["pt_br","Brazilian Portuguese",[1,2], 2],
["rm", "Romansh", [1,2], 2],
["ro", "Romanian", [1,2,20],20],
["ru", "Russian", [1,2,5],4],
["sah", "Yakut", [1], 3],
["sco", "Scots", [1,2], 2],
["se", "Northern Sami",[1,2], 2],
["si", "Sinhala", [1,2], 2],
["sk", "Slovak", [1,2,5],6],
["sl", "Slovenian",[5,1,2,3],21],
["so", "Somali", [1,2], 2],
["son", "Songhay", [1,2], 2],
["sq", "Albanian", [1,2], 2],
["sr", "Serbian", [1,2,5],4],
["su", "Sundanese",[1], 3],
["sv", "Swedish", [1,2], 2],
["sw", "Swahili", [1,2], 2],
["ta", "Tamil", [1,2], 2],
["te", "Telugu", [1,2], 2],
["tg", "Tajik", [1,2], 1],
["th", "Thai", [1], 3],
["ti", "Tigrinya", [1,2], 1],
["tk", "Turkmen", [1,2], 2],
["tr", "Turkish", [1,2], 1],
["tt", "Tatar", [1], 3],
["ug", "Uyghur", [1], 3],
["uk", "Ukrainian",[1,2,5],4],
["ur", "Urdu", [1,2], 2],
["uz", "Uzbek", [1,2], 1],
["vi", "Vietnamese",[1], 3],
["wa", "Walloon", [1,2], 1],
["wo", "Wolof", [1], 3],
["yo", "Yoruba", [1,2], 2],
["zh", "Chinese", [1], 3]
];
var _rulesPluralsTypes = {
1: function(n) {return Number(n > 1);},
2: function(n) {return Number(n != 1);},
3: function(n) {return 0;},
4: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);},
5: function(n) {return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5);},
6: function(n) {return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);},
7: function(n) {return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);},
8: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3);},
9: function(n) {return Number(n >= 2);},
10: function(n) {return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;},
11: function(n) {return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3);},
12: function(n) {return Number(n%10!=1 || n%100==11);},
13: function(n) {return Number(n !== 0);},
14: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3);},
15: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);},
16: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2);},
17: function(n) {return Number(n==1 || n%10==1 ? 0 : 1);},
18: function(n) {return Number(0 ? 0 : n==1 ? 1 : 2);},
19: function(n) {return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);},
20: function(n) {return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);},
21: function(n) {return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); }
};
var pluralExtensions = {
rules: (function () {
var l, rules = {};
for (l=_rules.length; l-- ;) {
rules[_rules[l][0]] = {
name: _rules[l][1],
numbers: _rules[l][2],
plurals: _rulesPluralsTypes[_rules[l][3]]
}
}
return rules;
}()),
// you can add your own pluralExtensions
addRule: function(lng, obj) {
pluralExtensions.rules[lng] = obj;
},
setCurrentLng: function(lng) {
if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) {
var parts = lng.split('-');
pluralExtensions.currentRule = {
lng: lng,
rule: pluralExtensions.rules[parts[0]]
};
}
},
needsPlural: function(lng, count) {
var parts = lng.split('-');
var ext;
if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
ext = pluralExtensions.currentRule.rule;
} else {
ext = pluralExtensions.rules[parts[f.getCountyIndexOfLng(lng)]];
}
if (ext && ext.numbers.length <= 1) {
return false;
} else {
return this.get(lng, count) !== 1;
}
},
get: function(lng, count) {
var parts = lng.split('-');
function getResult(l, c) {
var ext;
if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
ext = pluralExtensions.currentRule.rule;
} else {
ext = pluralExtensions.rules[l];
}
if (ext) {
var i;
if (ext.noAbs) {
i = ext.plurals(c);
} else {
i = ext.plurals(Math.abs(c));
}
var number = ext.numbers[i];
if (ext.numbers.length === 2 && ext.numbers[0] === 1) {
if (number === 2) {
number = -1; // regular plural
} else if (number === 1) {
number = 1; // singular
}
}//console.log(count + '-' + number);
return number;
} else {
return c === 1 ? '1' : '-1';
}
}
return getResult(parts[f.getCountyIndexOfLng(lng)], count);
}
};
var postProcessors = {};
var addPostProcessor = function(name, fc) {
postProcessors[name] = fc;
};
// sprintf support
var sprintf = (function() {
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
}
function str_repeat(input, multiplier) {
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join('');
}
var str_format = function() {
if (!str_format.cache.hasOwnProperty(arguments[0])) {
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
}
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
};
str_format.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i]);
if (node_type === 'string') {
output.push(parse_tree[i]);
}
else if (node_type === 'array') {
match = parse_tree[i]; // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor];
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
}
arg = arg[match[2][k]];
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]];
}
else { // positional argument (implicit)
arg = argv[cursor++];
}
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
}
switch (match[8]) {
case 'b': arg = arg.toString(2); break;
case 'c': arg = String.fromCharCode(arg); break;
case 'd': arg = parseInt(arg, 10); break;
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
case 'o': arg = arg.toString(8); break;
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
case 'u': arg = Math.abs(arg); break;
case 'x': arg = arg.toString(16); break;
case 'X': arg = arg.toString(16).toUpperCase(); break;
}
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
pad_length = match[6] - String(arg).length;
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
output.push(match[5] ? arg + pad : pad + arg);
}
}
return output.join('');
};
str_format.cache = {};
str_format.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) {
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
parse_tree.push(match[0]);
}
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
parse_tree.push('%');
}
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1;
var field_list = [], replacement_field = match[2], field_match = [];
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else {
throw('[sprintf] huh?');
}
}
}
else {
throw('[sprintf] huh?');
}
match[2] = field_list;
}
else {
arg_names |= 2;
}
if (arg_names === 3) {
throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
}
parse_tree.push(match);
}
else {
throw('[sprintf] huh?');
}
_fmt = _fmt.substring(match[0].length);
}
return parse_tree;
};
return str_format;
})();
var vsprintf = function(fmt, argv) {
argv.unshift(fmt);
return sprintf.apply(null, argv);
};
addPostProcessor("sprintf", function(val, key, opts) {
if (!opts.sprintf) return val;
if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') {
return vsprintf(val, opts.sprintf);
} else if (typeof opts.sprintf === 'object') {
return sprintf(val, opts.sprintf);
}
return val;
});
// public api interface
i18n.init = init;
i18n.setLng = setLng;
i18n.preload = preload;
i18n.addResourceBundle = addResourceBundle;
i18n.hasResourceBundle = hasResourceBundle;
i18n.addResource = addResource;
i18n.addResources = addResources;
i18n.removeResourceBundle = removeResourceBundle;
i18n.loadNamespace = loadNamespace;
i18n.loadNamespaces = loadNamespaces;
i18n.setDefaultNamespace = setDefaultNamespace;
i18n.t = translate;
i18n.translate = translate;
i18n.exists = exists;
i18n.detectLanguage = f.detectLanguage;
i18n.pluralExtensions = pluralExtensions;
i18n.sync = sync;
i18n.functions = f;
i18n.lng = lng;
i18n.addPostProcessor = addPostProcessor;
i18n.options = o;
})();
},{"jquery":"jquery"}],63:[function(require,module,exports){
// Top level file is just a mixin of submodules & constants
'use strict';
var assign = require('./lib/utils/common').assign;
var deflate = require('./lib/deflate');
var inflate = require('./lib/inflate');
var constants = require('./lib/zlib/constants');
var pako = {};
assign(pako, deflate, inflate, constants);
module.exports = pako;
},{"./lib/deflate":64,"./lib/inflate":65,"./lib/utils/common":66,"./lib/zlib/constants":69}],64:[function(require,module,exports){
'use strict';
var zlib_deflate = require('./zlib/deflate.js');
var utils = require('./utils/common');
var strings = require('./utils/strings');
var msg = require('./zlib/messages');
var zstream = require('./zlib/zstream');
var toString = Object.prototype.toString;
/* Public constants ==========================================================*/
/* ===========================================================================*/
var Z_NO_FLUSH = 0;
var Z_FINISH = 4;
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_DEFAULT_COMPRESSION = -1;
var Z_DEFAULT_STRATEGY = 0;
var Z_DEFLATED = 8;
/* ===========================================================================*/
/**
* class Deflate
*
* Generic JS-style wrapper for zlib calls. If you don't need
* streaming behaviour - use more simple functions: [[deflate]],
* [[deflateRaw]] and [[gzip]].
**/
/* internal
* Deflate.chunks -> Array
*
* Chunks of output data, if [[Deflate#onData]] not overriden.
**/
/**
* Deflate.result -> Uint8Array|Array
*
* Compressed result, generated by default [[Deflate#onData]]
* and [[Deflate#onEnd]] handlers. Filled after you push last chunk
* (call [[Deflate#push]] with `Z_FINISH` / `true` param).
**/
/**
* Deflate.err -> Number
*
* Error code after deflate finished. 0 (Z_OK) on success.
* You will not need it in real life, because deflate errors
* are possible only on wrong options or bad `onData` / `onEnd`
* custom handlers.
**/
/**
* Deflate.msg -> String
*
* Error message, if [[Deflate.err]] != 0
**/
/**
* new Deflate(options)
* - options (Object): zlib deflate options.
*
* Creates new deflator instance with specified params. Throws exception
* on bad params. Supported options:
*
* - `level`
* - `windowBits`
* - `memLevel`
* - `strategy`
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Additional options, for internal needs:
*
* - `chunkSize` - size of generated data chunks (16K by default)
* - `raw` (Boolean) - do raw deflate
* - `gzip` (Boolean) - create gzip wrapper
* - `to` (String) - if equal to 'string', then result will be "binary string"
* (each char code [0..255])
* - `header` (Object) - custom header for gzip
* - `text` (Boolean) - true if compressed data believed to be text
* - `time` (Number) - modification time, unix timestamp
* - `os` (Number) - operation system code
* - `extra` (Array) - array of bytes with extra data (max 65536)
* - `name` (String) - file name (binary string)
* - `comment` (String) - comment (binary string)
* - `hcrc` (Boolean) - true if header crc should be added
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
* , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
*
* var deflate = new pako.Deflate({ level: 3});
*
* deflate.push(chunk1, false);
* deflate.push(chunk2, true); // true -> last chunk
*
* if (deflate.err) { throw new Error(deflate.err); }
*
* console.log(deflate.result);
* ```
**/
var Deflate = function(options) {
this.options = utils.assign({
level: Z_DEFAULT_COMPRESSION,
method: Z_DEFLATED,
chunkSize: 16384,
windowBits: 15,
memLevel: 8,
strategy: Z_DEFAULT_STRATEGY,
to: ''
}, options || {});
var opt = this.options;
if (opt.raw && (opt.windowBits > 0)) {
opt.windowBits = -opt.windowBits;
}
else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
opt.windowBits += 16;
}
this.err = 0; // error code, if happens (0 = Z_OK)
this.msg = ''; // error message
this.ended = false; // used to avoid multiple onEnd() calls
this.chunks = []; // chunks of compressed data
this.strm = new zstream();
this.strm.avail_out = 0;
var status = zlib_deflate.deflateInit2(
this.strm,
opt.level,
opt.method,
opt.windowBits,
opt.memLevel,
opt.strategy
);
if (status !== Z_OK) {
throw new Error(msg[status]);
}
if (opt.header) {
zlib_deflate.deflateSetHeader(this.strm, opt.header);
}
};
/**
* Deflate#push(data[, mode]) -> Boolean
* - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
* converted to utf8 byte sequence.
* - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
*
* Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
* new compressed chunks. Returns `true` on success. The last data block must have
* mode Z_FINISH (or `true`). That flush internal pending buffers and call
* [[Deflate#onEnd]].
*
* On fail call [[Deflate#onEnd]] with error code and return false.
*
* We strongly recommend to use `Uint8Array` on input for best speed (output
* array format is detected automatically). Also, don't skip last param and always
* use the same type in your code (boolean or number). That will improve JS speed.
*
* For regular `Array`-s make sure all elements are [0..255].
*
* ##### Example
*
* ```javascript
* push(chunk, false); // push one of data chunks
* ...
* push(chunk, true); // push last chunk
* ```
**/
Deflate.prototype.push = function(data, mode) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var status, _mode;
if (this.ended) { return false; }
_mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);
// Convert data if needed
if (typeof data === 'string') {
// If we need to compress text, change encoding to utf8.
strm.input = strings.string2buf(data);
} else if (toString.call(data) === '[object ArrayBuffer]') {
strm.input = new Uint8Array(data);
} else {
strm.input = data;
}
strm.next_in = 0;
strm.avail_in = strm.input.length;
do {
if (strm.avail_out === 0) {
strm.output = new utils.Buf8(chunkSize);
strm.next_out = 0;
strm.avail_out = chunkSize;
}
status = zlib_deflate.deflate(strm, _mode); /* no bad return value */
if (status !== Z_STREAM_END && status !== Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if (strm.avail_out === 0 || (strm.avail_in === 0 && _mode === Z_FINISH)) {
if (this.options.to === 'string') {
this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
} else {
this.onData(utils.shrinkBuf(strm.output, strm.next_out));
}
}
} while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);
// Finalize on the last chunk.
if (_mode === Z_FINISH) {
status = zlib_deflate.deflateEnd(this.strm);
this.onEnd(status);
this.ended = true;
return status === Z_OK;
}
return true;
};
/**
* Deflate#onData(chunk) -> Void
* - chunk (Uint8Array|Array|String): ouput data. Type of array depends
* on js engine support. When string output requested, each chunk
* will be string.
*
* By default, stores data blocks in `chunks[]` property and glue
* those in `onEnd`. Override this handler, if you need another behaviour.
**/
Deflate.prototype.onData = function(chunk) {
this.chunks.push(chunk);
};
/**
* Deflate#onEnd(status) -> Void
* - status (Number): deflate status. 0 (Z_OK) on success,
* other if not.
*
* Called once after you tell deflate that input stream complete
* or error happenned. By default - join collected chunks,
* free memory and fill `results` / `err` properties.
**/
Deflate.prototype.onEnd = function(status) {
// On success - join
if (status === Z_OK) {
if (this.options.to === 'string') {
this.result = this.chunks.join('');
} else {
this.result = utils.flattenChunks(this.chunks);
}
}
this.chunks = [];
this.err = status;
this.msg = this.strm.msg;
};
/**
* deflate(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* Compress `data` with deflate alrorythm and `options`.
*
* Supported options are:
*
* - level
* - windowBits
* - memLevel
* - strategy
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Sugar (options):
*
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
* negative windowBits implicitly.
* - `to` (String) - if equal to 'string', then result will be "binary string"
* (each char code [0..255])
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
*
* console.log(pako.deflate(data));
* ```
**/
function deflate(input, options) {
var deflator = new Deflate(options);
deflator.push(input, true);
// That will never happens, if you don't cheat with options :)
if (deflator.err) { throw deflator.msg; }
return deflator.result;
}
/**
* deflateRaw(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [[deflate]], but creates raw data, without wrapper
* (header and adler32 crc).
**/
function deflateRaw(input, options) {
options = options || {};
options.raw = true;
return deflate(input, options);
}
/**
* gzip(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [[deflate]], but create gzip wrapper instead of
* deflate one.
**/
function gzip(input, options) {
options = options || {};
options.gzip = true;
return deflate(input, options);
}
exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;
},{"./utils/common":66,"./utils/strings":67,"./zlib/deflate.js":71,"./zlib/messages":76,"./zlib/zstream":78}],65:[function(require,module,exports){
'use strict';
var zlib_inflate = require('./zlib/inflate.js');
var utils = require('./utils/common');
var strings = require('./utils/strings');
var c = require('./zlib/constants');
var msg = require('./zlib/messages');
var zstream = require('./zlib/zstream');
var gzheader = require('./zlib/gzheader');
var toString = Object.prototype.toString;
/**
* class Inflate
*
* Generic JS-style wrapper for zlib calls. If you don't need
* streaming behaviour - use more simple functions: [[inflate]]
* and [[inflateRaw]].
**/
/* internal
* inflate.chunks -> Array
*
* Chunks of output data, if [[Inflate#onData]] not overriden.
**/
/**
* Inflate.result -> Uint8Array|Array|String
*
* Uncompressed result, generated by default [[Inflate#onData]]
* and [[Inflate#onEnd]] handlers. Filled after you push last chunk
* (call [[Inflate#push]] with `Z_FINISH` / `true` param).
**/
/**
* Inflate.err -> Number
*
* Error code after inflate finished. 0 (Z_OK) on success.
* Should be checked if broken data possible.
**/
/**
* Inflate.msg -> String
*
* Error message, if [[Inflate.err]] != 0
**/
/**
* new Inflate(options)
* - options (Object): zlib inflate options.
*
* Creates new inflator instance with specified params. Throws exception
* on bad params. Supported options:
*
* - `windowBits`
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Additional options, for internal needs:
*
* - `chunkSize` - size of generated data chunks (16K by default)
* - `raw` (Boolean) - do raw inflate
* - `to` (String) - if equal to 'string', then result will be converted
* from utf8 to utf16 (javascript) string. When string output requested,
* chunk length can differ from `chunkSize`, depending on content.
*
* By default, when no options set, autodetect deflate/gzip data format via
* wrapper header.
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
* , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
*
* var inflate = new pako.Inflate({ level: 3});
*
* inflate.push(chunk1, false);
* inflate.push(chunk2, true); // true -> last chunk
*
* if (inflate.err) { throw new Error(inflate.err); }
*
* console.log(inflate.result);
* ```
**/
var Inflate = function(options) {
this.options = utils.assign({
chunkSize: 16384,
windowBits: 0,
to: ''
}, options || {});
var opt = this.options;
// Force window size for `raw` data, if not set directly,
// because we have no header for autodetect.
if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
opt.windowBits = -opt.windowBits;
if (opt.windowBits === 0) { opt.windowBits = -15; }
}
// If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
!(options && options.windowBits)) {
opt.windowBits += 32;
}
// Gzip header has no info about windows size, we can do autodetect only
// for deflate. So, if window size not set, force it to max when gzip possible
if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
// bit 3 (16) -> gzipped data
// bit 4 (32) -> autodetect gzip/deflate
if ((opt.windowBits & 15) === 0) {
opt.windowBits |= 15;
}
}
this.err = 0; // error code, if happens (0 = Z_OK)
this.msg = ''; // error message
this.ended = false; // used to avoid multiple onEnd() calls
this.chunks = []; // chunks of compressed data
this.strm = new zstream();
this.strm.avail_out = 0;
var status = zlib_inflate.inflateInit2(
this.strm,
opt.windowBits
);
if (status !== c.Z_OK) {
throw new Error(msg[status]);
}
this.header = new gzheader();
zlib_inflate.inflateGetHeader(this.strm, this.header);
};
/**
* Inflate#push(data[, mode]) -> Boolean
* - data (Uint8Array|Array|ArrayBuffer|String): input data
* - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
*
* Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
* new output chunks. Returns `true` on success. The last data block must have
* mode Z_FINISH (or `true`). That flush internal pending buffers and call
* [[Inflate#onEnd]].
*
* On fail call [[Inflate#onEnd]] with error code and return false.
*
* We strongly recommend to use `Uint8Array` on input for best speed (output
* format is detected automatically). Also, don't skip last param and always
* use the same type in your code (boolean or number). That will improve JS speed.
*
* For regular `Array`-s make sure all elements are [0..255].
*
* ##### Example
*
* ```javascript
* push(chunk, false); // push one of data chunks
* ...
* push(chunk, true); // push last chunk
* ```
**/
Inflate.prototype.push = function(data, mode) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var status, _mode;
var next_out_utf8, tail, utf8str;
if (this.ended) { return false; }
_mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
// Convert data if needed
if (typeof data === 'string') {
// Only binary strings can be decompressed on practice
strm.input = strings.binstring2buf(data);
} else if (toString.call(data) === '[object ArrayBuffer]') {
strm.input = new Uint8Array(data);
} else {
strm.input = data;
}
strm.next_in = 0;
strm.avail_in = strm.input.length;
do {
if (strm.avail_out === 0) {
strm.output = new utils.Buf8(chunkSize);
strm.next_out = 0;
strm.avail_out = chunkSize;
}
status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */
if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if (strm.next_out) {
if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && _mode === c.Z_FINISH)) {
if (this.options.to === 'string') {
next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
tail = strm.next_out - next_out_utf8;
utf8str = strings.buf2string(strm.output, next_out_utf8);
// move tail
strm.next_out = tail;
strm.avail_out = chunkSize - tail;
if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }
this.onData(utf8str);
} else {
this.onData(utils.shrinkBuf(strm.output, strm.next_out));
}
}
}
} while ((strm.avail_in > 0) && status !== c.Z_STREAM_END);
if (status === c.Z_STREAM_END) {
_mode = c.Z_FINISH;
}
// Finalize on the last chunk.
if (_mode === c.Z_FINISH) {
status = zlib_inflate.inflateEnd(this.strm);
this.onEnd(status);
this.ended = true;
return status === c.Z_OK;
}
return true;
};
/**
* Inflate#onData(chunk) -> Void
* - chunk (Uint8Array|Array|String): ouput data. Type of array depends
* on js engine support. When string output requested, each chunk
* will be string.
*
* By default, stores data blocks in `chunks[]` property and glue
* those in `onEnd`. Override this handler, if you need another behaviour.
**/
Inflate.prototype.onData = function(chunk) {
this.chunks.push(chunk);
};
/**
* Inflate#onEnd(status) -> Void
* - status (Number): inflate status. 0 (Z_OK) on success,
* other if not.
*
* Called once after you tell inflate that input stream complete
* or error happenned. By default - join collected chunks,
* free memory and fill `results` / `err` properties.
**/
Inflate.prototype.onEnd = function(status) {
// On success - join
if (status === c.Z_OK) {
if (this.options.to === 'string') {
// Glue & convert here, until we teach pako to send
// utf8 alligned strings to onData
this.result = this.chunks.join('');
} else {
this.result = utils.flattenChunks(this.chunks);
}
}
this.chunks = [];
this.err = status;
this.msg = this.strm.msg;
};
/**
* inflate(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* Decompress `data` with inflate/ungzip and `options`. Autodetect
* format via wrapper header by default. That's why we don't provide
* separate `ungzip` method.
*
* Supported options are:
*
* - windowBits
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information.
*
* Sugar (options):
*
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
* negative windowBits implicitly.
* - `to` (String) - if equal to 'string', then result will be converted
* from utf8 to utf16 (javascript) string. When string output requested,
* chunk length can differ from `chunkSize`, depending on content.
*
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , input = pako.deflate([1,2,3,4,5,6,7,8,9])
* , output;
*
* try {
* output = pako.inflate(input);
* } catch (err)
* console.log(err);
* }
* ```
**/
function inflate(input, options) {
var inflator = new Inflate(options);
inflator.push(input, true);
// That will never happens, if you don't cheat with options :)
if (inflator.err) { throw inflator.msg; }
return inflator.result;
}
/**
* inflateRaw(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* The same as [[inflate]], but creates raw data, without wrapper
* (header and adler32 crc).
**/
function inflateRaw(input, options) {
options = options || {};
options.raw = true;
return inflate(input, options);
}
/**
* ungzip(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* Just shortcut to [[inflate]], because it autodetects format
* by header.content. Done for convenience.
**/
exports.Inflate = Inflate;
exports.inflate = inflate;
exports.inflateRaw = inflateRaw;
exports.ungzip = inflate;
},{"./utils/common":66,"./utils/strings":67,"./zlib/constants":69,"./zlib/gzheader":72,"./zlib/inflate.js":74,"./zlib/messages":76,"./zlib/zstream":78}],66:[function(require,module,exports){
'use strict';
var TYPED_OK = (typeof Uint8Array !== 'undefined') &&
(typeof Uint16Array !== 'undefined') &&
(typeof Int32Array !== 'undefined');
exports.assign = function (obj /*from1, from2, from3, ...*/) {
var sources = Array.prototype.slice.call(arguments, 1);
while (sources.length) {
var source = sources.shift();
if (!source) { continue; }
if (typeof(source) !== 'object') {
throw new TypeError(source + 'must be non-object');
}
for (var p in source) {
if (source.hasOwnProperty(p)) {
obj[p] = source[p];
}
}
}
return obj;
};
// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
if (buf.length === size) { return buf; }
if (buf.subarray) { return buf.subarray(0, size); }
buf.length = size;
return buf;
};
var fnTyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
if (src.subarray && dest.subarray) {
dest.set(src.subarray(src_offs, src_offs+len), dest_offs);
return;
}
// Fallback to ordinary array
for(var i=0; i<len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function(chunks) {
var i, l, len, pos, chunk, result;
// calculate data length
len = 0;
for (i=0, l=chunks.length; i<l; i++) {
len += chunks[i].length;
}
// join chunks
result = new Uint8Array(len);
pos = 0;
for (i=0, l=chunks.length; i<l; i++) {
chunk = chunks[i];
result.set(chunk, pos);
pos += chunk.length;
}
return result;
}
};
var fnUntyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
for(var i=0; i<len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function(chunks) {
return [].concat.apply([], chunks);
}
};
// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
if (on) {
exports.Buf8 = Uint8Array;
exports.Buf16 = Uint16Array;
exports.Buf32 = Int32Array;
exports.assign(exports, fnTyped);
} else {
exports.Buf8 = Array;
exports.Buf16 = Array;
exports.Buf32 = Array;
exports.assign(exports, fnUntyped);
}
};
exports.setTyped(TYPED_OK);
},{}],67:[function(require,module,exports){
// String encode/decode helpers
'use strict';
var utils = require('./common');
// Quick check if we can use fast array to bin string conversion
//
// - apply(Array) can fail on Android 2.2
// - apply(Uint8Array) can fail on iOS 5.1 Safary
//
var STR_APPLY_OK = true;
var STR_APPLY_UIA_OK = true;
try { String.fromCharCode.apply(null, [0]); } catch(__) { STR_APPLY_OK = false; }
try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch(__) { STR_APPLY_UIA_OK = false; }
// Table with utf8 lengths (calculated by first byte of sequence)
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
// because max possible codepoint is 0x10ffff
var _utf8len = new utils.Buf8(256);
for (var i=0; i<256; i++) {
_utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1);
}
_utf8len[254]=_utf8len[254]=1; // Invalid sequence start
// convert string to array (typed, when possible)
exports.string2buf = function (str) {
var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
// count binary size
for (m_pos = 0; m_pos < str_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
c2 = str.charCodeAt(m_pos+1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
}
// allocate buffer
buf = new utils.Buf8(buf_len);
// convert
for (i=0, m_pos = 0; i < buf_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
c2 = str.charCodeAt(m_pos+1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
if (c < 0x80) {
/* one byte */
buf[i++] = c;
} else if (c < 0x800) {
/* two bytes */
buf[i++] = 0xC0 | (c >>> 6);
buf[i++] = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
/* three bytes */
buf[i++] = 0xE0 | (c >>> 12);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
} else {
/* four bytes */
buf[i++] = 0xf0 | (c >>> 18);
buf[i++] = 0x80 | (c >>> 12 & 0x3f);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
}
}
return buf;
};
// Helper (used in 2 places)
function buf2binstring(buf, len) {
// use fallback for big arrays to avoid stack overflow
if (len < 65537) {
if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
}
}
var result = '';
for(var i=0; i < len; i++) {
result += String.fromCharCode(buf[i]);
}
return result;
}
// Convert byte array to binary string
exports.buf2binstring = function(buf) {
return buf2binstring(buf, buf.length);
};
// Convert binary string (typed, when possible)
exports.binstring2buf = function(str) {
var buf = new utils.Buf8(str.length);
for(var i=0, len=buf.length; i < len; i++) {
buf[i] = str.charCodeAt(i);
}
return buf;
};
// convert array to string
exports.buf2string = function (buf, max) {
var i, out, c, c_len;
var len = max || buf.length;
// Reserve max possible length (2 words per char)
// NB: by unknown reasons, Array is significantly faster for
// String.fromCharCode.apply than Uint16Array.
var utf16buf = new Array(len*2);
for (out=0, i=0; i<len;) {
c = buf[i++];
// quick process ascii
if (c < 0x80) { utf16buf[out++] = c; continue; }
c_len = _utf8len[c];
// skip 5 & 6 byte codes
if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; }
// apply mask on first byte
c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
// join the rest
while (c_len > 1 && i < len) {
c = (c << 6) | (buf[i++] & 0x3f);
c_len--;
}
// terminated by end of string?
if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
if (c < 0x10000) {
utf16buf[out++] = c;
} else {
c -= 0x10000;
utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
utf16buf[out++] = 0xdc00 | (c & 0x3ff);
}
}
return buf2binstring(utf16buf, out);
};
// Calculate max possible position in utf8 buffer,
// that will not break sequence. If that's not possible
// - (very small limits) return max size as is.
//
// buf[] - utf8 bytes array
// max - length limit (mandatory);
exports.utf8border = function(buf, max) {
var pos;
max = max || buf.length;
if (max > buf.length) { max = buf.length; }
// go back from last position, until start of sequence found
pos = max-1;
while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
// Fuckup - very small and broken sequence,
// return max, because we should return something anyway.
if (pos < 0) { return max; }
// If we came to start of buffer - that means vuffer is too small,
// return max too.
if (pos === 0) { return max; }
return (pos + _utf8len[buf[pos]] > max) ? pos : max;
};
},{"./common":66}],68:[function(require,module,exports){
'use strict';
// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It doesn't worth to make additional optimizationa as in original.
// Small size is preferable.
function adler32(adler, buf, len, pos) {
var s1 = (adler & 0xffff) |0
, s2 = ((adler >>> 16) & 0xffff) |0
, n = 0;
while (len !== 0) {
// Set limit ~ twice less than 5552, to keep
// s2 in 31-bits, because we force signed ints.
// in other case %= will fail.
n = len > 2000 ? 2000 : len;
len -= n;
do {
s1 = (s1 + buf[pos++]) |0;
s2 = (s2 + s1) |0;
} while (--n);
s1 %= 65521;
s2 %= 65521;
}
return (s1 | (s2 << 16)) |0;
}
module.exports = adler32;
},{}],69:[function(require,module,exports){
module.exports = {
/* Allowed flush values; see deflate() and inflate() below for details */
Z_NO_FLUSH: 0,
Z_PARTIAL_FLUSH: 1,
Z_SYNC_FLUSH: 2,
Z_FULL_FLUSH: 3,
Z_FINISH: 4,
Z_BLOCK: 5,
Z_TREES: 6,
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
Z_OK: 0,
Z_STREAM_END: 1,
Z_NEED_DICT: 2,
Z_ERRNO: -1,
Z_STREAM_ERROR: -2,
Z_DATA_ERROR: -3,
//Z_MEM_ERROR: -4,
Z_BUF_ERROR: -5,
//Z_VERSION_ERROR: -6,
/* compression levels */
Z_NO_COMPRESSION: 0,
Z_BEST_SPEED: 1,
Z_BEST_COMPRESSION: 9,
Z_DEFAULT_COMPRESSION: -1,
Z_FILTERED: 1,
Z_HUFFMAN_ONLY: 2,
Z_RLE: 3,
Z_FIXED: 4,
Z_DEFAULT_STRATEGY: 0,
/* Possible values of the data_type field (though see inflate()) */
Z_BINARY: 0,
Z_TEXT: 1,
//Z_ASCII: 1, // = Z_TEXT (deprecated)
Z_UNKNOWN: 2,
/* The deflate compression method */
Z_DEFLATED: 8
//Z_NULL: null // Use -1 or null inline, depending on var type
};
},{}],70:[function(require,module,exports){
'use strict';
// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.
// Use ordinary array, since untyped makes no boost here
function makeTable() {
var c, table = [];
for(var n =0; n < 256; n++){
c = n;
for(var k =0; k < 8; k++){
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
table[n] = c;
}
return table;
}
// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable
, end = pos + len;
crc = crc ^ (-1);
for (var i = pos; i < end; i++ ) {
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;
}
module.exports = crc32;
},{}],71:[function(require,module,exports){
'use strict';
var utils = require('../utils/common');
var trees = require('./trees');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var msg = require('./messages');
/* Public constants ==========================================================*/
/* ===========================================================================*/
/* Allowed flush values; see deflate() and inflate() below for details */
var Z_NO_FLUSH = 0;
var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH = 2;
var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_BLOCK = 5;
//var Z_TREES = 6;
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
var Z_OK = 0;
var Z_STREAM_END = 1;
//var Z_NEED_DICT = 2;
//var Z_ERRNO = -1;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
//var Z_MEM_ERROR = -4;
var Z_BUF_ERROR = -5;
//var Z_VERSION_ERROR = -6;
/* compression levels */
//var Z_NO_COMPRESSION = 0;
//var Z_BEST_SPEED = 1;
//var Z_BEST_COMPRESSION = 9;
var Z_DEFAULT_COMPRESSION = -1;
var Z_FILTERED = 1;
var Z_HUFFMAN_ONLY = 2;
var Z_RLE = 3;
var Z_FIXED = 4;
var Z_DEFAULT_STRATEGY = 0;
/* Possible values of the data_type field (though see inflate()) */
//var Z_BINARY = 0;
//var Z_TEXT = 1;
//var Z_ASCII = 1; // = Z_TEXT
var Z_UNKNOWN = 2;
/* The deflate compression method */
var Z_DEFLATED = 8;
/*============================================================================*/
var MAX_MEM_LEVEL = 9;
/* Maximum value for memLevel in deflateInit2 */
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_MEM_LEVEL = 8;
var LENGTH_CODES = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS = 256;
/* number of literal bytes 0..255 */
var L_CODES = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES = 30;
/* number of distance codes */
var BL_CODES = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE = 2*L_CODES + 1;
/* maximum heap size */
var MAX_BITS = 15;
/* All codes must not exceed MAX_BITS bits */
var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
var PRESET_DICT = 0x20;
var INIT_STATE = 42;
var EXTRA_STATE = 69;
var NAME_STATE = 73;
var COMMENT_STATE = 91;
var HCRC_STATE = 103;
var BUSY_STATE = 113;
var FINISH_STATE = 666;
var BS_NEED_MORE = 1; /* block not completed, need more input or more output */
var BS_BLOCK_DONE = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */
var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
function err(strm, errorCode) {
strm.msg = msg[errorCode];
return errorCode;
}
function rank(f) {
return ((f) << 1) - ((f) > 4 ? 9 : 0);
}
function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
/* =========================================================================
* Flush as much pending output as possible. All deflate() output goes
* through this function so some applications may wish to modify it
* to avoid allocating a large strm->output buffer and copying into it.
* (See also read_buf()).
*/
function flush_pending(strm) {
var s = strm.state;
//_tr_flush_bits(s);
var len = s.pending;
if (len > strm.avail_out) {
len = strm.avail_out;
}
if (len === 0) { return; }
utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
strm.next_out += len;
s.pending_out += len;
strm.total_out += len;
strm.avail_out -= len;
s.pending -= len;
if (s.pending === 0) {
s.pending_out = 0;
}
}
function flush_block_only (s, last) {
trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
s.block_start = s.strstart;
flush_pending(s.strm);
}
function put_byte(s, b) {
s.pending_buf[s.pending++] = b;
}
/* =========================================================================
* Put a short in the pending buffer. The 16-bit value is put in MSB order.
* IN assertion: the stream state is correct and there is enough room in
* pending_buf.
*/
function putShortMSB(s, b) {
// put_byte(s, (Byte)(b >> 8));
// put_byte(s, (Byte)(b & 0xff));
s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
s.pending_buf[s.pending++] = b & 0xff;
}
/* ===========================================================================
* Read a new buffer from the current input stream, update the adler32
* and total number of bytes read. All deflate() input goes through
* this function so some applications may wish to modify it to avoid
* allocating a large strm->input buffer and copying from it.
* (See also flush_pending()).
*/
function read_buf(strm, buf, start, size) {
var len = strm.avail_in;
if (len > size) { len = size; }
if (len === 0) { return 0; }
strm.avail_in -= len;
utils.arraySet(buf, strm.input, strm.next_in, len, start);
if (strm.state.wrap === 1) {
strm.adler = adler32(strm.adler, buf, len, start);
}
else if (strm.state.wrap === 2) {
strm.adler = crc32(strm.adler, buf, len, start);
}
strm.next_in += len;
strm.total_in += len;
return len;
}
/* ===========================================================================
* Set match_start to the longest match starting at the given string and
* return its length. Matches shorter or equal to prev_length are discarded,
* in which case the result is equal to prev_length and match_start is
* garbage.
* IN assertions: cur_match is the head of the hash chain for the current
* string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
* OUT assertion: the match length is not greater than s->lookahead.
*/
function longest_match(s, cur_match) {
var chain_length = s.max_chain_length; /* max hash chain length */
var scan = s.strstart; /* current string */
var match; /* matched string */
var len; /* length of current match */
var best_len = s.prev_length; /* best match length so far */
var nice_match = s.nice_match; /* stop if match long enough */
var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;
var _win = s.window; // shortcut
var wmask = s.w_mask;
var prev = s.prev;
/* Stop when cur_match becomes <= limit. To simplify the code,
* we prevent matches with the string of window index 0.
*/
var strend = s.strstart + MAX_MATCH;
var scan_end1 = _win[scan + best_len - 1];
var scan_end = _win[scan + best_len];
/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
* It is easy to get rid of this optimization if necessary.
*/
// Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
/* Do not waste too much time if we already have a good match: */
if (s.prev_length >= s.good_match) {
chain_length >>= 2;
}
/* Do not look for matches beyond the end of the input. This is necessary
* to make deflate deterministic.
*/
if (nice_match > s.lookahead) { nice_match = s.lookahead; }
// Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
do {
// Assert(cur_match < s->strstart, "no future");
match = cur_match;
/* Skip to next match if the match length cannot increase
* or if the match length is less than 2. Note that the checks below
* for insufficient lookahead only occur occasionally for performance
* reasons. Therefore uninitialized memory will be accessed, and
* conditional jumps will be made that depend on those values.
* However the length of the match is limited to the lookahead, so
* the output of deflate is not affected by the uninitialized values.
*/
if (_win[match + best_len] !== scan_end ||
_win[match + best_len - 1] !== scan_end1 ||
_win[match] !== _win[scan] ||
_win[++match] !== _win[scan + 1]) {
continue;
}
/* The check at best_len-1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
* the hash keys are equal and that HASH_BITS >= 8.
*/
scan += 2;
match++;
// Assert(*scan == *match, "match[2]?");
/* We check for insufficient lookahead only every 8th comparison;
* the 256th check will be made at strstart+258.
*/
do {
/*jshint noempty:false*/
} while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
scan < strend);
// Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
len = MAX_MATCH - (strend - scan);
scan = strend - MAX_MATCH;
if (len > best_len) {
s.match_start = cur_match;
best_len = len;
if (len >= nice_match) {
break;
}
scan_end1 = _win[scan + best_len - 1];
scan_end = _win[scan + best_len];
}
} while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
if (best_len <= s.lookahead) {
return best_len;
}
return s.lookahead;
}
/* ===========================================================================
* Fill the window when the lookahead becomes insufficient.
* Updates strstart and lookahead.
*
* IN assertion: lookahead < MIN_LOOKAHEAD
* OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
* At least one byte has been read, or avail_in == 0; reads are
* performed for at least two bytes (required for the zip translate_eol
* option -- not supported here).
*/
function fill_window(s) {
var _w_size = s.w_size;
var p, n, m, more, str;
//Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
do {
more = s.window_size - s.lookahead - s.strstart;
// JS ints have 32 bit, block below not needed
/* Deal with !@#$% 64K limit: */
//if (sizeof(int) <= 2) {
// if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
// more = wsize;
//
// } else if (more == (unsigned)(-1)) {
// /* Very unlikely, but possible on 16 bit machine if
// * strstart == 0 && lookahead == 1 (input done a byte at time)
// */
// more--;
// }
//}
/* If the window is almost full and there is insufficient lookahead,
* move the upper half to the lower one to make room in the upper half.
*/
if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
s.match_start -= _w_size;
s.strstart -= _w_size;
/* we now have strstart >= MAX_DIST */
s.block_start -= _w_size;
/* Slide the hash table (could be avoided with 32 bit values
at the expense of memory usage). We slide even when level == 0
to keep the hash table consistent if we switch back to level > 0
later. (Using level 0 permanently is not an optimal usage of
zlib, so we don't care about this pathological case.)
*/
n = s.hash_size;
p = n;
do {
m = s.head[--p];
s.head[p] = (m >= _w_size ? m - _w_size : 0);
} while (--n);
n = _w_size;
p = n;
do {
m = s.prev[--p];
s.prev[p] = (m >= _w_size ? m - _w_size : 0);
/* If n is not on any hash chain, prev[n] is garbage but
* its value will never be used.
*/
} while (--n);
more += _w_size;
}
if (s.strm.avail_in === 0) {
break;
}
/* If there was no sliding:
* strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
* more == window_size - lookahead - strstart
* => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
* => more >= window_size - 2*WSIZE + 2
* In the BIG_MEM or MMAP case (not yet supported),
* window_size == input_size + MIN_LOOKAHEAD &&
* strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
* Otherwise, window_size == 2*WSIZE so more >= 2.
* If there was sliding, more >= WSIZE. So in all cases, more >= 2.
*/
//Assert(more >= 2, "more < 2");
n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
s.lookahead += n;
/* Initialize the hash value now that we have some input: */
if (s.lookahead + s.insert >= MIN_MATCH) {
str = s.strstart - s.insert;
s.ins_h = s.window[str];
/* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
// Call update_hash() MIN_MATCH-3 more times
//#endif
while (s.insert) {
/* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH-1]) & s.hash_mask;
s.prev[str & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = str;
str++;
s.insert--;
if (s.lookahead + s.insert < MIN_MATCH) {
break;
}
}
}
/* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
* but this is not important since only literal bytes will be emitted.
*/
} while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);
/* If the WIN_INIT bytes after the end of the current data have never been
* written, then zero those bytes in order to avoid memory check reports of
* the use of uninitialized (or uninitialised as Julian writes) bytes by
* the longest match routines. Update the high water mark for the next
* time through here. WIN_INIT is set to MAX_MATCH since the longest match
* routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
*/
// if (s.high_water < s.window_size) {
// var curr = s.strstart + s.lookahead;
// var init = 0;
//
// if (s.high_water < curr) {
// /* Previous high water mark below current data -- zero WIN_INIT
// * bytes or up to end of window, whichever is less.
// */
// init = s.window_size - curr;
// if (init > WIN_INIT)
// init = WIN_INIT;
// zmemzero(s->window + curr, (unsigned)init);
// s->high_water = curr + init;
// }
// else if (s->high_water < (ulg)curr + WIN_INIT) {
// /* High water mark at or above current data, but below current data
// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
// * to end of window, whichever is less.
// */
// init = (ulg)curr + WIN_INIT - s->high_water;
// if (init > s->window_size - s->high_water)
// init = s->window_size - s->high_water;
// zmemzero(s->window + s->high_water, (unsigned)init);
// s->high_water += init;
// }
// }
//
// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
// "not enough room for search");
}
/* ===========================================================================
* Copy without compression as much as possible from the input stream, return
* the current block state.
* This function does not insert new strings in the dictionary since
* uncompressible data is probably not useful. This function is used
* only for the level=0 compression option.
* NOTE: this function should be optimized to avoid extra copying from
* window to pending_buf.
*/
function deflate_stored(s, flush) {
/* Stored blocks are limited to 0xffff bytes, pending_buf is limited
* to pending_buf_size, and each stored block has a 5 byte header:
*/
var max_block_size = 0xffff;
if (max_block_size > s.pending_buf_size - 5) {
max_block_size = s.pending_buf_size - 5;
}
/* Copy as much as possible from input to output: */
for (;;) {
/* Fill the window as much as possible: */
if (s.lookahead <= 1) {
//Assert(s->strstart < s->w_size+MAX_DIST(s) ||
// s->block_start >= (long)s->w_size, "slide too late");
// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
// s.block_start >= s.w_size)) {
// throw new Error("slide too late");
// }
fill_window(s);
if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) {
break;
}
/* flush the current block */
}
//Assert(s->block_start >= 0L, "block gone");
// if (s.block_start < 0) throw new Error("block gone");
s.strstart += s.lookahead;
s.lookahead = 0;
/* Emit a stored block if pending_buf will be full: */
var max_start = s.block_start + max_block_size;
if (s.strstart === 0 || s.strstart >= max_start) {
/* strstart == 0 is possible when wraparound on 16-bit machine */
s.lookahead = s.strstart - max_start;
s.strstart = max_start;
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
/* Flush if we may have to slide, otherwise block_start may become
* negative and the data will be gone:
*/
if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.strstart > s.block_start) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_NEED_MORE;
}
/* ===========================================================================
* Compress as much as possible from the input stream, return the current
* block state.
* This function does not perform lazy evaluation of matches and inserts
* new strings in the dictionary only for unmatched strings or for short
* matches. It is used only for the fast compression options.
*/
function deflate_fast(s, flush) {
var hash_head; /* head of the hash chain */
var bflush; /* set if current block must be flushed */
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the next match, plus MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s.lookahead < MIN_LOOKAHEAD) {
fill_window(s);
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) {
break; /* flush the current block */
}
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = 0/*NIL*/;
if (s.lookahead >= MIN_MATCH) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
/* Find the longest match, discarding those <= prev_length.
* At this point we have always match_length < MIN_MATCH
*/
if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
/* To simplify the code, we prevent matches with the string
* of window index 0 (in particular we have to avoid a match
* of the string with itself at the start of the input file).
*/
s.match_length = longest_match(s, hash_head);
/* longest_match() sets match_start */
}
if (s.match_length >= MIN_MATCH) {
// check_match(s, s.strstart, s.match_start, s.match_length); // for debug only
/*** _tr_tally_dist(s, s.strstart - s.match_start,
s.match_length - MIN_MATCH, bflush); ***/
bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
s.lookahead -= s.match_length;
/* Insert new strings in the hash table only if the match length
* is not too large. This saves time but degrades compression.
*/
if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
s.match_length--; /* string at strstart already in table */
do {
s.strstart++;
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
/* strstart never exceeds WSIZE-MAX_MATCH, so there are
* always MIN_MATCH bytes ahead.
*/
} while (--s.match_length !== 0);
s.strstart++;
} else
{
s.strstart += s.match_length;
s.match_length = 0;
s.ins_h = s.window[s.strstart];
/* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
// Call UPDATE_HASH() MIN_MATCH-3 more times
//#endif
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
* matter since it will be recomputed at next deflate call.
*/
}
} else {
/* No match, output a literal byte */
//Tracevv((stderr,"%c", s.window[s.strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
}
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = ((s.strstart < (MIN_MATCH-1)) ? s.strstart : MIN_MATCH-1);
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* Same as above, but achieves better compression. We use a lazy
* evaluation for matches: a match is finally adopted only if there is
* no better match at the next window position.
*/
function deflate_slow(s, flush) {
var hash_head; /* head of hash chain */
var bflush; /* set if current block must be flushed */
var max_insert;
/* Process the input block. */
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the next match, plus MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s.lookahead < MIN_LOOKAHEAD) {
fill_window(s);
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) { break; } /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = 0/*NIL*/;
if (s.lookahead >= MIN_MATCH) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
/* Find the longest match, discarding those <= prev_length.
*/
s.prev_length = s.match_length;
s.prev_match = s.match_start;
s.match_length = MIN_MATCH-1;
if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
s.strstart - hash_head <= (s.w_size-MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
/* To simplify the code, we prevent matches with the string
* of window index 0 (in particular we have to avoid a match
* of the string with itself at the start of the input file).
*/
s.match_length = longest_match(s, hash_head);
/* longest_match() sets match_start */
if (s.match_length <= 5 &&
(s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {
/* If prev_match is also MIN_MATCH, match_start is garbage
* but we will ignore the current match anyway.
*/
s.match_length = MIN_MATCH-1;
}
}
/* If there was a match at the previous step and the current
* match is not better, output the previous match:
*/
if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
max_insert = s.strstart + s.lookahead - MIN_MATCH;
/* Do not insert strings in hash table beyond this. */
//check_match(s, s.strstart-1, s.prev_match, s.prev_length);
/***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
s.prev_length - MIN_MATCH, bflush);***/
bflush = trees._tr_tally(s, s.strstart - 1- s.prev_match, s.prev_length - MIN_MATCH);
/* Insert in hash table all strings up to the end of the match.
* strstart-1 and strstart are already inserted. If there is not
* enough lookahead, the last two strings are not inserted in
* the hash table.
*/
s.lookahead -= s.prev_length-1;
s.prev_length -= 2;
do {
if (++s.strstart <= max_insert) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
} while (--s.prev_length !== 0);
s.match_available = 0;
s.match_length = MIN_MATCH-1;
s.strstart++;
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
} else if (s.match_available) {
/* If there was no match at the previous position, output a
* single literal. If there was a match but the current match
* is longer, truncate the previous match to a single literal.
*/
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart-1]);
if (bflush) {
/*** FLUSH_BLOCK_ONLY(s, 0) ***/
flush_block_only(s, false);
/***/
}
s.strstart++;
s.lookahead--;
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
} else {
/* There is no previous match to compare with, wait for
* the next step to decide.
*/
s.match_available = 1;
s.strstart++;
s.lookahead--;
}
}
//Assert (flush != Z_NO_FLUSH, "no flush?");
if (s.match_available) {
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart-1]);
s.match_available = 0;
}
s.insert = s.strstart < MIN_MATCH-1 ? s.strstart : MIN_MATCH-1;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* For Z_RLE, simply look for runs of bytes, generate matches only of distance
* one. Do not maintain a hash table. (It will be regenerated if this run of
* deflate switches away from Z_RLE.)
*/
function deflate_rle(s, flush) {
var bflush; /* set if current block must be flushed */
var prev; /* byte at distance one to match */
var scan, strend; /* scan goes up to strend for length of run */
var _win = s.window;
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the longest run, plus one for the unrolled loop.
*/
if (s.lookahead <= MAX_MATCH) {
fill_window(s);
if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) { break; } /* flush the current block */
}
/* See how many times the previous byte repeats */
s.match_length = 0;
if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
scan = s.strstart - 1;
prev = _win[scan];
if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
strend = s.strstart + MAX_MATCH;
do {
/*jshint noempty:false*/
} while (prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
scan < strend);
s.match_length = MAX_MATCH - (strend - scan);
if (s.match_length > s.lookahead) {
s.match_length = s.lookahead;
}
}
//Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
}
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
if (s.match_length >= MIN_MATCH) {
//check_match(s, s.strstart, s.strstart - 1, s.match_length);
/*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);
s.lookahead -= s.match_length;
s.strstart += s.match_length;
s.match_length = 0;
} else {
/* No match, output a literal byte */
//Tracevv((stderr,"%c", s->window[s->strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
}
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
* (It will be regenerated if this run of deflate switches away from Huffman.)
*/
function deflate_huff(s, flush) {
var bflush; /* set if current block must be flushed */
for (;;) {
/* Make sure that we have a literal to write. */
if (s.lookahead === 0) {
fill_window(s);
if (s.lookahead === 0) {
if (flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
break; /* flush the current block */
}
}
/* Output a literal byte */
s.match_length = 0;
//Tracevv((stderr,"%c", s->window[s->strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* Values for max_lazy_match, good_match and max_chain_length, depending on
* the desired pack level (0..9). The values given below have been tuned to
* exclude worst case performance for pathological files. Better values may be
* found for specific files.
*/
var Config = function (good_length, max_lazy, nice_length, max_chain, func) {
this.good_length = good_length;
this.max_lazy = max_lazy;
this.nice_length = nice_length;
this.max_chain = max_chain;
this.func = func;
};
var configuration_table;
configuration_table = [
/* good lazy nice chain */
new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */
new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */
new Config(4, 5, 16, 8, deflate_fast), /* 2 */
new Config(4, 6, 32, 32, deflate_fast), /* 3 */
new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */
new Config(8, 16, 32, 32, deflate_slow), /* 5 */
new Config(8, 16, 128, 128, deflate_slow), /* 6 */
new Config(8, 32, 128, 256, deflate_slow), /* 7 */
new Config(32, 128, 258, 1024, deflate_slow), /* 8 */
new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */
];
/* ===========================================================================
* Initialize the "longest match" routines for a new zlib stream
*/
function lm_init(s) {
s.window_size = 2 * s.w_size;
/*** CLEAR_HASH(s); ***/
zero(s.head); // Fill with NIL (= 0);
/* Set the default configuration parameters:
*/
s.max_lazy_match = configuration_table[s.level].max_lazy;
s.good_match = configuration_table[s.level].good_length;
s.nice_match = configuration_table[s.level].nice_length;
s.max_chain_length = configuration_table[s.level].max_chain;
s.strstart = 0;
s.block_start = 0;
s.lookahead = 0;
s.insert = 0;
s.match_length = s.prev_length = MIN_MATCH - 1;
s.match_available = 0;
s.ins_h = 0;
}
function DeflateState() {
this.strm = null; /* pointer back to this zlib stream */
this.status = 0; /* as the name implies */
this.pending_buf = null; /* output still pending */
this.pending_buf_size = 0; /* size of pending_buf */
this.pending_out = 0; /* next pending byte to output to the stream */
this.pending = 0; /* nb of bytes in the pending buffer */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
this.gzhead = null; /* gzip header information to write */
this.gzindex = 0; /* where in extra, name, or comment */
this.method = Z_DEFLATED; /* can only be DEFLATED */
this.last_flush = -1; /* value of flush param for previous deflate call */
this.w_size = 0; /* LZ77 window size (32K by default) */
this.w_bits = 0; /* log2(w_size) (8..16) */
this.w_mask = 0; /* w_size - 1 */
this.window = null;
/* Sliding window. Input bytes are read into the second half of the window,
* and move to the first half later to keep a dictionary of at least wSize
* bytes. With this organization, matches are limited to a distance of
* wSize-MAX_MATCH bytes, but this ensures that IO is always
* performed with a length multiple of the block size.
*/
this.window_size = 0;
/* Actual size of window: 2*wSize, except when the user input buffer
* is directly used as sliding window.
*/
this.prev = null;
/* Link to older string with same hash index. To limit the size of this
* array to 64K, this link is maintained only for the last 32K strings.
* An index in this array is thus a window index modulo 32K.
*/
this.head = null; /* Heads of the hash chains or NIL. */
this.ins_h = 0; /* hash index of string to be inserted */
this.hash_size = 0; /* number of elements in hash table */
this.hash_bits = 0; /* log2(hash_size) */
this.hash_mask = 0; /* hash_size-1 */
this.hash_shift = 0;
/* Number of bits by which ins_h must be shifted at each input
* step. It must be such that after MIN_MATCH steps, the oldest
* byte no longer takes part in the hash key, that is:
* hash_shift * MIN_MATCH >= hash_bits
*/
this.block_start = 0;
/* Window position at the beginning of the current output block. Gets
* negative when the window is moved backwards.
*/
this.match_length = 0; /* length of best match */
this.prev_match = 0; /* previous match */
this.match_available = 0; /* set if previous match exists */
this.strstart = 0; /* start of string to insert */
this.match_start = 0; /* start of matching string */
this.lookahead = 0; /* number of valid bytes ahead in window */
this.prev_length = 0;
/* Length of the best match at previous step. Matches not greater than this
* are discarded. This is used in the lazy match evaluation.
*/
this.max_chain_length = 0;
/* To speed up deflation, hash chains are never searched beyond this
* length. A higher limit improves compression ratio but degrades the
* speed.
*/
this.max_lazy_match = 0;
/* Attempt to find a better match only when the current match is strictly
* smaller than this value. This mechanism is used only for compression
* levels >= 4.
*/
// That's alias to max_lazy_match, don't use directly
//this.max_insert_length = 0;
/* Insert new strings in the hash table only if the match length is not
* greater than this length. This saves time but degrades compression.
* max_insert_length is used only for compression levels <= 3.
*/
this.level = 0; /* compression level (1..9) */
this.strategy = 0; /* favor or force Huffman coding*/
this.good_match = 0;
/* Use a faster search when the previous match is longer than this */
this.nice_match = 0; /* Stop searching when current match exceeds this */
/* used by trees.c: */
/* Didn't use ct_data typedef below to suppress compiler warning */
// struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
// struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
// struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
// Use flat array of DOUBLE size, with interleaved fata,
// because JS does not support effective
this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2);
this.dyn_dtree = new utils.Buf16((2*D_CODES+1) * 2);
this.bl_tree = new utils.Buf16((2*BL_CODES+1) * 2);
zero(this.dyn_ltree);
zero(this.dyn_dtree);
zero(this.bl_tree);
this.l_desc = null; /* desc. for literal tree */
this.d_desc = null; /* desc. for distance tree */
this.bl_desc = null; /* desc. for bit length tree */
//ush bl_count[MAX_BITS+1];
this.bl_count = new utils.Buf16(MAX_BITS+1);
/* number of codes at each bit length for an optimal tree */
//int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
this.heap = new utils.Buf16(2*L_CODES+1); /* heap used to build the Huffman trees */
zero(this.heap);
this.heap_len = 0; /* number of elements in the heap */
this.heap_max = 0; /* element of largest frequency */
/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
* The same heap array is used to build all trees.
*/
this.depth = new utils.Buf16(2*L_CODES+1); //uch depth[2*L_CODES+1];
zero(this.depth);
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
this.l_buf = 0; /* buffer index for literals or lengths */
this.lit_bufsize = 0;
/* Size of match buffer for literals/lengths. There are 4 reasons for
* limiting lit_bufsize to 64K:
* - frequencies can be kept in 16 bit counters
* - if compression is not successful for the first block, all input
* data is still in the window so we can still emit a stored block even
* when input comes from standard input. (This can also be done for
* all blocks if lit_bufsize is not greater than 32K.)
* - if compression is not successful for a file smaller than 64K, we can
* even emit a stored file instead of a stored block (saving 5 bytes).
* This is applicable only for zip (not gzip or zlib).
* - creating new Huffman trees less frequently may not provide fast
* adaptation to changes in the input data statistics. (Take for
* example a binary file with poorly compressible code followed by
* a highly compressible string table.) Smaller buffer sizes give
* fast adaptation but have of course the overhead of transmitting
* trees more frequently.
* - I can't count above 4
*/
this.last_lit = 0; /* running index in l_buf */
this.d_buf = 0;
/* Buffer index for distances. To simplify the code, d_buf and l_buf have
* the same number of elements. To use different lengths, an extra flag
* array would be necessary.
*/
this.opt_len = 0; /* bit length of current block with optimal trees */
this.static_len = 0; /* bit length of current block with static trees */
this.matches = 0; /* number of string matches in current block */
this.insert = 0; /* bytes at end of window left to insert */
this.bi_buf = 0;
/* Output buffer. bits are inserted starting at the bottom (least
* significant bits).
*/
this.bi_valid = 0;
/* Number of valid bits in bi_buf. All bits above the last valid bit
* are always zero.
*/
// Used for window memory init. We safely ignore it for JS. That makes
// sense only for pointers and memory check tools.
//this.high_water = 0;
/* High water mark offset in window for initialized bytes -- bytes above
* this are set to zero in order to avoid memory check warnings when
* longest match routines access bytes past the input. This is then
* updated to the new high water mark.
*/
}
function deflateResetKeep(strm) {
var s;
if (!strm || !strm.state) {
return err(strm, Z_STREAM_ERROR);
}
strm.total_in = strm.total_out = 0;
strm.data_type = Z_UNKNOWN;
s = strm.state;
s.pending = 0;
s.pending_out = 0;
if (s.wrap < 0) {
s.wrap = -s.wrap;
/* was made negative by deflate(..., Z_FINISH); */
}
s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
strm.adler = (s.wrap === 2) ?
0 // crc32(0, Z_NULL, 0)
:
1; // adler32(0, Z_NULL, 0)
s.last_flush = Z_NO_FLUSH;
trees._tr_init(s);
return Z_OK;
}
function deflateReset(strm) {
var ret = deflateResetKeep(strm);
if (ret === Z_OK) {
lm_init(strm.state);
}
return ret;
}
function deflateSetHeader(strm, head) {
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
strm.state.gzhead = head;
return Z_OK;
}
function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
if (!strm) { // === Z_NULL
return Z_STREAM_ERROR;
}
var wrap = 1;
if (level === Z_DEFAULT_COMPRESSION) {
level = 6;
}
if (windowBits < 0) { /* suppress zlib wrapper */
wrap = 0;
windowBits = -windowBits;
}
else if (windowBits > 15) {
wrap = 2; /* write gzip wrapper instead */
windowBits -= 16;
}
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
strategy < 0 || strategy > Z_FIXED) {
return err(strm, Z_STREAM_ERROR);
}
if (windowBits === 8) {
windowBits = 9;
}
/* until 256-byte window bug fixed */
var s = new DeflateState();
strm.state = s;
s.strm = strm;
s.wrap = wrap;
s.gzhead = null;
s.w_bits = windowBits;
s.w_size = 1 << s.w_bits;
s.w_mask = s.w_size - 1;
s.hash_bits = memLevel + 7;
s.hash_size = 1 << s.hash_bits;
s.hash_mask = s.hash_size - 1;
s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
s.window = new utils.Buf8(s.w_size * 2);
s.head = new utils.Buf16(s.hash_size);
s.prev = new utils.Buf16(s.w_size);
// Don't need mem init magic for JS.
//s.high_water = 0; /* nothing written to s->window yet */
s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
s.pending_buf_size = s.lit_bufsize * 4;
s.pending_buf = new utils.Buf8(s.pending_buf_size);
s.d_buf = s.lit_bufsize >> 1;
s.l_buf = (1 + 2) * s.lit_bufsize;
s.level = level;
s.strategy = strategy;
s.method = method;
return deflateReset(strm);
}
function deflateInit(strm, level) {
return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}
function deflate(strm, flush) {
var old_flush, s;
var beg, val; // for gzip header write only
if (!strm || !strm.state ||
flush > Z_BLOCK || flush < 0) {
return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
}
s = strm.state;
if (!strm.output ||
(!strm.input && strm.avail_in !== 0) ||
(s.status === FINISH_STATE && flush !== Z_FINISH)) {
return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
}
s.strm = strm; /* just in case */
old_flush = s.last_flush;
s.last_flush = flush;
/* Write the header */
if (s.status === INIT_STATE) {
if (s.wrap === 2) { // GZIP header
strm.adler = 0; //crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (!s.gzhead) { // s->gzhead == Z_NULL
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, s.level === 9 ? 2 :
(s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
4 : 0));
put_byte(s, OS_CODE);
s.status = BUSY_STATE;
}
else {
put_byte(s, (s.gzhead.text ? 1 : 0) +
(s.gzhead.hcrc ? 2 : 0) +
(!s.gzhead.extra ? 0 : 4) +
(!s.gzhead.name ? 0 : 8) +
(!s.gzhead.comment ? 0 : 16)
);
put_byte(s, s.gzhead.time & 0xff);
put_byte(s, (s.gzhead.time >> 8) & 0xff);
put_byte(s, (s.gzhead.time >> 16) & 0xff);
put_byte(s, (s.gzhead.time >> 24) & 0xff);
put_byte(s, s.level === 9 ? 2 :
(s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
4 : 0));
put_byte(s, s.gzhead.os & 0xff);
if (s.gzhead.extra && s.gzhead.extra.length) {
put_byte(s, s.gzhead.extra.length & 0xff);
put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
}
if (s.gzhead.hcrc) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
}
s.gzindex = 0;
s.status = EXTRA_STATE;
}
}
else // DEFLATE header
{
var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
var level_flags = -1;
if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
level_flags = 0;
} else if (s.level < 6) {
level_flags = 1;
} else if (s.level === 6) {
level_flags = 2;
} else {
level_flags = 3;
}
header |= (level_flags << 6);
if (s.strstart !== 0) { header |= PRESET_DICT; }
header += 31 - (header % 31);
s.status = BUSY_STATE;
putShortMSB(s, header);
/* Save the adler32 of the preset dictionary: */
if (s.strstart !== 0) {
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
strm.adler = 1; // adler32(0L, Z_NULL, 0);
}
}
//#ifdef GZIP
if (s.status === EXTRA_STATE) {
if (s.gzhead.extra/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
break;
}
}
put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
s.gzindex++;
}
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (s.gzindex === s.gzhead.extra.length) {
s.gzindex = 0;
s.status = NAME_STATE;
}
}
else {
s.status = NAME_STATE;
}
}
if (s.status === NAME_STATE) {
if (s.gzhead.name/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
//int val;
do {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.name.length) {
val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg){
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (val === 0) {
s.gzindex = 0;
s.status = COMMENT_STATE;
}
}
else {
s.status = COMMENT_STATE;
}
}
if (s.status === COMMENT_STATE) {
if (s.gzhead.comment/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
//int val;
do {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.comment.length) {
val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (val === 0) {
s.status = HCRC_STATE;
}
}
else {
s.status = HCRC_STATE;
}
}
if (s.status === HCRC_STATE) {
if (s.gzhead.hcrc) {
if (s.pending + 2 > s.pending_buf_size) {
flush_pending(strm);
}
if (s.pending + 2 <= s.pending_buf_size) {
put_byte(s, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
strm.adler = 0; //crc32(0L, Z_NULL, 0);
s.status = BUSY_STATE;
}
}
else {
s.status = BUSY_STATE;
}
}
//#endif
/* Flush as much pending output as possible */
if (s.pending !== 0) {
flush_pending(strm);
if (strm.avail_out === 0) {
/* Since avail_out is 0, deflate will be called again with
* more output space, but possibly with both pending and
* avail_in equal to zero. There won't be anything to do,
* but this is not an error situation so make sure we
* return OK instead of BUF_ERROR at next call of deflate:
*/
s.last_flush = -1;
return Z_OK;
}
/* Make sure there is something to do and avoid duplicate consecutive
* flushes. For repeated and useless calls with Z_FINISH, we keep
* returning Z_STREAM_END instead of Z_BUF_ERROR.
*/
} else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
flush !== Z_FINISH) {
return err(strm, Z_BUF_ERROR);
}
/* User must not provide more input after the first FINISH: */
if (s.status === FINISH_STATE && strm.avail_in !== 0) {
return err(strm, Z_BUF_ERROR);
}
/* Start a new block or continue the current one.
*/
if (strm.avail_in !== 0 || s.lookahead !== 0 ||
(flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
(s.strategy === Z_RLE ? deflate_rle(s, flush) :
configuration_table[s.level].func(s, flush));
if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
s.status = FINISH_STATE;
}
if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
if (strm.avail_out === 0) {
s.last_flush = -1;
/* avoid BUF_ERROR next call, see above */
}
return Z_OK;
/* If flush != Z_NO_FLUSH && avail_out == 0, the next call
* of deflate should use the same flush parameter to make sure
* that the flush is complete. So we don't have to output an
* empty block here, this will be done at next call. This also
* ensures that for a very small output buffer, we emit at most
* one empty block.
*/
}
if (bstate === BS_BLOCK_DONE) {
if (flush === Z_PARTIAL_FLUSH) {
trees._tr_align(s);
}
else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
trees._tr_stored_block(s, 0, 0, false);
/* For a full flush, this empty block will be recognized
* as a special marker by inflate_sync().
*/
if (flush === Z_FULL_FLUSH) {
/*** CLEAR_HASH(s); ***/ /* forget history */
zero(s.head); // Fill with NIL (= 0);
if (s.lookahead === 0) {
s.strstart = 0;
s.block_start = 0;
s.insert = 0;
}
}
}
flush_pending(strm);
if (strm.avail_out === 0) {
s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
return Z_OK;
}
}
}
//Assert(strm->avail_out > 0, "bug2");
//if (strm.avail_out <= 0) { throw new Error("bug2");}
if (flush !== Z_FINISH) { return Z_OK; }
if (s.wrap <= 0) { return Z_STREAM_END; }
/* Write the trailer */
if (s.wrap === 2) {
put_byte(s, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
put_byte(s, (strm.adler >> 16) & 0xff);
put_byte(s, (strm.adler >> 24) & 0xff);
put_byte(s, strm.total_in & 0xff);
put_byte(s, (strm.total_in >> 8) & 0xff);
put_byte(s, (strm.total_in >> 16) & 0xff);
put_byte(s, (strm.total_in >> 24) & 0xff);
}
else
{
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
flush_pending(strm);
/* If avail_out is zero, the application will call deflate again
* to flush the rest.
*/
if (s.wrap > 0) { s.wrap = -s.wrap; }
/* write the trailer only once! */
return s.pending !== 0 ? Z_OK : Z_STREAM_END;
}
function deflateEnd(strm) {
var status;
if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
return Z_STREAM_ERROR;
}
status = strm.state.status;
if (status !== INIT_STATE &&
status !== EXTRA_STATE &&
status !== NAME_STATE &&
status !== COMMENT_STATE &&
status !== HCRC_STATE &&
status !== BUSY_STATE &&
status !== FINISH_STATE
) {
return err(strm, Z_STREAM_ERROR);
}
strm.state = null;
return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
}
/* =========================================================================
* Copy the source state to the destination state
*/
//function deflateCopy(dest, source) {
//
//}
exports.deflateInit = deflateInit;
exports.deflateInit2 = deflateInit2;
exports.deflateReset = deflateReset;
exports.deflateResetKeep = deflateResetKeep;
exports.deflateSetHeader = deflateSetHeader;
exports.deflate = deflate;
exports.deflateEnd = deflateEnd;
exports.deflateInfo = 'pako deflate (from Nodeca project)';
/* Not implemented
exports.deflateBound = deflateBound;
exports.deflateCopy = deflateCopy;
exports.deflateSetDictionary = deflateSetDictionary;
exports.deflateParams = deflateParams;
exports.deflatePending = deflatePending;
exports.deflatePrime = deflatePrime;
exports.deflateTune = deflateTune;
*/
},{"../utils/common":66,"./adler32":68,"./crc32":70,"./messages":76,"./trees":77}],72:[function(require,module,exports){
'use strict';
function GZheader() {
/* true if compressed data believed to be text */
this.text = 0;
/* modification time */
this.time = 0;
/* extra flags (not used when writing a gzip file) */
this.xflags = 0;
/* operating system */
this.os = 0;
/* pointer to extra field or Z_NULL if none */
this.extra = null;
/* extra field length (valid if extra != Z_NULL) */
this.extra_len = 0; // Actually, we don't need it in JS,
// but leave for few code modifications
//
// Setup limits is not necessary because in js we should not preallocate memory
// for inflate use constant limit in 65536 bytes
//
/* space at extra (only when reading header) */
// this.extra_max = 0;
/* pointer to zero-terminated file name or Z_NULL */
this.name = '';
/* space at name (only when reading header) */
// this.name_max = 0;
/* pointer to zero-terminated comment or Z_NULL */
this.comment = '';
/* space at comment (only when reading header) */
// this.comm_max = 0;
/* true if there was or will be a header crc */
this.hcrc = 0;
/* true when done reading gzip header (not used when writing a gzip file) */
this.done = false;
}
module.exports = GZheader;
},{}],73:[function(require,module,exports){
'use strict';
// See state defs from inflate.js
var BAD = 30; /* got a data error -- remain here until reset */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
/*
Decode literal, length, and distance codes and write out the resulting
literal and match bytes until either not enough input or output is
available, an end-of-block is encountered, or a data error is encountered.
When large enough input and output buffers are supplied to inflate(), for
example, a 16K input buffer and a 64K output buffer, more than 95% of the
inflate execution time is spent in this routine.
Entry assumptions:
state.mode === LEN
strm.avail_in >= 6
strm.avail_out >= 258
start >= strm.avail_out
state.bits < 8
On return, state.mode is one of:
LEN -- ran out of enough output space or enough available input
TYPE -- reached end of block code, inflate() to interpret next block
BAD -- error in block data
Notes:
- The maximum input bits used by a length/distance pair is 15 bits for the
length code, 5 bits for the length extra, 15 bits for the distance code,
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
Therefore if strm.avail_in >= 6, then there is enough input to avoid
checking for available input while decoding.
- The maximum bytes that a single length/distance pair can output is 258
bytes, which is the maximum length that can be coded. inflate_fast()
requires strm.avail_out >= 258 for each loop to avoid checking for
output space.
*/
module.exports = function inflate_fast(strm, start) {
var state;
var _in; /* local strm.input */
var last; /* have enough input while in < last */
var _out; /* local strm.output */
var beg; /* inflate()'s initial strm.output */
var end; /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
var dmax; /* maximum distance from zlib header */
//#endif
var wsize; /* window size or zero if not using window */
var whave; /* valid bytes in the window */
var wnext; /* window write index */
var window; /* allocated sliding window, if wsize != 0 */
var hold; /* local strm.hold */
var bits; /* local strm.bits */
var lcode; /* local strm.lencode */
var dcode; /* local strm.distcode */
var lmask; /* mask for first level of length codes */
var dmask; /* mask for first level of distance codes */
var here; /* retrieved table entry */
var op; /* code bits, operation, extra bits, or */
/* window position, window bytes to copy */
var len; /* match length, unused bytes */
var dist; /* match distance */
var from; /* where to copy match from */
var from_source;
var input, output; // JS specific, because we have no pointers
/* copy state to local variables */
state = strm.state;
//here = state.here;
_in = strm.next_in;
input = strm.input;
last = _in + (strm.avail_in - 5);
_out = strm.next_out;
output = strm.output;
beg = _out - (start - strm.avail_out);
end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
dmax = state.dmax;
//#endif
wsize = state.wsize;
whave = state.whave;
wnext = state.wnext;
window = state.window;
hold = state.hold;
bits = state.bits;
lcode = state.lencode;
dcode = state.distcode;
lmask = (1 << state.lenbits) - 1;
dmask = (1 << state.distbits) - 1;
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
top:
do {
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = lcode[hold & lmask];
dolen:
for (;;) { // Goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op === 0) { /* literal */
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
output[_out++] = here & 0xffff/*here.val*/;
}
else if (op & 16) { /* length base */
len = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (op) {
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
len += hold & ((1 << op) - 1);
hold >>>= op;
bits -= op;
}
//Tracevv((stderr, "inflate: length %u\n", len));
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = dcode[hold & dmask];
dodist:
for (;;) { // goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op & 16) { /* distance base */
dist = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
}
dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
if (dist > dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
//#endif
hold >>>= op;
bits -= op;
//Tracevv((stderr, "inflate: distance %u\n", dist));
op = _out - beg; /* max distance in output */
if (dist > op) { /* see if copy from window */
op = dist - op; /* distance back in window */
if (op > whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// if (len <= op - whave) {
// do {
// output[_out++] = 0;
// } while (--len);
// continue top;
// }
// len -= op - whave;
// do {
// output[_out++] = 0;
// } while (--op > whave);
// if (op === 0) {
// from = _out - dist;
// do {
// output[_out++] = output[from++];
// } while (--len);
// continue top;
// }
//#endif
}
from = 0; // window index
from_source = window;
if (wnext === 0) { /* very common case */
from += wsize - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
else if (wnext < op) { /* wrap around window */
from += wsize + wnext - op;
op -= wnext;
if (op < len) { /* some from end of window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = 0;
if (wnext < len) { /* some from start of window */
op = wnext;
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
}
else { /* contiguous in window */
from += wnext - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
while (len > 2) {
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
len -= 3;
}
if (len) {
output[_out++] = from_source[from++];
if (len > 1) {
output[_out++] = from_source[from++];
}
}
}
else {
from = _out - dist; /* copy direct from output */
do { /* minimum length is three */
output[_out++] = output[from++];
output[_out++] = output[from++];
output[_out++] = output[from++];
len -= 3;
} while (len > 2);
if (len) {
output[_out++] = output[from++];
if (len > 1) {
output[_out++] = output[from++];
}
}
}
}
else if ((op & 64) === 0) { /* 2nd level distance code */
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist;
}
else {
strm.msg = 'invalid distance code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
}
else if ((op & 64) === 0) { /* 2nd level length code */
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen;
}
else if (op & 32) { /* end-of-block */
//Tracevv((stderr, "inflate: end of block\n"));
state.mode = TYPE;
break top;
}
else {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
} while (_in < last && _out < end);
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
len = bits >> 3;
_in -= len;
bits -= len << 3;
hold &= (1 << bits) - 1;
/* update state and return */
strm.next_in = _in;
strm.next_out = _out;
strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
state.hold = hold;
state.bits = bits;
return;
};
},{}],74:[function(require,module,exports){
'use strict';
var utils = require('../utils/common');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var inflate_fast = require('./inffast');
var inflate_table = require('./inftrees');
var CODES = 0;
var LENS = 1;
var DISTS = 2;
/* Public constants ==========================================================*/
/* ===========================================================================*/
/* Allowed flush values; see deflate() and inflate() below for details */
//var Z_NO_FLUSH = 0;
//var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH = 2;
//var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_BLOCK = 5;
var Z_TREES = 6;
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_NEED_DICT = 2;
//var Z_ERRNO = -1;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
var Z_MEM_ERROR = -4;
var Z_BUF_ERROR = -5;
//var Z_VERSION_ERROR = -6;
/* The deflate compression method */
var Z_DEFLATED = 8;
/* STATES ====================================================================*/
/* ===========================================================================*/
var HEAD = 1; /* i: waiting for magic header */
var FLAGS = 2; /* i: waiting for method and flags (gzip) */
var TIME = 3; /* i: waiting for modification time (gzip) */
var OS = 4; /* i: waiting for extra flags and operating system (gzip) */
var EXLEN = 5; /* i: waiting for extra length (gzip) */
var EXTRA = 6; /* i: waiting for extra bytes (gzip) */
var NAME = 7; /* i: waiting for end of file name (gzip) */
var COMMENT = 8; /* i: waiting for end of comment (gzip) */
var HCRC = 9; /* i: waiting for header crc (gzip) */
var DICTID = 10; /* i: waiting for dictionary check value */
var DICT = 11; /* waiting for inflateSetDictionary() call */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */
var STORED = 14; /* i: waiting for stored size (length and complement) */
var COPY_ = 15; /* i/o: same as COPY below, but only first time in */
var COPY = 16; /* i/o: waiting for input or output to copy stored block */
var TABLE = 17; /* i: waiting for dynamic block table lengths */
var LENLENS = 18; /* i: waiting for code length code lengths */
var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */
var LEN_ = 20; /* i: same as LEN below, but only first time in */
var LEN = 21; /* i: waiting for length/lit/eob code */
var LENEXT = 22; /* i: waiting for length extra bits */
var DIST = 23; /* i: waiting for distance code */
var DISTEXT = 24; /* i: waiting for distance extra bits */
var MATCH = 25; /* o: waiting for output space to copy string */
var LIT = 26; /* o: waiting for output space to write literal */
var CHECK = 27; /* i: waiting for 32-bit check value */
var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */
var DONE = 29; /* finished check, done -- remain here until reset */
var BAD = 30; /* got a data error -- remain here until reset */
var MEM = 31; /* got an inflate() memory error -- remain here until reset */
var SYNC = 32; /* looking for synchronization bytes to restart inflate() */
/* ===========================================================================*/
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_WBITS = MAX_WBITS;
function ZSWAP32(q) {
return (((q >>> 24) & 0xff) +
((q >>> 8) & 0xff00) +
((q & 0xff00) << 8) +
((q & 0xff) << 24));
}
function InflateState() {
this.mode = 0; /* current inflate mode */
this.last = false; /* true if processing last block */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
this.havedict = false; /* true if dictionary provided */
this.flags = 0; /* gzip header method and flags (0 if zlib) */
this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
this.check = 0; /* protected copy of check value */
this.total = 0; /* protected copy of output count */
// TODO: may be {}
this.head = null; /* where to save gzip header information */
/* sliding window */
this.wbits = 0; /* log base 2 of requested window size */
this.wsize = 0; /* window size or zero if not using window */
this.whave = 0; /* valid bytes in the window */
this.wnext = 0; /* window write index */
this.window = null; /* allocated sliding window, if needed */
/* bit accumulator */
this.hold = 0; /* input bit accumulator */
this.bits = 0; /* number of bits in "in" */
/* for string and stored block copying */
this.length = 0; /* literal or length of data to copy */
this.offset = 0; /* distance back to copy string from */
/* for table and code decoding */
this.extra = 0; /* extra bits needed */
/* fixed and dynamic code tables */
this.lencode = null; /* starting table for length/literal codes */
this.distcode = null; /* starting table for distance codes */
this.lenbits = 0; /* index bits for lencode */
this.distbits = 0; /* index bits for distcode */
/* dynamic table building */
this.ncode = 0; /* number of code length code lengths */
this.nlen = 0; /* number of length code lengths */
this.ndist = 0; /* number of distance code lengths */
this.have = 0; /* number of code lengths in lens[] */
this.next = null; /* next available space in codes[] */
this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
this.work = new utils.Buf16(288); /* work area for code table building */
/*
because we don't have pointers in js, we use lencode and distcode directly
as buffers so we don't need codes
*/
//this.codes = new utils.Buf32(ENOUGH); /* space for code tables */
this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
this.distdyn = null; /* dynamic table for distance codes (JS specific) */
this.sane = 0; /* if false, allow invalid distance too far */
this.back = 0; /* bits back of last unprocessed length/lit */
this.was = 0; /* initial length of match */
}
function inflateResetKeep(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
strm.total_in = strm.total_out = state.total = 0;
strm.msg = ''; /*Z_NULL*/
if (state.wrap) { /* to support ill-conceived Java test suite */
strm.adler = state.wrap & 1;
}
state.mode = HEAD;
state.last = 0;
state.havedict = 0;
state.dmax = 32768;
state.head = null/*Z_NULL*/;
state.hold = 0;
state.bits = 0;
//state.lencode = state.distcode = state.next = state.codes;
state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
state.sane = 1;
state.back = -1;
//Tracev((stderr, "inflate: reset\n"));
return Z_OK;
}
function inflateReset(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
state.wsize = 0;
state.whave = 0;
state.wnext = 0;
return inflateResetKeep(strm);
}
function inflateReset2(strm, windowBits) {
var wrap;
var state;
/* get the state */
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
wrap = 0;
windowBits = -windowBits;
}
else {
wrap = (windowBits >> 4) + 1;
if (windowBits < 48) {
windowBits &= 15;
}
}
/* set number of window bits, free window if different */
if (windowBits && (windowBits < 8 || windowBits > 15)) {
return Z_STREAM_ERROR;
}
if (state.window !== null && state.wbits !== windowBits) {
state.window = null;
}
/* update state and reset the rest of it */
state.wrap = wrap;
state.wbits = windowBits;
return inflateReset(strm);
}
function inflateInit2(strm, windowBits) {
var ret;
var state;
if (!strm) { return Z_STREAM_ERROR; }
//strm.msg = Z_NULL; /* in case we return an error */
state = new InflateState();
//if (state === Z_NULL) return Z_MEM_ERROR;
//Tracev((stderr, "inflate: allocated\n"));
strm.state = state;
state.window = null/*Z_NULL*/;
ret = inflateReset2(strm, windowBits);
if (ret !== Z_OK) {
strm.state = null/*Z_NULL*/;
}
return ret;
}
function inflateInit(strm) {
return inflateInit2(strm, DEF_WBITS);
}
/*
Return state with length and distance decoding tables and index sizes set to
fixed code decoding. Normally this returns fixed tables from inffixed.h.
If BUILDFIXED is defined, then instead this routine builds the tables the
first time it's called, and returns those tables the first time and
thereafter. This reduces the size of the code by about 2K bytes, in
exchange for a little execution time. However, BUILDFIXED should not be
used for threaded applications, since the rewriting of the tables and virgin
may not be thread-safe.
*/
var virgin = true;
var lenfix, distfix; // We have no pointers in JS, so keep tables separate
function fixedtables(state) {
/* build fixed huffman tables if first call (may not be thread safe) */
if (virgin) {
var sym;
lenfix = new utils.Buf32(512);
distfix = new utils.Buf32(32);
/* literal/length table */
sym = 0;
while (sym < 144) { state.lens[sym++] = 8; }
while (sym < 256) { state.lens[sym++] = 9; }
while (sym < 280) { state.lens[sym++] = 7; }
while (sym < 288) { state.lens[sym++] = 8; }
inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, {bits: 9});
/* distance table */
sym = 0;
while (sym < 32) { state.lens[sym++] = 5; }
inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, {bits: 5});
/* do this just once */
virgin = false;
}
state.lencode = lenfix;
state.lenbits = 9;
state.distcode = distfix;
state.distbits = 5;
}
/*
Update the window with the last wsize (normally 32K) bytes written before
returning. If window does not exist yet, create it. This is only called
when a window is already in use, or when output has been written during this
inflate call, but the end of the deflate stream has not been reached yet.
It is also called to create a window for dictionary data when a dictionary
is loaded.
Providing output buffers larger than 32K to inflate() should provide a speed
advantage, since only the last 32K of output is copied to the sliding window
upon return from inflate(), and since all distances after the first 32K of
output will fall in the output data, making match copies simpler and faster.
The advantage may be dependent on the size of the processor's data caches.
*/
function updatewindow(strm, src, end, copy) {
var dist;
var state = strm.state;
/* if it hasn't been done already, allocate space for the window */
if (state.window === null) {
state.wsize = 1 << state.wbits;
state.wnext = 0;
state.whave = 0;
state.window = new utils.Buf8(state.wsize);
}
/* copy state->wsize or less output bytes into the circular window */
if (copy >= state.wsize) {
utils.arraySet(state.window,src, end - state.wsize, state.wsize, 0);
state.wnext = 0;
state.whave = state.wsize;
}
else {
dist = state.wsize - state.wnext;
if (dist > copy) {
dist = copy;
}
//zmemcpy(state->window + state->wnext, end - copy, dist);
utils.arraySet(state.window,src, end - copy, dist, state.wnext);
copy -= dist;
if (copy) {
//zmemcpy(state->window, end - copy, copy);
utils.arraySet(state.window,src, end - copy, copy, 0);
state.wnext = copy;
state.whave = state.wsize;
}
else {
state.wnext += dist;
if (state.wnext === state.wsize) { state.wnext = 0; }
if (state.whave < state.wsize) { state.whave += dist; }
}
}
return 0;
}
function inflate(strm, flush) {
var state;
var input, output; // input/output buffers
var next; /* next input INDEX */
var put; /* next output INDEX */
var have, left; /* available input and output */
var hold; /* bit buffer */
var bits; /* bits in bit buffer */
var _in, _out; /* save starting available input and output */
var copy; /* number of stored or match bytes to copy */
var from; /* where to copy match bytes from */
var from_source;
var here = 0; /* current decoding table entry */
var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
//var last; /* parent table entry */
var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
var len; /* length to copy for repeats, bits to drop */
var ret; /* return code */
var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */
var opts;
var n; // temporary var for NEED_BITS
var order = /* permutation of code lengths */
[16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
if (!strm || !strm.state || !strm.output ||
(!strm.input && strm.avail_in !== 0)) {
return Z_STREAM_ERROR;
}
state = strm.state;
if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
_in = have;
_out = left;
ret = Z_OK;
inf_leave: // goto emulation
for (;;) {
switch (state.mode) {
case HEAD:
if (state.wrap === 0) {
state.mode = TYPEDO;
break;
}
//=== NEEDBITS(16);
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */
state.check = 0/*crc32(0L, Z_NULL, 0)*/;
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = FLAGS;
break;
}
state.flags = 0; /* expect zlib header */
if (state.head) {
state.head.done = false;
}
if (!(state.wrap & 1) || /* check if zlib header allowed */
(((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
strm.msg = 'incorrect header check';
state.mode = BAD;
break;
}
if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
len = (hold & 0x0f)/*BITS(4)*/ + 8;
if (state.wbits === 0) {
state.wbits = len;
}
else if (len > state.wbits) {
strm.msg = 'invalid window size';
state.mode = BAD;
break;
}
state.dmax = 1 << len;
//Tracev((stderr, "inflate: zlib header ok\n"));
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = hold & 0x200 ? DICTID : TYPE;
//=== INITBITS();
hold = 0;
bits = 0;
//===//
break;
case FLAGS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.flags = hold;
if ((state.flags & 0xff) !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
if (state.flags & 0xe000) {
strm.msg = 'unknown header flags set';
state.mode = BAD;
break;
}
if (state.head) {
state.head.text = ((hold >> 8) & 1);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = TIME;
/* falls through */
case TIME:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.time = hold;
}
if (state.flags & 0x0200) {
//=== CRC4(state.check, hold)
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
hbuf[2] = (hold >>> 16) & 0xff;
hbuf[3] = (hold >>> 24) & 0xff;
state.check = crc32(state.check, hbuf, 4, 0);
//===
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = OS;
/* falls through */
case OS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.xflags = (hold & 0xff);
state.head.os = (hold >> 8);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = EXLEN;
/* falls through */
case EXLEN:
if (state.flags & 0x0400) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length = hold;
if (state.head) {
state.head.extra_len = hold;
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
else if (state.head) {
state.head.extra = null/*Z_NULL*/;
}
state.mode = EXTRA;
/* falls through */
case EXTRA:
if (state.flags & 0x0400) {
copy = state.length;
if (copy > have) { copy = have; }
if (copy) {
if (state.head) {
len = state.head.extra_len - state.length;
if (!state.head.extra) {
// Use untyped array for more conveniend processing later
state.head.extra = new Array(state.head.extra_len);
}
utils.arraySet(
state.head.extra,
input,
next,
// extra field is limited to 65536 bytes
// - no need for additional size check
copy,
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
len
);
//zmemcpy(state.head.extra + len, next,
// len + copy > state.head.extra_max ?
// state.head.extra_max - len : copy);
}
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
state.length -= copy;
}
if (state.length) { break inf_leave; }
}
state.length = 0;
state.mode = NAME;
/* falls through */
case NAME:
if (state.flags & 0x0800) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
// TODO: 2 or 1 bytes?
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.name_max*/)) {
state.head.name += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.name = null;
}
state.length = 0;
state.mode = COMMENT;
/* falls through */
case COMMENT:
if (state.flags & 0x1000) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.comm_max*/)) {
state.head.comment += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.comment = null;
}
state.mode = HCRC;
/* falls through */
case HCRC:
if (state.flags & 0x0200) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.check & 0xffff)) {
strm.msg = 'header crc mismatch';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
if (state.head) {
state.head.hcrc = ((state.flags >> 9) & 1);
state.head.done = true;
}
strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/;
state.mode = TYPE;
break;
case DICTID:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
strm.adler = state.check = ZSWAP32(hold);
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = DICT;
/* falls through */
case DICT:
if (state.havedict === 0) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
return Z_NEED_DICT;
}
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = TYPE;
/* falls through */
case TYPE:
if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
/* falls through */
case TYPEDO:
if (state.last) {
//--- BYTEBITS() ---//
hold >>>= bits & 7;
bits -= bits & 7;
//---//
state.mode = CHECK;
break;
}
//=== NEEDBITS(3); */
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.last = (hold & 0x01)/*BITS(1)*/;
//--- DROPBITS(1) ---//
hold >>>= 1;
bits -= 1;
//---//
switch ((hold & 0x03)/*BITS(2)*/) {
case 0: /* stored block */
//Tracev((stderr, "inflate: stored block%s\n",
// state.last ? " (last)" : ""));
state.mode = STORED;
break;
case 1: /* fixed block */
fixedtables(state);
//Tracev((stderr, "inflate: fixed codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = LEN_; /* decode codes */
if (flush === Z_TREES) {
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break inf_leave;
}
break;
case 2: /* dynamic block */
//Tracev((stderr, "inflate: dynamic codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = TABLE;
break;
case 3:
strm.msg = 'invalid block type';
state.mode = BAD;
}
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break;
case STORED:
//--- BYTEBITS() ---// /* go to byte boundary */
hold >>>= bits & 7;
bits -= bits & 7;
//---//
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
strm.msg = 'invalid stored block lengths';
state.mode = BAD;
break;
}
state.length = hold & 0xffff;
//Tracev((stderr, "inflate: stored length %u\n",
// state.length));
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = COPY_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case COPY_:
state.mode = COPY;
/* falls through */
case COPY:
copy = state.length;
if (copy) {
if (copy > have) { copy = have; }
if (copy > left) { copy = left; }
if (copy === 0) { break inf_leave; }
//--- zmemcpy(put, next, copy); ---
utils.arraySet(output, input, next, copy, put);
//---//
have -= copy;
next += copy;
left -= copy;
put += copy;
state.length -= copy;
break;
}
//Tracev((stderr, "inflate: stored end\n"));
state.mode = TYPE;
break;
case TABLE:
//=== NEEDBITS(14); */
while (bits < 14) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
//#ifndef PKZIP_BUG_WORKAROUND
if (state.nlen > 286 || state.ndist > 30) {
strm.msg = 'too many length or distance symbols';
state.mode = BAD;
break;
}
//#endif
//Tracev((stderr, "inflate: table sizes ok\n"));
state.have = 0;
state.mode = LENLENS;
/* falls through */
case LENLENS:
while (state.have < state.ncode) {
//=== NEEDBITS(3);
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
while (state.have < 19) {
state.lens[order[state.have++]] = 0;
}
// We have separate tables & no pointers. 2 commented lines below not needed.
//state.next = state.codes;
//state.lencode = state.next;
// Switch to use dynamic table
state.lencode = state.lendyn;
state.lenbits = 7;
opts = {bits: state.lenbits};
ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
state.lenbits = opts.bits;
if (ret) {
strm.msg = 'invalid code lengths set';
state.mode = BAD;
break;
}
//Tracev((stderr, "inflate: code lengths ok\n"));
state.have = 0;
state.mode = CODELENS;
/* falls through */
case CODELENS:
while (state.have < state.nlen + state.ndist) {
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_val < 16) {
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.lens[state.have++] = here_val;
}
else {
if (here_val === 16) {
//=== NEEDBITS(here.bits + 2);
n = here_bits + 2;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
if (state.have === 0) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
len = state.lens[state.have - 1];
copy = 3 + (hold & 0x03);//BITS(2);
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
}
else if (here_val === 17) {
//=== NEEDBITS(here.bits + 3);
n = here_bits + 3;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 3 + (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
else {
//=== NEEDBITS(here.bits + 7);
n = here_bits + 7;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 11 + (hold & 0x7f);//BITS(7);
//--- DROPBITS(7) ---//
hold >>>= 7;
bits -= 7;
//---//
}
if (state.have + copy > state.nlen + state.ndist) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
while (copy--) {
state.lens[state.have++] = len;
}
}
}
/* handle error breaks in while */
if (state.mode === BAD) { break; }
/* check for end-of-block code (better have one) */
if (state.lens[256] === 0) {
strm.msg = 'invalid code -- missing end-of-block';
state.mode = BAD;
break;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state.lenbits = 9;
opts = {bits: state.lenbits};
ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.lenbits = opts.bits;
// state.lencode = state.next;
if (ret) {
strm.msg = 'invalid literal/lengths set';
state.mode = BAD;
break;
}
state.distbits = 6;
//state.distcode.copy(state.codes);
// Switch to use dynamic table
state.distcode = state.distdyn;
opts = {bits: state.distbits};
ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.distbits = opts.bits;
// state.distcode = state.next;
if (ret) {
strm.msg = 'invalid distances set';
state.mode = BAD;
break;
}
//Tracev((stderr, 'inflate: codes ok\n'));
state.mode = LEN_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case LEN_:
state.mode = LEN;
/* falls through */
case LEN:
if (have >= 6 && left >= 258) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
inflate_fast(strm, _out);
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
if (state.mode === TYPE) {
state.back = -1;
}
break;
}
state.back = 0;
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) -1)]; /*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if (here_bits <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_op && (here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.lencode[last_val +
((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
state.length = here_val;
if (here_op === 0) {
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
state.mode = LIT;
break;
}
if (here_op & 32) {
//Tracevv((stderr, "inflate: end of block\n"));
state.back = -1;
state.mode = TYPE;
break;
}
if (here_op & 64) {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break;
}
state.extra = here_op & 15;
state.mode = LENEXT;
/* falls through */
case LENEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//Tracevv((stderr, "inflate: length %u\n", state.length));
state.was = state.length;
state.mode = DIST;
/* falls through */
case DIST:
for (;;) {
here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if ((here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.distcode[last_val +
((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
if (here_op & 64) {
strm.msg = 'invalid distance code';
state.mode = BAD;
break;
}
state.offset = here_val;
state.extra = (here_op) & 15;
state.mode = DISTEXT;
/* falls through */
case DISTEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//#ifdef INFLATE_STRICT
if (state.offset > state.dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
//#endif
//Tracevv((stderr, "inflate: distance %u\n", state.offset));
state.mode = MATCH;
/* falls through */
case MATCH:
if (left === 0) { break inf_leave; }
copy = _out - left;
if (state.offset > copy) { /* copy from window */
copy = state.offset - copy;
if (copy > state.whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// Trace((stderr, "inflate.c too far\n"));
// copy -= state.whave;
// if (copy > state.length) { copy = state.length; }
// if (copy > left) { copy = left; }
// left -= copy;
// state.length -= copy;
// do {
// output[put++] = 0;
// } while (--copy);
// if (state.length === 0) { state.mode = LEN; }
// break;
//#endif
}
if (copy > state.wnext) {
copy -= state.wnext;
from = state.wsize - copy;
}
else {
from = state.wnext - copy;
}
if (copy > state.length) { copy = state.length; }
from_source = state.window;
}
else { /* copy from output */
from_source = output;
from = put - state.offset;
copy = state.length;
}
if (copy > left) { copy = left; }
left -= copy;
state.length -= copy;
do {
output[put++] = from_source[from++];
} while (--copy);
if (state.length === 0) { state.mode = LEN; }
break;
case LIT:
if (left === 0) { break inf_leave; }
output[put++] = state.length;
left--;
state.mode = LEN;
break;
case CHECK:
if (state.wrap) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
// Use '|' insdead of '+' to make sure that result is signed
hold |= input[next++] << bits;
bits += 8;
}
//===//
_out -= left;
strm.total_out += _out;
state.total += _out;
if (_out) {
strm.adler = state.check =
/*UPDATE(state.check, put - _out, _out);*/
(state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
}
_out = left;
// NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too
if ((state.flags ? hold : ZSWAP32(hold)) !== state.check) {
strm.msg = 'incorrect data check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: check matches trailer\n"));
}
state.mode = LENGTH;
/* falls through */
case LENGTH:
if (state.wrap && state.flags) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.total & 0xffffffff)) {
strm.msg = 'incorrect length check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: length matches trailer\n"));
}
state.mode = DONE;
/* falls through */
case DONE:
ret = Z_STREAM_END;
break inf_leave;
case BAD:
ret = Z_DATA_ERROR;
break inf_leave;
case MEM:
return Z_MEM_ERROR;
case SYNC:
/* falls through */
default:
return Z_STREAM_ERROR;
}
}
// inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
/*
Return from inflate(), updating the total counts and the check value.
If there was no progress during the inflate() call, return a buffer
error. Call updatewindow() to create and/or update the window state.
Note: a memory error from inflate() is non-recoverable.
*/
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
(state.mode < CHECK || flush !== Z_FINISH))) {
if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
state.mode = MEM;
return Z_MEM_ERROR;
}
}
_in -= strm.avail_in;
_out -= strm.avail_out;
strm.total_in += _in;
strm.total_out += _out;
state.total += _out;
if (state.wrap && _out) {
strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
(state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
}
strm.data_type = state.bits + (state.last ? 64 : 0) +
(state.mode === TYPE ? 128 : 0) +
(state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
ret = Z_BUF_ERROR;
}
return ret;
}
function inflateEnd(strm) {
if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
return Z_STREAM_ERROR;
}
var state = strm.state;
if (state.window) {
state.window = null;
}
strm.state = null;
return Z_OK;
}
function inflateGetHeader(strm, head) {
var state;
/* check state */
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }
/* save header structure */
state.head = head;
head.done = false;
return Z_OK;
}
exports.inflateReset = inflateReset;
exports.inflateReset2 = inflateReset2;
exports.inflateResetKeep = inflateResetKeep;
exports.inflateInit = inflateInit;
exports.inflateInit2 = inflateInit2;
exports.inflate = inflate;
exports.inflateEnd = inflateEnd;
exports.inflateGetHeader = inflateGetHeader;
exports.inflateInfo = 'pako inflate (from Nodeca project)';
/* Not implemented
exports.inflateCopy = inflateCopy;
exports.inflateGetDictionary = inflateGetDictionary;
exports.inflateMark = inflateMark;
exports.inflatePrime = inflatePrime;
exports.inflateSetDictionary = inflateSetDictionary;
exports.inflateSync = inflateSync;
exports.inflateSyncPoint = inflateSyncPoint;
exports.inflateUndermine = inflateUndermine;
*/
},{"../utils/common":66,"./adler32":68,"./crc32":70,"./inffast":73,"./inftrees":75}],75:[function(require,module,exports){
'use strict';
var utils = require('../utils/common');
var MAXBITS = 15;
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var CODES = 0;
var LENS = 1;
var DISTS = 2;
var lbase = [ /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
];
var lext = [ /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
];
var dbase = [ /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577, 0, 0
];
var dext = [ /* Distance codes 0..29 extra */
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
28, 28, 29, 29, 64, 64
];
module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
{
var bits = opts.bits;
//here = opts.here; /* table entry for duplication */
var len = 0; /* a code's length in bits */
var sym = 0; /* index of code symbols */
var min = 0, max = 0; /* minimum and maximum code lengths */
var root = 0; /* number of index bits for root table */
var curr = 0; /* number of index bits for current table */
var drop = 0; /* code bits to drop for sub-table */
var left = 0; /* number of prefix codes available */
var used = 0; /* code entries in table used */
var huff = 0; /* Huffman code */
var incr; /* for incrementing code, index */
var fill; /* index for replicating entries */
var low; /* low bits for current root entry */
var mask; /* mask for low root bits */
var next; /* next available space in table */
var base = null; /* base value table to use */
var base_index = 0;
// var shoextra; /* extra bits table to use */
var end; /* use base and extra for symbol > end */
var count = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* number of codes of each length */
var offs = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* offsets in table for each length */
var extra = null;
var extra_index = 0;
var here_bits, here_op, here_val;
/*
Process a set of code lengths to create a canonical Huffman code. The
code lengths are lens[0..codes-1]. Each length corresponds to the
symbols 0..codes-1. The Huffman code is generated by first sorting the
symbols by length from short to long, and retaining the symbol order
for codes with equal lengths. Then the code starts with all zero bits
for the first code of the shortest length, and the codes are integer
increments for the same length, and zeros are appended as the length
increases. For the deflate format, these bits are stored backwards
from their more natural integer increment ordering, and so when the
decoding tables are built in the large loop below, the integer codes
are incremented backwards.
This routine assumes, but does not check, that all of the entries in
lens[] are in the range 0..MAXBITS. The caller must assure this.
1..MAXBITS is interpreted as that code length. zero means that that
symbol does not occur in this code.
The codes are sorted by computing a count of codes for each length,
creating from that a table of starting indices for each length in the
sorted table, and then entering the symbols in order in the sorted
table. The sorted table is work[], with that space being provided by
the caller.
The length counts are used for other purposes as well, i.e. finding
the minimum and maximum length codes, determining if there are any
codes at all, checking for a valid set of lengths, and looking ahead
at length counts to determine sub-table sizes when building the
decoding tables.
*/
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
for (len = 0; len <= MAXBITS; len++) {
count[len] = 0;
}
for (sym = 0; sym < codes; sym++) {
count[lens[lens_index + sym]]++;
}
/* bound code lengths, force root to be within code lengths */
root = bits;
for (max = MAXBITS; max >= 1; max--) {
if (count[max] !== 0) { break; }
}
if (root > max) {
root = max;
}
if (max === 0) { /* no symbols to code at all */
//table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
//table.bits[opts.table_index] = 1; //here.bits = (var char)1;
//table.val[opts.table_index++] = 0; //here.val = (var short)0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
//table.op[opts.table_index] = 64;
//table.bits[opts.table_index] = 1;
//table.val[opts.table_index++] = 0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
opts.bits = 1;
return 0; /* no symbols, but wait for decoding to report error */
}
for (min = 1; min < max; min++) {
if (count[min] !== 0) { break; }
}
if (root < min) {
root = min;
}
/* check for an over-subscribed or incomplete set of lengths */
left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= count[len];
if (left < 0) {
return -1;
} /* over-subscribed */
}
if (left > 0 && (type === CODES || max !== 1)) {
return -1; /* incomplete set */
}
/* generate offsets into symbol table for each length for sorting */
offs[1] = 0;
for (len = 1; len < MAXBITS; len++) {
offs[len + 1] = offs[len] + count[len];
}
/* sort symbols by length, by symbol order within each length */
for (sym = 0; sym < codes; sym++) {
if (lens[lens_index + sym] !== 0) {
work[offs[lens[lens_index + sym]]++] = sym;
}
}
/*
Create and fill in decoding tables. In this loop, the table being
filled is at next and has curr index bits. The code being used is huff
with length len. That code is converted to an index by dropping drop
bits off of the bottom. For codes where len is less than drop + curr,
those top drop + curr - len bits are incremented through all values to
fill the table with replicated entries.
root is the number of index bits for the root table. When len exceeds
root, sub-tables are created pointed to by the root entry with an index
of the low root bits of huff. This is saved in low to check for when a
new sub-table should be started. drop is zero when the root table is
being filled, and drop is root when sub-tables are being filled.
When a new sub-table is needed, it is necessary to look ahead in the
code lengths to determine what size sub-table is needed. The length
counts are used for this, and so count[] is decremented as codes are
entered in the tables.
used keeps track of how many table entries have been allocated from the
provided *table space. It is checked for LENS and DIST tables against
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
the initial root table size constants. See the comments in inftrees.h
for more information.
sym increments through all symbols, and the loop terminates when
all codes of length max, i.e. all codes, have been processed. This
routine permits incomplete codes, so another loop after this one fills
in the rest of the decoding tables with invalid code markers.
*/
/* set up for code type */
// poor man optimization - use if-else instead of switch,
// to avoid deopts in old v8
if (type === CODES) {
base = extra = work; /* dummy value--not used */
end = 19;
} else if (type === LENS) {
base = lbase;
base_index -= 257;
extra = lext;
extra_index -= 257;
end = 256;
} else { /* DISTS */
base = dbase;
extra = dext;
end = -1;
}
/* initialize opts for loop */
huff = 0; /* starting code */
sym = 0; /* starting code symbol */
len = min; /* starting code length */
next = table_index; /* current table to fill in */
curr = root; /* current table index bits */
drop = 0; /* current bits to drop from code for index */
low = -1; /* trigger new sub-table when len > root */
used = 1 << root; /* use root table entries */
mask = used - 1; /* mask for comparing low */
/* check available table space */
if ((type === LENS && used > ENOUGH_LENS) ||
(type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
var i=0;
/* process all codes and make table entries */
for (;;) {
i++;
/* create table entry */
here_bits = len - drop;
if (work[sym] < end) {
here_op = 0;
here_val = work[sym];
}
else if (work[sym] > end) {
here_op = extra[extra_index + work[sym]];
here_val = base[base_index + work[sym]];
}
else {
here_op = 32 + 64; /* end of block */
here_val = 0;
}
/* replicate for those indices with low len bits equal to huff */
incr = 1 << (len - drop);
fill = 1 << curr;
min = fill; /* save offset to next table */
do {
fill -= incr;
table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
} while (fill !== 0);
/* backwards increment the len-bit code huff */
incr = 1 << (len - 1);
while (huff & incr) {
incr >>= 1;
}
if (incr !== 0) {
huff &= incr - 1;
huff += incr;
} else {
huff = 0;
}
/* go to next symbol, update count, len */
sym++;
if (--count[len] === 0) {
if (len === max) { break; }
len = lens[lens_index + work[sym]];
}
/* create new sub-table if needed */
if (len > root && (huff & mask) !== low) {
/* if first time, transition to sub-tables */
if (drop === 0) {
drop = root;
}
/* increment past last table */
next += min; /* here min is 1 << curr */
/* determine length of next table */
curr = len - drop;
left = 1 << curr;
while (curr + drop < max) {
left -= count[curr + drop];
if (left <= 0) { break; }
curr++;
left <<= 1;
}
/* check for enough space */
used += 1 << curr;
if ((type === LENS && used > ENOUGH_LENS) ||
(type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
/* point entry in root table to sub-table */
low = huff & mask;
/*table.op[low] = curr;
table.bits[low] = root;
table.val[low] = next - opts.table_index;*/
table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
}
}
/* fill in remaining table entry if code is incomplete (guaranteed to have
at most one remaining entry, since if the code is incomplete, the
maximum code length that was allowed to get this far is one bit) */
if (huff !== 0) {
//table.op[next + huff] = 64; /* invalid code marker */
//table.bits[next + huff] = len - drop;
//table.val[next + huff] = 0;
table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
}
/* set return parameters */
//opts.table_index += used;
opts.bits = root;
return 0;
};
},{"../utils/common":66}],76:[function(require,module,exports){
'use strict';
module.exports = {
'2': 'need dictionary', /* Z_NEED_DICT 2 */
'1': 'stream end', /* Z_STREAM_END 1 */
'0': '', /* Z_OK 0 */
'-1': 'file error', /* Z_ERRNO (-1) */
'-2': 'stream error', /* Z_STREAM_ERROR (-2) */
'-3': 'data error', /* Z_DATA_ERROR (-3) */
'-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */
'-5': 'buffer error', /* Z_BUF_ERROR (-5) */
'-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
};
},{}],77:[function(require,module,exports){
'use strict';
var utils = require('../utils/common');
/* Public constants ==========================================================*/
/* ===========================================================================*/
//var Z_FILTERED = 1;
//var Z_HUFFMAN_ONLY = 2;
//var Z_RLE = 3;
var Z_FIXED = 4;
//var Z_DEFAULT_STRATEGY = 0;
/* Possible values of the data_type field (though see inflate()) */
var Z_BINARY = 0;
var Z_TEXT = 1;
//var Z_ASCII = 1; // = Z_TEXT
var Z_UNKNOWN = 2;
/*============================================================================*/
function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
// From zutil.h
var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES = 2;
/* The three kinds of block type */
var MIN_MATCH = 3;
var MAX_MATCH = 258;
/* The minimum and maximum match lengths */
// From deflate.h
/* ===========================================================================
* Internal compression state.
*/
var LENGTH_CODES = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS = 256;
/* number of literal bytes 0..255 */
var L_CODES = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES = 30;
/* number of distance codes */
var BL_CODES = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE = 2*L_CODES + 1;
/* maximum heap size */
var MAX_BITS = 15;
/* All codes must not exceed MAX_BITS bits */
var Buf_size = 16;
/* size of bit buffer in bi_buf */
/* ===========================================================================
* Constants
*/
var MAX_BL_BITS = 7;
/* Bit length codes must not exceed MAX_BL_BITS bits */
var END_BLOCK = 256;
/* end of block literal code */
var REP_3_6 = 16;
/* repeat previous bit length 3-6 times (2 bits of repeat count) */
var REPZ_3_10 = 17;
/* repeat a zero length 3-10 times (3 bits of repeat count) */
var REPZ_11_138 = 18;
/* repeat a zero length 11-138 times (7 bits of repeat count) */
var extra_lbits = /* extra bits for each length code */
[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
var extra_dbits = /* extra bits for each distance code */
[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
var extra_blbits = /* extra bits for each bit length code */
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];
var bl_order =
[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
/* The lengths of the bit length codes are sent in order of decreasing
* probability, to avoid transmitting the lengths for unused bit length codes.
*/
/* ===========================================================================
* Local data. These are initialized only once.
*/
// We pre-fill arrays with 0 to avoid uninitialized gaps
var DIST_CODE_LEN = 512; /* see definition of array dist_code below */
// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1
var static_ltree = new Array((L_CODES+2) * 2);
zero(static_ltree);
/* The static literal tree. Since the bit lengths are imposed, there is no
* need for the L_CODES extra codes used during heap construction. However
* The codes 286 and 287 are needed to build a canonical tree (see _tr_init
* below).
*/
var static_dtree = new Array(D_CODES * 2);
zero(static_dtree);
/* The static distance tree. (Actually a trivial tree since all codes use
* 5 bits.)
*/
var _dist_code = new Array(DIST_CODE_LEN);
zero(_dist_code);
/* Distance codes. The first 256 values correspond to the distances
* 3 .. 258, the last 256 values correspond to the top 8 bits of
* the 15 bit distances.
*/
var _length_code = new Array(MAX_MATCH-MIN_MATCH+1);
zero(_length_code);
/* length code for each normalized match length (0 == MIN_MATCH) */
var base_length = new Array(LENGTH_CODES);
zero(base_length);
/* First normalized length for each code (0 = MIN_MATCH) */
var base_dist = new Array(D_CODES);
zero(base_dist);
/* First normalized distance for each code (0 = distance of 1) */
var StaticTreeDesc = function (static_tree, extra_bits, extra_base, elems, max_length) {
this.static_tree = static_tree; /* static tree or NULL */
this.extra_bits = extra_bits; /* extra bits for each code or NULL */
this.extra_base = extra_base; /* base index for extra_bits */
this.elems = elems; /* max number of elements in the tree */
this.max_length = max_length; /* max bit length for the codes */
// show if `static_tree` has data or dummy - needed for monomorphic objects
this.has_stree = static_tree && static_tree.length;
};
var static_l_desc;
var static_d_desc;
var static_bl_desc;
var TreeDesc = function(dyn_tree, stat_desc) {
this.dyn_tree = dyn_tree; /* the dynamic tree */
this.max_code = 0; /* largest code with non zero frequency */
this.stat_desc = stat_desc; /* the corresponding static tree */
};
function d_code(dist) {
return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
}
/* ===========================================================================
* Output a short LSB first on the stream.
* IN assertion: there is enough room in pendingBuf.
*/
function put_short (s, w) {
// put_byte(s, (uch)((w) & 0xff));
// put_byte(s, (uch)((ush)(w) >> 8));
s.pending_buf[s.pending++] = (w) & 0xff;
s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
}
/* ===========================================================================
* Send a value on a given number of bits.
* IN assertion: length <= 16 and value fits in length bits.
*/
function send_bits(s, value, length) {
if (s.bi_valid > (Buf_size - length)) {
s.bi_buf |= (value << s.bi_valid) & 0xffff;
put_short(s, s.bi_buf);
s.bi_buf = value >> (Buf_size - s.bi_valid);
s.bi_valid += length - Buf_size;
} else {
s.bi_buf |= (value << s.bi_valid) & 0xffff;
s.bi_valid += length;
}
}
function send_code(s, c, tree) {
send_bits(s, tree[c*2]/*.Code*/, tree[c*2 + 1]/*.Len*/);
}
/* ===========================================================================
* Reverse the first len bits of a code, using straightforward code (a faster
* method would use a table)
* IN assertion: 1 <= len <= 15
*/
function bi_reverse(code, len) {
var res = 0;
do {
res |= code & 1;
code >>>= 1;
res <<= 1;
} while (--len > 0);
return res >>> 1;
}
/* ===========================================================================
* Flush the bit buffer, keeping at most 7 bits in it.
*/
function bi_flush(s) {
if (s.bi_valid === 16) {
put_short(s, s.bi_buf);
s.bi_buf = 0;
s.bi_valid = 0;
} else if (s.bi_valid >= 8) {
s.pending_buf[s.pending++] = s.bi_buf & 0xff;
s.bi_buf >>= 8;
s.bi_valid -= 8;
}
}
/* ===========================================================================
* Compute the optimal bit lengths for a tree and update the total bit length
* for the current block.
* IN assertion: the fields freq and dad are set, heap[heap_max] and
* above are the tree nodes sorted by increasing frequency.
* OUT assertions: the field len is set to the optimal bit length, the
* array bl_count contains the frequencies for each bit length.
* The length opt_len is updated; static_len is also updated if stree is
* not null.
*/
function gen_bitlen(s, desc)
// deflate_state *s;
// tree_desc *desc; /* the tree descriptor */
{
var tree = desc.dyn_tree;
var max_code = desc.max_code;
var stree = desc.stat_desc.static_tree;
var has_stree = desc.stat_desc.has_stree;
var extra = desc.stat_desc.extra_bits;
var base = desc.stat_desc.extra_base;
var max_length = desc.stat_desc.max_length;
var h; /* heap index */
var n, m; /* iterate over the tree elements */
var bits; /* bit length */
var xbits; /* extra bits */
var f; /* frequency */
var overflow = 0; /* number of elements with bit length too large */
for (bits = 0; bits <= MAX_BITS; bits++) {
s.bl_count[bits] = 0;
}
/* In a first pass, compute the optimal bit lengths (which may
* overflow in the case of the bit length tree).
*/
tree[s.heap[s.heap_max]*2 + 1]/*.Len*/ = 0; /* root of the heap */
for (h = s.heap_max+1; h < HEAP_SIZE; h++) {
n = s.heap[h];
bits = tree[tree[n*2 +1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
if (bits > max_length) {
bits = max_length;
overflow++;
}
tree[n*2 + 1]/*.Len*/ = bits;
/* We overwrite tree[n].Dad which is no longer needed */
if (n > max_code) { continue; } /* not a leaf node */
s.bl_count[bits]++;
xbits = 0;
if (n >= base) {
xbits = extra[n-base];
}
f = tree[n * 2]/*.Freq*/;
s.opt_len += f * (bits + xbits);
if (has_stree) {
s.static_len += f * (stree[n*2 + 1]/*.Len*/ + xbits);
}
}
if (overflow === 0) { return; }
// Trace((stderr,"\nbit length overflow\n"));
/* This happens for example on obj2 and pic of the Calgary corpus */
/* Find the first bit length which could increase: */
do {
bits = max_length-1;
while (s.bl_count[bits] === 0) { bits--; }
s.bl_count[bits]--; /* move one leaf down the tree */
s.bl_count[bits+1] += 2; /* move one overflow item as its brother */
s.bl_count[max_length]--;
/* The brother of the overflow item also moves one step up,
* but this does not affect bl_count[max_length]
*/
overflow -= 2;
} while (overflow > 0);
/* Now recompute all bit lengths, scanning in increasing frequency.
* h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
* lengths instead of fixing only the wrong ones. This idea is taken
* from 'ar' written by Haruhiko Okumura.)
*/
for (bits = max_length; bits !== 0; bits--) {
n = s.bl_count[bits];
while (n !== 0) {
m = s.heap[--h];
if (m > max_code) { continue; }
if (tree[m*2 + 1]/*.Len*/ !== bits) {
// Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
s.opt_len += (bits - tree[m*2 + 1]/*.Len*/)*tree[m*2]/*.Freq*/;
tree[m*2 + 1]/*.Len*/ = bits;
}
n--;
}
}
}
/* ===========================================================================
* Generate the codes for a given tree and bit counts (which need not be
* optimal).
* IN assertion: the array bl_count contains the bit length statistics for
* the given tree and the field len is set for all tree elements.
* OUT assertion: the field code is set for all tree elements of non
* zero code length.
*/
function gen_codes(tree, max_code, bl_count)
// ct_data *tree; /* the tree to decorate */
// int max_code; /* largest code with non zero frequency */
// ushf *bl_count; /* number of codes at each bit length */
{
var next_code = new Array(MAX_BITS+1); /* next code value for each bit length */
var code = 0; /* running code value */
var bits; /* bit index */
var n; /* code index */
/* The distribution counts are first used to generate the code values
* without bit reversal.
*/
for (bits = 1; bits <= MAX_BITS; bits++) {
next_code[bits] = code = (code + bl_count[bits-1]) << 1;
}
/* Check that the bit counts in bl_count are consistent. The last code
* must be all ones.
*/
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
// "inconsistent bit counts");
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
for (n = 0; n <= max_code; n++) {
var len = tree[n*2 + 1]/*.Len*/;
if (len === 0) { continue; }
/* Now reverse the bits */
tree[n*2]/*.Code*/ = bi_reverse(next_code[len]++, len);
//Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
// n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
}
}
/* ===========================================================================
* Initialize the various 'constant' tables.
*/
function tr_static_init() {
var n; /* iterates over tree elements */
var bits; /* bit counter */
var length; /* length value */
var code; /* code value */
var dist; /* distance index */
var bl_count = new Array(MAX_BITS+1);
/* number of codes at each bit length for an optimal tree */
// do check in _tr_init()
//if (static_init_done) return;
/* For some embedded targets, global variables are not initialized: */
/*#ifdef NO_INIT_GLOBAL_POINTERS
static_l_desc.static_tree = static_ltree;
static_l_desc.extra_bits = extra_lbits;
static_d_desc.static_tree = static_dtree;
static_d_desc.extra_bits = extra_dbits;
static_bl_desc.extra_bits = extra_blbits;
#endif*/
/* Initialize the mapping length (0..255) -> length code (0..28) */
length = 0;
for (code = 0; code < LENGTH_CODES-1; code++) {
base_length[code] = length;
for (n = 0; n < (1<<extra_lbits[code]); n++) {
_length_code[length++] = code;
}
}
//Assert (length == 256, "tr_static_init: length != 256");
/* Note that the length 255 (match length 258) can be represented
* in two different ways: code 284 + 5 bits or code 285, so we
* overwrite length_code[255] to use the best encoding:
*/
_length_code[length-1] = code;
/* Initialize the mapping dist (0..32K) -> dist code (0..29) */
dist = 0;
for (code = 0 ; code < 16; code++) {
base_dist[code] = dist;
for (n = 0; n < (1<<extra_dbits[code]); n++) {
_dist_code[dist++] = code;
}
}
//Assert (dist == 256, "tr_static_init: dist != 256");
dist >>= 7; /* from now on, all distances are divided by 128 */
for ( ; code < D_CODES; code++) {
base_dist[code] = dist << 7;
for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
_dist_code[256 + dist++] = code;
}
}
//Assert (dist == 256, "tr_static_init: 256+dist != 512");
/* Construct the codes of the static literal tree */
for (bits = 0; bits <= MAX_BITS; bits++) {
bl_count[bits] = 0;
}
n = 0;
while (n <= 143) {
static_ltree[n*2 + 1]/*.Len*/ = 8;
n++;
bl_count[8]++;
}
while (n <= 255) {
static_ltree[n*2 + 1]/*.Len*/ = 9;
n++;
bl_count[9]++;
}
while (n <= 279) {
static_ltree[n*2 + 1]/*.Len*/ = 7;
n++;
bl_count[7]++;
}
while (n <= 287) {
static_ltree[n*2 + 1]/*.Len*/ = 8;
n++;
bl_count[8]++;
}
/* Codes 286 and 287 do not exist, but we must include them in the
* tree construction to get a canonical Huffman tree (longest code
* all ones)
*/
gen_codes(static_ltree, L_CODES+1, bl_count);
/* The static distance tree is trivial: */
for (n = 0; n < D_CODES; n++) {
static_dtree[n*2 + 1]/*.Len*/ = 5;
static_dtree[n*2]/*.Code*/ = bi_reverse(n, 5);
}
// Now data ready and we can init static trees
static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS);
static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS);
static_bl_desc =new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS);
//static_init_done = true;
}
/* ===========================================================================
* Initialize a new block.
*/
function init_block(s) {
var n; /* iterates over tree elements */
/* Initialize the trees. */
for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n*2]/*.Freq*/ = 0; }
for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n*2]/*.Freq*/ = 0; }
for (n = 0; n < BL_CODES; n++) { s.bl_tree[n*2]/*.Freq*/ = 0; }
s.dyn_ltree[END_BLOCK*2]/*.Freq*/ = 1;
s.opt_len = s.static_len = 0;
s.last_lit = s.matches = 0;
}
/* ===========================================================================
* Flush the bit buffer and align the output on a byte boundary
*/
function bi_windup(s)
{
if (s.bi_valid > 8) {
put_short(s, s.bi_buf);
} else if (s.bi_valid > 0) {
//put_byte(s, (Byte)s->bi_buf);
s.pending_buf[s.pending++] = s.bi_buf;
}
s.bi_buf = 0;
s.bi_valid = 0;
}
/* ===========================================================================
* Copy a stored block, storing first the length and its
* one's complement if requested.
*/
function copy_block(s, buf, len, header)
//DeflateState *s;
//charf *buf; /* the input data */
//unsigned len; /* its length */
//int header; /* true if block header must be written */
{
bi_windup(s); /* align on byte boundary */
if (header) {
put_short(s, len);
put_short(s, ~len);
}
// while (len--) {
// put_byte(s, *buf++);
// }
utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
s.pending += len;
}
/* ===========================================================================
* Compares to subtrees, using the tree depth as tie breaker when
* the subtrees have equal frequency. This minimizes the worst case length.
*/
function smaller(tree, n, m, depth) {
var _n2 = n*2;
var _m2 = m*2;
return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
(tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
}
/* ===========================================================================
* Restore the heap property by moving down the tree starting at node k,
* exchanging a node with the smallest of its two sons if necessary, stopping
* when the heap property is re-established (each father smaller than its
* two sons).
*/
function pqdownheap(s, tree, k)
// deflate_state *s;
// ct_data *tree; /* the tree to restore */
// int k; /* node to move down */
{
var v = s.heap[k];
var j = k << 1; /* left son of k */
while (j <= s.heap_len) {
/* Set j to the smallest of the two sons: */
if (j < s.heap_len &&
smaller(tree, s.heap[j+1], s.heap[j], s.depth)) {
j++;
}
/* Exit if v is smaller than both sons */
if (smaller(tree, v, s.heap[j], s.depth)) { break; }
/* Exchange v with the smallest son */
s.heap[k] = s.heap[j];
k = j;
/* And continue down the tree, setting j to the left son of k */
j <<= 1;
}
s.heap[k] = v;
}
// inlined manually
// var SMALLEST = 1;
/* ===========================================================================
* Send the block data compressed using the given Huffman trees
*/
function compress_block(s, ltree, dtree)
// deflate_state *s;
// const ct_data *ltree; /* literal tree */
// const ct_data *dtree; /* distance tree */
{
var dist; /* distance of matched string */
var lc; /* match length or unmatched char (if dist == 0) */
var lx = 0; /* running index in l_buf */
var code; /* the code to send */
var extra; /* number of extra bits to send */
if (s.last_lit !== 0) {
do {
dist = (s.pending_buf[s.d_buf + lx*2] << 8) | (s.pending_buf[s.d_buf + lx*2 + 1]);
lc = s.pending_buf[s.l_buf + lx];
lx++;
if (dist === 0) {
send_code(s, lc, ltree); /* send a literal byte */
//Tracecv(isgraph(lc), (stderr," '%c' ", lc));
} else {
/* Here, lc is the match length - MIN_MATCH */
code = _length_code[lc];
send_code(s, code+LITERALS+1, ltree); /* send the length code */
extra = extra_lbits[code];
if (extra !== 0) {
lc -= base_length[code];
send_bits(s, lc, extra); /* send the extra length bits */
}
dist--; /* dist is now the match distance - 1 */
code = d_code(dist);
//Assert (code < D_CODES, "bad d_code");
send_code(s, code, dtree); /* send the distance code */
extra = extra_dbits[code];
if (extra !== 0) {
dist -= base_dist[code];
send_bits(s, dist, extra); /* send the extra distance bits */
}
} /* literal or match pair ? */
/* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
//Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
// "pendingBuf overflow");
} while (lx < s.last_lit);
}
send_code(s, END_BLOCK, ltree);
}
/* ===========================================================================
* Construct one Huffman tree and assigns the code bit strings and lengths.
* Update the total bit length for the current block.
* IN assertion: the field freq is set for all tree elements.
* OUT assertions: the fields len and code are set to the optimal bit length
* and corresponding code. The length opt_len is updated; static_len is
* also updated if stree is not null. The field max_code is set.
*/
function build_tree(s, desc)
// deflate_state *s;
// tree_desc *desc; /* the tree descriptor */
{
var tree = desc.dyn_tree;
var stree = desc.stat_desc.static_tree;
var has_stree = desc.stat_desc.has_stree;
var elems = desc.stat_desc.elems;
var n, m; /* iterate over heap elements */
var max_code = -1; /* largest code with non zero frequency */
var node; /* new node being created */
/* Construct the initial heap, with least frequent element in
* heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
* heap[0] is not used.
*/
s.heap_len = 0;
s.heap_max = HEAP_SIZE;
for (n = 0; n < elems; n++) {
if (tree[n * 2]/*.Freq*/ !== 0) {
s.heap[++s.heap_len] = max_code = n;
s.depth[n] = 0;
} else {
tree[n*2 + 1]/*.Len*/ = 0;
}
}
/* The pkzip format requires that at least one distance code exists,
* and that at least one bit should be sent even if there is only one
* possible code. So to avoid special checks later on we force at least
* two codes of non zero frequency.
*/
while (s.heap_len < 2) {
node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
tree[node * 2]/*.Freq*/ = 1;
s.depth[node] = 0;
s.opt_len--;
if (has_stree) {
s.static_len -= stree[node*2 + 1]/*.Len*/;
}
/* node is 0 or 1 so it does not have extra bits */
}
desc.max_code = max_code;
/* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
* establish sub-heaps of increasing lengths:
*/
for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }
/* Construct the Huffman tree by repeatedly combining the least two
* frequent nodes.
*/
node = elems; /* next internal node of the tree */
do {
//pqremove(s, tree, n); /* n = node of least frequency */
/*** pqremove ***/
n = s.heap[1/*SMALLEST*/];
s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
pqdownheap(s, tree, 1/*SMALLEST*/);
/***/
m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */
s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
s.heap[--s.heap_max] = m;
/* Create a new node father of n and m */
tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
tree[n*2 + 1]/*.Dad*/ = tree[m*2 + 1]/*.Dad*/ = node;
/* and insert the new node in the heap */
s.heap[1/*SMALLEST*/] = node++;
pqdownheap(s, tree, 1/*SMALLEST*/);
} while (s.heap_len >= 2);
s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];
/* At this point, the fields freq and dad are set. We can now
* generate the bit lengths.
*/
gen_bitlen(s, desc);
/* The field len is now set, we can generate the bit codes */
gen_codes(tree, max_code, s.bl_count);
}
/* ===========================================================================
* Scan a literal or distance tree to determine the frequencies of the codes
* in the bit length tree.
*/
function scan_tree(s, tree, max_code)
// deflate_state *s;
// ct_data *tree; /* the tree to be scanned */
// int max_code; /* and its largest code of non zero frequency */
{
var n; /* iterates over all tree elements */
var prevlen = -1; /* last emitted length */
var curlen; /* length of current code */
var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */
var count = 0; /* repeat count of the current code */
var max_count = 7; /* max repeat count */
var min_count = 4; /* min repeat count */
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
tree[(max_code+1)*2 + 1]/*.Len*/ = 0xffff; /* guard */
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n+1)*2 + 1]/*.Len*/;
if (++count < max_count && curlen === nextlen) {
continue;
} else if (count < min_count) {
s.bl_tree[curlen * 2]/*.Freq*/ += count;
} else if (curlen !== 0) {
if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
s.bl_tree[REP_3_6*2]/*.Freq*/++;
} else if (count <= 10) {
s.bl_tree[REPZ_3_10*2]/*.Freq*/++;
} else {
s.bl_tree[REPZ_11_138*2]/*.Freq*/++;
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen === nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
/* ===========================================================================
* Send a literal or distance tree in compressed form, using the codes in
* bl_tree.
*/
function send_tree(s, tree, max_code)
// deflate_state *s;
// ct_data *tree; /* the tree to be scanned */
// int max_code; /* and its largest code of non zero frequency */
{
var n; /* iterates over all tree elements */
var prevlen = -1; /* last emitted length */
var curlen; /* length of current code */
var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */
var count = 0; /* repeat count of the current code */
var max_count = 7; /* max repeat count */
var min_count = 4; /* min repeat count */
/* tree[max_code+1].Len = -1; */ /* guard already set */
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n+1)*2 + 1]/*.Len*/;
if (++count < max_count && curlen === nextlen) {
continue;
} else if (count < min_count) {
do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);
} else if (curlen !== 0) {
if (curlen !== prevlen) {
send_code(s, curlen, s.bl_tree);
count--;
}
//Assert(count >= 3 && count <= 6, " 3_6?");
send_code(s, REP_3_6, s.bl_tree);
send_bits(s, count-3, 2);
} else if (count <= 10) {
send_code(s, REPZ_3_10, s.bl_tree);
send_bits(s, count-3, 3);
} else {
send_code(s, REPZ_11_138, s.bl_tree);
send_bits(s, count-11, 7);
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen === nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
/* ===========================================================================
* Construct the Huffman tree for the bit lengths and return the index in
* bl_order of the last bit length code to send.
*/
function build_bl_tree(s) {
var max_blindex; /* index of last bit length code of non zero freq */
/* Determine the bit length frequencies for literal and distance trees */
scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
/* Build the bit length tree: */
build_tree(s, s.bl_desc);
/* opt_len now includes the length of the tree representations, except
* the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
*/
/* Determine the number of bit length codes to send. The pkzip format
* requires that at least 4 bit length codes be sent. (appnote.txt says
* 3 but the actual value used is 4.)
*/
for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
if (s.bl_tree[bl_order[max_blindex]*2 + 1]/*.Len*/ !== 0) {
break;
}
}
/* Update opt_len to include the bit length tree and counts */
s.opt_len += 3*(max_blindex+1) + 5+5+4;
//Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
// s->opt_len, s->static_len));
return max_blindex;
}
/* ===========================================================================
* Send the header for a block using dynamic Huffman trees: the counts, the
* lengths of the bit length codes, the literal tree and the distance tree.
* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
*/
function send_all_trees(s, lcodes, dcodes, blcodes)
// deflate_state *s;
// int lcodes, dcodes, blcodes; /* number of codes for each tree */
{
var rank; /* index in bl_order */
//Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
//Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
// "too many codes");
//Tracev((stderr, "\nbl counts: "));
send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
send_bits(s, dcodes-1, 5);
send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
for (rank = 0; rank < blcodes; rank++) {
//Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
send_bits(s, s.bl_tree[bl_order[rank]*2 + 1]/*.Len*/, 3);
}
//Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
send_tree(s, s.dyn_ltree, lcodes-1); /* literal tree */
//Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
send_tree(s, s.dyn_dtree, dcodes-1); /* distance tree */
//Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}
/* ===========================================================================
* Check if the data type is TEXT or BINARY, using the following algorithm:
* - TEXT if the two conditions below are satisfied:
* a) There are no non-portable control characters belonging to the
* "black list" (0..6, 14..25, 28..31).
* b) There is at least one printable character belonging to the
* "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
* - BINARY otherwise.
* - The following partially-portable control characters form a
* "gray list" that is ignored in this detection algorithm:
* (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
* IN assertion: the fields Freq of dyn_ltree are set.
*/
function detect_data_type(s) {
/* black_mask is the bit mask of black-listed bytes
* set bits 0..6, 14..25, and 28..31
* 0xf3ffc07f = binary 11110011111111111100000001111111
*/
var black_mask = 0xf3ffc07f;
var n;
/* Check for non-textual ("black-listed") bytes. */
for (n = 0; n <= 31; n++, black_mask >>>= 1) {
if ((black_mask & 1) && (s.dyn_ltree[n*2]/*.Freq*/ !== 0)) {
return Z_BINARY;
}
}
/* Check for textual ("white-listed") bytes. */
if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
return Z_TEXT;
}
for (n = 32; n < LITERALS; n++) {
if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
return Z_TEXT;
}
}
/* There are no "black-listed" or "white-listed" bytes:
* this stream either is empty or has tolerated ("gray-listed") bytes only.
*/
return Z_BINARY;
}
var static_init_done = false;
/* ===========================================================================
* Initialize the tree data structures for a new zlib stream.
*/
function _tr_init(s)
{
if (!static_init_done) {
tr_static_init();
static_init_done = true;
}
s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
s.bi_buf = 0;
s.bi_valid = 0;
/* Initialize the first block of the first file: */
init_block(s);
}
/* ===========================================================================
* Send a stored block
*/
function _tr_stored_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf; /* input block */
//ulg stored_len; /* length of input block */
//int last; /* one if this is the last block for a file */
{
send_bits(s, (STORED_BLOCK<<1)+(last ? 1 : 0), 3); /* send block type */
copy_block(s, buf, stored_len, true); /* with header */
}
/* ===========================================================================
* Send one empty static block to give enough lookahead for inflate.
* This takes 10 bits, of which 7 may remain in the bit buffer.
*/
function _tr_align(s) {
send_bits(s, STATIC_TREES<<1, 3);
send_code(s, END_BLOCK, static_ltree);
bi_flush(s);
}
/* ===========================================================================
* Determine the best encoding for the current block: dynamic trees, static
* trees or store, and output the encoded block to the zip file.
*/
function _tr_flush_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf; /* input block, or NULL if too old */
//ulg stored_len; /* length of input block */
//int last; /* one if this is the last block for a file */
{
var opt_lenb, static_lenb; /* opt_len and static_len in bytes */
var max_blindex = 0; /* index of last bit length code of non zero freq */
/* Build the Huffman trees unless a stored block is forced */
if (s.level > 0) {
/* Check if the file is binary or text */
if (s.strm.data_type === Z_UNKNOWN) {
s.strm.data_type = detect_data_type(s);
}
/* Construct the literal and distance trees */
build_tree(s, s.l_desc);
// Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
// s->static_len));
build_tree(s, s.d_desc);
// Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
// s->static_len));
/* At this point, opt_len and static_len are the total bit lengths of
* the compressed block data, excluding the tree representations.
*/
/* Build the bit length tree for the above two trees, and get the index
* in bl_order of the last bit length code to send.
*/
max_blindex = build_bl_tree(s);
/* Determine the best encoding. Compute the block lengths in bytes. */
opt_lenb = (s.opt_len+3+7) >>> 3;
static_lenb = (s.static_len+3+7) >>> 3;
// Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
// opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
// s->last_lit));
if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }
} else {
// Assert(buf != (char*)0, "lost buf");
opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
}
if ((stored_len+4 <= opt_lenb) && (buf !== -1)) {
/* 4: two words for the lengths */
/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
* Otherwise we can't have processed more than WSIZE input bytes since
* the last block flush, because compression would have been
* successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
* transform a block into a stored block.
*/
_tr_stored_block(s, buf, stored_len, last);
} else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {
send_bits(s, (STATIC_TREES<<1) + (last ? 1 : 0), 3);
compress_block(s, static_ltree, static_dtree);
} else {
send_bits(s, (DYN_TREES<<1) + (last ? 1 : 0), 3);
send_all_trees(s, s.l_desc.max_code+1, s.d_desc.max_code+1, max_blindex+1);
compress_block(s, s.dyn_ltree, s.dyn_dtree);
}
// Assert (s->compressed_len == s->bits_sent, "bad compressed size");
/* The above check is made mod 2^32, for files larger than 512 MB
* and uLong implemented on 32 bits.
*/
init_block(s);
if (last) {
bi_windup(s);
}
// Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
// s->compressed_len-7*last));
}
/* ===========================================================================
* Save the match info and tally the frequency counts. Return true if
* the current block must be flushed.
*/
function _tr_tally(s, dist, lc)
// deflate_state *s;
// unsigned dist; /* distance of matched string */
// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
//var out_length, in_length, dcode;
s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff;
s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;
s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
s.last_lit++;
if (dist === 0) {
/* lc is the unmatched char */
s.dyn_ltree[lc*2]/*.Freq*/++;
} else {
s.matches++;
/* Here, lc is the match length - MIN_MATCH */
dist--; /* dist = match distance - 1 */
//Assert((ush)dist < (ush)MAX_DIST(s) &&
// (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
// (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
s.dyn_ltree[(_length_code[lc]+LITERALS+1) * 2]/*.Freq*/++;
s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef TRUNCATE_BLOCK
// /* Try to guess if it is profitable to stop the current block here */
// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
// /* Compute an upper bound for the compressed length */
// out_length = s.last_lit*8;
// in_length = s.strstart - s.block_start;
//
// for (dcode = 0; dcode < D_CODES; dcode++) {
// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
// }
// out_length >>>= 3;
// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
// // s->last_lit, in_length, out_length,
// // 100L - out_length*100L/in_length));
// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
// return true;
// }
// }
//#endif
return (s.last_lit === s.lit_bufsize-1);
/* We avoid equality with lit_bufsize because of wraparound at 64K
* on 16 bit machines and because stored blocks are restricted to
* 64K-1 bytes.
*/
}
exports._tr_init = _tr_init;
exports._tr_stored_block = _tr_stored_block;
exports._tr_flush_block = _tr_flush_block;
exports._tr_tally = _tr_tally;
exports._tr_align = _tr_align;
},{"../utils/common":66}],78:[function(require,module,exports){
'use strict';
function ZStream() {
/* next input byte */
this.input = null; // JS specific, because we have no pointers
this.next_in = 0;
/* number of bytes available at input */
this.avail_in = 0;
/* total number of input bytes read so far */
this.total_in = 0;
/* next output byte should be put there */
this.output = null; // JS specific, because we have no pointers
this.next_out = 0;
/* remaining free space at output */
this.avail_out = 0;
/* total number of bytes output so far */
this.total_out = 0;
/* last error message, NULL if no error */
this.msg = ''/*Z_NULL*/;
/* not visible by applications */
this.state = null;
/* best guess about the data type: binary or text */
this.data_type = 2/*Z_UNKNOWN*/;
/* adler32 value of the uncompressed data */
this.adler = 0;
}
module.exports = ZStream;
},{}],79:[function(require,module,exports){
module.exports = 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 (!arrayEquals.apply(this[i], [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;
}
},{}],80:[function(require,module,exports){
exports.Interop = require('./interop');
},{"./interop":81}],81:[function(require,module,exports){
var transform = require('./transform');
var arrayEquals = require('./array-equals');
function Interop() { }
module.exports = Interop;
/**
* This map holds the most recent Plan A offer/answer SDP that was converted
* to Plan B, with the SDP type ('offer' or 'answer') as keys and the SDP
* string as values.
*
* @type {{}}
*/
var cache = {};
/**
* This method transforms a Plan A SDP to an equivalent Plan B SDP. A
* PeerConnection wrapper transforms the SDP to Plan B before passing it to the
* application.
*
* @param desc
* @returns {*}
*/
Interop.prototype.toPlanB = function(desc) {
//#region Preliminary input validation.
if (typeof desc !== 'object' || desc === null ||
typeof desc.sdp !== 'string') {
console.warn('An empty description was passed as an argument.');
return desc;
}
// Objectify the SDP for easier manipulation.
var session = transform.parse(desc.sdp);
// If the SDP contains no media, there's nothing to transform.
if (typeof session.media === 'undefined' ||
!Array.isArray(session.media) || session.media.length === 0) {
console.warn('The description has no media.');
return desc;
}
// Try some heuristics to "make sure" this is a Plan A SDP. Plan B SDP has
// a video, an audio and a data "channel" at most.
if (session.media.length <= 3 && session.media.every(function(m) {
return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
})) {
console.warn('This description does not look like Plan A.');
return desc;
}
//#endregion
// Plan A SDP is our "precious". Cache it for later use in the Plan B ->
// Plan A transformation.
cache[desc.type] = desc.sdp;
//#region Convert from Plan A to Plan B.
// We rebuild the session.media array.
var media = session.media;
session.media = [];
// Associative array that maps channel types to channel objects for fast
// access to channel objects by their type, e.g. channels['audio']->channel
// obj.
var channels = {};
// Used to build the group:BUNDLE value after the channels construction
// loop.
var types = [];
// Implode the Plan A m-lines/tracks into Plan B "channels".
media.forEach(function(mLine) {
// rtcp-mux is required in the Plan B SDP.
if (typeof mLine.rtcpMux !== 'string' ||
mLine.rtcpMux !== 'rtcp-mux') {
throw new Error('Cannot convert to Plan B because m-lines ' +
'without the rtcp-mux attribute were found.');
}
// If we don't have a channel for this mLine.type, then use this mLine
// as the channel basis.
if (typeof channels[mLine.type] === 'undefined') {
channels[mLine.type] = mLine;
}
// Add sources to the channel and handle a=msid.
if (typeof mLine.sources === 'object') {
Object.keys(mLine.sources).forEach(function(ssrc) {
// Assign the sources to the channel.
channels[mLine.type].sources[ssrc] = mLine.sources[ssrc];
// In Plan B the msid is an SSRC attribute. Also, we don't care
// about the obsolete label and mslabel attributes.
channels[mLine.type].sources[ssrc].msid = mLine.msid;
// NOTE ssrcs in ssrc groups will share msids, as
// draft-uberti-rtcweb-plan-00 mandates.
});
}
// Add ssrc groups to the channel.
if (typeof mLine.ssrcGroups !== 'undefined' &&
Array.isArray(mLine.ssrcGroups)) {
// Create the ssrcGroups array, if it's not defined.
if (typeof channel.ssrcGroups === 'undefined' ||
!Array.isArray(channel.ssrcGroups)) {
channel.ssrcGroups = [];
}
channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups);
}
if (channels[mLine.type] === mLine) {
// Copy ICE related stuff from the principal media line.
mLine.candidates = media[0].candidates;
mLine.iceUfrag = media[0].iceUfrag;
mLine.icePwd = media[0].icePwd;
mLine.fingerprint = media[0].fingerprint;
// Plan B mids are in ['audio', 'video', 'data']
mLine.mid = mLine.type;
// Plan B doesn't support/need the bundle-only attribute.
delete mLine.bundleOnly;
// In Plan B the msid is an SSRC attribute.
delete mLine.msid;
// Used to build the group:BUNDLE value after this loop.
types.push(mLine.type);
// Add the channel to the new media array.
session.media.push(mLine);
}
});
// We regenerate the BUNDLE group with the new mids.
session.groups.every(function(group) {
if (group.type === 'BUNDLE') {
group.mids = types.join(' ');
return false;
} else {
return true;
}
});
// msid semantic
session.msidSemantic = {
semantic: 'WMS',
token: '*'
};
var resStr = transform.write(session);
return new RTCSessionDescription({
type: desc.type,
sdp: resStr
});
//#endregion
};
/**
* This method transforms a Plan B SDP to an equivalent Plan A SDP. A
* PeerConnection wrapper transforms the SDP to Plan A before passing it to FF.
*
* @param desc
* @returns {*}
*/
Interop.prototype.toPlanA = function(desc) {
//#region Preliminary input validation.
if (typeof desc !== 'object' || desc === null ||
typeof desc.sdp !== 'string') {
console.warn('An empty description was passed as an argument.');
return desc;
}
var session = transform.parse(desc.sdp);
// If the SDP contains no media, there's nothing to transform.
if (typeof session.media === 'undefined' ||
!Array.isArray(session.media) || session.media.length === 0) {
console.warn('The description has no media.');
return desc;
}
// Try some heuristics to "make sure" this is a Plan B SDP. Plan B SDP has
// a video, an audio and a data "channel" at most.
if (session.media.length > 3 || !session.media.every(function(m) {
return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
})) {
console.warn('This description does not look like Plan B.');
return desc;
}
// Make sure this Plan B SDP can be converted to a Plan A SDP.
var mids = [];
session.media.forEach(function(m) {
mids.push(m.mid);
});
var hasBundle = false;
if (typeof session.groups !== 'undefined' &&
Array.isArray(session.groups)) {
hasBundle = session.groups.every(function(g) {
return g.type !== 'BUNDLE' ||
arrayEquals.apply(g.mids.sort(), [mids.sort()]);
});
}
if (!hasBundle) {
throw new Error("Cannot convert to Plan A because m-lines that are " +
"not bundled were found.");
}
//#endregion
//#region Convert from Plan B to Plan A.
// Unfortunately, a Plan B offer/answer doesn't have enough information to
// rebuild an equivalent Plan A offer/answer.
//
// For example, if this is a local answer (in Plan A style) that we convert
// to Plan B prior to handing it over to the application (the
// PeerConnection wrapper called us, for instance, after a successful
// createAnswer), we want to remember the m-line at which we've seen the
// (local) SSRC. That's because when the application wants to do call the
// SLD method, forcing us to do the inverse transformation (from Plan B to
// Plan A), we need to know to which m-line to assign the (local) SSRC. We
// also need to know all the other m-lines that the original answer had and
// include them in the transformed answer as well.
//
// Another example is if this is a remote offer that we convert to Plan B
// prior to giving it to the application, we want to remember the mid at
// which we've seen the (remote) SSRC.
//
// In the iteration that follows, we use the cached Plan A (if it exists)
// to assign mids to ssrcs.
var cached;
if (typeof cache[desc.type] !== 'undefined') {
cached = transform.parse(cache[desc.type]);
}
// A helper map that sends mids to m-line objects. We use it later to
// rebuild the Plan A style session.media array.
var media = {};
session.media.forEach(function(channel) {
if (typeof channel.rtcpMux !== 'string' ||
channel.rtcpMux !== 'rtcp-mux') {
throw new Error("Cannot convert to Plan A because m-lines " +
"without the rtcp-mux attribute were found.");
}
// With rtcp-mux and bundle all the channels should have the same ICE
// stuff.
var sources = channel.sources;
var ssrcGroups = channel.ssrcGroups;
var candidates = channel.candidates;
var iceUfrag = channel.iceUfrag;
var icePwd = channel.icePwd;
var fingerprint = channel.fingerprint;
var port = channel.port;
// We'll use the "channel" object as a prototype for each new "mLine"
// that we create, but first we need to clean it up a bit.
delete channel.sources;
delete channel.ssrcGroups;
delete channel.candidates;
delete channel.iceUfrag;
delete channel.icePwd;
delete channel.fingerprint;
delete channel.port;
delete channel.mid;
// inverted ssrc group map
var invertedGroups = {};
if (typeof ssrcGroups !== 'undefined' && Array.isArray(ssrcGroups)) {
ssrcGroups.forEach(function (ssrcGroup) {
// TODO(gp) find out how to receive simulcast with FF. For the
// time being, hide it.
if (ssrcGroup.semantics === 'SIM') {
return;
}
if (typeof ssrcGroup.ssrcs !== 'undefined' &&
Array.isArray(ssrcGroup.ssrcs)) {
ssrcGroup.ssrcs.forEach(function (ssrc) {
if (typeof invertedGroups[ssrc] === 'undefined') {
invertedGroups[ssrc] = [];
}
invertedGroups[ssrc].push(ssrcGroup);
});
}
});
}
// ssrc to m-line index.
var mLines = {};
if (typeof sources === 'object') {
// Explode the Plan B channel sources with one m-line per source.
Object.keys(sources).forEach(function(ssrc) {
var mLine;
if (typeof invertedGroups[ssrc] !== 'undefined' &&
Array.isArray(invertedGroups[ssrc])) {
invertedGroups[ssrc].every(function (ssrcGroup) {
// ssrcGroup.ssrcs *is* an Array, no need to check
// again here.
return ssrcGroup.ssrcs.every(function (related) {
if (typeof mLines[related] === 'object') {
mLine = mLines[related];
return false;
} else {
return true;
}
});
});
}
if (typeof mLine === 'object') {
// the m-line already exists. Just add the source.
mLine.sources[ssrc] = sources[ssrc];
delete sources[ssrc].msid;
} else {
// Use the "channel" as a prototype for the "mLine".
mLine = Object.create(channel);
mLines[ssrc] = mLine;
// Assign the msid of the source to the m-line.
mLine.msid = sources[ssrc].msid;
delete sources[ssrc].msid;
// We assign one SSRC per media line.
mLine.sources = {};
mLine.sources[ssrc] = sources[ssrc];
mLine.ssrcGroups = invertedGroups[ssrc];
// Use the cached Plan A SDP (if it exists) to assign SSRCs to
// mids.
if (typeof cached !== 'undefined' &&
typeof cached.media !== 'undefined' &&
Array.isArray(cached.media)) {
cached.media.forEach(function(m) {
if (typeof m.sources === 'object') {
Object.keys(m.sources).forEach(function(s) {
if (s === ssrc) {
mLine.mid = m.mid;
}
});
}
});
}
if (typeof mLine.mid === 'undefined') {
// If this is an SSRC that we see for the first time assign
// it a new mid. This is typically the case when this
// method is called to transform a remote description for
// the first time or when there is a new SSRC in the remote
// description because a new peer has joined the
// conference. Local SSRCs should have already been added
// to the map in the toPlanB method.
//
// Because FF generates answers in Plan A style, we MUST
// already have a cached answer with all the local SSRCs
// mapped to some mLine/mid.
if (desc.type === 'answer') {
throw new Error("An unmapped SSRC was found.");
}
mLine.mid = [channel.type, '-', ssrc].join('');
}
// Include the candidates in the 1st media line.
mLine.candidates = candidates;
mLine.iceUfrag = iceUfrag;
mLine.icePwd = icePwd;
mLine.fingerprint = fingerprint;
mLine.port = port;
media[mLine.mid] = mLine;
}
});
}
});
// Rebuild the media array in the right order and add the missing mLines
// (missing from the Plan B SDP).
session.media = [];
mids = []; // reuse
if (desc.type === 'answer') {
// The media lines in the answer must match the media lines in the
// offer. The order is important too. Here we use the cached offer to
// find the m-lines that are missing (from the converted answer), and
// use the cached answer to complete the converted answer.
if (typeof cache['offer'] === 'undefined') {
throw new Error("An answer is being processed but we couldn't " +
"find a cached offer.");
}
var cachedOffer = transform.parse(cache['offer']);
if (typeof cachedOffer === 'undefined' ||
typeof cachedOffer.media === 'undefined' ||
!Array.isArray(cachedOffer.media)) {
// FIXME(gp) is this really a problem in the general case?
throw new Error("The cached offer has no media.");
}
cachedOffer.media.forEach(function(mo) {
var mLine;
if (typeof media[mo.mid] === 'undefined') {
// This is probably an m-line containing a remote track only.
// It MUST exist in the cached answer as a remote track only
// mLine.
cached.media.every(function(ma) {
if (mo.mid == ma.mid) {
mLine = ma;
return false;
} else {
return true;
}
});
} else {
mLine = media[mo.mid];
}
if (typeof mLine === 'undefined') {
throw new Error("The cached offer contains an m-line that " +
"doesn't exist neither in the cached answer nor in " +
"the converted answer.");
}
session.media.push(mLine);
mids.push(mLine.mid);
});
} else {
// SDP offer/answer (and the JSEP spec) forbids removing an m-section
// under any circumstances. If we are no longer interested in sending a
// track, we just remove the msid and ssrc attributes and set it to
// either a=recvonly (as the reofferer, we must use recvonly if the
// other side was previously sending on the m-section, but we can also
// leave the possibility open if it wasn't previously in use), or
// a=inacive.
if (typeof cached !== 'undefined' &&
typeof cached.media !== 'undefined' &&
Array.isArray(cached.media)) {
cached.media.forEach(function(pm) {
mids.push(pm.mid);
if (typeof media[pm.mid] !== 'undefined') {
session.media.push(media[pm.mid]);
} else {
delete pm.msid;
delete pm.sources;
delete pm.ssrcGroups;
pm.direction = 'recvonly';
session.media.push(pm);
}
});
}
// Add all the remaining (new) m-lines of the transformed SDP.
Object.keys(media).forEach(function(mid) {
if (mids.indexOf(mid) === -1) {
mids.push(mid);
session.media.push(media[mid]);
}
});
}
// We regenerate the BUNDLE group (since we regenerated the mids)
session.groups.every(function(group) {
if (group.type === 'BUNDLE') {
group.mids = mids.join(' ');
return false;
} else {
return true;
}
});
// msid semantic
session.msidSemantic = {
semantic: 'WMS',
token: '*'
};
var resStr = transform.write(session);
// Cache the transformed SDP (Plan A) for later re-use in this function.
cache[desc.type] = resStr;
return new RTCSessionDescription({
type: desc.type,
sdp: resStr
});
//#endregion
};
},{"./array-equals":79,"./transform":82}],82:[function(require,module,exports){
var transform = require('sdp-transform');
exports.write = function(session, opts) {
if (typeof session !== 'undefined' &&
typeof session.media !== 'undefined' &&
Array.isArray(session.media)) {
session.media.forEach(function (mLine) {
// expand sources to ssrcs
if (typeof mLine.sources !== 'undefined' &&
Object.keys(mLine.sources).length !== 0) {
mLine.ssrcs = [];
Object.keys(mLine.sources).forEach(function (ssrc) {
var source = mLine.sources[ssrc];
Object.keys(source).forEach(function (attribute) {
mLine.ssrcs.push({
id: ssrc,
attribute: attribute,
value: source[attribute]
});
});
});
delete mLine.sources;
}
// join ssrcs in ssrc groups
if (typeof mLine.ssrcGroups !== 'undefined' &&
Array.isArray(mLine.ssrcGroups)) {
mLine.ssrcGroups.forEach(function (ssrcGroup) {
if (typeof ssrcGroup.ssrcs !== 'undefined' &&
Array.isArray(ssrcGroup.ssrcs)) {
ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
}
});
}
});
}
// join group mids
if (typeof session !== 'undefined' &&
typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
session.groups.forEach(function (g) {
if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
g.mids = g.mids.join(' ');
}
});
}
return transform.write(session, opts);
};
exports.parse = function(sdp) {
var session = transform.parse(sdp);
if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
Array.isArray(session.media)) {
session.media.forEach(function (mLine) {
// group sources attributes by ssrc
if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
mLine.sources = {};
mLine.ssrcs.forEach(function (ssrc) {
if (!mLine.sources[ssrc.id])
mLine.sources[ssrc.id] = {};
mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
});
delete mLine.ssrcs;
}
// split ssrcs in ssrc groups
if (typeof mLine.ssrcGroups !== 'undefined' &&
Array.isArray(mLine.ssrcGroups)) {
mLine.ssrcGroups.forEach(function (ssrcGroup) {
if (typeof ssrcGroup.ssrcs === 'string') {
ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
}
});
}
});
}
// split group mids
if (typeof session !== 'undefined' &&
typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
session.groups.forEach(function (g) {
if (typeof g.mids === 'string') {
g.mids = g.mids.split(' ');
}
});
}
return session;
};
},{"sdp-transform":84}],83:[function(require,module,exports){
var grammar = module.exports = {
v: [{
name: 'version',
reg: /^(\d*)$/
}],
o: [{ //o=- 20518 0 IN IP4 203.0.113.1
// NB: sessionId will be a String in most cases because it is huge
name: 'origin',
reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
format: "%s %s %d %s IP%d %s"
}],
// default parsing of these only (though some of these feel outdated)
s: [{ name: 'name' }],
i: [{ name: 'description' }],
u: [{ name: 'uri' }],
e: [{ name: 'email' }],
p: [{ name: 'phone' }],
z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..
r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly
//k: [{}], // outdated thing ignored
t: [{ //t=0 0
name: 'timing',
reg: /^(\d*) (\d*)/,
names: ['start', 'stop'],
format: "%d %d"
}],
c: [{ //c=IN IP4 10.47.197.26
name: 'connection',
reg: /^IN IP(\d) (\S*)/,
names: ['version', 'ip'],
format: "IN IP%d %s"
}],
b: [{ //b=AS:4000
push: 'bandwidth',
reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
names: ['type', 'limit'],
format: "%s:%s"
}],
m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31
// NB: special - pushes to session
// TODO: rtp/fmtp should be filtered by the payloads found here?
reg: /^(\w*) (\d*) ([\w\/]*)(?: (.*))?/,
names: ['type', 'port', 'protocol', 'payloads'],
format: "%s %d %s %s"
}],
a: [
{ //a=rtpmap:110 opus/48000/2
push: 'rtp',
reg: /^rtpmap:(\d*) ([\w\-]*)\/(\d*)(?:\s*\/(\S*))?/,
names: ['payload', 'codec', 'rate', 'encoding'],
format: function (o) {
return (o.encoding) ?
"rtpmap:%d %s/%s/%s":
"rtpmap:%d %s/%s";
}
},
{ //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
push: 'fmtp',
reg: /^fmtp:(\d*) (\S*)/,
names: ['payload', 'config'],
format: "fmtp:%d %s"
},
{ //a=control:streamid=0
name: 'control',
reg: /^control:(.*)/,
format: "control:%s"
},
{ //a=rtcp:65179 IN IP4 193.84.77.194
name: 'rtcp',
reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
names: ['port', 'netType', 'ipVer', 'address'],
format: function (o) {
return (o.address != null) ?
"rtcp:%d %s IP%d %s":
"rtcp:%d";
}
},
{ //a=rtcp-fb:98 trr-int 100
push: 'rtcpFbTrrInt',
reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
names: ['payload', 'value'],
format: "rtcp-fb:%d trr-int %d"
},
{ //a=rtcp-fb:98 nack rpsi
push: 'rtcpFb',
reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
names: ['payload', 'type', 'subtype'],
format: function (o) {
return (o.subtype != null) ?
"rtcp-fb:%s %s %s":
"rtcp-fb:%s %s";
}
},
{ //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
//a=extmap:1/recvonly URI-gps-string
push: 'ext',
reg: /^extmap:([\w_\/]*) (\S*)(?: (\S*))?/,
names: ['value', 'uri', 'config'], // value may include "/direction" suffix
format: function (o) {
return (o.config != null) ?
"extmap:%s %s %s":
"extmap:%s %s";
}
},
{
//a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
push: 'crypto',
reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
names: ['id', 'suite', 'config', 'sessionConfig'],
format: function (o) {
return (o.sessionConfig != null) ?
"crypto:%d %s %s %s":
"crypto:%d %s %s";
}
},
{ //a=setup:actpass
name: 'setup',
reg: /^setup:(\w*)/,
format: "setup:%s"
},
{ //a=mid:1
name: 'mid',
reg: /^mid:([^\s]*)/,
format: "mid:%s"
},
{ //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
name: 'msid',
reg: /^msid:(.*)/,
format: "msid:%s"
},
{ //a=ptime:20
name: 'ptime',
reg: /^ptime:(\d*)/,
format: "ptime:%d"
},
{ //a=maxptime:60
name: 'maxptime',
reg: /^maxptime:(\d*)/,
format: "maxptime:%d"
},
{ //a=sendrecv
name: 'direction',
reg: /^(sendrecv|recvonly|sendonly|inactive)/
},
{ //a=ice-lite
name: 'icelite',
reg: /^(ice-lite)/
},
{ //a=ice-ufrag:F7gI
name: 'iceUfrag',
reg: /^ice-ufrag:(\S*)/,
format: "ice-ufrag:%s"
},
{ //a=ice-pwd:x9cml/YzichV2+XlhiMu8g
name: 'icePwd',
reg: /^ice-pwd:(\S*)/,
format: "ice-pwd:%s"
},
{ //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
name: 'fingerprint',
reg: /^fingerprint:(\S*) (\S*)/,
names: ['type', 'hash'],
format: "fingerprint:%s %s"
},
{
//a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
//a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0
//a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0
push:'candidates',
reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: generation (\d*))?/,
names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'generation'],
format: function (o) {
var str = "candidate:%s %d %s %d %s %d typ %s";
// NB: candidate has two optional chunks, so %void middle one if it's missing
str += (o.raddr != null) ? " raddr %s rport %d" : "%v%v";
if (o.generation != null) {
str += " generation %d";
}
return str;
}
},
{ //a=end-of-candidates (keep after the candidates line for readability)
name: 'endOfCandidates',
reg: /^(end-of-candidates)/
},
{ //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
name: 'remoteCandidates',
reg: /^remote-candidates:(.*)/,
format: "remote-candidates:%s"
},
{ //a=ice-options:google-ice
name: 'iceOptions',
reg: /^ice-options:(\S*)/,
format: "ice-options:%s"
},
{ //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
push: "ssrcs",
reg: /^ssrc:(\d*) ([\w_]*):(.*)/,
names: ['id', 'attribute', 'value'],
format: "ssrc:%d %s:%s"
},
{ //a=ssrc-group:FEC 1 2
push: "ssrcGroups",
reg: /^ssrc-group:(\w*) (.*)/,
names: ['semantics', 'ssrcs'],
format: "ssrc-group:%s %s"
},
{ //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
name: "msidSemantic",
reg: /^msid-semantic:\s?(\w*) (\S*)/,
names: ['semantic', 'token'],
format: "msid-semantic: %s %s" // space after ":" is not accidental
},
{ //a=group:BUNDLE audio video
push: 'groups',
reg: /^group:(\w*) (.*)/,
names: ['type', 'mids'],
format: "group:%s %s"
},
{ //a=rtcp-mux
name: 'rtcpMux',
reg: /^(rtcp-mux)/
},
{ // any a= that we don't understand is kepts verbatim on media.invalid
push: 'invalid',
names: ["value"]
}
]
};
// set sensible defaults to avoid polluting the grammar with boring details
Object.keys(grammar).forEach(function (key) {
var objs = grammar[key];
objs.forEach(function (obj) {
if (!obj.reg) {
obj.reg = /(.*)/;
}
if (!obj.format) {
obj.format = "%s";
}
});
});
},{}],84:[function(require,module,exports){
var parser = require('./parser');
var writer = require('./writer');
exports.write = writer;
exports.parse = parser.parse;
exports.parseFmtpConfig = parser.parseFmtpConfig;
exports.parsePayloads = parser.parsePayloads;
exports.parseRemoteCandidates = parser.parseRemoteCandidates;
},{"./parser":85,"./writer":86}],85:[function(require,module,exports){
var toIntIfInt = function (v) {
return String(Number(v)) === v ? Number(v) : v;
};
var attachProperties = function (match, location, names, rawName) {
if (rawName && !names) {
location[rawName] = toIntIfInt(match[1]);
}
else {
for (var i = 0; i < names.length; i += 1) {
if (match[i+1] != null) {
location[names[i]] = toIntIfInt(match[i+1]);
}
}
}
};
var parseReg = function (obj, location, content) {
var needsBlank = obj.name && obj.names;
if (obj.push && !location[obj.push]) {
location[obj.push] = [];
}
else if (needsBlank && !location[obj.name]) {
location[obj.name] = {};
}
var keyLocation = obj.push ?
{} : // blank object that will be pushed
needsBlank ? location[obj.name] : location; // otherwise, named location or root
attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);
if (obj.push) {
location[obj.push].push(keyLocation);
}
};
var grammar = require('./grammar');
var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);
exports.parse = function (sdp) {
var session = {}
, media = []
, location = session; // points at where properties go under (one of the above)
// parse lines we understand
sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
var type = l[0];
var content = l.slice(2);
if (type === 'm') {
media.push({rtp: [], fmtp: []});
location = media[media.length-1]; // point at latest media line
}
for (var j = 0; j < (grammar[type] || []).length; j += 1) {
var obj = grammar[type][j];
if (obj.reg.test(content)) {
return parseReg(obj, location, content);
}
}
});
session.media = media; // link it up
return session;
};
var fmtpReducer = function (acc, expr) {
var s = expr.split('=');
if (s.length === 2) {
acc[s[0]] = toIntIfInt(s[1]);
}
return acc;
};
exports.parseFmtpConfig = function (str) {
return str.split(';').reduce(fmtpReducer, {});
};
exports.parsePayloads = function (str) {
return str.split(' ').map(Number);
};
exports.parseRemoteCandidates = function (str) {
var candidates = [];
var parts = str.split(' ').map(toIntIfInt);
for (var i = 0; i < parts.length; i += 3) {
candidates.push({
component: parts[i],
ip: parts[i + 1],
port: parts[i + 2]
});
}
return candidates;
};
},{"./grammar":83}],86:[function(require,module,exports){
var grammar = require('./grammar');
// customized util.format - discards excess arguments and can void middle ones
var formatRegExp = /%[sdv%]/g;
var format = function (formatStr) {
var i = 1;
var args = arguments;
var len = args.length;
return formatStr.replace(formatRegExp, function (x) {
if (i >= len) {
return x; // missing argument
}
var arg = args[i];
i += 1;
switch (x) {
case '%%':
return '%';
case '%s':
return String(arg);
case '%d':
return Number(arg);
case '%v':
return '';
}
});
// NB: we discard excess arguments - they are typically undefined from makeLine
};
var makeLine = function (type, obj, location) {
var str = obj.format instanceof Function ?
(obj.format(obj.push ? location : location[obj.name])) :
obj.format;
var args = [type + '=' + str];
if (obj.names) {
for (var i = 0; i < obj.names.length; i += 1) {
var n = obj.names[i];
if (obj.name) {
args.push(location[obj.name][n]);
}
else { // for mLine and push attributes
args.push(location[obj.names[i]]);
}
}
}
else {
args.push(location[obj.name]);
}
return format.apply(null, args);
};
// RFC specified order
// TODO: extend this with all the rest
var defaultOuterOrder = [
'v', 'o', 's', 'i',
'u', 'e', 'p', 'c',
'b', 't', 'r', 'z', 'a'
];
var defaultInnerOrder = ['i', 'c', 'b', 'a'];
module.exports = function (session, opts) {
opts = opts || {};
// ensure certain properties exist
if (session.version == null) {
session.version = 0; // "v=0" must be there (only defined version atm)
}
if (session.name == null) {
session.name = " "; // "s= " must be there if no meaningful name set
}
session.media.forEach(function (mLine) {
if (mLine.payloads == null) {
mLine.payloads = "";
}
});
var outerOrder = opts.outerOrder || defaultOuterOrder;
var innerOrder = opts.innerOrder || defaultInnerOrder;
var sdp = [];
// loop through outerOrder for matching properties on session
outerOrder.forEach(function (type) {
grammar[type].forEach(function (obj) {
if (obj.name in session && session[obj.name] != null) {
sdp.push(makeLine(type, obj, session));
}
else if (obj.push in session && session[obj.push] != null) {
session[obj.push].forEach(function (el) {
sdp.push(makeLine(type, obj, el));
});
}
});
});
// then for each media line, follow the innerOrder
session.media.forEach(function (mLine) {
sdp.push(makeLine('m', grammar.m[0], mLine));
innerOrder.forEach(function (type) {
grammar[type].forEach(function (obj) {
if (obj.name in mLine && mLine[obj.name] != null) {
sdp.push(makeLine(type, obj, mLine));
}
else if (obj.push in mLine && mLine[obj.push] != null) {
mLine[obj.push].forEach(function (el) {
sdp.push(makeLine(type, obj, el));
});
}
});
});
});
return sdp.join('\r\n') + '\r\n';
};
},{"./grammar":83}],87:[function(require,module,exports){
var MediaStreamType = {
VIDEO_TYPE: "Video",
AUDIO_TYPE: "Audio"
};
module.exports = MediaStreamType;
},{}],88:[function(require,module,exports){
var RTCBrowserType = {
RTC_BROWSER_CHROME: "rtc_browser.chrome",
RTC_BROWSER_FIREFOX: "rtc_browser.firefox"
};
module.exports = RTCBrowserType;
},{}],89:[function(require,module,exports){
var RTCEvents = {
LASTN_CHANGED: "rtc.lastn_changed",
DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed",
LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
SIMULCAST_LAYER_CHANGED: "rtc.simulcast_layer_changed",
SIMULCAST_LAYER_CHANGING: "rtc.simulcast_layer_changing",
SIMULCAST_START: "rtc.simlcast_start",
SIMULCAST_STOP: "rtc.simlcast_stop",
AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed"
};
module.exports = RTCEvents;
},{}],90:[function(require,module,exports){
var Resolutions = {
"1080": {
width: 1920,
height: 1080,
order: 7
},
"fullhd": {
width: 1920,
height: 1080,
order: 7
},
"720": {
width: 1280,
height: 720,
order: 6
},
"hd": {
width: 1280,
height: 720,
order: 6
},
"960": {
width: 960,
height: 720,
order: 5
},
"640": {
width: 640,
height: 480,
order: 4
},
"vga": {
width: 640,
height: 480,
order: 4
},
"360": {
width: 640,
height: 360,
order: 3
},
"320": {
width: 320,
height: 240,
order: 2
},
"180": {
width: 320,
height: 180,
order: 1
}
};
module.exports = Resolutions;
},{}],91:[function(require,module,exports){
var StreamEventTypes = {
EVENT_TYPE_LOCAL_CREATED: "stream.local_created",
EVENT_TYPE_LOCAL_CHANGED: "stream.local_changed",
EVENT_TYPE_LOCAL_ENDED: "stream.local_ended",
EVENT_TYPE_REMOTE_CREATED: "stream.remote_created",
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended",
EVENT_TYPE_REMOTE_CHANGED: "stream.changed"
};
module.exports = StreamEventTypes;
},{}],92:[function(require,module,exports){
var UIEvents = {
NICKNAME_CHANGED: "UI.nickname_changed",
SELECTED_ENDPOINT: "UI.selected_endpoint",
PINNED_ENDPOINT: "UI.pinned_endpoint"
};
module.exports = UIEvents;
},{}],93:[function(require,module,exports){
var AuthenticationEvents = {
/**
* Event callback arguments:
* function(authenticationEnabled, userIdentity)
* authenticationEnabled - indicates whether authentication has been enabled
* in this session
* userIdentity - if user has been logged in then it contains user name. If
* contains 'null' or 'undefined' then user is not logged in.
*/
IDENTITY_UPDATED: "authentication.identity_updated"
};
module.exports = AuthenticationEvents;
},{}],94:[function(require,module,exports){
var CQEvents = {
LOCALSTATS_UPDATED: "cq.localstats_updated",
REMOTESTATS_UPDATED: "cq.remotestats_updated",
STOP: "cq.stop"
};
module.exports = CQEvents;
},{}],95:[function(require,module,exports){
var DesktopSharingEventTypes = {
INIT: "ds.init",
SWITCHING_DONE: "ds.switching_done",
NEW_STREAM_CREATED: "ds.new_stream_created"
};
module.exports = DesktopSharingEventTypes;
},{}],96:[function(require,module,exports){
module.exports = {
getLanguages : function () {
var languages = [];
for(var lang in this)
{
if(typeof this[lang] === "string")
languages.push(this[lang]);
}
return languages;
},
EN: "en",
BG: "bg",
DE: "de",
TR: "tr"
}
},{}],97:[function(require,module,exports){
var XMPPEvents = {
CONFERENCE_CERATED: "xmpp.conferenceCreated.jingle",
CALL_TERMINATED: "xmpp.callterminated.jingle",
CALL_INCOMING: "xmpp.callincoming.jingle",
DISPOSE_CONFERENCE: "xmpp.dispoce_confernce",
GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
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",
MUC_ENTER: "xmpp.muc_enter",
MUC_ROLE_CHANGED: "xmpp.muc_role_changed",
MUC_LEFT: "xmpp.muc_left",
MUC_DESTROYED: "xmpp.muc_destroyed",
DISPLAY_NAME_CHANGED: "xmpp.display_name_changed",
REMOTE_STATS: "xmpp.remote_stats",
LOCALROLE_CHANGED: "xmpp.localrole_changed",
PRESENCE_STATUS: "xmpp.presence_status",
RESERVATION_ERROR: "xmpp.room_reservation_error",
SUBJECT_CHANGED: "xmpp.subject_changed",
MESSAGE_RECEIVED: "xmpp.message_received",
SENDING_CHAT_MESSAGE: "xmpp.sending_chat_message",
PASSWORD_REQUIRED: "xmpp.password_required",
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
ETHERPAD: "xmpp.etherpad",
DEVICE_AVAILABLE: "xmpp.device_available"
};
module.exports = XMPPEvents;
},{}],98:[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
}
throw TypeError('Uncaught, unspecified "error" event.');
}
}
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;
}
},{}]},{},[1])(1)
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Vzci9sb2NhbC9saWIvbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImFwcC5qcyIsIm1vZHVsZXMvQVBJL0FQSS5qcyIsIm1vZHVsZXMvUlRDL0RhdGFDaGFubmVscy5qcyIsIm1vZHVsZXMvUlRDL0xvY2FsU3RyZWFtLmpzIiwibW9kdWxlcy9SVEMvTWVkaWFTdHJlYW0uanMiLCJtb2R1bGVzL1JUQy9SVEMuanMiLCJtb2R1bGVzL1JUQy9SVENVdGlscy5qcyIsIm1vZHVsZXMvVUkvVUkuanMiLCJtb2R1bGVzL1VJL2F1ZGlvX2xldmVscy9BdWRpb0xldmVscy5qcyIsIm1vZHVsZXMvVUkvYXVkaW9fbGV2ZWxzL0NhbnZhc1V0aWxzLmpzIiwibW9kdWxlcy9VSS9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvbi5qcyIsIm1vZHVsZXMvVUkvYXV0aGVudGljYXRpb24vTG9naW5EaWFsb2cuanMiLCJtb2R1bGVzL1VJL2F2YXRhci9BdmF0YXIuanMiLCJtb2R1bGVzL1VJL2V0aGVycGFkL0V0aGVycGFkLmpzIiwibW9kdWxlcy9VSS9wcmV6aS9QcmV6aS5qcyIsIm1vZHVsZXMvVUkvcHJlemkvUHJlemlQbGF5ZXIuanMiLCJtb2R1bGVzL1VJL3NpZGVfcGFubmVscy9TaWRlUGFuZWxUb2dnbGVyLmpzIiwibW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY2hhdC9DaGF0LmpzIiwibW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY2hhdC9Db21tYW5kcy5qcyIsIm1vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL2NoYXQvUmVwbGFjZW1lbnQuanMiLCJtb2R1bGVzL1VJL3NpZGVfcGFubmVscy9jaGF0L3NtaWxleXMuanNvbiIsIm1vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL2NvbnRhY3RsaXN0L0NvbnRhY3RMaXN0LmpzIiwibW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvc2V0dGluZ3MvU2V0dGluZ3NNZW51LmpzIiwibW9kdWxlcy9VSS90b29sYmFycy9Cb3R0b21Ub29sYmFyLmpzIiwibW9kdWxlcy9VSS90b29sYmFycy9Ub29sYmFyLmpzIiwibW9kdWxlcy9VSS90b29sYmFycy9Ub29sYmFyVG9nZ2xlci5qcyIsIm1vZHVsZXMvVUkvdXRpbC9KaXRzaVBvcG92ZXIuanMiLCJtb2R1bGVzL1VJL3V0aWwvTWVzc2FnZUhhbmRsZXIuanMiLCJtb2R1bGVzL1VJL3V0aWwvTmlja25hbWVIYW5kbGVyLmpzIiwibW9kdWxlcy9VSS91dGlsL1VJVXRpbC5qcyIsIm1vZHVsZXMvVUkvdmlkZW9sYXlvdXQvQ29ubmVjdGlvbkluZGljYXRvci5qcyIsIm1vZHVsZXMvVUkvdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXQuanMiLCJtb2R1bGVzL1VJL3dlbGNvbWVfcGFnZS9Sb29tbmFtZUdlbmVyYXRvci5qcyIsIm1vZHVsZXMvVUkvd2VsY29tZV9wYWdlL1dlbGNvbWVQYWdlLmpzIiwibW9kdWxlcy9jb25uZWN0aW9ucXVhbGl0eS9jb25uZWN0aW9ucXVhbGl0eS5qcyIsIm1vZHVsZXMvZGVza3RvcHNoYXJpbmcvZGVza3RvcHNoYXJpbmcuanMiLCJtb2R1bGVzL2tleWJvYXJkc2hvcnRjdXQva2V5Ym9hcmRzaG9ydGN1dC5qcyIsIm1vZHVsZXMvc2V0dGluZ3MvU2V0dGluZ3MuanMiLCJtb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RMb2dnZXIuanMiLCJtb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RSZWNlaXZlci5qcyIsIm1vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFNlbmRlci5qcyIsIm1vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFV0aWxzLmpzIiwibW9kdWxlcy9zaW11bGNhc3Qvc2ltdWxjYXN0LmpzIiwibW9kdWxlcy9zdGF0aXN0aWNzL0xvY2FsU3RhdHNDb2xsZWN0b3IuanMiLCJtb2R1bGVzL3N0YXRpc3RpY3MvUlRQU3RhdHNDb2xsZWN0b3IuanMiLCJtb2R1bGVzL3N0YXRpc3RpY3Mvc3RhdGlzdGljcy5qcyIsIm1vZHVsZXMvdHJhbnNsYXRpb24vdHJhbnNsYXRpb24uanMiLCJtb2R1bGVzL3htcHAvSmluZ2xlU2Vzc2lvbi5qcyIsIm1vZHVsZXMveG1wcC9TRFAuanMiLCJtb2R1bGVzL3htcHAvU0RQRGlmZmVyLmpzIiwibW9kdWxlcy94bXBwL1NEUFV0aWwuanMiLCJtb2R1bGVzL3htcHAvVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24uanMiLCJtb2R1bGVzL3htcHAvbW9kZXJhdG9yLmpzIiwibW9kdWxlcy94bXBwL3JlY29yZGluZy5qcyIsIm1vZHVsZXMveG1wcC9zdHJvcGhlLmVtdWMuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5qaW5nbGUuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5sb2dnZXIuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5tb2RlcmF0ZS5qcyIsIm1vZHVsZXMveG1wcC9zdHJvcGhlLnJheW8uanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS51dGlsLmpzIiwibW9kdWxlcy94bXBwL3htcHAuanMiLCJub2RlX21vZHVsZXMvaTE4bmV4dC1jbGllbnQvaTE4bmV4dC5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL2RlZmxhdGUuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvaW5mbGF0ZS5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi91dGlscy9jb21tb24uanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvdXRpbHMvc3RyaW5ncy5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2FkbGVyMzIuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9jb25zdGFudHMuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9jcmMzMi5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2RlZmxhdGUuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9nemhlYWRlci5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2luZmZhc3QuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9pbmZsYXRlLmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL3psaWIvaW5mdHJlZXMuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9tZXNzYWdlcy5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL3RyZWVzLmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL3psaWIvenN0cmVhbS5qcyIsIm5vZGVfbW9kdWxlcy9zZHAtaW50ZXJvcC9saWIvYXJyYXktZXF1YWxzLmpzIiwibm9kZV9tb2R1bGVzL3NkcC1pbnRlcm9wL2xpYi9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9zZHAtaW50ZXJvcC9saWIvaW50ZXJvcC5qcyIsIm5vZGVfbW9kdWxlcy9zZHAtaW50ZXJvcC9saWIvdHJhbnNmb3JtLmpzIiwibm9kZV9tb2R1bGVzL3NkcC1pbnRlcm9wL25vZGVfbW9kdWxlcy9zZHAtdHJhbnNmb3JtL2xpYi9ncmFtbWFyLmpzIiwibm9kZV9tb2R1bGVzL3NkcC1pbnRlcm9wL25vZGVfbW9kdWxlcy9zZHAtdHJhbnNmb3JtL2xpYi9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9zZHAtaW50ZXJvcC9ub2RlX21vZHVsZXMvc2RwLXRyYW5zZm9ybS9saWIvcGFyc2VyLmpzIiwibm9kZV9tb2R1bGVzL3NkcC1pbnRlcm9wL25vZGVfbW9kdWxlcy9zZHAtdHJhbnNmb3JtL2xpYi93cml0ZXIuanMiLCJzZXJ2aWNlL1JUQy9NZWRpYVN0cmVhbVR5cGVzLmpzIiwic2VydmljZS9SVEMvUlRDQnJvd3NlclR5cGUuanMiLCJzZXJ2aWNlL1JUQy9SVENFdmVudHMuanMiLCJzZXJ2aWNlL1JUQy9SZXNvbHV0aW9ucy5qcyIsInNlcnZpY2UvUlRDL1N0cmVhbUV2ZW50VHlwZXMuanMiLCJzZXJ2aWNlL1VJL1VJRXZlbnRzLmpzIiwic2VydmljZS9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvbkV2ZW50cy5qcyIsInNlcnZpY2UvY29ubmVjdGlvbnF1YWxpdHkvQ1FFdmVudHMuanMiLCJzZXJ2aWNlL2Rlc2t0b3BzaGFyaW5nL0Rlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5qcyIsInNlcnZpY2UvdHJhbnNsYXRpb24vbGFuZ3VhZ2VzLmpzIiwic2VydmljZS94bXBwL1hNUFBFdmVudHMuanMiLCIuLi8uLi8uLi8uLi8uLi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9ldmVudHMvZXZlbnRzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMU9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDclBBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ252QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeFFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5R0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25PQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbE1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0WEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdFNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9QQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcFdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0VBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsbkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOU1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4dkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNySUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM1FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6Z0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeE9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNudUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM3pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNW1CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5VEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3phQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDemtFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9XQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcHVEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzk5Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5cUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hCQTtBQUNBOztBQ0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6Z0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25QQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1hBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNkQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNkQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoganNoaW50IC1XMTE3ICovXG4vKiBhcHBsaWNhdGlvbiBzcGVjaWZpYyBsb2dpYyAqL1xuXG52YXIgQVBQID1cbntcbiAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoaXMuVUkgPSByZXF1aXJlKFwiLi9tb2R1bGVzL1VJL1VJXCIpO1xuICAgICAgICB0aGlzLkFQSSA9IHJlcXVpcmUoXCIuL21vZHVsZXMvQVBJL0FQSVwiKTtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9ucXVhbGl0eSA9IHJlcXVpcmUoXCIuL21vZHVsZXMvY29ubmVjdGlvbnF1YWxpdHkvY29ubmVjdGlvbnF1YWxpdHlcIik7XG4gICAgICAgIHRoaXMuc3RhdGlzdGljcyA9IHJlcXVpcmUoXCIuL21vZHVsZXMvc3RhdGlzdGljcy9zdGF0aXN0aWNzXCIpO1xuICAgICAgICB0aGlzLlJUQyA9IHJlcXVpcmUoXCIuL21vZHVsZXMvUlRDL1JUQ1wiKTtcbiAgICAgICAgdGhpcy5zaW11bGNhc3QgPSByZXF1aXJlKFwiLi9tb2R1bGVzL3NpbXVsY2FzdC9zaW11bGNhc3RcIik7XG4gICAgICAgIHRoaXMuZGVza3RvcHNoYXJpbmcgPSByZXF1aXJlKFwiLi9tb2R1bGVzL2Rlc2t0b3BzaGFyaW5nL2Rlc2t0b3BzaGFyaW5nXCIpO1xuICAgICAgICB0aGlzLnhtcHAgPSByZXF1aXJlKFwiLi9tb2R1bGVzL3htcHAveG1wcFwiKTtcbiAgICAgICAgdGhpcy5rZXlib2FyZHNob3J0Y3V0ID0gcmVxdWlyZShcIi4vbW9kdWxlcy9rZXlib2FyZHNob3J0Y3V0L2tleWJvYXJkc2hvcnRjdXRcIik7XG4gICAgICAgIHRoaXMudHJhbnNsYXRpb24gPSByZXF1aXJlKFwiLi9tb2R1bGVzL3RyYW5zbGF0aW9uL3RyYW5zbGF0aW9uXCIpO1xuICAgICAgICB0aGlzLnNldHRpbmdzID0gcmVxdWlyZShcIi4vbW9kdWxlcy9zZXR0aW5ncy9TZXR0aW5nc1wiKTtcbiAgICB9XG59O1xuXG5mdW5jdGlvbiBpbml0KCkge1xuXG4gICAgQVBQLlJUQy5zdGFydCgpO1xuICAgIEFQUC54bXBwLnN0YXJ0KCk7XG4gICAgQVBQLnN0YXRpc3RpY3Muc3RhcnQoKTtcbiAgICBBUFAuY29ubmVjdGlvbnF1YWxpdHkuaW5pdCgpO1xuXG4gICAgLy8gU2V0IGRlZmF1bHQgZGVza3RvcCBzaGFyaW5nIG1ldGhvZFxuICAgIEFQUC5kZXNrdG9wc2hhcmluZy5pbml0KCk7XG5cbiAgICBBUFAua2V5Ym9hcmRzaG9ydGN1dC5pbml0KCk7XG59XG5cblxuJChkb2N1bWVudCkucmVhZHkoZnVuY3Rpb24gKCkge1xuXG4gICAgQVBQLmluaXQoKTtcblxuICAgIEFQUC50cmFuc2xhdGlvbi5pbml0KCk7XG5cbiAgICBpZihBUFAuQVBJLmlzRW5hYmxlZCgpKVxuICAgICAgICBBUFAuQVBJLmluaXQoKTtcblxuICAgIEFQUC5VSS5zdGFydChpbml0KTtcblxufSk7XG5cbiQod2luZG93KS5iaW5kKCdiZWZvcmV1bmxvYWQnLCBmdW5jdGlvbiAoKSB7XG4gICAgaWYoQVBQLkFQSS5pc0VuYWJsZWQoKSlcbiAgICAgICAgQVBQLkFQSS5kaXNwb3NlKCk7XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBBUFA7XG5cbiIsIi8qKlxuICogSW1wbGVtZW50cyBBUEkgY2xhc3MgdGhhdCBjb21tdW5pY2F0ZXMgd2l0aCBleHRlcm5hbCBhcGkgY2xhc3NcbiAqIGFuZCBwcm92aWRlcyBpbnRlcmZhY2UgdG8gYWNjZXNzIEppdHNpIE1lZXQgZmVhdHVyZXMgYnkgZXh0ZXJuYWxcbiAqIGFwcGxpY2F0aW9ucyB0aGF0IGVtYmVkIEppdHNpIE1lZXRcbiAqL1xuXG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcblxuLyoqXG4gKiBMaXN0IG9mIHRoZSBhdmFpbGFibGUgY29tbWFuZHMuXG4gKiBAdHlwZSB7e1xuICogICAgICAgICAgICAgIGRpc3BsYXlOYW1lOiBpbnB1dERpc3BsYXlOYW1lSGFuZGxlcixcbiAqICAgICAgICAgICAgICBtdXRlQXVkaW86IHRvZ2dsZUF1ZGlvLFxuICogICAgICAgICAgICAgIG11dGVWaWRlbzogdG9nZ2xlVmlkZW8sXG4gKiAgICAgICAgICAgICAgZmlsbVN0cmlwOiB0b2dnbGVGaWxtU3RyaXBcbiAqICAgICAgICAgIH19XG4gKi9cbnZhciBjb21tYW5kcyA9XG57XG4gICAgZGlzcGxheU5hbWU6IEFQUC5VSS5pbnB1dERpc3BsYXlOYW1lSGFuZGxlcixcbiAgICBtdXRlQXVkaW86IEFQUC5VSS50b2dnbGVBdWRpbyxcbiAgICBtdXRlVmlkZW86IEFQUC5VSS50b2dnbGVWaWRlbyxcbiAgICB0b2dnbGVGaWxtU3RyaXA6IEFQUC5VSS50b2dnbGVGaWxtU3RyaXAsXG4gICAgdG9nZ2xlQ2hhdDogQVBQLlVJLnRvZ2dsZUNoYXQsXG4gICAgdG9nZ2xlQ29udGFjdExpc3Q6IEFQUC5VSS50b2dnbGVDb250YWN0TGlzdFxufTtcblxuXG4vKipcbiAqIE1hcHMgdGhlIHN1cHBvcnRlZCBldmVudHMgYW5kIHRoZWlyIHN0YXR1c1xuICogKHRydWUgaXQgdGhlIGV2ZW50IGlzIGVuYWJsZWQgYW5kIGZhbHNlIGlmIGl0IGlzIGRpc2FibGVkKVxuICogQHR5cGUge3tcbiAqICAgICAgICAgICAgICBpbmNvbWluZ01lc3NhZ2U6IGJvb2xlYW4sXG4gKiAgICAgICAgICAgICAgb3V0Z29pbmdNZXNzYWdlOiBib29sZWFuLFxuICogICAgICAgICAgICAgIGRpc3BsYXlOYW1lQ2hhbmdlOiBib29sZWFuLFxuICogICAgICAgICAgICAgIHBhcnRpY2lwYW50Sm9pbmVkOiBib29sZWFuLFxuICogICAgICAgICAgICAgIHBhcnRpY2lwYW50TGVmdDogYm9vbGVhblxuICogICAgICB9fVxuICovXG52YXIgZXZlbnRzID1cbntcbiAgICBpbmNvbWluZ01lc3NhZ2U6IGZhbHNlLFxuICAgIG91dGdvaW5nTWVzc2FnZTpmYWxzZSxcbiAgICBkaXNwbGF5TmFtZUNoYW5nZTogZmFsc2UsXG4gICAgcGFydGljaXBhbnRKb2luZWQ6IGZhbHNlLFxuICAgIHBhcnRpY2lwYW50TGVmdDogZmFsc2Vcbn07XG5cbnZhciBkaXNwbGF5TmFtZSA9IHt9O1xuXG4vKipcbiAqIFByb2Nlc3NlcyBjb21tYW5kcyBmcm9tIGV4dGVybmFsIGFwcGxpY2FpdG9uLlxuICogQHBhcmFtIG1lc3NhZ2UgdGhlIG9iamVjdCB3aXRoIHRoZSBjb21tYW5kXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NDb21tYW5kKG1lc3NhZ2UpXG57XG4gICAgaWYobWVzc2FnZS5hY3Rpb24gIT0gXCJleGVjdXRlXCIpXG4gICAge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5rbm93biBhY3Rpb24gb2YgdGhlIG1lc3NhZ2VcIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZm9yKHZhciBrZXkgaW4gbWVzc2FnZSlcbiAgICB7XG4gICAgICAgIGlmKGNvbW1hbmRzW2tleV0pXG4gICAgICAgICAgICBjb21tYW5kc1trZXldLmFwcGx5KG51bGwsIG1lc3NhZ2Vba2V5XSk7XG4gICAgfVxufVxuXG4vKipcbiAqIFByb2Nlc3NlcyBldmVudHMgb2JqZWN0cyBmcm9tIGV4dGVybmFsIGFwcGxpY2F0aW9uc1xuICogQHBhcmFtIGV2ZW50IHRoZSBldmVudFxuICovXG5mdW5jdGlvbiBwcm9jZXNzRXZlbnQoZXZlbnQpIHtcbiAgICBpZighZXZlbnQuYWN0aW9uKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkV2ZW50IHdpdGggbm8gYWN0aW9uIGlzIHJlY2VpdmVkLlwiKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBpID0gMDtcbiAgICBzd2l0Y2goZXZlbnQuYWN0aW9uKVxuICAgIHtcbiAgICAgICAgY2FzZSBcImFkZFwiOlxuICAgICAgICAgICAgZm9yKDsgaSA8IGV2ZW50LmV2ZW50cy5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBldmVudHNbZXZlbnQuZXZlbnRzW2ldXSA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInJlbW92ZVwiOlxuICAgICAgICAgICAgZm9yKDsgaSA8IGV2ZW50LmV2ZW50cy5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBldmVudHNbZXZlbnQuZXZlbnRzW2ldXSA9IGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5rbm93biBhY3Rpb24gZm9yIGV2ZW50LlwiKTtcbiAgICB9XG5cbn1cblxuLyoqXG4gKiBTZW5kcyBtZXNzYWdlIHRvIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvbi5cbiAqIEBwYXJhbSBvYmplY3RcbiAqL1xuZnVuY3Rpb24gc2VuZE1lc3NhZ2Uob2JqZWN0KSB7XG4gICAgd2luZG93LnBhcmVudC5wb3N0TWVzc2FnZShKU09OLnN0cmluZ2lmeShvYmplY3QpLCBcIipcIik7XG59XG5cbi8qKlxuICogUHJvY2Vzc2VzIGEgbWVzc2FnZSBldmVudCBmcm9tIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvblxuICogQHBhcmFtIGV2ZW50IHRoZSBtZXNzYWdlIGV2ZW50XG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NNZXNzYWdlKGV2ZW50KVxue1xuICAgIHZhciBtZXNzYWdlO1xuICAgIHRyeSB7XG4gICAgICAgIG1lc3NhZ2UgPSBKU09OLnBhcnNlKGV2ZW50LmRhdGEpO1xuICAgIH0gY2F0Y2ggKGUpIHt9XG5cbiAgICBpZighbWVzc2FnZS50eXBlKVxuICAgICAgICByZXR1cm47XG4gICAgc3dpdGNoIChtZXNzYWdlLnR5cGUpXG4gICAge1xuICAgICAgICBjYXNlIFwiY29tbWFuZFwiOlxuICAgICAgICAgICAgcHJvY2Vzc0NvbW1hbmQobWVzc2FnZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImV2ZW50XCI6XG4gICAgICAgICAgICBwcm9jZXNzRXZlbnQobWVzc2FnZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJVbmtub3duIHR5cGUgb2YgdGhlIG1lc3NhZ2VcIik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgfVxuXG59XG5cbmZ1bmN0aW9uIHNldHVwTGlzdGVuZXJzKCkge1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0VOVEVSLCBmdW5jdGlvbiAoZnJvbSkge1xuICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwicGFydGljaXBhbnRKb2luZWRcIiwge2ppZDogZnJvbX0pO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTUVTU0FHRV9SRUNFSVZFRCwgZnVuY3Rpb24gKGZyb20sIG5pY2ssIHR4dCwgbXlqaWQpIHtcbiAgICAgICAgaWYgKGZyb20gIT0gbXlqaWQpXG4gICAgICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwiaW5jb21pbmdNZXNzYWdlXCIsXG4gICAgICAgICAgICAgICAge1wiZnJvbVwiOiBmcm9tLCBcIm5pY2tcIjogbmljaywgXCJtZXNzYWdlXCI6IHR4dH0pO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0xFRlQsIGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgQVBJLnRyaWdnZXJFdmVudChcInBhcnRpY2lwYW50TGVmdFwiLCB7amlkOiBqaWR9KTtcbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkRJU1BMQVlfTkFNRV9DSEFOR0VELCBmdW5jdGlvbiAoamlkLCBuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICBuYW1lID0gZGlzcGxheU5hbWVbamlkXTtcbiAgICAgICAgaWYoIW5hbWUgfHwgbmFtZSAhPSBuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgQVBJLnRyaWdnZXJFdmVudChcImRpc3BsYXlOYW1lQ2hhbmdlXCIsIHtqaWQ6IGppZCwgZGlzcGxheW5hbWU6IG5ld0Rpc3BsYXlOYW1lfSk7XG4gICAgICAgICAgICBkaXNwbGF5TmFtZVtqaWRdID0gbmV3RGlzcGxheU5hbWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLlNFTkRJTkdfQ0hBVF9NRVNTQUdFLCBmdW5jdGlvbiAoYm9keSkge1xuICAgICAgICBBUFAuQVBJLnRyaWdnZXJFdmVudChcIm91dGdvaW5nTWVzc2FnZVwiLCB7XCJtZXNzYWdlXCI6IGJvZHl9KTtcbiAgICB9KTtcbn1cblxudmFyIEFQSSA9IHtcbiAgICAvKipcbiAgICAgKiBDaGVjayB3aGV0aGVyIHRoZSBBUEkgc2hvdWxkIGJlIGVuYWJsZWQgb3Igbm90LlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAqL1xuICAgIGlzRW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaGFzaCA9IGxvY2F0aW9uLmhhc2g7XG4gICAgICAgIGlmKGhhc2ggJiYgaGFzaC5pbmRleE9mKFwiZXh0ZXJuYWxcIikgPiAtMSAmJiB3aW5kb3cucG9zdE1lc3NhZ2UpXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZXMgdGhlIEFQSUNvbm5lY3Rvci4gU2V0dXBzIG1lc3NhZ2UgZXZlbnQgbGlzdGVuZXJzIHRoYXQgd2lsbFxuICAgICAqIHJlY2VpdmUgaW5mb3JtYXRpb24gZnJvbSBleHRlcm5hbCBhcHBsaWNhdGlvbnMgdGhhdCBlbWJlZCBKaXRzaSBNZWV0LlxuICAgICAqIEl0IGFsc28gc2VuZHMgYSBtZXNzYWdlIHRvIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvbiB0aGF0IEFQSUNvbm5lY3RvclxuICAgICAqIGlzIGluaXRpYWxpemVkLlxuICAgICAqL1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsXG4gICAgICAgICAgICAgICAgcHJvY2Vzc01lc3NhZ2UsIGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5hdHRhY2hFdmVudCgnb25tZXNzYWdlJywgcHJvY2Vzc01lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICAgIHNlbmRNZXNzYWdlKHt0eXBlOiBcInN5c3RlbVwiLCBsb2FkZWQ6IHRydWV9KTtcbiAgICAgICAgc2V0dXBMaXN0ZW5lcnMoKTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIENoZWNrcyB3aGV0aGVyIHRoZSBldmVudCBpcyBlbmFibGVkIG90IG5vdC5cbiAgICAgKiBAcGFyYW0gbmFtZSB0aGUgbmFtZSBvZiB0aGUgZXZlbnQuXG4gICAgICogQHJldHVybnMgeyp9XG4gICAgICovXG4gICAgaXNFdmVudEVuYWJsZWQ6IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgIHJldHVybiBldmVudHNbbmFtZV07XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFNlbmRzIGV2ZW50IG9iamVjdCB0byB0aGUgZXh0ZXJuYWwgYXBwbGljYXRpb24gdGhhdCBoYXMgYmVlbiBzdWJzY3JpYmVkXG4gICAgICogZm9yIHRoYXQgZXZlbnQuXG4gICAgICogQHBhcmFtIG5hbWUgdGhlIG5hbWUgZXZlbnRcbiAgICAgKiBAcGFyYW0gb2JqZWN0IGRhdGEgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudFxuICAgICAqL1xuICAgIHRyaWdnZXJFdmVudDogZnVuY3Rpb24gKG5hbWUsIG9iamVjdCkge1xuICAgICAgICBpZih0aGlzLmlzRW5hYmxlZCgpICYmIHRoaXMuaXNFdmVudEVuYWJsZWQobmFtZSkpXG4gICAgICAgICAgICBzZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgdHlwZTogXCJldmVudFwiLCBhY3Rpb246IFwicmVzdWx0XCIsIGV2ZW50OiBuYW1lLCByZXN1bHQ6IG9iamVjdH0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIHRoZSBsaXN0ZW5lcnMuXG4gICAgICovXG4gICAgZGlzcG9zZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZih3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcilcbiAgICAgICAge1xuICAgICAgICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsXG4gICAgICAgICAgICAgICAgcHJvY2Vzc01lc3NhZ2UsIGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5kZXRhY2hFdmVudCgnb25tZXNzYWdlJywgcHJvY2Vzc01lc3NhZ2UpO1xuICAgICAgICB9XG5cbiAgICB9XG5cblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBBUEk7IiwiLyogZ2xvYmFsIFN0cm9waGUsIGZvY3VzZWRWaWRlb1NyYyovXG5cbi8vIGNhY2hlIGRhdGFjaGFubmVscyB0byBhdm9pZCBnYXJiYWdlIGNvbGxlY3Rpb25cbi8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD00MDU1NDVcbnZhciBSVENFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvUlRDRXZlbnRzXCIpO1xuXG52YXIgX2RhdGFDaGFubmVscyA9IFtdO1xudmFyIGV2ZW50RW1pdHRlciA9IG51bGw7XG5cblxuXG5cbnZhciBEYXRhQ2hhbm5lbHMgPVxue1xuXG4gICAgLyoqXG4gICAgICogQ2FsbGJhY2sgdHJpZ2dlcmVkIGJ5IFBlZXJDb25uZWN0aW9uIHdoZW4gbmV3IGRhdGEgY2hhbm5lbCBpcyBvcGVuZWRcbiAgICAgKiBvbiB0aGUgYnJpZGdlLlxuICAgICAqIEBwYXJhbSBldmVudCB0aGUgZXZlbnQgaW5mbyBvYmplY3QuXG4gICAgICovXG5cbiAgICBvbkRhdGFDaGFubmVsOiBmdW5jdGlvbiAoZXZlbnQpXG4gICAge1xuICAgICAgICB2YXIgZGF0YUNoYW5uZWwgPSBldmVudC5jaGFubmVsO1xuXG4gICAgICAgIGRhdGFDaGFubmVsLm9ub3BlbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkRhdGEgY2hhbm5lbCBvcGVuZWQgYnkgdGhlIFZpZGVvYnJpZGdlIVwiLCBkYXRhQ2hhbm5lbCk7XG5cbiAgICAgICAgICAgIC8vIENvZGUgc2FtcGxlIGZvciBzZW5kaW5nIHN0cmluZyBhbmQvb3IgYmluYXJ5IGRhdGFcbiAgICAgICAgICAgIC8vIFNlbmRzIFN0cmluZyBtZXNzYWdlIHRvIHRoZSBicmlkZ2VcbiAgICAgICAgICAgIC8vZGF0YUNoYW5uZWwuc2VuZChcIkhlbGxvIGJyaWRnZSFcIik7XG4gICAgICAgICAgICAvLyBTZW5kcyAxMiBieXRlcyBiaW5hcnkgbWVzc2FnZSB0byB0aGUgYnJpZGdlXG4gICAgICAgICAgICAvL2RhdGFDaGFubmVsLnNlbmQobmV3IEFycmF5QnVmZmVyKDEyKSk7XG5cbiAgICAgICAgICAgIC8vIHdoZW4gdGhlIGRhdGEgY2hhbm5lbCBiZWNvbWVzIGF2YWlsYWJsZSwgdGVsbCB0aGUgYnJpZGdlIGFib3V0IHZpZGVvXG4gICAgICAgICAgICAvLyBzZWxlY3Rpb25zIHNvIHRoYXQgaXQgY2FuIGRvIGFkYXB0aXZlIHNpbXVsY2FzdCxcbiAgICAgICAgICAgIC8vIHdlIHdhbnQgdGhlIG5vdGlmaWNhdGlvbiB0byB0cmlnZ2VyIGV2ZW4gaWYgdXNlckppZCBpcyB1bmRlZmluZWQsXG4gICAgICAgICAgICAvLyBvciBudWxsLlxuICAgICAgICAgICAgdmFyIHVzZXJKaWQgPSBBUFAuVUkuZ2V0TGFyZ2VWaWRlb1N0YXRlKCkudXNlclJlc291cmNlSmlkO1xuICAgICAgICAgICAgLy8gd2Ugd2FudCB0aGUgbm90aWZpY2F0aW9uIHRvIHRyaWdnZXIgZXZlbiBpZiB1c2VySmlkIGlzIHVuZGVmaW5lZCxcbiAgICAgICAgICAgIC8vIG9yIG51bGwuXG4gICAgICAgICAgICBvblNlbGVjdGVkRW5kcG9pbnRDaGFuZ2VkKHVzZXJKaWQpO1xuICAgICAgICB9O1xuXG4gICAgICAgIGRhdGFDaGFubmVsLm9uZXJyb3IgPSBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJEYXRhIENoYW5uZWwgRXJyb3I6XCIsIGVycm9yLCBkYXRhQ2hhbm5lbCk7XG4gICAgICAgIH07XG5cbiAgICAgICAgZGF0YUNoYW5uZWwub25tZXNzYWdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICB2YXIgZGF0YSA9IGV2ZW50LmRhdGE7XG4gICAgICAgICAgICAvLyBKU09OXG4gICAgICAgICAgICB2YXIgb2JqO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIG9iaiA9IEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICAgICAgICAgIFwiRmFpbGVkIHRvIHBhcnNlIGRhdGEgY2hhbm5lbCBtZXNzYWdlIGFzIEpTT046IFwiLFxuICAgICAgICAgICAgICAgICAgICBkYXRhLFxuICAgICAgICAgICAgICAgICAgICBkYXRhQ2hhbm5lbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoKCd1bmRlZmluZWQnICE9PSB0eXBlb2Yob2JqKSkgJiYgKG51bGwgIT09IG9iaikpIHtcbiAgICAgICAgICAgICAgICB2YXIgY29saWJyaUNsYXNzID0gb2JqLmNvbGlicmlDbGFzcztcblxuICAgICAgICAgICAgICAgIGlmIChcIkRvbWluYW50U3BlYWtlckVuZHBvaW50Q2hhbmdlRXZlbnRcIiA9PT0gY29saWJyaUNsYXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIEVuZHBvaW50IElEIGZyb20gdGhlIFZpZGVvYnJpZGdlLlxuICAgICAgICAgICAgICAgICAgICB2YXIgZG9taW5hbnRTcGVha2VyRW5kcG9pbnQgPSBvYmouZG9taW5hbnRTcGVha2VyRW5kcG9pbnQ7XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJEYXRhIGNoYW5uZWwgbmV3IGRvbWluYW50IHNwZWFrZXIgZXZlbnQ6IFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgZG9taW5hbnRTcGVha2VyRW5kcG9pbnQpO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChSVENFdmVudHMuRE9NSU5BTlRTUEVBS0VSX0NIQU5HRUQsIGRvbWluYW50U3BlYWtlckVuZHBvaW50KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoXCJJbkxhc3ROQ2hhbmdlRXZlbnRcIiA9PT0gY29saWJyaUNsYXNzKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG9sZFZhbHVlID0gb2JqLm9sZFZhbHVlO1xuICAgICAgICAgICAgICAgICAgICB2YXIgbmV3VmFsdWUgPSBvYmoubmV3VmFsdWU7XG4gICAgICAgICAgICAgICAgICAgIC8vIE1ha2Ugc3VyZSB0aGF0IG9sZFZhbHVlIGFuZCBuZXdWYWx1ZSBhcmUgb2YgdHlwZSBib29sZWFuLlxuICAgICAgICAgICAgICAgICAgICB2YXIgdHlwZTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoKHR5cGUgPSB0eXBlb2Ygb2xkVmFsdWUpICE9PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0eXBlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZFZhbHVlID0gKG9sZFZhbHVlID09IFwidHJ1ZVwiKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb2xkVmFsdWUgPSBuZXcgQm9vbGVhbihvbGRWYWx1ZSkudmFsdWVPZigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmICgodHlwZSA9IHR5cGVvZiBuZXdWYWx1ZSkgIT09ICdib29sZWFuJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3VmFsdWUgPSAobmV3VmFsdWUgPT0gXCJ0cnVlXCIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdWYWx1ZSA9IG5ldyBCb29sZWFuKG5ld1ZhbHVlKS52YWx1ZU9mKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChSVENFdmVudHMuTEFTVE5fQ0hBTkdFRCwgb2xkVmFsdWUsIG5ld1ZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoXCJMYXN0TkVuZHBvaW50c0NoYW5nZUV2ZW50XCIgPT09IGNvbGlicmlDbGFzcylcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFRoZSBuZXcvbGF0ZXN0IGxpc3Qgb2YgbGFzdC1uIGVuZHBvaW50IElEcy5cbiAgICAgICAgICAgICAgICAgICAgdmFyIGxhc3RORW5kcG9pbnRzID0gb2JqLmxhc3RORW5kcG9pbnRzO1xuICAgICAgICAgICAgICAgICAgICAvLyBUaGUgbGlzdCBvZiBlbmRwb2ludCBJRHMgd2hpY2ggYXJlIGVudGVyaW5nIHRoZSBsaXN0IG9mXG4gICAgICAgICAgICAgICAgICAgIC8vIGxhc3QtbiBhdCB0aGlzIHRpbWUgaS5lLiB3ZXJlIG5vdCBpbiB0aGUgb2xkIGxpc3Qgb2YgbGFzdC1uXG4gICAgICAgICAgICAgICAgICAgIC8vIGVuZHBvaW50IElEcy5cbiAgICAgICAgICAgICAgICAgICAgdmFyIGVuZHBvaW50c0VudGVyaW5nTGFzdE4gPSBvYmouZW5kcG9pbnRzRW50ZXJpbmdMYXN0TjtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHN0cmVhbSA9IG9iai5zdHJlYW07XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgICAgICBcIkRhdGEgY2hhbm5lbCBuZXcgbGFzdC1uIGV2ZW50OiBcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RORW5kcG9pbnRzLCBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLCBvYmopO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChSVENFdmVudHMuTEFTVE5fRU5EUE9JTlRfQ0hBTkdFRCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RORW5kcG9pbnRzLCBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLCBvYmopO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChcIlNpbXVsY2FzdExheWVyc0NoYW5nZWRFdmVudFwiID09PSBjb2xpYnJpQ2xhc3MpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChSVENFdmVudHMuU0lNVUxDQVNUX0xBWUVSX0NIQU5HRUQsXG4gICAgICAgICAgICAgICAgICAgICAgICBvYmouZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChcIlNpbXVsY2FzdExheWVyc0NoYW5naW5nRXZlbnRcIiA9PT0gY29saWJyaUNsYXNzKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLlNJTVVMQ0FTVF9MQVlFUl9DSEFOR0lORyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG9iai5lbmRwb2ludFNpbXVsY2FzdExheWVycyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKFwiU3RhcnRTaW11bGNhc3RMYXllckV2ZW50XCIgPT09IGNvbGlicmlDbGFzcylcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFJUQ0V2ZW50cy5TSU1VTENBU1RfU1RBUlQsIG9iai5zaW11bGNhc3RMYXllcik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKFwiU3RvcFNpbXVsY2FzdExheWVyRXZlbnRcIiA9PT0gY29saWJyaUNsYXNzKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLlNJTVVMQ0FTVF9TVE9QLCBvYmouc2ltdWxjYXN0TGF5ZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmRlYnVnKFwiRGF0YSBjaGFubmVsIEpTT04tZm9ybWF0dGVkIG1lc3NhZ2U6IFwiLCBvYmopO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBkYXRhQ2hhbm5lbC5vbmNsb3NlID0gZnVuY3Rpb24gKClcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiVGhlIERhdGEgQ2hhbm5lbCBjbG9zZWRcIiwgZGF0YUNoYW5uZWwpO1xuICAgICAgICAgICAgdmFyIGlkeCA9IF9kYXRhQ2hhbm5lbHMuaW5kZXhPZihkYXRhQ2hhbm5lbCk7XG4gICAgICAgICAgICBpZiAoaWR4ID4gLTEpXG4gICAgICAgICAgICAgICAgX2RhdGFDaGFubmVscyA9IF9kYXRhQ2hhbm5lbHMuc3BsaWNlKGlkeCwgMSk7XG4gICAgICAgIH07XG4gICAgICAgIF9kYXRhQ2hhbm5lbHMucHVzaChkYXRhQ2hhbm5lbCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEJpbmRzIFwib25kYXRhY2hhbm5lbFwiIGV2ZW50IGxpc3RlbmVyIHRvIGdpdmVuIFBlZXJDb25uZWN0aW9uIGluc3RhbmNlLlxuICAgICAqIEBwYXJhbSBwZWVyQ29ubmVjdGlvbiBXZWJSVEMgcGVlciBjb25uZWN0aW9uIGluc3RhbmNlLlxuICAgICAqL1xuICAgIGluaXQ6IGZ1bmN0aW9uIChwZWVyQ29ubmVjdGlvbiwgZW1pdHRlcikge1xuICAgICAgICBpZighY29uZmlnLm9wZW5TY3RwKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHBlZXJDb25uZWN0aW9uLm9uZGF0YWNoYW5uZWwgPSB0aGlzLm9uRGF0YUNoYW5uZWw7XG4gICAgICAgIGV2ZW50RW1pdHRlciA9IGVtaXR0ZXI7XG5cbiAgICAgICAgLy8gU2FtcGxlIGNvZGUgZm9yIG9wZW5pbmcgbmV3IGRhdGEgY2hhbm5lbCBmcm9tIEppdHNpIE1lZXQgdG8gdGhlIGJyaWRnZS5cbiAgICAgICAgLy8gQWx0aG91Z2ggaXQncyBub3QgYSByZXF1aXJlbWVudCB0byBvcGVuIHNlcGFyYXRlIGNoYW5uZWxzIGZyb20gYm90aCBicmlkZ2VcbiAgICAgICAgLy8gYW5kIHBlZXIgYXMgc2luZ2xlIGNoYW5uZWwgY2FuIGJlIHVzZWQgZm9yIHNlbmRpbmcgYW5kIHJlY2VpdmluZyBkYXRhLlxuICAgICAgICAvLyBTbyBlaXRoZXIgY2hhbm5lbCBvcGVuZWQgYnkgdGhlIGJyaWRnZSBvciB0aGUgb25lIG9wZW5lZCBoZXJlIGlzIGVub3VnaFxuICAgICAgICAvLyBmb3IgY29tbXVuaWNhdGlvbiB3aXRoIHRoZSBicmlkZ2UuXG4gICAgICAgIC8qdmFyIGRhdGFDaGFubmVsT3B0aW9ucyA9XG4gICAgICAgICB7XG4gICAgICAgICByZWxpYWJsZTogdHJ1ZVxuICAgICAgICAgfTtcbiAgICAgICAgIHZhciBkYXRhQ2hhbm5lbFxuICAgICAgICAgPSBwZWVyQ29ubmVjdGlvbi5jcmVhdGVEYXRhQ2hhbm5lbChcIm15Q2hhbm5lbFwiLCBkYXRhQ2hhbm5lbE9wdGlvbnMpO1xuXG4gICAgICAgICAvLyBDYW4gYmUgdXNlZCBvbmx5IHdoZW4gaXMgaW4gb3BlbiBzdGF0ZVxuICAgICAgICAgZGF0YUNoYW5uZWwub25vcGVuID0gZnVuY3Rpb24gKClcbiAgICAgICAgIHtcbiAgICAgICAgIGRhdGFDaGFubmVsLnNlbmQoXCJNeSBjaGFubmVsICEhIVwiKTtcbiAgICAgICAgIH07XG4gICAgICAgICBkYXRhQ2hhbm5lbC5vbm1lc3NhZ2UgPSBmdW5jdGlvbiAoZXZlbnQpXG4gICAgICAgICB7XG4gICAgICAgICB2YXIgbXNnRGF0YSA9IGV2ZW50LmRhdGE7XG4gICAgICAgICBjb25zb2xlLmluZm8oXCJHb3QgTXkgRGF0YSBDaGFubmVsIE1lc3NhZ2U6XCIsIG1zZ0RhdGEsIGRhdGFDaGFubmVsKTtcbiAgICAgICAgIH07Ki9cbiAgICB9LFxuICAgIGhhbmRsZVNlbGVjdGVkRW5kcG9pbnRFdmVudDogb25TZWxlY3RlZEVuZHBvaW50Q2hhbmdlZCxcbiAgICBoYW5kbGVQaW5uZWRFbmRwb2ludEV2ZW50OiBvblBpbm5lZEVuZHBvaW50Q2hhbmdlZFxuXG59O1xuXG5mdW5jdGlvbiBvblNlbGVjdGVkRW5kcG9pbnRDaGFuZ2VkKHVzZXJSZXNvdXJjZSlcbntcbiAgICBjb25zb2xlLmxvZygnc2VsZWN0ZWQgZW5kcG9pbnQgY2hhbmdlZDogJywgdXNlclJlc291cmNlKTtcbiAgICBpZiAoX2RhdGFDaGFubmVscyAmJiBfZGF0YUNoYW5uZWxzLmxlbmd0aCAhPSAwKVxuICAgIHtcbiAgICAgICAgX2RhdGFDaGFubmVscy5zb21lKGZ1bmN0aW9uIChkYXRhQ2hhbm5lbCkge1xuICAgICAgICAgICAgaWYgKGRhdGFDaGFubmVsLnJlYWR5U3RhdGUgPT0gJ29wZW4nKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzZW5kaW5nIHNlbGVjdGVkIGVuZHBvaW50IGNoYW5nZWQgJyBcbiAgICAgICAgICAgICAgICAgICAgKyAnbm90aWZpY2F0aW9uIHRvIHRoZSBicmlkZ2U6ICcsIHVzZXJSZXNvdXJjZSk7XG4gICAgICAgICAgICAgICAgZGF0YUNoYW5uZWwuc2VuZChKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgICAgICAgICAgICdjb2xpYnJpQ2xhc3MnOiAnU2VsZWN0ZWRFbmRwb2ludENoYW5nZWRFdmVudCcsXG4gICAgICAgICAgICAgICAgICAgICdzZWxlY3RlZEVuZHBvaW50JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICghdXNlclJlc291cmNlIHx8IHVzZXJSZXNvdXJjZSA9PT0gbnVsbCk/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVsbCA6IHVzZXJSZXNvdXJjZVxuICAgICAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIG9uUGlubmVkRW5kcG9pbnRDaGFuZ2VkKHVzZXJSZXNvdXJjZSlcbntcbiAgICBjb25zb2xlLmxvZygncGlubmVkIGVuZHBvaW50IGNoYW5nZWQ6ICcsIHVzZXJSZXNvdXJjZSk7XG4gICAgaWYgKF9kYXRhQ2hhbm5lbHMgJiYgX2RhdGFDaGFubmVscy5sZW5ndGggIT0gMClcbiAgICB7XG4gICAgICAgIF9kYXRhQ2hhbm5lbHMuc29tZShmdW5jdGlvbiAoZGF0YUNoYW5uZWwpIHtcbiAgICAgICAgICAgIGlmIChkYXRhQ2hhbm5lbC5yZWFkeVN0YXRlID09ICdvcGVuJylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBkYXRhQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICAgICAgICAgICAgJ2NvbGlicmlDbGFzcyc6ICdQaW5uZWRFbmRwb2ludENoYW5nZWRFdmVudCcsXG4gICAgICAgICAgICAgICAgICAgICdwaW5uZWRFbmRwb2ludCc6XG4gICAgICAgICAgICAgICAgICAgICAgICAoIXVzZXJSZXNvdXJjZSB8fCB1c2VyUmVzb3VyY2UgPT0gbnVsbCk/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVsbCA6IHVzZXJSZXNvdXJjZVxuICAgICAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gRGF0YUNoYW5uZWxzO1xuXG4iLCJ2YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9TdHJlYW1FdmVudFR5cGVzLmpzXCIpO1xuXG5cbmZ1bmN0aW9uIExvY2FsU3RyZWFtKHN0cmVhbSwgdHlwZSwgZXZlbnRFbWl0dGVyLCB2aWRlb1R5cGUpXG57XG4gICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5ldmVudEVtaXR0ZXIgPSBldmVudEVtaXR0ZXI7XG4gICAgdGhpcy50eXBlID0gdHlwZTtcbiAgICB0aGlzLnZpZGVvVHlwZSA9IHZpZGVvVHlwZTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYodHlwZSA9PSBcImF1ZGlvXCIpXG4gICAge1xuICAgICAgICB0aGlzLmdldFRyYWNrcyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLnN0cmVhbS5nZXRBdWRpb1RyYWNrcygpO1xuICAgICAgICB9O1xuICAgIH1cbiAgICBlbHNlXG4gICAge1xuICAgICAgICB0aGlzLmdldFRyYWNrcyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLnN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIHRoaXMuc3RyZWFtLm9uZW5kZWQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICBzZWxmLnN0cmVhbUVuZGVkKCk7XG4gICAgfTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLnN0cmVhbUVuZGVkID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMuZXZlbnRFbWl0dGVyLmVtaXQoU3RyZWFtRXZlbnRUeXBlcy5FVkVOVF9UWVBFX0xPQ0FMX0VOREVELCB0aGlzKTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLmdldE9yaWdpbmFsU3RyZWFtID0gZnVuY3Rpb24oKVxue1xuICAgIHJldHVybiB0aGlzLnN0cmVhbTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLmlzQXVkaW9TdHJlYW0gPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuICh0aGlzLnN0cmVhbS5nZXRBdWRpb1RyYWNrcygpICYmIHRoaXMuc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkubGVuZ3RoID4gMCk7XG59O1xuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUubXV0ZSA9IGZ1bmN0aW9uKClcbntcbiAgICB2YXIgaXNtdXRlZCA9IGZhbHNlO1xuICAgIHZhciB0cmFja3MgPSB0aGlzLmdldFRyYWNrcygpO1xuXG4gICAgZm9yICh2YXIgaWR4ID0gMDsgaWR4IDwgdHJhY2tzLmxlbmd0aDsgaWR4KyspIHtcbiAgICAgICAgaXNtdXRlZCA9ICF0cmFja3NbaWR4XS5lbmFibGVkO1xuICAgICAgICB0cmFja3NbaWR4XS5lbmFibGVkID0gaXNtdXRlZDtcbiAgICB9XG4gICAgcmV0dXJuIGlzbXV0ZWQ7XG59O1xuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUuc2V0TXV0ZSA9IGZ1bmN0aW9uKG11dGUpXG57XG5cbiAgICBpZih3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wgIT0gXCJodHRwczpcIiB8fFxuICAgICAgICB0aGlzLmlzQXVkaW9TdHJlYW0oKSB8fCB0aGlzLnZpZGVvVHlwZSA9PT0gXCJzY3JlZW5cIilcbiAgICB7XG4gICAgICAgIHZhciB0cmFja3MgPSB0aGlzLmdldFRyYWNrcygpO1xuXG4gICAgICAgIGZvciAodmFyIGlkeCA9IDA7IGlkeCA8IHRyYWNrcy5sZW5ndGg7IGlkeCsrKSB7XG4gICAgICAgICAgICB0cmFja3NbaWR4XS5lbmFibGVkID0gbXV0ZTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBlbHNlXG4gICAge1xuICAgICAgICBpZihtdXRlID09PSBmYWxzZSkge1xuICAgICAgICAgICAgQVBQLnhtcHAucmVtb3ZlU3RyZWFtKHRoaXMuc3RyZWFtKTtcbiAgICAgICAgICAgIHRoaXMuc3RyZWFtLnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIEFQUC5SVEMucnRjVXRpbHMub2J0YWluQXVkaW9BbmRWaWRlb1Blcm1pc3Npb25zKFtcInZpZGVvXCJdLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5jaGFuZ2VMb2NhbFZpZGVvKHN0cmVhbSwgZmFsc2UsIGZ1bmN0aW9uICgpIHt9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cbkxvY2FsU3RyZWFtLnByb3RvdHlwZS5pc011dGVkID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciB0cmFja3MgPSBbXTtcbiAgICBpZih0aGlzLnR5cGUgPT0gXCJhdWRpb1wiKVxuICAgIHtcbiAgICAgICAgdHJhY2tzID0gdGhpcy5zdHJlYW0uZ2V0QXVkaW9UcmFja3MoKTtcbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgaWYodGhpcy5zdHJlYW0uZW5kZWQpXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgdHJhY2tzID0gdGhpcy5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICB9XG4gICAgZm9yICh2YXIgaWR4ID0gMDsgaWR4IDwgdHJhY2tzLmxlbmd0aDsgaWR4KyspIHtcbiAgICAgICAgaWYodHJhY2tzW2lkeF0uZW5hYmxlZClcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5cbkxvY2FsU3RyZWFtLnByb3RvdHlwZS5nZXRJZCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5zdHJlYW0uZ2V0VHJhY2tzKClbMF0uaWQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gTG9jYWxTdHJlYW07XG4iLCIvLy8vVGhlc2UgbGluZXMgc2hvdWxkIGJlIHVuY29tbWVudGVkIHdoZW4gcmVxdWlyZSB3b3JrcyBpbiBhcHAuanNcbnZhciBNZWRpYVN0cmVhbVR5cGUgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvTWVkaWFTdHJlYW1UeXBlc1wiKTtcbnZhciBTdHJlYW1FdmVudFR5cGUgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvU3RyZWFtRXZlbnRUeXBlc1wiKTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgTWVkaWFTdHJlYW0gb2JqZWN0IGZvciB0aGUgZ2l2ZW4gZGF0YSwgc2Vzc2lvbiBpZCBhbmQgc3NyYy5cbiAqIEl0IGlzIGEgd3JhcHBlciBjbGFzcyBmb3IgdGhlIE1lZGlhU3RyZWFtLlxuICpcbiAqIEBwYXJhbSBkYXRhIHRoZSBkYXRhIG9iamVjdCBmcm9tIHdoaWNoIHdlIG9idGFpbiB0aGUgc3RyZWFtLFxuICogdGhlIHBlZXJqaWQsIGV0Yy5cbiAqIEBwYXJhbSBzaWQgdGhlIHNlc3Npb24gaWRcbiAqIEBwYXJhbSBzc3JjIHRoZSBzc3JjIGNvcnJlc3BvbmRpbmcgdG8gdGhpcyBNZWRpYVN0cmVhbVxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBNZWRpYVN0cmVhbShkYXRhLCBzaWQsIHNzcmMsIGJyb3dzZXIsIGV2ZW50RW1pdHRlcikge1xuXG4gICAgLy8gWFhYKGdwKSB0byBtaW5pbWl6ZSBoZWFkYWNoZXMgaW4gdGhlIGZ1dHVyZSwgd2Ugc2hvdWxkIGJ1aWxkIG91clxuICAgIC8vIGFic3RyYWN0aW9ucyBhcm91bmQgdHJhY2tzIGFuZCBub3Qgc3RyZWFtcy4gT1JUQyBpcyB0cmFjayBiYXNlZCBBUEkuXG4gICAgLy8gTW96aWxsYSBleHBlY3RzIG0tbGluZXMgdG8gcmVwcmVzZW50IG1lZGlhIHRyYWNrcy5cbiAgICAvL1xuICAgIC8vIFByYWN0aWNhbGx5LCB3aGF0IEknbSBzYXlpbmcgaXMgdGhhdCB3ZSBzaG91bGQgaGF2ZSBhIE1lZGlhVHJhY2sgY2xhc3NcbiAgICAvLyBhbmQgbm90IGEgTWVkaWFTdHJlYW0gY2xhc3MuXG4gICAgLy9cbiAgICAvLyBBbHNvLCB3ZSBzaG91bGQgYmUgYWJsZSB0byBhc3NvY2lhdGUgbXVsdGlwbGUgU1NSQ3Mgd2l0aCBhIE1lZGlhVHJhY2sgYXNcbiAgICAvLyBhIHRyYWNrIG1pZ2h0IGhhdmUgYW4gYXNzb2NpYXRlZCBSVFggYW5kIEZFQyBzb3VyY2VzLlxuXG4gICAgdGhpcy5zaWQgPSBzaWQ7XG4gICAgdGhpcy5zdHJlYW0gPSBkYXRhLnN0cmVhbTtcbiAgICB0aGlzLnBlZXJqaWQgPSBkYXRhLnBlZXJqaWQ7XG4gICAgdGhpcy5zc3JjID0gc3NyYztcbiAgICB0aGlzLnR5cGUgPSAodGhpcy5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPiAwKT9cbiAgICAgICAgTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEUgOiBNZWRpYVN0cmVhbVR5cGUuQVVESU9fVFlQRTtcbiAgICB0aGlzLnZpZGVvVHlwZSA9IG51bGw7XG4gICAgdGhpcy5tdXRlZCA9IGZhbHNlO1xuICAgIHRoaXMuZXZlbnRFbWl0dGVyID0gZXZlbnRFbWl0dGVyO1xufVxuXG5cbk1lZGlhU3RyZWFtLnByb3RvdHlwZS5nZXRPcmlnaW5hbFN0cmVhbSA9IGZ1bmN0aW9uKClcbntcbiAgICByZXR1cm4gdGhpcy5zdHJlYW07XG59O1xuXG5NZWRpYVN0cmVhbS5wcm90b3R5cGUuc2V0TXV0ZSA9IGZ1bmN0aW9uICh2YWx1ZSlcbntcbiAgICB0aGlzLnN0cmVhbS5tdXRlZCA9IHZhbHVlO1xuICAgIHRoaXMubXV0ZWQgPSB2YWx1ZTtcbn07XG5cbk1lZGlhU3RyZWFtLnByb3RvdHlwZS5zZXRWaWRlb1R5cGUgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICBpZih0aGlzLnZpZGVvVHlwZSA9PT0gdmFsdWUpXG4gICAgICAgIHJldHVybjtcbiAgICB0aGlzLnZpZGVvVHlwZSA9IHZhbHVlO1xuICAgIHRoaXMuZXZlbnRFbWl0dGVyLmVtaXQoU3RyZWFtRXZlbnRUeXBlLkVWRU5UX1RZUEVfUkVNT1RFX0NIQU5HRUQsXG4gICAgICAgIHRoaXMucGVlcmppZCk7XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gTWVkaWFTdHJlYW07XG4iLCJ2YXIgRXZlbnRFbWl0dGVyID0gcmVxdWlyZShcImV2ZW50c1wiKTtcbnZhciBSVENVdGlscyA9IHJlcXVpcmUoXCIuL1JUQ1V0aWxzLmpzXCIpO1xudmFyIExvY2FsU3RyZWFtID0gcmVxdWlyZShcIi4vTG9jYWxTdHJlYW0uanNcIik7XG52YXIgRGF0YUNoYW5uZWxzID0gcmVxdWlyZShcIi4vRGF0YUNoYW5uZWxzXCIpO1xudmFyIE1lZGlhU3RyZWFtID0gcmVxdWlyZShcIi4vTWVkaWFTdHJlYW0uanNcIik7XG52YXIgRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXG4gICAgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9kZXNrdG9wc2hhcmluZy9EZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXNcIik7XG52YXIgTWVkaWFTdHJlYW1UeXBlID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL01lZGlhU3RyZWFtVHlwZXNcIik7XG52YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9TdHJlYW1FdmVudFR5cGVzLmpzXCIpO1xudmFyIFJUQ0V2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENFdmVudHMuanNcIik7XG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcbnZhciBVSUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1VJL1VJRXZlbnRzXCIpO1xuXG52YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG52YXIgUlRDID0ge1xuICAgIHJ0Y1V0aWxzOiBudWxsLFxuICAgIGRldmljZXM6IHtcbiAgICAgICAgYXVkaW86IGZhbHNlLFxuICAgICAgICB2aWRlbzogZmFsc2VcbiAgICB9LFxuICAgIGxvY2FsU3RyZWFtczogW10sXG4gICAgcmVtb3RlU3RyZWFtczoge30sXG4gICAgbG9jYWxBdWRpbzogbnVsbCxcbiAgICBsb2NhbFZpZGVvOiBudWxsLFxuICAgIGFkZFN0cmVhbUxpc3RlbmVyOiBmdW5jdGlvbiAobGlzdGVuZXIsIGV2ZW50VHlwZSkge1xuICAgICAgICBldmVudEVtaXR0ZXIub24oZXZlbnRUeXBlLCBsaXN0ZW5lcik7XG4gICAgfSxcbiAgICBhZGRMaXN0ZW5lcjogZnVuY3Rpb24gKHR5cGUsIGxpc3RlbmVyKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbih0eXBlLCBsaXN0ZW5lcik7XG4gICAgfSxcbiAgICByZW1vdmVTdHJlYW1MaXN0ZW5lcjogZnVuY3Rpb24gKGxpc3RlbmVyLCBldmVudFR5cGUpIHtcbiAgICAgICAgaWYoIShldmVudFR5cGUgaW5zdGFuY2VvZiBTdHJlYW1FdmVudFR5cGVzKSlcbiAgICAgICAgICAgIHRocm93IFwiSWxsZWdhbCBhcmd1bWVudFwiO1xuXG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcihldmVudFR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGNyZWF0ZUxvY2FsU3RyZWFtOiBmdW5jdGlvbiAoc3RyZWFtLCB0eXBlLCBjaGFuZ2UsIHZpZGVvVHlwZSkge1xuXG4gICAgICAgIHZhciBsb2NhbFN0cmVhbSA9ICBuZXcgTG9jYWxTdHJlYW0oc3RyZWFtLCB0eXBlLCBldmVudEVtaXR0ZXIsIHZpZGVvVHlwZSk7XG4gICAgICAgIC8vaW4gZmlyZWZveCB3ZSBoYXZlIG9ubHkgb25lIHN0cmVhbSBvYmplY3RcbiAgICAgICAgaWYodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoID09IDAgfHxcbiAgICAgICAgICAgIHRoaXMubG9jYWxTdHJlYW1zWzBdLmdldE9yaWdpbmFsU3RyZWFtKCkgIT0gc3RyZWFtKVxuICAgICAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXMucHVzaChsb2NhbFN0cmVhbSk7XG5cbiAgICAgICAgaWYodHlwZSA9PSBcImF1ZGlvXCIpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMubG9jYWxBdWRpbyA9IGxvY2FsU3RyZWFtO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgdGhpcy5sb2NhbFZpZGVvID0gbG9jYWxTdHJlYW07XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGV2ZW50VHlwZSA9IFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DUkVBVEVEO1xuICAgICAgICBpZihjaGFuZ2UpXG4gICAgICAgICAgICBldmVudFR5cGUgPSBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ0hBTkdFRDtcblxuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChldmVudFR5cGUsIGxvY2FsU3RyZWFtKTtcbiAgICAgICAgcmV0dXJuIGxvY2FsU3RyZWFtO1xuICAgIH0sXG4gICAgcmVtb3ZlTG9jYWxTdHJlYW06IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgZm9yKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTdHJlYW1zLmxlbmd0aDsgaSsrKVxuICAgICAgICB7XG4gICAgICAgICAgICBpZih0aGlzLmxvY2FsU3RyZWFtc1tpXS5nZXRPcmlnaW5hbFN0cmVhbSgpID09PSBzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5sb2NhbFN0cmVhbXNbaV07XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcbiAgICBjcmVhdGVSZW1vdGVTdHJlYW06IGZ1bmN0aW9uIChkYXRhLCBzaWQsIHRoZXNzcmMpIHtcbiAgICAgICAgdmFyIHJlbW90ZVN0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbShkYXRhLCBzaWQsIHRoZXNzcmMsXG4gICAgICAgICAgICB0aGlzLmdldEJyb3dzZXJUeXBlKCksIGV2ZW50RW1pdHRlcik7XG4gICAgICAgIHZhciBqaWQgPSBkYXRhLnBlZXJqaWQgfHwgQVBQLnhtcHAubXlKaWQoKTtcbiAgICAgICAgaWYoIXRoaXMucmVtb3RlU3RyZWFtc1tqaWRdKSB7XG4gICAgICAgICAgICB0aGlzLnJlbW90ZVN0cmVhbXNbamlkXSA9IHt9O1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucmVtb3RlU3RyZWFtc1tqaWRdW3JlbW90ZVN0cmVhbS50eXBlXT0gcmVtb3RlU3RyZWFtO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfUkVNT1RFX0NSRUFURUQsIHJlbW90ZVN0cmVhbSk7XG4gICAgICAgIHJldHVybiByZW1vdGVTdHJlYW07XG4gICAgfSxcbiAgICBnZXRCcm93c2VyVHlwZTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5ydGNVdGlscy5icm93c2VyO1xuICAgIH0sXG4gICAgZ2V0UENDb25zdHJhaW50czogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5ydGNVdGlscy5wY19jb25zdHJhaW50cztcbiAgICB9LFxuICAgIGdldFVzZXJNZWRpYVdpdGhDb25zdHJhaW50czpmdW5jdGlvbih1bSwgc3VjY2Vzc19jYWxsYmFjayxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpbHVyZV9jYWxsYmFjaywgcmVzb2x1dGlvbixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFuZHdpZHRoLCBmcHMsIGRlc2t0b3BTdHJlYW0pXG4gICAge1xuICAgICAgICByZXR1cm4gdGhpcy5ydGNVdGlscy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHModW0sIHN1Y2Nlc3NfY2FsbGJhY2ssXG4gICAgICAgICAgICBmYWlsdXJlX2NhbGxiYWNrLCByZXNvbHV0aW9uLCBiYW5kd2lkdGgsIGZwcywgZGVza3RvcFN0cmVhbSk7XG4gICAgfSxcbiAgICBhdHRhY2hNZWRpYVN0cmVhbTogIGZ1bmN0aW9uIChlbGVtZW50LCBzdHJlYW0pIHtcbiAgICAgICAgdGhpcy5ydGNVdGlscy5hdHRhY2hNZWRpYVN0cmVhbShlbGVtZW50LCBzdHJlYW0pO1xuICAgIH0sXG4gICAgZ2V0U3RyZWFtSUQ6ICBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ0Y1V0aWxzLmdldFN0cmVhbUlEKHN0cmVhbSk7XG4gICAgfSxcbiAgICBnZXRWaWRlb1NyYzogZnVuY3Rpb24gKGVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucnRjVXRpbHMuZ2V0VmlkZW9TcmMoZWxlbWVudCk7XG4gICAgfSxcbiAgICBzZXRWaWRlb1NyYzogZnVuY3Rpb24gKGVsZW1lbnQsIHNyYykge1xuICAgICAgICB0aGlzLnJ0Y1V0aWxzLnNldFZpZGVvU3JjKGVsZW1lbnQsIHNyYyk7XG4gICAgfSxcbiAgICBkaXNwb3NlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKHRoaXMucnRjVXRpbHMpIHtcbiAgICAgICAgICAgIHRoaXMucnRjVXRpbHMgPSBudWxsO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdG9wOiAgZnVuY3Rpb24gKCkge1xuICAgICAgICB0aGlzLmRpc3Bvc2UoKTtcbiAgICB9LFxuICAgIHN0YXJ0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgQVBQLmRlc2t0b3BzaGFyaW5nLmFkZExpc3RlbmVyKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSwgaXNVc2luZ1NjcmVlblN0cmVhbSwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICBzZWxmLmNoYW5nZUxvY2FsVmlkZW8oc3RyZWFtLCBpc1VzaW5nU2NyZWVuU3RyZWFtLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LCBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuTkVXX1NUUkVBTV9DUkVBVEVEKTtcbiAgICAgICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5DSEFOR0VEX1NUUkVBTVMsIGZ1bmN0aW9uIChqaWQsIGNoYW5nZWRTdHJlYW1zKSB7XG4gICAgICAgICAgICBmb3IodmFyIGkgPSAwOyBpIDwgY2hhbmdlZFN0cmVhbXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IGNoYW5nZWRTdHJlYW1zW2ldLnR5cGU7XG4gICAgICAgICAgICAgICAgaWYgKHR5cGUgIT0gXCJhdWRpb1wiKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwZWVyU3RyZWFtcyA9IHNlbGYucmVtb3RlU3RyZWFtc1tqaWRdO1xuICAgICAgICAgICAgICAgICAgICBpZighcGVlclN0cmVhbXMpXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gcGVlclN0cmVhbXNbTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICAgICAgICAgICAgICBpZighdmlkZW9TdHJlYW0pXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9TdHJlYW0uc2V0VmlkZW9UeXBlKGNoYW5nZWRTdHJlYW1zW2ldLnR5cGUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQ0FMTF9JTkNPTUlORywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICAgIERhdGFDaGFubmVscy5pbml0KGV2ZW50LnBlZXJjb25uZWN0aW9uLCBldmVudEVtaXR0ZXIpO1xuICAgICAgICB9KTtcbiAgICAgICAgQVBQLlVJLmFkZExpc3RlbmVyKFVJRXZlbnRzLlNFTEVDVEVEX0VORFBPSU5ULFxuICAgICAgICAgICAgRGF0YUNoYW5uZWxzLmhhbmRsZVNlbGVjdGVkRW5kcG9pbnRFdmVudCk7XG4gICAgICAgIEFQUC5VSS5hZGRMaXN0ZW5lcihVSUV2ZW50cy5QSU5ORURfRU5EUE9JTlQsXG4gICAgICAgICAgICBEYXRhQ2hhbm5lbHMuaGFuZGxlUGlubmVkRW5kcG9pbnRFdmVudCk7XG4gICAgICAgIHRoaXMucnRjVXRpbHMgPSBuZXcgUlRDVXRpbHModGhpcyk7XG4gICAgICAgIHRoaXMucnRjVXRpbHMub2J0YWluQXVkaW9BbmRWaWRlb1Blcm1pc3Npb25zKCk7XG4gICAgfSxcbiAgICBtdXRlUmVtb3RlVmlkZW9TdHJlYW06IGZ1bmN0aW9uIChqaWQsIHZhbHVlKSB7XG4gICAgICAgIHZhciBzdHJlYW07XG5cbiAgICAgICAgaWYodGhpcy5yZW1vdGVTdHJlYW1zW2ppZF0gJiZcbiAgICAgICAgICAgIHRoaXMucmVtb3RlU3RyZWFtc1tqaWRdW01lZGlhU3RyZWFtVHlwZS5WSURFT19UWVBFXSlcbiAgICAgICAge1xuICAgICAgICAgICAgc3RyZWFtID0gdGhpcy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoIXN0cmVhbSlcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuXG4gICAgICAgIGlmICh2YWx1ZSAhPSBzdHJlYW0ubXV0ZWQpIHtcbiAgICAgICAgICAgIHN0cmVhbS5zZXRNdXRlKHZhbHVlKTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9LFxuICAgIHN3aXRjaFZpZGVvU3RyZWFtczogZnVuY3Rpb24gKG5ld19zdHJlYW0pIHtcbiAgICAgICAgdGhpcy5sb2NhbFZpZGVvLnN0cmVhbSA9IG5ld19zdHJlYW07XG5cbiAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcblxuICAgICAgICAvL2luIGZpcmVmb3ggd2UgaGF2ZSBvbmx5IG9uZSBzdHJlYW0gb2JqZWN0XG4gICAgICAgIGlmICh0aGlzLmxvY2FsQXVkaW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSAhPSBuZXdfc3RyZWFtKVxuICAgICAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXMucHVzaCh0aGlzLmxvY2FsQXVkaW8pO1xuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtcy5wdXNoKHRoaXMubG9jYWxWaWRlbyk7XG4gICAgfSxcbiAgICBjaGFuZ2VMb2NhbFZpZGVvOiBmdW5jdGlvbiAoc3RyZWFtLCBpc1VzaW5nU2NyZWVuU3RyZWFtLCBjYWxsYmFjaykge1xuICAgICAgICB2YXIgb2xkU3RyZWFtID0gdGhpcy5sb2NhbFZpZGVvLmdldE9yaWdpbmFsU3RyZWFtKCk7XG4gICAgICAgIHZhciB0eXBlID0gKGlzVXNpbmdTY3JlZW5TdHJlYW0/IFwic2NyZWVuXCIgOiBcInZpZGVvXCIpO1xuICAgICAgICB2YXIgbG9jYWxDYWxsYmFjayA9IGNhbGxiYWNrO1xuICAgICAgICBpZih0aGlzLmxvY2FsVmlkZW8uaXNNdXRlZCgpICYmIHRoaXMubG9jYWxWaWRlby52aWRlb1R5cGUgIT09IHR5cGUpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGxvY2FsQ2FsbGJhY2sgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBBUFAueG1wcC5zZXRWaWRlb011dGUoZmFsc2UsIEFQUC5VSS5zZXRWaWRlb011dGVCdXR0b25zU3RhdGUpO1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIHZhciB2aWRlb1N0cmVhbSA9IHRoaXMucnRjVXRpbHMuY3JlYXRlVmlkZW9TdHJlYW0oc3RyZWFtKTtcbiAgICAgICAgdGhpcy5sb2NhbFZpZGVvID0gdGhpcy5jcmVhdGVMb2NhbFN0cmVhbSh2aWRlb1N0cmVhbSwgXCJ2aWRlb1wiLCB0cnVlLCB0eXBlKTtcbiAgICAgICAgLy8gU3RvcCB0aGUgc3RyZWFtIHRvIHRyaWdnZXIgb25lbmRlZCBldmVudCBmb3Igb2xkIHN0cmVhbVxuICAgICAgICBvbGRTdHJlYW0uc3RvcCgpO1xuICAgICAgICBBUFAueG1wcC5zd2l0Y2hTdHJlYW1zKHZpZGVvU3RyZWFtLCBvbGRTdHJlYW0sbG9jYWxDYWxsYmFjayk7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgdmlkZW8gaWRlbnRpZmllZCBieSBnaXZlbiBzcmMgaXMgZGVza3RvcCBzdHJlYW0uXG4gICAgICogQHBhcmFtIHZpZGVvU3JjIGVnLlxuICAgICAqIGJsb2I6aHR0cHMlM0EvL3Bhd2VsLmppdHNpLm5ldC85YTQ2ZTBiZC0xMzFlLTRkMTgtOWMxNC1hOTI2NGU4ZGIzOTVcbiAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgKi9cbiAgICBpc1ZpZGVvU3JjRGVza3RvcDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZighamlkKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB2YXIgaXNEZXNrdG9wID0gZmFsc2U7XG4gICAgICAgIHZhciBzdHJlYW0gPSBudWxsO1xuICAgICAgICBpZiAoQVBQLnhtcHAubXlKaWQoKSA9PT0gamlkKSB7XG4gICAgICAgICAgICAvLyBsb2NhbCB2aWRlb1xuICAgICAgICAgICAgc3RyZWFtID0gdGhpcy5sb2NhbFZpZGVvO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdmFyIHBlZXJTdHJlYW1zID0gdGhpcy5yZW1vdGVTdHJlYW1zW2ppZF07XG4gICAgICAgICAgICBpZighcGVlclN0cmVhbXMpXG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgc3RyZWFtID0gcGVlclN0cmVhbXNbTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoc3RyZWFtKVxuICAgICAgICAgICAgaXNEZXNrdG9wID0gKHN0cmVhbS52aWRlb1R5cGUgPT09IFwic2NyZWVuXCIpO1xuXG4gICAgICAgIHJldHVybiBpc0Rlc2t0b3A7XG4gICAgfSxcbiAgICBzZXRWaWRlb011dGU6IGZ1bmN0aW9uKG11dGUsIGNhbGxiYWNrLCBvcHRpb25zKSB7XG4gICAgICAgIGlmKCF0aGlzLmxvY2FsVmlkZW8pXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgaWYgKG11dGUgPT0gQVBQLlJUQy5sb2NhbFZpZGVvLmlzTXV0ZWQoKSlcbiAgICAgICAge1xuICAgICAgICAgICAgQVBQLnhtcHAuc2VuZFZpZGVvSW5mb1ByZXNlbmNlKG11dGUpO1xuICAgICAgICAgICAgaWYoY2FsbGJhY2spXG4gICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIEFQUC5SVEMubG9jYWxWaWRlby5zZXRNdXRlKCFtdXRlKTtcbiAgICAgICAgICAgIEFQUC54bXBwLnNldFZpZGVvTXV0ZShcbiAgICAgICAgICAgICAgICBtdXRlLFxuICAgICAgICAgICAgICAgIGNhbGxiYWNrLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZXREZXZpY2VBdmFpbGFiaWxpdHk6IGZ1bmN0aW9uIChkZXZpY2VzKSB7XG4gICAgICAgIGlmKCFkZXZpY2VzKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBpZihkZXZpY2VzLmF1ZGlvID09PSB0cnVlIHx8IGRldmljZXMuYXVkaW8gPT09IGZhbHNlKVxuICAgICAgICAgICAgdGhpcy5kZXZpY2VzLmF1ZGlvID0gZGV2aWNlcy5hdWRpbztcbiAgICAgICAgaWYoZGV2aWNlcy52aWRlbyA9PT0gdHJ1ZSB8fCBkZXZpY2VzLnZpZGVvID09PSBmYWxzZSlcbiAgICAgICAgICAgIHRoaXMuZGV2aWNlcy52aWRlbyA9IGRldmljZXMudmlkZW87XG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFJUQ0V2ZW50cy5BVkFJTEFCTEVfREVWSUNFU19DSEFOR0VELCB0aGlzLmRldmljZXMpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gUlRDO1xuIiwidmFyIFJUQ0Jyb3dzZXJUeXBlID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1JUQ0Jyb3dzZXJUeXBlLmpzXCIpO1xudmFyIFJlc29sdXRpb25zID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1Jlc29sdXRpb25zXCIpO1xuXG52YXIgY3VycmVudFJlc29sdXRpb24gPSBudWxsO1xuXG5mdW5jdGlvbiBnZXRQcmV2aW91c1Jlc29sdXRpb24ocmVzb2x1dGlvbikge1xuICAgIGlmKCFSZXNvbHV0aW9uc1tyZXNvbHV0aW9uXSlcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgdmFyIG9yZGVyID0gUmVzb2x1dGlvbnNbcmVzb2x1dGlvbl0ub3JkZXI7XG4gICAgdmFyIHJlcyA9IG51bGw7XG4gICAgdmFyIHJlc05hbWUgPSBudWxsO1xuICAgIGZvcih2YXIgaSBpbiBSZXNvbHV0aW9ucylcbiAgICB7XG4gICAgICAgIHZhciB0bXAgPSBSZXNvbHV0aW9uc1tpXTtcbiAgICAgICAgaWYocmVzID09IG51bGwgfHwgKHJlcy5vcmRlciA8IHRtcC5vcmRlciAmJiB0bXAub3JkZXIgPCBvcmRlcikpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc05hbWUgPSBpO1xuICAgICAgICAgICAgcmVzID0gdG1wO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXNOYW1lO1xufVxuXG5mdW5jdGlvbiBzZXRSZXNvbHV0aW9uQ29uc3RyYWludHMoY29uc3RyYWludHMsIHJlc29sdXRpb24sIGlzQW5kcm9pZClcbntcbiAgICBpZiAocmVzb2x1dGlvbiAmJiAhY29uc3RyYWludHMudmlkZW8gfHwgaXNBbmRyb2lkKSB7XG4gICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0geyBtYW5kYXRvcnk6IHt9LCBvcHRpb25hbDogW10gfTsvLyBzYW1lIGJlaGF2aW91ciBhcyB0cnVlXG4gICAgfVxuXG4gICAgaWYoUmVzb2x1dGlvbnNbcmVzb2x1dGlvbl0pXG4gICAge1xuICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluV2lkdGggPSBSZXNvbHV0aW9uc1tyZXNvbHV0aW9uXS53aWR0aDtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IFJlc29sdXRpb25zW3Jlc29sdXRpb25dLmhlaWdodDtcbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgaWYgKGlzQW5kcm9pZCkge1xuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbldpZHRoID0gMzIwO1xuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IDI0MDtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5tYXhGcmFtZVJhdGUgPSAxNTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmIChjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluV2lkdGgpXG4gICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5tYXhXaWR0aCA9IGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aDtcbiAgICBpZiAoY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodClcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1heEhlaWdodCA9IGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5IZWlnaHQ7XG59XG5cbmZ1bmN0aW9uIGdldENvbnN0cmFpbnRzKHVtLCByZXNvbHV0aW9uLCBiYW5kd2lkdGgsIGZwcywgZGVza3RvcFN0cmVhbSwgaXNBbmRyb2lkKVxue1xuICAgIHZhciBjb25zdHJhaW50cyA9IHthdWRpbzogZmFsc2UsIHZpZGVvOiBmYWxzZX07XG5cbiAgICBpZiAodW0uaW5kZXhPZigndmlkZW8nKSA+PSAwKSB7XG4gICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0geyBtYW5kYXRvcnk6IHt9LCBvcHRpb25hbDogW10gfTsvLyBzYW1lIGJlaGF2aW91ciBhcyB0cnVlXG4gICAgfVxuICAgIGlmICh1bS5pbmRleE9mKCdhdWRpbycpID49IDApIHtcbiAgICAgICAgY29uc3RyYWludHMuYXVkaW8gPSB7IG1hbmRhdG9yeToge30sIG9wdGlvbmFsOiBbXX07Ly8gc2FtZSBiZWhhdmlvdXIgYXMgdHJ1ZVxuICAgIH1cbiAgICBpZiAodW0uaW5kZXhPZignc2NyZWVuJykgPj0gMCkge1xuICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IHtcbiAgICAgICAgICAgIG1hbmRhdG9yeToge1xuICAgICAgICAgICAgICAgIGNocm9tZU1lZGlhU291cmNlOiBcInNjcmVlblwiLFxuICAgICAgICAgICAgICAgIGdvb2dMZWFreUJ1Y2tldDogdHJ1ZSxcbiAgICAgICAgICAgICAgICBtYXhXaWR0aDogd2luZG93LnNjcmVlbi53aWR0aCxcbiAgICAgICAgICAgICAgICBtYXhIZWlnaHQ6IHdpbmRvdy5zY3JlZW4uaGVpZ2h0LFxuICAgICAgICAgICAgICAgIG1heEZyYW1lUmF0ZTogM1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIG9wdGlvbmFsOiBbXVxuICAgICAgICB9O1xuICAgIH1cbiAgICBpZiAodW0uaW5kZXhPZignZGVza3RvcCcpID49IDApIHtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8gPSB7XG4gICAgICAgICAgICBtYW5kYXRvcnk6IHtcbiAgICAgICAgICAgICAgICBjaHJvbWVNZWRpYVNvdXJjZTogXCJkZXNrdG9wXCIsXG4gICAgICAgICAgICAgICAgY2hyb21lTWVkaWFTb3VyY2VJZDogZGVza3RvcFN0cmVhbSxcbiAgICAgICAgICAgICAgICBnb29nTGVha3lCdWNrZXQ6IHRydWUsXG4gICAgICAgICAgICAgICAgbWF4V2lkdGg6IHdpbmRvdy5zY3JlZW4ud2lkdGgsXG4gICAgICAgICAgICAgICAgbWF4SGVpZ2h0OiB3aW5kb3cuc2NyZWVuLmhlaWdodCxcbiAgICAgICAgICAgICAgICBtYXhGcmFtZVJhdGU6IDNcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvcHRpb25hbDogW11cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBpZiAoY29uc3RyYWludHMuYXVkaW8pIHtcbiAgICAgICAgLy8gaWYgaXQgaXMgZ29vZCBlbm91Z2ggZm9yIGhhbmdvdXRzLi4uXG4gICAgICAgIGNvbnN0cmFpbnRzLmF1ZGlvLm9wdGlvbmFsLnB1c2goXG4gICAgICAgICAgICB7Z29vZ0VjaG9DYW5jZWxsYXRpb246IHRydWV9LFxuICAgICAgICAgICAge2dvb2dBdXRvR2FpbkNvbnRyb2w6IHRydWV9LFxuICAgICAgICAgICAge2dvb2dOb2lzZVN1cHJlc3Npb246IHRydWV9LFxuICAgICAgICAgICAge2dvb2dIaWdocGFzc0ZpbHRlcjogdHJ1ZX0sXG4gICAgICAgICAgICB7Z29vZ05vaXNlc3VwcHJlc3Npb24yOiB0cnVlfSxcbiAgICAgICAgICAgIHtnb29nRWNob0NhbmNlbGxhdGlvbjI6IHRydWV9LFxuICAgICAgICAgICAge2dvb2dBdXRvR2FpbkNvbnRyb2wyOiB0cnVlfVxuICAgICAgICApO1xuICAgIH1cbiAgICBpZiAoY29uc3RyYWludHMudmlkZW8pIHtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ub3B0aW9uYWwucHVzaChcbiAgICAgICAgICAgIHtnb29nTm9pc2VSZWR1Y3Rpb246IGZhbHNlfSAvLyBjaHJvbWUgMzcgd29ya2Fyb3VuZCBmb3IgaXNzdWUgMzgwNywgcmVlbmFibGUgaW4gTTM4XG4gICAgICAgICk7XG4gICAgICAgIGlmICh1bS5pbmRleE9mKCd2aWRlbycpID49IDApIHtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm9wdGlvbmFsLnB1c2goXG4gICAgICAgICAgICAgICAge2dvb2dMZWFreUJ1Y2tldDogdHJ1ZX1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodW0uaW5kZXhPZigndmlkZW8nKSA+PSAwKSB7XG4gICAgICAgIHNldFJlc29sdXRpb25Db25zdHJhaW50cyhjb25zdHJhaW50cywgcmVzb2x1dGlvbiwgaXNBbmRyb2lkKTtcbiAgICB9XG5cbiAgICBpZiAoYmFuZHdpZHRoKSB7IC8vIGRvZXNuJ3Qgd29yayBjdXJyZW50bHksIHNlZSB3ZWJydGMgaXNzdWUgMTg0NlxuICAgICAgICBpZiAoIWNvbnN0cmFpbnRzLnZpZGVvKSBjb25zdHJhaW50cy52aWRlbyA9IHttYW5kYXRvcnk6IHt9LCBvcHRpb25hbDogW119Oy8vc2FtZSBiZWhhdmlvdXIgYXMgdHJ1ZVxuICAgICAgICBjb25zdHJhaW50cy52aWRlby5vcHRpb25hbC5wdXNoKHtiYW5kd2lkdGg6IGJhbmR3aWR0aH0pO1xuICAgIH1cbiAgICBpZiAoZnBzKSB7IC8vIGZvciBzb21lIGNhbWVyYXMgaXQgbWlnaHQgYmUgbmVjZXNzYXJ5IHRvIHJlcXVlc3QgMzBmcHNcbiAgICAgICAgLy8gc28gdGhleSBjaG9vc2UgMzBmcHMgbWpwZyBvdmVyIDEwZnBzIHl1eTJcbiAgICAgICAgaWYgKCFjb25zdHJhaW50cy52aWRlbykgY29uc3RyYWludHMudmlkZW8gPSB7bWFuZGF0b3J5OiB7fSwgb3B0aW9uYWw6IFtdfTsvLyBzYW1lIGJlaGF2aW91ciBhcyB0cnVlO1xuICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluRnJhbWVSYXRlID0gZnBzO1xuICAgIH1cblxuICAgIHJldHVybiBjb25zdHJhaW50cztcbn1cblxuXG5mdW5jdGlvbiBSVENVdGlscyhSVENTZXJ2aWNlKVxue1xuICAgIHRoaXMuc2VydmljZSA9IFJUQ1NlcnZpY2U7XG4gICAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1RoaXMgYXBwZWFycyB0byBiZSBGaXJlZm94Jyk7XG4gICAgICAgIHZhciB2ZXJzaW9uID0gcGFyc2VJbnQobmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvRmlyZWZveFxcLyhbMC05XSspXFwuLylbMV0sIDEwKTtcbiAgICAgICAgaWYgKHZlcnNpb24gPj0gMzkpIHtcbiAgICAgICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSBtb3pSVENQZWVyQ29ubmVjdGlvbjtcbiAgICAgICAgICAgIHRoaXMuYnJvd3NlciA9IFJUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0ZJUkVGT1g7XG4gICAgICAgICAgICB0aGlzLmdldFVzZXJNZWRpYSA9IG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEuYmluZChuYXZpZ2F0b3IpO1xuICAgICAgICAgICAgdGhpcy5wY19jb25zdHJhaW50cyA9IHt9O1xuICAgICAgICAgICAgdGhpcy5hdHRhY2hNZWRpYVN0cmVhbSA9ICBmdW5jdGlvbiAoZWxlbWVudCwgc3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgLy8gIHNyY09iamVjdCBpcyBiZWluZyBzdGFuZGFyZGl6ZWQgYW5kIEZGIHdpbGwgZXZlbnR1YWxseVxuICAgICAgICAgICAgICAgIC8vICBzdXBwb3J0IHRoYXQgdW5wcmVmaXhlZC4gRkYgYWxzbyBzdXBwb3J0cyB0aGVcbiAgICAgICAgICAgICAgICAvLyAgXCJlbGVtZW50LnNyYyA9IFVSTC5jcmVhdGVPYmplY3RVUkwoLi4uKVwiIGNvbWJvLCBidXQgdGhhdFxuICAgICAgICAgICAgICAgIC8vICB3aWxsIGJlIGRlcHJlY2F0ZWQgaW4gZmF2b3VyIG9mIHNyY09iamVjdC5cbiAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ3JvdXBzLmdvb2dsZS5jb20vZm9ydW0vIyF0b3BpYy9tb3ppbGxhLmRldi5tZWRpYS9wS09paW9Yb25KZ1xuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS93ZWJydGMvc2FtcGxlcy9pc3N1ZXMvMzAyXG4gICAgICAgICAgICAgICAgZWxlbWVudFswXS5tb3pTcmNPYmplY3QgPSBzdHJlYW07XG4gICAgICAgICAgICAgICAgZWxlbWVudFswXS5wbGF5KCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5nZXRTdHJlYW1JRCA9ICBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRyYWNrcyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICAgICAgICAgIGlmKCF0cmFja3MgfHwgdHJhY2tzLmxlbmd0aCA9PSAwKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdHJhY2tzID0gc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiB0cmFja3NbMF0uaWQucmVwbGFjZSgvW1xceyxcXH1dL2csXCJcIik7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5nZXRWaWRlb1NyYyA9IGZ1bmN0aW9uIChlbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGVsZW1lbnQubW96U3JjT2JqZWN0O1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuc2V0VmlkZW9TcmMgPSBmdW5jdGlvbiAoZWxlbWVudCwgc3JjKSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudC5tb3pTcmNPYmplY3QgPSBzcmM7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uID0gbW96UlRDU2Vzc2lvbkRlc2NyaXB0aW9uO1xuICAgICAgICAgICAgUlRDSWNlQ2FuZGlkYXRlID0gbW96UlRDSWNlQ2FuZGlkYXRlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLmhyZWYgPSAndW5zdXBwb3J0ZWRfYnJvd3Nlci5odG1sJztcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgfSBlbHNlIGlmIChuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdUaGlzIGFwcGVhcnMgdG8gYmUgQ2hyb21lJyk7XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSB3ZWJraXRSVENQZWVyQ29ubmVjdGlvbjtcbiAgICAgICAgdGhpcy5icm93c2VyID0gUlRDQnJvd3NlclR5cGUuUlRDX0JST1dTRVJfQ0hST01FO1xuICAgICAgICB0aGlzLmdldFVzZXJNZWRpYSA9IG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEuYmluZChuYXZpZ2F0b3IpO1xuICAgICAgICB0aGlzLmF0dGFjaE1lZGlhU3RyZWFtID0gZnVuY3Rpb24gKGVsZW1lbnQsIHN0cmVhbSkge1xuICAgICAgICAgICAgZWxlbWVudC5hdHRyKCdzcmMnLCB3ZWJraXRVUkwuY3JlYXRlT2JqZWN0VVJMKHN0cmVhbSkpO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmdldFN0cmVhbUlEID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgLy8gc3RyZWFtcyBmcm9tIEZGIGVuZHBvaW50cyBoYXZlIHRoZSBjaGFyYWN0ZXJzICd7JyBhbmQgJ30nXG4gICAgICAgICAgICAvLyB0aGF0IG1ha2UgalF1ZXJ5IGNob2tlLlxuICAgICAgICAgICAgcmV0dXJuIHN0cmVhbS5pZC5yZXBsYWNlKC9bXFx7LFxcfV0vZyxcIlwiKTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXRWaWRlb1NyYyA9IGZ1bmN0aW9uIChlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm4gZWxlbWVudC5nZXRBdHRyaWJ1dGUoXCJzcmNcIik7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2V0VmlkZW9TcmMgPSBmdW5jdGlvbiAoZWxlbWVudCwgc3JjKSB7XG4gICAgICAgICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShcInNyY1wiLCBzcmMpO1xuICAgICAgICB9O1xuICAgICAgICAvLyBEVExTIHNob3VsZCBub3cgYmUgZW5hYmxlZCBieSBkZWZhdWx0IGJ1dC4uXG4gICAgICAgIHRoaXMucGNfY29uc3RyYWludHMgPSB7J29wdGlvbmFsJzogW3snRHRsc1NydHBLZXlBZ3JlZW1lbnQnOiAndHJ1ZSd9XX07XG4gICAgICAgIGlmIChuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ0FuZHJvaWQnKSAhPSAtMSkge1xuICAgICAgICAgICAgdGhpcy5wY19jb25zdHJhaW50cyA9IHt9OyAvLyBkaXNhYmxlIERUTFMgb24gQW5kcm9pZFxuICAgICAgICB9XG4gICAgICAgIGlmICghd2Via2l0TWVkaWFTdHJlYW0ucHJvdG90eXBlLmdldFZpZGVvVHJhY2tzKSB7XG4gICAgICAgICAgICB3ZWJraXRNZWRpYVN0cmVhbS5wcm90b3R5cGUuZ2V0VmlkZW9UcmFja3MgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMudmlkZW9UcmFja3M7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIGlmICghd2Via2l0TWVkaWFTdHJlYW0ucHJvdG90eXBlLmdldEF1ZGlvVHJhY2tzKSB7XG4gICAgICAgICAgICB3ZWJraXRNZWRpYVN0cmVhbS5wcm90b3R5cGUuZ2V0QXVkaW9UcmFja3MgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuYXVkaW9UcmFja3M7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHRyeSB7IGNvbnNvbGUubG9nKCdCcm93c2VyIGRvZXMgbm90IGFwcGVhciB0byBiZSBXZWJSVEMtY2FwYWJsZScpOyB9IGNhdGNoIChlKSB7IH1cblxuICAgICAgICB3aW5kb3cubG9jYXRpb24uaHJlZiA9ICd1bnN1cHBvcnRlZF9icm93c2VyLmh0bWwnO1xuICAgICAgICByZXR1cm47XG4gICAgfVxufVxuXG5cblJUQ1V0aWxzLnByb3RvdHlwZS5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMgPSBmdW5jdGlvbihcbiAgICB1bSwgc3VjY2Vzc19jYWxsYmFjaywgZmFpbHVyZV9jYWxsYmFjaywgcmVzb2x1dGlvbixiYW5kd2lkdGgsIGZwcyxcbiAgICBkZXNrdG9wU3RyZWFtKVxue1xuICAgIGN1cnJlbnRSZXNvbHV0aW9uID0gcmVzb2x1dGlvbjtcbiAgICAvLyBDaGVjayBpZiB3ZSBhcmUgcnVubmluZyBvbiBBbmRyb2lkIGRldmljZVxuICAgIHZhciBpc0FuZHJvaWQgPSBuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ0FuZHJvaWQnKSAhPSAtMTtcblxuICAgIHZhciBjb25zdHJhaW50cyA9IGdldENvbnN0cmFpbnRzKFxuICAgICAgICB1bSwgcmVzb2x1dGlvbiwgYmFuZHdpZHRoLCBmcHMsIGRlc2t0b3BTdHJlYW0sIGlzQW5kcm9pZCk7XG5cbiAgICB2YXIgaXNGRiA9IG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKS5pbmRleE9mKCdmaXJlZm94JykgPiAtMTtcblxuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIHRyeSB7XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlU2ltdWxjYXN0XG4gICAgICAgICAgICAmJiBjb25zdHJhaW50cy52aWRlb1xuICAgICAgICAgICAgJiYgY29uc3RyYWludHMudmlkZW8uY2hyb21lTWVkaWFTb3VyY2UgIT09ICdzY3JlZW4nXG4gICAgICAgICAgICAmJiBjb25zdHJhaW50cy52aWRlby5jaHJvbWVNZWRpYVNvdXJjZSAhPT0gJ2Rlc2t0b3AnXG4gICAgICAgICAgICAmJiAhaXNBbmRyb2lkXG5cbiAgICAgICAgICAgIC8vIFdlIGN1cnJlbnRseSBkbyBub3Qgc3VwcG9ydCBGRiwgYXMgaXQgZG9lc24ndCBoYXZlIG11bHRpc3RyZWFtIHN1cHBvcnQuXG4gICAgICAgICAgICAmJiAhaXNGRikge1xuICAgICAgICAgICAgQVBQLnNpbXVsY2FzdC5nZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ29uVXNlck1lZGlhU3VjY2VzcycpO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnNldEF2YWlsYWJsZURldmljZXModW0sIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKHN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdGYWlsZWQgdG8gZ2V0IGFjY2VzcyB0byBsb2NhbCBtZWRpYS4gRXJyb3IgJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnNldEF2YWlsYWJsZURldmljZXModW0sIGZhbHNlKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGZhaWx1cmVfY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhaWx1cmVfY2FsbGJhY2soZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgIHRoaXMuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ29uVXNlck1lZGlhU3VjY2VzcycpO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnNldEF2YWlsYWJsZURldmljZXModW0sIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKHN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5zZXRBdmFpbGFibGVEZXZpY2VzKHVtLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignRmFpbGVkIHRvIGdldCBhY2Nlc3MgdG8gbG9jYWwgbWVkaWEuIEVycm9yICcsXG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvciwgY29uc3RyYWludHMpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZmFpbHVyZV9jYWxsYmFjaykge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmFpbHVyZV9jYWxsYmFjayhlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdHVU0gZmFpbGVkOiAnLCBlKTtcbiAgICAgICAgaWYoZmFpbHVyZV9jYWxsYmFjaykge1xuICAgICAgICAgICAgZmFpbHVyZV9jYWxsYmFjayhlKTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cblJUQ1V0aWxzLnByb3RvdHlwZS5zZXRBdmFpbGFibGVEZXZpY2VzID0gZnVuY3Rpb24gKHVtLCBhdmFpbGFibGUpIHtcbiAgICB2YXIgZGV2aWNlcyA9IHt9O1xuICAgIGlmKHVtLmluZGV4T2YoXCJ2aWRlb1wiKSAhPSAtMSlcbiAgICB7XG4gICAgICAgIGRldmljZXMudmlkZW8gPSBhdmFpbGFibGU7XG4gICAgfVxuICAgIGlmKHVtLmluZGV4T2YoXCJhdWRpb1wiKSAhPSAtMSlcbiAgICB7XG4gICAgICAgIGRldmljZXMuYXVkaW8gPSBhdmFpbGFibGU7XG4gICAgfVxuICAgIHRoaXMuc2VydmljZS5zZXREZXZpY2VBdmFpbGFiaWxpdHkoZGV2aWNlcyk7XG59XG5cbi8qKlxuICogV2UgYXNrIGZvciBhdWRpbyBhbmQgdmlkZW8gY29tYmluZWQgc3RyZWFtIGluIG9yZGVyIHRvIGdldCBwZXJtaXNzaW9ucyBhbmRcbiAqIG5vdCB0byBhc2sgdHdpY2UuXG4gKi9cblJUQ1V0aWxzLnByb3RvdHlwZS5vYnRhaW5BdWRpb0FuZFZpZGVvUGVybWlzc2lvbnMgPSBmdW5jdGlvbihkZXZpY2VzLCBjYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBHZXQgQVZcblxuICAgIGlmKCFkZXZpY2VzKVxuICAgICAgICBkZXZpY2VzID0gWydhdWRpbycsICd2aWRlbyddO1xuXG4gICAgdGhpcy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMoXG4gICAgICAgIGRldmljZXMsXG4gICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgIGlmKGNhbGxiYWNrKVxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKHN0cmVhbSk7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgc2VsZi5zdWNjZXNzQ2FsbGJhY2soc3RyZWFtKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBzZWxmLmVycm9yQ2FsbGJhY2soZXJyb3IpO1xuICAgICAgICB9LFxuICAgICAgICBjb25maWcucmVzb2x1dGlvbiB8fCAnMzYwJyk7XG59XG5cblJUQ1V0aWxzLnByb3RvdHlwZS5zdWNjZXNzQ2FsbGJhY2sgPSBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgaWYoc3RyZWFtKVxuICAgICAgICBjb25zb2xlLmxvZygnZ290Jywgc3RyZWFtLCBzdHJlYW0uZ2V0QXVkaW9UcmFja3MoKS5sZW5ndGgsXG4gICAgICAgICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGgpO1xuICAgIHRoaXMuaGFuZGxlTG9jYWxTdHJlYW0oc3RyZWFtKTtcbn07XG5cblJUQ1V0aWxzLnByb3RvdHlwZS5lcnJvckNhbGxiYWNrID0gZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGNvbnNvbGUuZXJyb3IoJ2ZhaWxlZCB0byBvYnRhaW4gYXVkaW8vdmlkZW8gc3RyZWFtIC0gdHJ5aW5nIGF1ZGlvIG9ubHknLCBlcnJvcik7XG4gICAgdmFyIHJlc29sdXRpb24gPSBnZXRQcmV2aW91c1Jlc29sdXRpb24oY3VycmVudFJlc29sdXRpb24pO1xuICAgIGlmKHR5cGVvZiBlcnJvciA9PSBcIm9iamVjdFwiICYmIGVycm9yLmNvbnN0cmFpbnROYW1lICYmIGVycm9yLm5hbWVcbiAgICAgICAgJiYgKGVycm9yLm5hbWUgPT0gXCJDb25zdHJhaW50Tm90U2F0aXNmaWVkRXJyb3JcIiB8fFxuICAgICAgICAgICAgZXJyb3IubmFtZSA9PSBcIk92ZXJjb25zdHJhaW5lZEVycm9yXCIpICYmXG4gICAgICAgIChlcnJvci5jb25zdHJhaW50TmFtZSA9PSBcIm1pbldpZHRoXCIgfHwgZXJyb3IuY29uc3RyYWludE5hbWUgPT0gXCJtYXhXaWR0aFwiIHx8XG4gICAgICAgICAgICBlcnJvci5jb25zdHJhaW50TmFtZSA9PSBcIm1pbkhlaWdodFwiIHx8IGVycm9yLmNvbnN0cmFpbnROYW1lID09IFwibWF4SGVpZ2h0XCIpXG4gICAgICAgICYmIHJlc29sdXRpb24gIT0gbnVsbClcbiAgICB7XG4gICAgICAgIHNlbGYuZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzKFsnYXVkaW8nLCAndmlkZW8nXSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2VsZi5zdWNjZXNzQ2FsbGJhY2soc3RyZWFtKTtcbiAgICAgICAgICAgIH0sIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIHJldHVybiBzZWxmLmVycm9yQ2FsbGJhY2soZXJyb3IpO1xuICAgICAgICAgICAgfSwgcmVzb2x1dGlvbik7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHNlbGYuZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzKFxuICAgICAgICAgICAgWydhdWRpbyddLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBzZWxmLnN1Y2Nlc3NDYWxsYmFjayhzdHJlYW0pO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2ZhaWxlZCB0byBvYnRhaW4gYXVkaW8vdmlkZW8gc3RyZWFtIC0gc3RvcCcsXG4gICAgICAgICAgICAgICAgICAgIGVycm9yKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2VsZi5zdWNjZXNzQ2FsbGJhY2sobnVsbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfVxuXG59XG5cblJUQ1V0aWxzLnByb3RvdHlwZS5oYW5kbGVMb2NhbFN0cmVhbSA9IGZ1bmN0aW9uKHN0cmVhbSlcbntcbiAgICBpZih3aW5kb3cud2Via2l0TWVkaWFTdHJlYW0pXG4gICAge1xuICAgICAgICB2YXIgYXVkaW9TdHJlYW0gPSBuZXcgd2Via2l0TWVkaWFTdHJlYW0oKTtcbiAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gbmV3IHdlYmtpdE1lZGlhU3RyZWFtKCk7XG4gICAgICAgIGlmKHN0cmVhbSkge1xuICAgICAgICAgICAgdmFyIGF1ZGlvVHJhY2tzID0gc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCk7XG5cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXVkaW9UcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBhdWRpb1N0cmVhbS5hZGRUcmFjayhhdWRpb1RyYWNrc1tpXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciB2aWRlb1RyYWNrcyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuXG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdmlkZW9UcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2aWRlb1N0cmVhbS5hZGRUcmFjayh2aWRlb1RyYWNrc1tpXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNlcnZpY2UuY3JlYXRlTG9jYWxTdHJlYW0oYXVkaW9TdHJlYW0sIFwiYXVkaW9cIik7XG5cbiAgICAgICAgdGhpcy5zZXJ2aWNlLmNyZWF0ZUxvY2FsU3RyZWFtKHZpZGVvU3RyZWFtLCBcInZpZGVvXCIpO1xuICAgIH1cbiAgICBlbHNlXG4gICAgey8vZmlyZWZveFxuICAgICAgICB0aGlzLnNlcnZpY2UuY3JlYXRlTG9jYWxTdHJlYW0oc3RyZWFtLCBcInN0cmVhbVwiKTtcbiAgICB9XG5cbn07XG5cblJUQ1V0aWxzLnByb3RvdHlwZS5jcmVhdGVWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uKHN0cmVhbSlcbntcbiAgICB2YXIgdmlkZW9TdHJlYW0gPSBudWxsO1xuICAgIGlmKHdpbmRvdy53ZWJraXRNZWRpYVN0cmVhbSlcbiAgICB7XG4gICAgICAgIHZpZGVvU3RyZWFtID0gbmV3IHdlYmtpdE1lZGlhU3RyZWFtKCk7XG4gICAgICAgIGlmKHN0cmVhbSlcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIHZpZGVvVHJhY2tzID0gc3RyZWFtLmdldFZpZGVvVHJhY2tzKCk7XG5cbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCB2aWRlb1RyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZpZGVvU3RyZWFtLmFkZFRyYWNrKHZpZGVvVHJhY2tzW2ldKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgfVxuICAgIGVsc2VcbiAgICAgICAgdmlkZW9TdHJlYW0gPSBzdHJlYW07XG5cbiAgICByZXR1cm4gdmlkZW9TdHJlYW07XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJUQ1V0aWxzO1xuIiwidmFyIFVJID0ge307XG5cbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0LmpzXCIpO1xudmFyIEF1ZGlvTGV2ZWxzID0gcmVxdWlyZShcIi4vYXVkaW9fbGV2ZWxzL0F1ZGlvTGV2ZWxzLmpzXCIpO1xudmFyIFByZXppID0gcmVxdWlyZShcIi4vcHJlemkvUHJlemkuanNcIik7XG52YXIgRXRoZXJwYWQgPSByZXF1aXJlKFwiLi9ldGhlcnBhZC9FdGhlcnBhZC5qc1wiKTtcbnZhciBDaGF0ID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL2NoYXQvQ2hhdC5qc1wiKTtcbnZhciBUb29sYmFyID0gcmVxdWlyZShcIi4vdG9vbGJhcnMvVG9vbGJhclwiKTtcbnZhciBUb29sYmFyVG9nZ2xlciA9IHJlcXVpcmUoXCIuL3Rvb2xiYXJzL1Rvb2xiYXJUb2dnbGVyXCIpO1xudmFyIEJvdHRvbVRvb2xiYXIgPSByZXF1aXJlKFwiLi90b29sYmFycy9Cb3R0b21Ub29sYmFyXCIpO1xudmFyIENvbnRhY3RMaXN0ID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL2NvbnRhY3RsaXN0L0NvbnRhY3RMaXN0XCIpO1xudmFyIEF2YXRhciA9IHJlcXVpcmUoXCIuL2F2YXRhci9BdmF0YXJcIik7XG52YXIgRXZlbnRFbWl0dGVyID0gcmVxdWlyZShcImV2ZW50c1wiKTtcbnZhciBTZXR0aW5nc01lbnUgPSByZXF1aXJlKFwiLi9zaWRlX3Bhbm5lbHMvc2V0dGluZ3MvU2V0dGluZ3NNZW51XCIpO1xudmFyIFNldHRpbmdzID0gcmVxdWlyZShcIi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NcIik7XG52YXIgUGFuZWxUb2dnbGVyID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG52YXIgUm9vbU5hbWVHZW5lcmF0b3IgPSByZXF1aXJlKFwiLi93ZWxjb21lX3BhZ2UvUm9vbW5hbWVHZW5lcmF0b3JcIik7XG5VSS5tZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgbWVzc2FnZUhhbmRsZXIgPSBVSS5tZXNzYWdlSGFuZGxlcjtcbnZhciBBdXRoZW50aWNhdGlvbiAgPSByZXF1aXJlKFwiLi9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvblwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi91dGlsL1VJVXRpbFwiKTtcbnZhciBOaWNrbmFtZUhhbmRsZXIgPSByZXF1aXJlKFwiLi91dGlsL05pY2tuYW1lSGFuZGxlclwiKTtcbnZhciBDUUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL2Nvbm5lY3Rpb25xdWFsaXR5L0NRRXZlbnRzXCIpO1xudmFyIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlc1xuICAgID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvZGVza3RvcHNoYXJpbmcvRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXCIpO1xudmFyIFJUQ0V2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENFdmVudHNcIik7XG52YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9TdHJlYW1FdmVudFR5cGVzXCIpO1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgcm9vbU5hbWUgPSBudWxsO1xuXG5cbmZ1bmN0aW9uIHNldHVwUHJlemkoKVxue1xuICAgICQoXCIjcmVsb2FkUHJlc2VudGF0aW9uTGlua1wiKS5jbGljayhmdW5jdGlvbigpXG4gICAge1xuICAgICAgICBQcmV6aS5yZWxvYWRQcmVzZW50YXRpb24oKTtcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gc2V0dXBDaGF0KClcbntcbiAgICBDaGF0LmluaXQoKTtcbiAgICAkKFwiI3RvZ2dsZV9zbWlsZXlzXCIpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgICBDaGF0LnRvZ2dsZVNtaWxleXMoKTtcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gc2V0dXBUb29sYmFycygpIHtcbiAgICBUb29sYmFyLmluaXQoVUkpO1xuICAgIFRvb2xiYXIuc2V0dXBCdXR0b25zRnJvbUNvbmZpZygpO1xuICAgIEJvdHRvbVRvb2xiYXIuaW5pdCgpO1xufVxuXG5mdW5jdGlvbiBzdHJlYW1IYW5kbGVyKHN0cmVhbSkge1xuICAgIHN3aXRjaCAoc3RyZWFtLnR5cGUpXG4gICAge1xuICAgICAgICBjYXNlIFwiYXVkaW9cIjpcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNoYW5nZUxvY2FsQXVkaW8oc3RyZWFtKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwidmlkZW9cIjpcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNoYW5nZUxvY2FsVmlkZW8oc3RyZWFtKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwic3RyZWFtXCI6XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFN0cmVhbShzdHJlYW0pO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBvbkRpc3Bvc2VDb25mZXJlbmNlKHVubG9hZCkge1xuICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbihmYWxzZSk7XG59O1xuXG5mdW5jdGlvbiBvbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgQ29udGFjdExpc3Qub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBTZXR0aW5nc01lbnUub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBWaWRlb0xheW91dC5vbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKTtcbn1cblxuZnVuY3Rpb24gcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihzdHJlYW1IYW5kbGVyLCBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ1JFQVRFRCk7XG5cbiAgICBBUFAuUlRDLmFkZFN0cmVhbUxpc3RlbmVyKHN0cmVhbUhhbmRsZXIsIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DSEFOR0VEKTtcbiAgICBBUFAuUlRDLmFkZFN0cmVhbUxpc3RlbmVyKGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgVmlkZW9MYXlvdXQub25SZW1vdGVTdHJlYW1BZGRlZChzdHJlYW0pO1xuICAgIH0sIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9SRU1PVEVfQ1JFQVRFRCk7XG4gICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgIFZpZGVvTGF5b3V0Lm9uVmlkZW9UeXBlQ2hhbmdlZChqaWQpO1xuICAgIH0sIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9SRU1PVEVfQ0hBTkdFRCk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuTEFTVE5fQ0hBTkdFRCwgb25MYXN0TkNoYW5nZWQpO1xuICAgIEFQUC5SVEMuYWRkTGlzdGVuZXIoUlRDRXZlbnRzLkRPTUlOQU5UU1BFQUtFUl9DSEFOR0VELCBmdW5jdGlvbiAocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgVmlkZW9MYXlvdXQub25Eb21pbmFudFNwZWFrZXJDaGFuZ2VkKHJlc291cmNlSmlkKTtcbiAgICB9KTtcbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5MQVNUTl9FTkRQT0lOVF9DSEFOR0VELFxuICAgICAgICBmdW5jdGlvbiAobGFzdE5FbmRwb2ludHMsIGVuZHBvaW50c0VudGVyaW5nTGFzdE4sIHN0cmVhbSkge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQub25MYXN0TkVuZHBvaW50c0NoYW5nZWQobGFzdE5FbmRwb2ludHMsXG4gICAgICAgICAgICAgICAgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Tiwgc3RyZWFtKTtcbiAgICAgICAgfSk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuU0lNVUxDQVNUX0xBWUVSX0NIQU5HRUQsXG4gICAgICAgIGZ1bmN0aW9uIChlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICAgICBWaWRlb0xheW91dC5vblNpbXVsY2FzdExheWVyc0NoYW5nZWQoZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpO1xuICAgICAgICB9KTtcbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5TSU1VTENBU1RfTEFZRVJfQ0hBTkdJTkcsXG4gICAgICAgIGZ1bmN0aW9uIChlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQub25TaW11bGNhc3RMYXllcnNDaGFuZ2luZyhlbmRwb2ludFNpbXVsY2FzdExheWVycyk7XG4gICAgICAgIH0pO1xuICAgIEFQUC5SVEMuYWRkTGlzdGVuZXIoUlRDRXZlbnRzLkFWQUlMQUJMRV9ERVZJQ0VTX0NIQU5HRUQsXG4gICAgICAgIGZ1bmN0aW9uIChkZXZpY2VzKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5zZXREZXZpY2VBdmFpbGFiaWxpdHlJY29ucyhudWxsLCBkZXZpY2VzKTtcbiAgICAgICAgfSlcbiAgICBBUFAuc3RhdGlzdGljcy5hZGRBdWRpb0xldmVsTGlzdGVuZXIoZnVuY3Rpb24oamlkLCBhdWRpb0xldmVsKVxuICAgIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkO1xuICAgICAgICBpZihqaWQgPT09IEFQUC5zdGF0aXN0aWNzLkxPQ0FMX0pJRClcbiAgICAgICAge1xuICAgICAgICAgICAgcmVzb3VyY2VKaWQgPSBBdWRpb0xldmVscy5MT0NBTF9MRVZFTDtcbiAgICAgICAgICAgIGlmKEFQUC5SVEMubG9jYWxBdWRpby5pc011dGVkKCkpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbCA9IDA7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBBdWRpb0xldmVscy51cGRhdGVBdWRpb0xldmVsKHJlc291cmNlSmlkLCBhdWRpb0xldmVsLFxuICAgICAgICAgICAgVUkuZ2V0TGFyZ2VWaWRlb1N0YXRlKCkudXNlclJlc291cmNlSmlkKTtcbiAgICB9KTtcbiAgICBBUFAuZGVza3RvcHNoYXJpbmcuYWRkTGlzdGVuZXIoZnVuY3Rpb24gKCkge1xuICAgICAgICBUb29sYmFyVG9nZ2xlci5zaG93RGVza3RvcFNoYXJpbmdCdXR0b24oKTtcbiAgICB9LCBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuSU5JVCk7XG4gICAgQVBQLmRlc2t0b3BzaGFyaW5nLmFkZExpc3RlbmVyKFxuICAgICAgICBUb29sYmFyLmNoYW5nZURlc2t0b3BTaGFyaW5nQnV0dG9uU3RhdGUsXG4gICAgICAgIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5TV0lUQ0hJTkdfRE9ORSk7XG4gICAgQVBQLmNvbm5lY3Rpb25xdWFsaXR5LmFkZExpc3RlbmVyKENRRXZlbnRzLkxPQ0FMU1RBVFNfVVBEQVRFRCxcbiAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTG9jYWxDb25uZWN0aW9uU3RhdHMpO1xuICAgIEFQUC5jb25uZWN0aW9ucXVhbGl0eS5hZGRMaXN0ZW5lcihDUUV2ZW50cy5SRU1PVEVTVEFUU19VUERBVEVELFxuICAgICAgICBWaWRlb0xheW91dC51cGRhdGVDb25uZWN0aW9uU3RhdHMpO1xuICAgIEFQUC5jb25uZWN0aW9ucXVhbGl0eS5hZGRMaXN0ZW5lcihDUUV2ZW50cy5TVE9QLFxuICAgICAgICBWaWRlb0xheW91dC5vblN0YXRzU3RvcCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5ESVNQT1NFX0NPTkZFUkVOQ0UsIG9uRGlzcG9zZUNvbmZlcmVuY2UpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuR1JBQ0VGVUxfU0hVVERPV04sIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2coXG4gICAgICAgICAgICAnZGlhbG9nLnNlcnZpY2VVbmF2YWlsYWJsZScsXG4gICAgICAgICAgICAnZGlhbG9nLmdyYWNlZnVsU2h1dGRvd24nXG4gICAgICAgICk7XG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5SRVNFUlZBVElPTl9FUlJPUiwgZnVuY3Rpb24gKGNvZGUsIG1zZykge1xuICAgICAgICB2YXIgdGl0bGUgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgICAgIFwiZGlhbG9nLnJlc2VydmF0aW9uRXJyb3JcIik7XG4gICAgICAgIHZhciBtZXNzYWdlID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICBcImRpYWxvZy5yZXNlcnZhdGlvbkVycm9yTXNnXCIsIHtjb2RlOiBjb2RlLCBtc2c6IG1zZ30pO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuRGlhbG9nKFxuICAgICAgICAgICAgdGl0bGUsXG4gICAgICAgICAgICBtZXNzYWdlLFxuICAgICAgICAgICAgdHJ1ZSwge30sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXZlbnQsIHZhbHVlLCBtZXNzYWdlLCBmb3JtVmFscylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5LSUNLRUQsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2coXCJkaWFsb2cuc2Vzc1Rlcm1pbmF0ZWRcIixcbiAgICAgICAgICAgIFwiZGlhbG9nLmtpY2tNZXNzYWdlXCIpO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0RFU1RST1lFRCwgZnVuY3Rpb24gKHJlYXNvbikge1xuICAgICAgICAvL0ZJWE1FOiB1c2UgU2Vzc2lvbiBUZXJtaW5hdGVkIGZyb20gdHJhbnNsYXRpb24sIGJ1dFxuICAgICAgICAvLyAncmVhc29uJyB0ZXh0IGNvbWVzIGZyb20gWE1QUCBwYWNrZXQgYW5kIGlzIG5vdCB0cmFuc2xhdGVkXG4gICAgICAgIHZhciB0aXRsZSA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwiZGlhbG9nLnNlc3NUZXJtaW5hdGVkXCIpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuRGlhbG9nKFxuICAgICAgICAgICAgdGl0bGUsIHJlYXNvbiwgdHJ1ZSwge30sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXZlbnQsIHZhbHVlLCBtZXNzYWdlLCBmb3JtVmFscylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5CUklER0VfRE9XTiwgZnVuY3Rpb24gKCkge1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoXCJkaWFsb2cuZXJyb3JcIixcbiAgICAgICAgICAgIFwiZGlhbG9nLmJyaWRnZVVuYXZhaWxhYmxlXCIpO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuVVNFUl9JRF9DSEFOR0VELCBmdW5jdGlvbiAoZnJvbSwgaWQpIHtcbiAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIoZnJvbSwgaWQpO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQ0hBTkdFRF9TVFJFQU1TLCBmdW5jdGlvbiAoamlkLCBjaGFuZ2VkU3RyZWFtcykge1xuICAgICAgICBmb3Ioc3RyZWFtIGluIGNoYW5nZWRTdHJlYW1zKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBtaWdodCBuZWVkIHRvIHVwZGF0ZSB0aGUgZGlyZWN0aW9uIGlmIHBhcnRpY2lwYW50IGp1c3Qgd2VudCBmcm9tIHNlbmRyZWN2IHRvIHJlY3Zvbmx5XG4gICAgICAgICAgICBpZiAoc3RyZWFtLnR5cGUgPT09ICd2aWRlbycgfHwgc3RyZWFtLnR5cGUgPT09ICdzY3JlZW4nKSB7XG4gICAgICAgICAgICAgICAgdmFyIGVsID0gJCgnI3BhcnRpY2lwYW50XycgICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSArICc+dmlkZW8nKTtcbiAgICAgICAgICAgICAgICBzd2l0Y2ggKHN0cmVhbS5kaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnc2VuZHJlY3YnOlxuICAgICAgICAgICAgICAgICAgICAgICAgZWwuc2hvdygpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgJ3JlY3Zvbmx5JzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsLmhpZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBDaGVjayBpZiB3ZSBoYXZlIHRvIGNoYW5nZSBsYXJnZSB2aWRlb1xuICAgICAgICAgICAgICAgICAgICAgICAgLy9WaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKGVsKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5ESVNQTEFZX05BTUVfQ0hBTkdFRCwgb25EaXNwbGF5TmFtZUNoYW5nZWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0pPSU5FRCwgb25NdWNKb2luZWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTE9DQUxST0xFX0NIQU5HRUQsIG9uTG9jYWxSb2xlQ2hhbmdlKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLk1VQ19FTlRFUiwgb25NdWNFbnRlcmVkKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLk1VQ19ST0xFX0NIQU5HRUQsIG9uTXVjUm9sZUNoYW5nZWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuUFJFU0VOQ0VfU1RBVFVTLCBvbk11Y1ByZXNlbmNlU3RhdHVzKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLlNVQkpFQ1RfQ0hBTkdFRCwgY2hhdFNldFN1YmplY3QpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTUVTU0FHRV9SRUNFSVZFRCwgdXBkYXRlQ2hhdENvbnZlcnNhdGlvbik7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5NVUNfTEVGVCwgb25NdWNMZWZ0KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLlBBU1NXT1JEX1JFUVVJUkVELCBvblBhc3N3b3JkUmVxaXVyZWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQ0hBVF9FUlJPUl9SRUNFSVZFRCwgY2hhdEFkZEVycm9yKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkVUSEVSUEFELCBpbml0RXRoZXJwYWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQVVUSEVOVElDQVRJT05fUkVRVUlSRUQsXG4gICAgICAgIG9uQXV0aGVudGljYXRpb25SZXF1aXJlZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5ERVZJQ0VfQVZBSUxBQkxFLFxuICAgICAgICBmdW5jdGlvbiAocmVzb3VyY2UsIGRldmljZXMpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldERldmljZUF2YWlsYWJpbGl0eUljb25zKHJlc291cmNlLCBkZXZpY2VzKTtcbiAgICAgICAgfSk7XG5cbn1cblxuXG4vKipcbiAqIE11dGVzL3VubXV0ZXMgdGhlIGxvY2FsIHZpZGVvLlxuICpcbiAqIEBwYXJhbSBtdXRlIDx0dD50cnVlPC90dD4gdG8gbXV0ZSB0aGUgbG9jYWwgdmlkZW87IG90aGVyd2lzZSwgPHR0PmZhbHNlPC90dD5cbiAqIEBwYXJhbSBvcHRpb25zIGFuIG9iamVjdCB3aGljaCBzcGVjaWZpZXMgb3B0aW9uYWwgYXJndW1lbnRzIHN1Y2ggYXMgdGhlXG4gKiA8dHQ+Ym9vbGVhbjwvdHQ+IGtleSA8dHQ+YnlVc2VyPC90dD4gd2l0aCBkZWZhdWx0IHZhbHVlIDx0dD50cnVlPC90dD4gd2hpY2hcbiAqIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBtZXRob2Qgd2FzIGluaXRpYXRlZCBpbiByZXNwb25zZSB0byBhIHVzZXIgY29tbWFuZCAoaW5cbiAqIGNvbnRyYXN0IHRvIGFuIGF1dG9tYXRpYyBkZWNpc2lvbiB0YWtlbiBieSB0aGUgYXBwbGljYXRpb24gbG9naWMpXG4gKi9cbmZ1bmN0aW9uIHNldFZpZGVvTXV0ZShtdXRlLCBvcHRpb25zKSB7XG4gICAgQVBQLlJUQy5zZXRWaWRlb011dGUobXV0ZSxcbiAgICAgICAgVUkuc2V0VmlkZW9NdXRlQnV0dG9uc1N0YXRlLFxuICAgICAgICBvcHRpb25zKTtcbn1cblxuXG5mdW5jdGlvbiBiaW5kRXZlbnRzKClcbntcbiAgICAvKipcbiAgICAgKiBSZXNpemVzIGFuZCByZXBvc2l0aW9ucyB2aWRlb3MgaW4gZnVsbCBzY3JlZW4gbW9kZS5cbiAgICAgKi9cbiAgICAkKGRvY3VtZW50KS5vbignd2Via2l0ZnVsbHNjcmVlbmNoYW5nZSBtb3pmdWxsc2NyZWVuY2hhbmdlIGZ1bGxzY3JlZW5jaGFuZ2UnLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVMYXJnZVZpZGVvQ29udGFpbmVyKCk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5wb3NpdGlvbkxhcmdlKCk7XG4gICAgICAgIH1cbiAgICApO1xuXG4gICAgJCh3aW5kb3cpLnJlc2l6ZShmdW5jdGlvbiAoKSB7XG4gICAgICAgIFZpZGVvTGF5b3V0LnJlc2l6ZUxhcmdlVmlkZW9Db250YWluZXIoKTtcbiAgICAgICAgVmlkZW9MYXlvdXQucG9zaXRpb25MYXJnZSgpO1xuICAgIH0pO1xufVxuXG5VSS5zdGFydCA9IGZ1bmN0aW9uIChpbml0KSB7XG4gICAgZG9jdW1lbnQudGl0bGUgPSBpbnRlcmZhY2VDb25maWcuQVBQX05BTUU7XG4gICAgaWYoY29uZmlnLmVuYWJsZVdlbGNvbWVQYWdlICYmIHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSA9PSBcIi9cIiAmJlxuICAgICAgICAoIXdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZCB8fCB3aW5kb3cubG9jYWxTdG9yYWdlLndlbGNvbWVQYWdlRGlzYWJsZWQgPT0gXCJmYWxzZVwiKSlcbiAgICB7XG4gICAgICAgICQoXCIjdmlkZW9jb25mZXJlbmNlX3BhZ2VcIikuaGlkZSgpO1xuICAgICAgICB2YXIgc2V0dXBXZWxjb21lUGFnZSA9IHJlcXVpcmUoXCIuL3dlbGNvbWVfcGFnZS9XZWxjb21lUGFnZVwiKTtcbiAgICAgICAgc2V0dXBXZWxjb21lUGFnZSgpO1xuXG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLlNIT1dfSklUU0lfV0FURVJNQVJLKSB7XG4gICAgICAgIHZhciBsZWZ0V2F0ZXJtYXJrRGl2XG4gICAgICAgICAgICA9ICQoXCIjbGFyZ2VWaWRlb0NvbnRhaW5lciBkaXZbY2xhc3M9J3dhdGVybWFyayBsZWZ0d2F0ZXJtYXJrJ11cIik7XG5cbiAgICAgICAgbGVmdFdhdGVybWFya0Rpdi5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICAgICAgbGVmdFdhdGVybWFya0Rpdi5wYXJlbnQoKS5nZXQoMCkuaHJlZlxuICAgICAgICAgICAgPSBpbnRlcmZhY2VDb25maWcuSklUU0lfV0FURVJNQVJLX0xJTks7XG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0JSQU5EX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgcmlnaHRXYXRlcm1hcmtEaXZcbiAgICAgICAgICAgID0gJChcIiNsYXJnZVZpZGVvQ29udGFpbmVyIGRpdltjbGFzcz0nd2F0ZXJtYXJrIHJpZ2h0d2F0ZXJtYXJrJ11cIik7XG5cbiAgICAgICAgcmlnaHRXYXRlcm1hcmtEaXYuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LnBhcmVudCgpLmdldCgwKS5ocmVmXG4gICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5CUkFORF9XQVRFUk1BUktfTElOSztcbiAgICAgICAgcmlnaHRXYXRlcm1hcmtEaXYuZ2V0KDApLnN0eWxlLmJhY2tncm91bmRJbWFnZVxuICAgICAgICAgICAgPSBcInVybChpbWFnZXMvcmlnaHR3YXRlcm1hcmsucG5nKVwiO1xuICAgIH1cblxuICAgIGlmIChpbnRlcmZhY2VDb25maWcuU0hPV19QT1dFUkVEX0JZKSB7XG4gICAgICAgICQoXCIjbGFyZ2VWaWRlb0NvbnRhaW5lcj5hW2NsYXNzPSdwb3dlcmVkYnknXVwiKS5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICB9XG5cbiAgICAkKFwiI3dlbGNvbWVfcGFnZVwiKS5oaWRlKCk7XG5cbiAgICBWaWRlb0xheW91dC5yZXNpemVMYXJnZVZpZGVvQ29udGFpbmVyKCk7XG4gICAgJChcIiN2aWRlb3NwYWNlXCIpLm1vdXNlbW92ZShmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBUb29sYmFyVG9nZ2xlci5zaG93VG9vbGJhcigpO1xuICAgIH0pO1xuICAgIC8vIFNldCB0aGUgZGVmYXVsdHMgZm9yIHByb21wdCBkaWFsb2dzLlxuICAgIGpRdWVyeS5wcm9tcHQuc2V0RGVmYXVsdHMoe3BlcnNpc3RlbnQ6IGZhbHNlfSk7XG5cbiAgICBWaWRlb0xheW91dC5pbml0KGV2ZW50RW1pdHRlcik7XG4gICAgQXVkaW9MZXZlbHMuaW5pdCgpO1xuICAgIE5pY2tuYW1lSGFuZGxlci5pbml0KGV2ZW50RW1pdHRlcik7XG4gICAgcmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICBiaW5kRXZlbnRzKCk7XG4gICAgc2V0dXBQcmV6aSgpO1xuICAgIHNldHVwVG9vbGJhcnMoKTtcbiAgICBzZXR1cENoYXQoKTtcblxuXG4gICAgZG9jdW1lbnQudGl0bGUgPSBpbnRlcmZhY2VDb25maWcuQVBQX05BTUU7XG5cbiAgICAkKFwiI2Rvd25sb2FkbG9nXCIpLmNsaWNrKGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBkdW1wKGV2ZW50LnRhcmdldCk7XG4gICAgfSk7XG5cbiAgICBpZihjb25maWcuZW5hYmxlV2VsY29tZVBhZ2UgJiYgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lID09IFwiL1wiICYmXG4gICAgICAgICghd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkIHx8IHdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZCA9PSBcImZhbHNlXCIpKVxuICAgIHtcbiAgICAgICAgJChcIiN2aWRlb2NvbmZlcmVuY2VfcGFnZVwiKS5oaWRlKCk7XG4gICAgICAgIHZhciBzZXR1cFdlbGNvbWVQYWdlID0gcmVxdWlyZShcIi4vd2VsY29tZV9wYWdlL1dlbGNvbWVQYWdlXCIpO1xuICAgICAgICBzZXR1cFdlbGNvbWVQYWdlKCk7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgICQoXCIjd2VsY29tZV9wYWdlXCIpLmhpZGUoKTtcblxuICAgIC8vIERpc3BsYXkgbm90aWNlIG1lc3NhZ2UgYXQgdGhlIHRvcCBvZiB0aGUgdG9vbGJhclxuICAgIGlmIChjb25maWcubm90aWNlTWVzc2FnZSkge1xuICAgICAgICAkKCcjbm90aWNlVGV4dCcpLnRleHQoY29uZmlnLm5vdGljZU1lc3NhZ2UpO1xuICAgICAgICAkKCcjbm90aWNlJykuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgfVxuXG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKS52b2x1bWUgPSAwO1xuXG4gICAgaWYgKCEkKCcjc2V0dGluZ3MnKS5pcygnOnZpc2libGUnKSkge1xuICAgICAgICBjb25zb2xlLmxvZygnaW5pdCcpO1xuICAgICAgICBpbml0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbG9naW5JbmZvLm9uc3VibWl0ID0gZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmIChlLnByZXZlbnREZWZhdWx0KSBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAkKCcjc2V0dGluZ3MnKS5oaWRlKCk7XG4gICAgICAgICAgICBpbml0KCk7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgdG9hc3RyLm9wdGlvbnMgPSB7XG4gICAgICAgIFwiY2xvc2VCdXR0b25cIjogdHJ1ZSxcbiAgICAgICAgXCJkZWJ1Z1wiOiBmYWxzZSxcbiAgICAgICAgXCJwb3NpdGlvbkNsYXNzXCI6IFwibm90aWZpY2F0aW9uLWJvdHRvbS1yaWdodFwiLFxuICAgICAgICBcIm9uY2xpY2tcIjogbnVsbCxcbiAgICAgICAgXCJzaG93RHVyYXRpb25cIjogXCIzMDBcIixcbiAgICAgICAgXCJoaWRlRHVyYXRpb25cIjogXCIxMDAwXCIsXG4gICAgICAgIFwidGltZU91dFwiOiBcIjIwMDBcIixcbiAgICAgICAgXCJleHRlbmRlZFRpbWVPdXRcIjogXCIxMDAwXCIsXG4gICAgICAgIFwic2hvd0Vhc2luZ1wiOiBcInN3aW5nXCIsXG4gICAgICAgIFwiaGlkZUVhc2luZ1wiOiBcImxpbmVhclwiLFxuICAgICAgICBcInNob3dNZXRob2RcIjogXCJmYWRlSW5cIixcbiAgICAgICAgXCJoaWRlTWV0aG9kXCI6IFwiZmFkZU91dFwiLFxuICAgICAgICBcInJlcG9zaXRpb25cIjogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZihQYW5lbFRvZ2dsZXIuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5hZGRDbGFzcyhcIm5vdGlmaWNhdGlvbi1ib3R0b20tcmlnaHQtY2VudGVyXCIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5yZW1vdmVDbGFzcyhcIm5vdGlmaWNhdGlvbi1ib3R0b20tcmlnaHQtY2VudGVyXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBcIm5ld2VzdE9uVG9wXCI6IGZhbHNlXG4gICAgfTtcblxuICAgIFNldHRpbmdzTWVudS5pbml0KCk7XG5cbn07XG5cbmZ1bmN0aW9uIGNoYXRBZGRFcnJvcihlcnJvck1lc3NhZ2UsIG9yaWdpbmFsVGV4dClcbntcbiAgICByZXR1cm4gQ2hhdC5jaGF0QWRkRXJyb3IoZXJyb3JNZXNzYWdlLCBvcmlnaW5hbFRleHQpO1xufTtcblxuZnVuY3Rpb24gY2hhdFNldFN1YmplY3QodGV4dClcbntcbiAgICByZXR1cm4gQ2hhdC5jaGF0U2V0U3ViamVjdCh0ZXh0KTtcbn07XG5cbmZ1bmN0aW9uIHVwZGF0ZUNoYXRDb252ZXJzYXRpb24oZnJvbSwgZGlzcGxheU5hbWUsIG1lc3NhZ2UpIHtcbiAgICByZXR1cm4gQ2hhdC51cGRhdGVDaGF0Q29udmVyc2F0aW9uKGZyb20sIGRpc3BsYXlOYW1lLCBtZXNzYWdlKTtcbn07XG5cbmZ1bmN0aW9uIG9uTXVjSm9pbmVkKGppZCwgaW5mbykge1xuICAgIFRvb2xiYXIudXBkYXRlUm9vbVVybCh3aW5kb3cubG9jYXRpb24uaHJlZik7XG4gICAgdmFyIG1lSFRNTCA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwibWVcIik7XG4gICAgJChcIiNsb2NhbE5pY2tcIikuaHRtbChTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpICsgXCIgKFwiICsgbWVIVE1MICsgXCIpXCIpO1xuXG4gICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICAvLyBBZGQgbXlzZWxmIHRvIHRoZSBjb250YWN0IGxpc3QuXG4gICAgQ29udGFjdExpc3QuYWRkQ29udGFjdChqaWQsIHNldHRpbmdzLmVtYWlsIHx8IHNldHRpbmdzLnVpZCk7XG5cbiAgICAvLyBPbmNlIHdlJ3ZlIGpvaW5lZCB0aGUgbXVjIHNob3cgdGhlIHRvb2xiYXJcbiAgICBUb29sYmFyVG9nZ2xlci5zaG93VG9vbGJhcigpO1xuXG4gICAgdmFyIGRpc3BsYXlOYW1lID0gIWNvbmZpZy5kaXNwbGF5Smlkc1xuICAgICAgICA/IGluZm8uZGlzcGxheU5hbWUgOiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuXG4gICAgaWYgKGRpc3BsYXlOYW1lKVxuICAgICAgICBvbkRpc3BsYXlOYW1lQ2hhbmdlZCgnbG9jYWxWaWRlb0NvbnRhaW5lcicsIGRpc3BsYXlOYW1lKTtcbn1cblxuZnVuY3Rpb24gaW5pdEV0aGVycGFkKG5hbWUpIHtcbiAgICBFdGhlcnBhZC5pbml0KG5hbWUpO1xufTtcblxuZnVuY3Rpb24gb25NdWNMZWZ0KGppZCkge1xuICAgIGNvbnNvbGUubG9nKCdsZWZ0Lm11YycsIGppZCk7XG4gICAgdmFyIGRpc3BsYXlOYW1lID0gJCgnI3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpICtcbiAgICAgICAgJz4uZGlzcGxheW5hbWUnKS5odG1sKCk7XG4gICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KGRpc3BsYXlOYW1lLCdub3RpZnkuc29tZWJvZHknLFxuICAgICAgICAnZGlzY29ubmVjdGVkJyxcbiAgICAgICAgJ25vdGlmeS5kaXNjb25uZWN0ZWQnKTtcbiAgICAvLyBOZWVkIHRvIGNhbGwgdGhpcyB3aXRoIGEgc2xpZ2h0IGRlbGF5LCBvdGhlcndpc2UgdGhlIGVsZW1lbnQgY291bGRuJ3QgYmVcbiAgICAvLyBmb3VuZCBmb3Igc29tZSByZWFzb24uXG4gICAgLy8gWFhYKGdwKSBpdCB3b3JrcyBmaW5lIHdpdGhvdXQgdGhlIHRpbWVvdXQgZm9yIG1lICh3aXRoIENocm9tZSAzOCkuXG4gICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXG4gICAgICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgQ29udGFjdExpc3QucmVtb3ZlQ29udGFjdChqaWQpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVtb3ZlQ29ubmVjdGlvbkluZGljYXRvcihqaWQpO1xuICAgICAgICAgICAgLy8gaGlkZSBoZXJlLCB3YWl0IGZvciB2aWRlbyB0byBjbG9zZSBiZWZvcmUgcmVtb3ZpbmdcbiAgICAgICAgICAgICQoY29udGFpbmVyKS5oaWRlKCk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cbiAgICB9LCAxMCk7XG5cbiAgICBWaWRlb0xheW91dC5wYXJ0aWNpcGFudExlZnQoamlkKTtcblxufTtcblxuXG5mdW5jdGlvbiBvbkxvY2FsUm9sZUNoYW5nZShqaWQsIGluZm8sIHByZXMsIGlzTW9kZXJhdG9yKVxue1xuXG4gICAgY29uc29sZS5pbmZvKFwiTXkgcm9sZSBjaGFuZ2VkLCBuZXcgcm9sZTogXCIgKyBpbmZvLnJvbGUpO1xuICAgIG9uTW9kZXJhdG9yU3RhdHVzQ2hhbmdlZChpc01vZGVyYXRvcik7XG4gICAgVmlkZW9MYXlvdXQuc2hvd01vZGVyYXRvckluZGljYXRvcigpO1xuXG4gICAgaWYgKGlzTW9kZXJhdG9yKSB7XG4gICAgICAgIEF1dGhlbnRpY2F0aW9uLmNsb3NlQXV0aGVudGljYXRpb25XaW5kb3coKTtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KG51bGwsIFwibm90aWZ5Lm1lXCIsXG4gICAgICAgICAgICAnY29ubmVjdGVkJywgXCJub3RpZnkubW9kZXJhdG9yXCIpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gb25Nb2RlcmF0b3JTdGF0dXNDaGFuZ2VkKGlzTW9kZXJhdG9yKSB7XG5cbiAgICBUb29sYmFyLnNob3dTaXBDYWxsQnV0dG9uKGlzTW9kZXJhdG9yKTtcbiAgICBUb29sYmFyLnNob3dSZWNvcmRpbmdCdXR0b24oXG4gICAgICAgIGlzTW9kZXJhdG9yKTsgLy8mJlxuICAgIC8vIEZJWE1FOlxuICAgIC8vIFJlY29yZGluZyB2aXNpYmxlIGlmXG4gICAgLy8gdGhlcmUgYXJlIGF0IGxlYXN0IDIoKyAxIGZvY3VzKSBwYXJ0aWNpcGFudHNcbiAgICAvL09iamVjdC5rZXlzKGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzKS5sZW5ndGggPj0gMyk7XG5cbiAgICBpZiAoaXNNb2RlcmF0b3IgJiYgY29uZmlnLmV0aGVycGFkX2Jhc2UpIHtcbiAgICAgICAgRXRoZXJwYWQuaW5pdCgpO1xuICAgIH1cbn07XG5cbmZ1bmN0aW9uIG9uUGFzc3dvcmRSZXFpdXJlZChjYWxsYmFjaykge1xuICAgIC8vIHBhc3N3b3JkIGlzIHJlcXVpcmVkXG4gICAgVG9vbGJhci5sb2NrTG9ja0J1dHRvbigpO1xuICAgIHZhciBtZXNzYWdlID0gJzxoMiBkYXRhLWkxOG49XCJkaWFsb2cucGFzc3dvcmRSZXF1aXJlZFwiPic7XG4gICAgbWVzc2FnZSArPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFxuICAgICAgICBcImRpYWxvZy5wYXNzd29yZFJlcXVpcmVkXCIpO1xuICAgIG1lc3NhZ2UgKz0gJzwvaDI+JyArXG4gICAgICAgICc8aW5wdXQgbmFtZT1cImxvY2tLZXlcIiB0eXBlPVwidGV4dFwiIGRhdGEtaTE4bj0nICtcbiAgICAgICAgJ1wiW3BsYWNlaG9sZGVyXWRpYWxvZy5wYXNzd29yZFwiIHBsYWNlaG9sZGVyPVwiJyArXG4gICAgICAgIEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVTdHJpbmcoXCJkaWFsb2cucGFzc3dvcmRcIikgK1xuICAgICAgICAnXCIgYXV0b2ZvY3VzPic7XG5cbiAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsIG51bGwsIG51bGwsIG1lc3NhZ2UsXG4gICAgICAgIHRydWUsXG4gICAgICAgIFwiZGlhbG9nLk9rXCIsXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7fSxcbiAgICAgICAgbnVsbCxcbiAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgdmFyIGxvY2tLZXkgPSBmLmxvY2tLZXk7XG4gICAgICAgICAgICAgICAgaWYgKGxvY2tLZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkobG9ja0tleSk7XG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKGxvY2tLZXkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgJzppbnB1dDpmaXJzdCdcbiAgICApO1xufVxuZnVuY3Rpb24gb25NdWNFbnRlcmVkKGppZCwgaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KGRpc3BsYXlOYW1lLCdub3RpZnkuc29tZWJvZHknLFxuICAgICAgICAnY29ubmVjdGVkJyxcbiAgICAgICAgJ25vdGlmeS5jb25uZWN0ZWQnKTtcblxuICAgIC8vIEFkZCBQZWVyJ3MgY29udGFpbmVyXG4gICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQsaWQpO1xufVxuXG5mdW5jdGlvbiBvbk11Y1ByZXNlbmNlU3RhdHVzKCBqaWQsIGluZm8pIHtcbiAgICBWaWRlb0xheW91dC5zZXRQcmVzZW5jZVN0YXR1cyhcbiAgICAgICAgICAgICdwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSwgaW5mby5zdGF0dXMpO1xufVxuXG5mdW5jdGlvbiBvbk11Y1JvbGVDaGFuZ2VkKHJvbGUsIGRpc3BsYXlOYW1lKSB7XG4gICAgVmlkZW9MYXlvdXQuc2hvd01vZGVyYXRvckluZGljYXRvcigpO1xuXG4gICAgaWYgKHJvbGUgPT09ICdtb2RlcmF0b3InKSB7XG4gICAgICAgIHZhciBtZXNzYWdlS2V5LCBtZXNzYWdlT3B0aW9ucyA9IHt9O1xuICAgICAgICBpZiAoIWRpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICBtZXNzYWdlS2V5ID0gXCJub3RpZnkuZ3JhbnRlZFRvVW5rbm93blwiO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgbWVzc2FnZUtleSA9IFwibm90aWZ5LmdyYW50ZWRUb1wiO1xuICAgICAgICAgICAgbWVzc2FnZU9wdGlvbnMgPSB7dG86IGRpc3BsYXlOYW1lfTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlSGFuZGxlci5ub3RpZnkoXG4gICAgICAgICAgICBkaXNwbGF5TmFtZSwnbm90aWZ5LnNvbWVib2R5JyxcbiAgICAgICAgICAgICdjb25uZWN0ZWQnLCBtZXNzYWdlS2V5LFxuICAgICAgICAgICAgbWVzc2FnZU9wdGlvbnMpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gb25BdXRoZW50aWNhdGlvblJlcXVpcmVkKGludGVydmFsQ2FsbGJhY2spIHtcbiAgICBBdXRoZW50aWNhdGlvbi5vcGVuQXV0aGVudGljYXRpb25EaWFsb2coXG4gICAgICAgIHJvb21OYW1lLCBpbnRlcnZhbENhbGxiYWNrLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBUb29sYmFyLmF1dGhlbnRpY2F0ZUNsaWNrZWQoKTtcbiAgICAgICAgfSk7XG59O1xuXG5cbmZ1bmN0aW9uIG9uTGFzdE5DaGFuZ2VkKG9sZFZhbHVlLCBuZXdWYWx1ZSkge1xuICAgIGlmIChjb25maWcubXV0ZUxvY2FsVmlkZW9JZk5vdEluTGFzdE4pIHtcbiAgICAgICAgc2V0VmlkZW9NdXRlKCFuZXdWYWx1ZSwgeyAnYnlVc2VyJzogZmFsc2UgfSk7XG4gICAgfVxufVxuXG5cblVJLnRvZ2dsZVNtaWxleXMgPSBmdW5jdGlvbiAoKSB7XG4gICAgQ2hhdC50b2dnbGVTbWlsZXlzKCk7XG59O1xuXG5VSS5nZXRTZXR0aW5ncyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbn07XG5cblVJLnRvZ2dsZUZpbG1TdHJpcCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gQm90dG9tVG9vbGJhci50b2dnbGVGaWxtU3RyaXAoKTtcbn07XG5cblVJLnRvZ2dsZUNoYXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ2hhdCgpO1xufTtcblxuVUkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ29udGFjdExpc3QoKTtcbn07XG5cblVJLmlucHV0RGlzcGxheU5hbWVIYW5kbGVyID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgVmlkZW9MYXlvdXQuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIodmFsdWUpO1xufTtcblxuXG5VSS5nZXRMYXJnZVZpZGVvU3RhdGUgPSBmdW5jdGlvbigpXG57XG4gICAgcmV0dXJuIFZpZGVvTGF5b3V0LmdldExhcmdlVmlkZW9TdGF0ZSgpO1xufTtcblxuVUkuZ2VuZXJhdGVSb29tTmFtZSA9IGZ1bmN0aW9uKCkge1xuICAgIGlmKHJvb21OYW1lKVxuICAgICAgICByZXR1cm4gcm9vbU5hbWU7XG4gICAgdmFyIHJvb21ub2RlID0gbnVsbDtcbiAgICB2YXIgcGF0aCA9IHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZTtcblxuICAgIC8vIGRldGVybWluZGUgdGhlIHJvb20gbm9kZSBmcm9tIHRoZSB1cmxcbiAgICAvLyBUT0RPOiBqdXN0IHRoZSByb29tbm9kZSBvciB0aGUgd2hvbGUgYmFyZSBqaWQ/XG4gICAgaWYgKGNvbmZpZy5nZXRyb29tbm9kZSAmJiB0eXBlb2YgY29uZmlnLmdldHJvb21ub2RlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIGN1c3RvbSBmdW5jdGlvbiBtaWdodCBiZSByZXNwb25zaWJsZSBmb3IgZG9pbmcgdGhlIHB1c2hzdGF0ZVxuICAgICAgICByb29tbm9kZSA9IGNvbmZpZy5nZXRyb29tbm9kZShwYXRoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvKiBmYWxsIGJhY2sgdG8gZGVmYXVsdCBzdHJhdGVneVxuICAgICAgICAgKiB0aGlzIGlzIG1ha2luZyBhc3N1bXB0aW9ucyBhYm91dCBob3cgdGhlIFVSTC0+cm9vbSBtYXBwaW5nIGhhcHBlbnMuXG4gICAgICAgICAqIEl0IGN1cnJlbnRseSBhc3N1bWVzIGRlcGxveW1lbnQgYXQgcm9vdCwgd2l0aCBhIHJld3JpdGUgbGlrZSB0aGVcbiAgICAgICAgICogZm9sbG93aW5nIG9uZSAoZm9yIG5naW54KTpcbiAgICAgICAgIGxvY2F0aW9uIH4gXi8oW2EtekEtWjAtOV0rKSQge1xuICAgICAgICAgcmV3cml0ZSBeLyguKikkIC8gYnJlYWs7XG4gICAgICAgICB9XG4gICAgICAgICAqL1xuICAgICAgICBpZiAocGF0aC5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICByb29tbm9kZSA9IHBhdGguc3Vic3RyKDEpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgd29yZCA9IFJvb21OYW1lR2VuZXJhdG9yLmdlbmVyYXRlUm9vbVdpdGhvdXRTZXBhcmF0b3IoKTtcbiAgICAgICAgICAgIHJvb21ub2RlID0gd29yZC50b0xvd2VyQ2FzZSgpO1xuXG4gICAgICAgICAgICB3aW5kb3cuaGlzdG9yeS5wdXNoU3RhdGUoJ1ZpZGVvQ2hhdCcsXG4gICAgICAgICAgICAgICAgICAgICdSb29tOiAnICsgd29yZCwgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lICsgd29yZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByb29tTmFtZSA9IHJvb21ub2RlICsgJ0AnICsgY29uZmlnLmhvc3RzLm11YztcbiAgICByZXR1cm4gcm9vbU5hbWU7XG59O1xuXG5cblVJLmNvbm5lY3Rpb25JbmRpY2F0b3JTaG93TW9yZSA9IGZ1bmN0aW9uKGlkKVxue1xuICAgIHJldHVybiBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0uc2hvd01vcmUoKTtcbn07XG5cblVJLnNob3dMb2dpblBvcHVwID0gZnVuY3Rpb24oY2FsbGJhY2spXG57XG4gICAgY29uc29sZS5sb2coJ3Bhc3N3b3JkIGlzIHJlcXVpcmVkJyk7XG4gICAgdmFyIG1lc3NhZ2UgPSAnPGgyIGRhdGEtaTE4bj1cImRpYWxvZy5wYXNzd29yZFJlcXVpcmVkXCI+JztcbiAgICBtZXNzYWdlICs9IEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVTdHJpbmcoXG4gICAgICAgIFwiZGlhbG9nLnBhc3N3b3JkUmVxdWlyZWRcIik7XG4gICAgbWVzc2FnZSArPSAnPC9oMj4nICtcbiAgICAgICAgJzxpbnB1dCBuYW1lPVwidXNlcm5hbWVcIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAncGxhY2Vob2xkZXI9XCJ1c2VyQGRvbWFpbi5uZXRcIiBhdXRvZm9jdXM+JyArXG4gICAgICAgICc8aW5wdXQgbmFtZT1cInBhc3N3b3JkXCIgJyArXG4gICAgICAgICd0eXBlPVwicGFzc3dvcmRcIiBkYXRhLWkxOG49XCJbcGxhY2Vob2xkZXJdZGlhbG9nLnVzZXJQYXNzd29yZFwiJyArXG4gICAgICAgICcgcGxhY2Vob2xkZXI9XCJ1c2VyIHBhc3N3b3JkXCI+JztcbiAgICBVSS5tZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsIG51bGwsIG51bGwsIG1lc3NhZ2UsXG4gICAgICAgIHRydWUsXG4gICAgICAgIFwiZGlhbG9nLk9rXCIsXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7XG4gICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgIGlmIChmLnVzZXJuYW1lICE9PSBudWxsICYmIGYucGFzc3dvcmQgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhmLnVzZXJuYW1lLCBmLnBhc3N3b3JkKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIG51bGwsIG51bGwsICc6aW5wdXQ6Zmlyc3QnXG5cbiAgICApO1xufVxuXG5VSS5jaGVja0Zvck5pY2tuYW1lQW5kSm9pbiA9IGZ1bmN0aW9uICgpIHtcblxuICAgIEF1dGhlbnRpY2F0aW9uLmNsb3NlQXV0aGVudGljYXRpb25EaWFsb2coKTtcbiAgICBBdXRoZW50aWNhdGlvbi5zdG9wSW50ZXJ2YWwoKTtcblxuICAgIHZhciBuaWNrID0gbnVsbDtcbiAgICBpZiAoY29uZmlnLnVzZU5pY2tzKSB7XG4gICAgICAgIG5pY2sgPSB3aW5kb3cucHJvbXB0KCdZb3VyIG5pY2tuYW1lIChvcHRpb25hbCknKTtcbiAgICB9XG4gICAgQVBQLnhtcHAuam9pblJvb20ocm9vbU5hbWUsIGNvbmZpZy51c2VOaWNrcywgbmljayk7XG59O1xuXG5cbmZ1bmN0aW9uIGR1bXAoZWxlbSwgZmlsZW5hbWUpIHtcbiAgICBlbGVtID0gZWxlbS5wYXJlbnROb2RlO1xuICAgIGVsZW0uZG93bmxvYWQgPSBmaWxlbmFtZSB8fCAnbWVldGxvZy5qc29uJztcbiAgICBlbGVtLmhyZWYgPSAnZGF0YTphcHBsaWNhdGlvbi9qc29uO2NoYXJzZXQ9dXRmLTgsXFxuJztcbiAgICB2YXIgZGF0YSA9IEFQUC54bXBwLnBvcHVsYXRlRGF0YSgpO1xuICAgIHZhciBtZXRhZGF0YSA9IHt9O1xuICAgIG1ldGFkYXRhLnRpbWUgPSBuZXcgRGF0ZSgpO1xuICAgIG1ldGFkYXRhLnVybCA9IHdpbmRvdy5sb2NhdGlvbi5ocmVmO1xuICAgIG1ldGFkYXRhLnVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudDtcbiAgICB2YXIgbG9nID0gQVBQLnhtcHAuZ2V0TG9nZ2VyKCk7XG4gICAgaWYgKGxvZykge1xuICAgICAgICBtZXRhZGF0YS54bXBwID0gbG9nO1xuICAgIH1cbiAgICBkYXRhLm1ldGFkYXRhID0gbWV0YWRhdGE7XG4gICAgZWxlbS5ocmVmICs9IGVuY29kZVVSSUNvbXBvbmVudChKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAnICAnKSk7XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuXG5VSS5nZXRSb29tTmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gcm9vbU5hbWU7XG59O1xuXG4vKipcbiAqIE11dGVzL3VubXV0ZXMgdGhlIGxvY2FsIHZpZGVvLlxuICovXG5VSS50b2dnbGVWaWRlbyA9IGZ1bmN0aW9uICgpIHtcbiAgICBzZXRWaWRlb011dGUoIUFQUC5SVEMubG9jYWxWaWRlby5pc011dGVkKCkpO1xufTtcblxuLyoqXG4gKiBNdXRlcyAvIHVubXV0ZXMgYXVkaW8gZm9yIHRoZSBsb2NhbCBwYXJ0aWNpcGFudC5cbiAqL1xuVUkudG9nZ2xlQXVkaW8gPSBmdW5jdGlvbigpIHtcbiAgICBVSS5zZXRBdWRpb011dGVkKCFBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKTtcbn07XG5cbi8qKlxuICogU2V0cyBtdXRlZCBhdWRpbyBzdGF0ZSBmb3IgdGhlIGxvY2FsIHBhcnRpY2lwYW50LlxuICovXG5VSS5zZXRBdWRpb011dGVkID0gZnVuY3Rpb24gKG11dGUpIHtcblxuICAgIGlmKCFBUFAueG1wcC5zZXRBdWRpb011dGUobXV0ZSwgZnVuY3Rpb24gKCkge1xuICAgICAgICBWaWRlb0xheW91dC5zaG93TG9jYWxBdWRpb0luZGljYXRvcihtdXRlKTtcblxuICAgICAgICBVSVV0aWwuYnV0dG9uQ2xpY2soXCIjbXV0ZVwiLCBcImljb24tbWljcm9waG9uZSBpY29uLW1pYy1kaXNhYmxlZFwiKTtcbiAgICB9KSlcbiAgICB7XG4gICAgICAgIC8vIFdlIHN0aWxsIGNsaWNrIHRoZSBidXR0b24uXG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNtdXRlXCIsIFwiaWNvbi1taWNyb3Bob25lIGljb24tbWljLWRpc2FibGVkXCIpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG59XG5cblVJLmFkZExpc3RlbmVyID0gZnVuY3Rpb24gKHR5cGUsIGxpc3RlbmVyKSB7XG4gICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbn1cblxuVUkuY2xpY2tPblZpZGVvID0gZnVuY3Rpb24gKHZpZGVvTnVtYmVyKSB7XG4gICAgdmFyIHJlbW90ZVZpZGVvcyA9ICQoXCIudmlkZW9jb250YWluZXI6bm90KCNtaXhlZHN0cmVhbSlcIik7XG4gICAgaWYgKHJlbW90ZVZpZGVvcy5sZW5ndGggPiB2aWRlb051bWJlcikge1xuICAgICAgICByZW1vdGVWaWRlb3NbdmlkZW9OdW1iZXJdLmNsaWNrKCk7XG4gICAgfVxufVxuXG4vL1VzZWQgYnkgdG9ydHVyZVxuVUkuc2hvd1Rvb2xiYXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIFRvb2xiYXJUb2dnbGVyLnNob3dUb29sYmFyKCk7XG59XG5cbi8vVXNlZCBieSB0b3J0dXJlXG5VSS5kb2NrVG9vbGJhciA9IGZ1bmN0aW9uIChpc0RvY2spIHtcbiAgICByZXR1cm4gVG9vbGJhclRvZ2dsZXIuZG9ja1Rvb2xiYXIoaXNEb2NrKTtcbn1cblxuVUkuc2V0VmlkZW9NdXRlQnV0dG9uc1N0YXRlID0gZnVuY3Rpb24gKG11dGUpIHtcbiAgICB2YXIgdmlkZW8gPSAkKCcjdmlkZW8nKTtcbiAgICB2YXIgY29tbXVuaWNhdGl2ZUNsYXNzID0gXCJpY29uLWNhbWVyYVwiO1xuICAgIHZhciBtdXRlQ2xhc3MgPSBcImljb24tY2FtZXJhIGljb24tY2FtZXJhLWRpc2FibGVkXCI7XG5cbiAgICBpZiAobXV0ZSkge1xuICAgICAgICB2aWRlby5yZW1vdmVDbGFzcyhjb21tdW5pY2F0aXZlQ2xhc3MpO1xuICAgICAgICB2aWRlby5hZGRDbGFzcyhtdXRlQ2xhc3MpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHZpZGVvLnJlbW92ZUNsYXNzKG11dGVDbGFzcyk7XG4gICAgICAgIHZpZGVvLmFkZENsYXNzKGNvbW11bmljYXRpdmVDbGFzcyk7XG4gICAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFVJO1xuXG4iLCJ2YXIgQ2FudmFzVXRpbCA9IHJlcXVpcmUoXCIuL0NhbnZhc1V0aWxzXCIpO1xuXG52YXIgQVNEcmF3Q29udGV4dCA9ICQoJyNhY3RpdmVTcGVha2VyQXVkaW9MZXZlbCcpWzBdLmdldENvbnRleHQoJzJkJyk7XG5cbmZ1bmN0aW9uIGluaXRBY3RpdmVTcGVha2VyQXVkaW9MZXZlbHMoKSB7XG4gICAgdmFyIEFTUmFkaXVzID0gaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFIC8gMjtcbiAgICB2YXIgQVNDZW50ZXIgPSAoaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFICsgQVNSYWRpdXMpIC8gMjtcblxuLy8gRHJhdyBhIGNpcmNsZS5cbiAgICBBU0RyYXdDb250ZXh0LmFyYyhBU0NlbnRlciwgQVNDZW50ZXIsIEFTUmFkaXVzLCAwLCAyICogTWF0aC5QSSk7XG5cbi8vIEFkZCBhIHNoYWRvdyBhcm91bmQgdGhlIGNpcmNsZVxuICAgIEFTRHJhd0NvbnRleHQuc2hhZG93Q29sb3IgPSBpbnRlcmZhY2VDb25maWcuU0hBRE9XX0NPTE9SO1xuICAgIEFTRHJhd0NvbnRleHQuc2hhZG93T2Zmc2V0WCA9IDA7XG4gICAgQVNEcmF3Q29udGV4dC5zaGFkb3dPZmZzZXRZID0gMDtcbn1cblxuLyoqXG4gKiBUaGUgYXVkaW8gTGV2ZWxzIHBsdWdpbi5cbiAqL1xudmFyIEF1ZGlvTGV2ZWxzID0gKGZ1bmN0aW9uKG15KSB7XG4gICAgdmFyIGF1ZGlvTGV2ZWxDYW52YXNDYWNoZSA9IHt9O1xuXG4gICAgbXkuTE9DQUxfTEVWRUwgPSAnbG9jYWwnO1xuXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaW5pdEFjdGl2ZVNwZWFrZXJBdWRpb0xldmVscygpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGF1ZGlvIGxldmVsIGNhbnZhcyBmb3IgdGhlIGdpdmVuIHBlZXJKaWQuIElmIHRoZSBjYW52YXNcbiAgICAgKiBkaWRuJ3QgZXhpc3Qgd2UgY3JlYXRlIGl0LlxuICAgICAqL1xuICAgIG15LnVwZGF0ZUF1ZGlvTGV2ZWxDYW52YXMgPSBmdW5jdGlvbiAocGVlckppZCwgVmlkZW9MYXlvdXQpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gbnVsbDtcbiAgICAgICAgaWYgKCFwZWVySmlkKVxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHZpZGVvU3BhbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHZpZGVvU3BhbklkKTtcblxuICAgICAgICBpZiAoIXZpZGVvU3Bhbikge1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkKVxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyBlbGVtZW50IGZvciBqaWRcIiwgcmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyB2aWRlbyBlbGVtZW50IGZvciBsb2NhbCB2aWRlby5cIik7XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBhdWRpb0xldmVsQ2FudmFzID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+Y2FudmFzJyk7XG5cbiAgICAgICAgdmFyIHZpZGVvU3BhY2VXaWR0aCA9ICQoJyNyZW1vdGVWaWRlb3MnKS53aWR0aCgpO1xuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9TcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHRodW1ibmFpbFdpZHRoID0gdGh1bWJuYWlsU2l6ZVswXTtcbiAgICAgICAgdmFyIHRodW1ibmFpbEhlaWdodCA9IHRodW1ibmFpbFNpemVbMV07XG5cbiAgICAgICAgaWYgKCFhdWRpb0xldmVsQ2FudmFzIHx8IGF1ZGlvTGV2ZWxDYW52YXMubGVuZ3RoID09PSAwKSB7XG5cbiAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMuY2xhc3NOYW1lID0gXCJhdWRpb2xldmVsXCI7XG4gICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzLnN0eWxlLmJvdHRvbSA9IFwiLVwiICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yICsgXCJweFwiO1xuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcy5zdHlsZS5sZWZ0ID0gXCItXCIgKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIgKyBcInB4XCI7XG4gICAgICAgICAgICByZXNpemVBdWRpb0xldmVsQ2FudmFzKCBhdWRpb0xldmVsQ2FudmFzLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KTtcblxuICAgICAgICAgICAgdmlkZW9TcGFuLmFwcGVuZENoaWxkKGF1ZGlvTGV2ZWxDYW52YXMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcyA9IGF1ZGlvTGV2ZWxDYW52YXMuZ2V0KDApO1xuXG4gICAgICAgICAgICByZXNpemVBdWRpb0xldmVsQ2FudmFzKCBhdWRpb0xldmVsQ2FudmFzLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBhdWRpbyBsZXZlbCBVSSBmb3IgdGhlIGdpdmVuIHJlc291cmNlSmlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSByZXNvdXJjZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCBmb3JcbiAgICAgKiB3aGljaCB3ZSBkcmF3IHRoZSBhdWRpbyBsZXZlbFxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBuZXdBdWRpbyBsZXZlbCB0byByZW5kZXJcbiAgICAgKi9cbiAgICBteS51cGRhdGVBdWRpb0xldmVsID0gZnVuY3Rpb24gKHJlc291cmNlSmlkLCBhdWRpb0xldmVsLCBsYXJnZVZpZGVvUmVzb3VyY2VKaWQpIHtcbiAgICAgICAgZHJhd0F1ZGlvTGV2ZWxDYW52YXMocmVzb3VyY2VKaWQsIGF1ZGlvTGV2ZWwpO1xuXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IGdldFZpZGVvU3BhbklkKHJlc291cmNlSmlkKTtcblxuICAgICAgICB2YXIgYXVkaW9MZXZlbENhbnZhcyA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPmNhbnZhcycpLmdldCgwKTtcblxuICAgICAgICBpZiAoIWF1ZGlvTGV2ZWxDYW52YXMpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdmFyIGRyYXdDb250ZXh0ID0gYXVkaW9MZXZlbENhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuXG4gICAgICAgIHZhciBjYW52YXNDYWNoZSA9IGF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF07XG5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xlYXJSZWN0ICgwLCAwLFxuICAgICAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMud2lkdGgsIGF1ZGlvTGV2ZWxDYW52YXMuaGVpZ2h0KTtcbiAgICAgICAgZHJhd0NvbnRleHQuZHJhd0ltYWdlKGNhbnZhc0NhY2hlLCAwLCAwKTtcblxuICAgICAgICBpZihyZXNvdXJjZUppZCA9PT0gQXVkaW9MZXZlbHMuTE9DQUxfTEVWRUwpIHtcbiAgICAgICAgICAgIGlmKCFBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmVzb3VyY2VKaWQgPSBBUFAueG1wcC5teVJlc291cmNlKCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZihyZXNvdXJjZUppZCAgPT09IGxhcmdlVmlkZW9SZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgQXVkaW9MZXZlbHMudXBkYXRlQWN0aXZlU3BlYWtlckF1ZGlvTGV2ZWwoYXVkaW9MZXZlbCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBteS51cGRhdGVBY3RpdmVTcGVha2VyQXVkaW9MZXZlbCA9IGZ1bmN0aW9uKGF1ZGlvTGV2ZWwpIHtcbiAgICAgICAgaWYoJChcIiNhY3RpdmVTcGVha2VyXCIpLmNzcyhcInZpc2liaWxpdHlcIikgPT0gXCJoaWRkZW5cIilcbiAgICAgICAgICAgIHJldHVybjtcblxuXG4gICAgICAgIEFTRHJhd0NvbnRleHQuY2xlYXJSZWN0KDAsIDAsIDMwMCwgMzAwKTtcbiAgICAgICAgaWYoYXVkaW9MZXZlbCA9PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIEFTRHJhd0NvbnRleHQuc2hhZG93Qmx1ciA9IGdldFNoYWRvd0xldmVsKGF1ZGlvTGV2ZWwpO1xuXG5cbiAgICAgICAgLy8gRmlsbCB0aGUgc2hhcGUuXG4gICAgICAgIEFTRHJhd0NvbnRleHQuZmlsbCgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRoZSBnaXZlbiBhdWRpbyBsZXZlbCBjYW52YXMgdG8gbWF0Y2ggdGhlIGdpdmVuIHRodW1ibmFpbCBzaXplLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHJlc2l6ZUF1ZGlvTGV2ZWxDYW52YXMoYXVkaW9MZXZlbENhbnZhcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRodW1ibmFpbFdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGh1bWJuYWlsSGVpZ2h0KSB7XG4gICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMud2lkdGggPSB0aHVtYm5haWxXaWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMuaGVpZ2h0ID0gdGh1bWJuYWlsSGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgYXVkaW8gbGV2ZWwgY2FudmFzIGludG8gdGhlIGNhY2hlZCBjYW52YXMgb2JqZWN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSByZXNvdXJjZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCBmb3JcbiAgICAgKiB3aGljaCB3ZSBkcmF3IHRoZSBhdWRpbyBsZXZlbFxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBuZXdBdWRpbyBsZXZlbCB0byByZW5kZXJcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBkcmF3QXVkaW9MZXZlbENhbnZhcyhyZXNvdXJjZUppZCwgYXVkaW9MZXZlbCkge1xuICAgICAgICBpZiAoIWF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF0pIHtcblxuICAgICAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gZ2V0VmlkZW9TcGFuSWQocmVzb3VyY2VKaWQpO1xuXG4gICAgICAgICAgICB2YXIgYXVkaW9MZXZlbENhbnZhc09yaWcgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5jYW52YXMnKS5nZXQoMCk7XG5cbiAgICAgICAgICAgIC8qXG4gICAgICAgICAgICAgKiBGSVhNRSBUZXN0aW5nIGhhcyBzaG93biB0aGF0IGF1ZGlvTGV2ZWxDYW52YXNPcmlnIG1heSBub3QgZXhpc3QuXG4gICAgICAgICAgICAgKiBJbiBzdWNoIGEgY2FzZSwgdGhlIG1ldGhvZCBDYW52YXNVdGlsLmNsb25lQ2FudmFzIG1heSB0aHJvdyBhblxuICAgICAgICAgICAgICogZXJyb3IuIFNpbmNlIGF1ZGlvIGxldmVscyBhcmUgZnJlcXVlbnRseSB1cGRhdGVkLCB0aGUgZXJyb3JzIGhhdmVcbiAgICAgICAgICAgICAqIGJlZW4gb2JzZXJ2ZWQgdG8gcGlsZSBpbnRvIHRoZSBjb25zb2xlLCBzdHJhaW4gdGhlIENQVS5cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgaWYgKGF1ZGlvTGV2ZWxDYW52YXNPcmlnKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF1cbiAgICAgICAgICAgICAgICAgICAgPSBDYW52YXNVdGlsLmNsb25lQ2FudmFzKGF1ZGlvTGV2ZWxDYW52YXNPcmlnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBjYW52YXMgPSBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdO1xuXG4gICAgICAgIGlmICghY2FudmFzKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBkcmF3Q29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LmNsZWFyUmVjdCgwLCAwLCBjYW52YXMud2lkdGgsIGNhbnZhcy5oZWlnaHQpO1xuXG4gICAgICAgIHZhciBzaGFkb3dMZXZlbCA9IGdldFNoYWRvd0xldmVsKGF1ZGlvTGV2ZWwpO1xuXG4gICAgICAgIGlmIChzaGFkb3dMZXZlbCA+IDApXG4gICAgICAgICAgICAvLyBkcmF3Q29udGV4dCwgeCwgeSwgdywgaCwgciwgc2hhZG93Q29sb3IsIHNoYWRvd0xldmVsXG4gICAgICAgICAgICBDYW52YXNVdGlsLmRyYXdSb3VuZFJlY3RHbG93KCAgIGRyYXdDb250ZXh0LFxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiwgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yLFxuICAgICAgICAgICAgICAgIGNhbnZhcy53aWR0aCAtIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEsXG4gICAgICAgICAgICAgICAgY2FudmFzLmhlaWdodCAtIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEsXG4gICAgICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19SQURJVVMsXG4gICAgICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLlNIQURPV19DT0xPUixcbiAgICAgICAgICAgICAgICBzaGFkb3dMZXZlbCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2hhZG93L2dsb3cgbGV2ZWwgZm9yIHRoZSBnaXZlbiBhdWRpbyBsZXZlbC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBhdWRpb0xldmVsIHRoZSBhdWRpbyBsZXZlbCBmcm9tIHdoaWNoIHdlIGRldGVybWluZSB0aGUgc2hhZG93XG4gICAgICogbGV2ZWxcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBnZXRTaGFkb3dMZXZlbCAoYXVkaW9MZXZlbCkge1xuICAgICAgICB2YXIgc2hhZG93TGV2ZWwgPSAwO1xuXG4gICAgICAgIGlmIChhdWRpb0xldmVsIDw9IDAuMykge1xuICAgICAgICAgICAgc2hhZG93TGV2ZWwgPSBNYXRoLnJvdW5kKGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiooYXVkaW9MZXZlbC8wLjMpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChhdWRpb0xldmVsIDw9IDAuNikge1xuICAgICAgICAgICAgc2hhZG93TGV2ZWwgPSBNYXRoLnJvdW5kKGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiooKGF1ZGlvTGV2ZWwgLSAwLjMpIC8gMC4zKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBzaGFkb3dMZXZlbCA9IE1hdGgucm91bmQoaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQS8yKigoYXVkaW9MZXZlbCAtIDAuNikgLyAwLjQpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gc2hhZG93TGV2ZWw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgdmlkZW8gc3BhbiBpZCBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiByZXNvdXJjZUppZCBvciBsb2NhbFxuICAgICAqIHVzZXIuXG4gICAgICovXG4gICAgZnVuY3Rpb24gZ2V0VmlkZW9TcGFuSWQocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gbnVsbDtcbiAgICAgICAgaWYgKHJlc291cmNlSmlkID09PSBBdWRpb0xldmVscy5MT0NBTF9MRVZFTFxuICAgICAgICAgICAgICAgIHx8IChBUFAueG1wcC5teVJlc291cmNlKCkgJiYgcmVzb3VyY2VKaWRcbiAgICAgICAgICAgICAgICAgICAgPT09IEFQUC54bXBwLm15UmVzb3VyY2UoKSkpXG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdsb2NhbFZpZGVvQ29udGFpbmVyJztcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuXG4gICAgICAgIHJldHVybiB2aWRlb1NwYW5JZDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgdGhhdCB0aGUgcmVtb3RlIHZpZGVvIGhhcyBiZWVuIHJlc2l6ZWQuXG4gICAgICovXG4gICAgJChkb2N1bWVudCkuYmluZCgncmVtb3RldmlkZW8ucmVzaXplZCcsIGZ1bmN0aW9uIChldmVudCwgd2lkdGgsIGhlaWdodCkge1xuICAgICAgICB2YXIgcmVzaXplZCA9IGZhbHNlO1xuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4+Y2FudmFzJykuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHZhciBjYW52YXMgPSAkKHRoaXMpLmdldCgwKTtcbiAgICAgICAgICAgIGlmIChjYW52YXMud2lkdGggIT09IHdpZHRoICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQSkge1xuICAgICAgICAgICAgICAgIGNhbnZhcy53aWR0aCA9IHdpZHRoICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgICAgICByZXNpemVkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGNhbnZhcy5oZWlnaCAhPT0gaGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQSkge1xuICAgICAgICAgICAgICAgIGNhbnZhcy5oZWlnaHQgPSBoZWlnaHQgKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBO1xuICAgICAgICAgICAgICAgIHJlc2l6ZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAocmVzaXplZClcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKGF1ZGlvTGV2ZWxDYW52YXNDYWNoZSkuZm9yRWFjaChmdW5jdGlvbiAocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdLndpZHRoXG4gICAgICAgICAgICAgICAgICAgID0gd2lkdGggKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBO1xuICAgICAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXNDYWNoZVtyZXNvdXJjZUppZF0uaGVpZ2h0XG4gICAgICAgICAgICAgICAgICAgID0gaGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIG15O1xuXG59KShBdWRpb0xldmVscyB8fCB7fSk7XG5cbm1vZHVsZS5leHBvcnRzID0gQXVkaW9MZXZlbHM7IiwiLyoqXG4gKiBVdGlsaXR5IGNsYXNzIGZvciBkcmF3aW5nIGNhbnZhcyBzaGFwZXMuXG4gKi9cbnZhciBDYW52YXNVdGlsID0gKGZ1bmN0aW9uKG15KSB7XG5cbiAgICAvKipcbiAgICAgKiBEcmF3cyBhIHJvdW5kIHJlY3RhbmdsZSB3aXRoIGEgZ2xvdy4gVGhlIGdsb3dXaWR0aCBpbmRpY2F0ZXMgdGhlIGRlcHRoXG4gICAgICogb2YgdGhlIGdsb3cuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZHJhd0NvbnRleHQgdGhlIGNvbnRleHQgb2YgdGhlIGNhbnZhcyB0byBkcmF3IHRvXG4gICAgICogQHBhcmFtIHggdGhlIHggY29vcmRpbmF0ZSBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIHkgdGhlIHkgY29vcmRpbmF0ZSBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIHcgdGhlIHdpZHRoIG9mIHRoZSByb3VuZCByZWN0YW5nbGVcbiAgICAgKiBAcGFyYW0gaCB0aGUgaGVpZ2h0IG9mIHRoZSByb3VuZCByZWN0YW5nbGVcbiAgICAgKiBAcGFyYW0gZ2xvd0NvbG9yIHRoZSBjb2xvciBvZiB0aGUgZ2xvd1xuICAgICAqIEBwYXJhbSBnbG93V2lkdGggdGhlIHdpZHRoIG9mIHRoZSBnbG93XG4gICAgICovXG4gICAgbXkuZHJhd1JvdW5kUmVjdEdsb3dcbiAgICAgICAgPSBmdW5jdGlvbihkcmF3Q29udGV4dCwgeCwgeSwgdywgaCwgciwgZ2xvd0NvbG9yLCBnbG93V2lkdGgpIHtcblxuICAgICAgICAvLyBTYXZlIHRoZSBwcmV2aW91cyBzdGF0ZSBvZiB0aGUgY29udGV4dC5cbiAgICAgICAgZHJhd0NvbnRleHQuc2F2ZSgpO1xuXG4gICAgICAgIGlmICh3IDwgMiAqIHIpIHIgPSB3IC8gMjtcbiAgICAgICAgaWYgKGggPCAyICogcikgciA9IGggLyAyO1xuXG4gICAgICAgIC8vIERyYXcgYSByb3VuZCByZWN0YW5nbGUuXG4gICAgICAgIGRyYXdDb250ZXh0LmJlZ2luUGF0aCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5tb3ZlVG8oeCtyLCB5KTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5LCAgIHgrdywgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5K2gsIHgsICAgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5K2gsIHgsICAgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5LCAgIHgrdywgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuY2xvc2VQYXRoKCk7XG5cbiAgICAgICAgLy8gQWRkIGEgc2hhZG93IGFyb3VuZCB0aGUgcmVjdGFuZ2xlXG4gICAgICAgIGRyYXdDb250ZXh0LnNoYWRvd0NvbG9yID0gZ2xvd0NvbG9yO1xuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dCbHVyID0gZ2xvd1dpZHRoO1xuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dPZmZzZXRYID0gMDtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93T2Zmc2V0WSA9IDA7XG5cbiAgICAgICAgLy8gRmlsbCB0aGUgc2hhcGUuXG4gICAgICAgIGRyYXdDb250ZXh0LmZpbGwoKTtcblxuICAgICAgICBkcmF3Q29udGV4dC5zYXZlKCk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQucmVzdG9yZSgpO1xuXG4vLyAgICAgIDEpIFVuY29tbWVudCB0aGlzIGxpbmUgdG8gdXNlIENvbXBvc2l0ZSBPcGVyYXRpb24sIHdoaWNoIGlzIGRvaW5nIHRoZVxuLy8gICAgICBzYW1lIGFzIHRoZSBjbGlwIGZ1bmN0aW9uIGJlbG93IGFuZCBpcyBhbHNvIGFudGlhbGlhc2luZyB0aGUgcm91bmRcbi8vICAgICAgYm9yZGVyLCBidXQgaXMgc2FpZCB0byBiZSBsZXNzIGZhc3QgcGVyZm9ybWFuY2Ugd2lzZS5cblxuLy8gICAgICBkcmF3Q29udGV4dC5nbG9iYWxDb21wb3NpdGVPcGVyYXRpb249J2Rlc3RpbmF0aW9uLW91dCc7XG5cbiAgICAgICAgZHJhd0NvbnRleHQuYmVnaW5QYXRoKCk7XG4gICAgICAgIGRyYXdDb250ZXh0Lm1vdmVUbyh4K3IsIHkpO1xuICAgICAgICBkcmF3Q29udGV4dC5hcmNUbyh4K3csIHksICAgeCt3LCB5K2gsIHIpO1xuICAgICAgICBkcmF3Q29udGV4dC5hcmNUbyh4K3csIHkraCwgeCwgICB5K2gsIHIpO1xuICAgICAgICBkcmF3Q29udGV4dC5hcmNUbyh4LCAgIHkraCwgeCwgICB5LCAgIHIpO1xuICAgICAgICBkcmF3Q29udGV4dC5hcmNUbyh4LCAgIHksICAgeCt3LCB5LCAgIHIpO1xuICAgICAgICBkcmF3Q29udGV4dC5jbG9zZVBhdGgoKTtcblxuLy8gICAgICAyKSBVbmNvbW1lbnQgdGhpcyBsaW5lIHRvIHVzZSBDb21wb3NpdGUgT3BlcmF0aW9uLCB3aGljaCBpcyBkb2luZyB0aGVcbi8vICAgICAgc2FtZSBhcyB0aGUgY2xpcCBmdW5jdGlvbiBiZWxvdyBhbmQgaXMgYWxzbyBhbnRpYWxpYXNpbmcgdGhlIHJvdW5kXG4vLyAgICAgIGJvcmRlciwgYnV0IGlzIHNhaWQgdG8gYmUgbGVzcyBmYXN0IHBlcmZvcm1hbmNlIHdpc2UuXG5cbi8vICAgICAgZHJhd0NvbnRleHQuZmlsbCgpO1xuXG4gICAgICAgIC8vIENvbW1lbnQgdGhlc2UgdHdvIGxpbmVzIGlmIGNob29zaW5nIHRvIGRvIHRoZSBzYW1lIHdpdGggY29tcG9zaXRlXG4gICAgICAgIC8vIG9wZXJhdGlvbiBhYm92ZSAxIGFuZCAyLlxuICAgICAgICBkcmF3Q29udGV4dC5jbGlwKCk7XG4gICAgICAgIGRyYXdDb250ZXh0LmNsZWFyUmVjdCgwLCAwLCAyNzcsIDIwMCk7XG5cbiAgICAgICAgLy8gUmVzdG9yZSB0aGUgcHJldmlvdXMgY29udGV4dCBzdGF0ZS5cbiAgICAgICAgZHJhd0NvbnRleHQucmVzdG9yZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDbG9uZXMgdGhlIGdpdmVuIGNhbnZhcy5cbiAgICAgKlxuICAgICAqIEByZXR1cm4gdGhlIG5ldyBjbG9uZWQgY2FudmFzLlxuICAgICAqL1xuICAgIG15LmNsb25lQ2FudmFzID0gZnVuY3Rpb24gKG9sZENhbnZhcykge1xuICAgICAgICAvKlxuICAgICAgICAgKiBGSVhNRSBUZXN0aW5nIGhhcyBzaG93biB0aGF0IG9sZENhbnZhcyBtYXkgbm90IGV4aXN0LiBJbiBzdWNoIGEgY2FzZSxcbiAgICAgICAgICogdGhlIG1ldGhvZCBDYW52YXNVdGlsLmNsb25lQ2FudmFzIG1heSB0aHJvdyBhbiBlcnJvci4gU2luY2UgYXVkaW9cbiAgICAgICAgICogbGV2ZWxzIGFyZSBmcmVxdWVudGx5IHVwZGF0ZWQsIHRoZSBlcnJvcnMgaGF2ZSBiZWVuIG9ic2VydmVkIHRvIHBpbGVcbiAgICAgICAgICogaW50byB0aGUgY29uc29sZSwgc3RyYWluIHRoZSBDUFUuXG4gICAgICAgICAqL1xuICAgICAgICBpZiAoIW9sZENhbnZhcylcbiAgICAgICAgICAgIHJldHVybiBvbGRDYW52YXM7XG5cbiAgICAgICAgLy9jcmVhdGUgYSBuZXcgY2FudmFzXG4gICAgICAgIHZhciBuZXdDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgICAgdmFyIGNvbnRleHQgPSBuZXdDYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcblxuICAgICAgICAvL3NldCBkaW1lbnNpb25zXG4gICAgICAgIG5ld0NhbnZhcy53aWR0aCA9IG9sZENhbnZhcy53aWR0aDtcbiAgICAgICAgbmV3Q2FudmFzLmhlaWdodCA9IG9sZENhbnZhcy5oZWlnaHQ7XG5cbiAgICAgICAgLy9hcHBseSB0aGUgb2xkIGNhbnZhcyB0byB0aGUgbmV3IG9uZVxuICAgICAgICBjb250ZXh0LmRyYXdJbWFnZShvbGRDYW52YXMsIDAsIDApO1xuXG4gICAgICAgIC8vcmV0dXJuIHRoZSBuZXcgY2FudmFzXG4gICAgICAgIHJldHVybiBuZXdDYW52YXM7XG4gICAgfTtcblxuICAgIHJldHVybiBteTtcbn0pKENhbnZhc1V0aWwgfHwge30pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IENhbnZhc1V0aWw7IiwiLyogZ2xvYmFsICQsIEFQUCovXG5cbnZhciBMb2dpbkRpYWxvZyA9IHJlcXVpcmUoJy4vTG9naW5EaWFsb2cnKTtcbnZhciBNb2RlcmF0b3IgPSByZXF1aXJlKCcuLi8uLi94bXBwL21vZGVyYXRvcicpO1xuXG4vKiBJbml0aWFsIFwiYXV0aGVudGljYXRpb24gcmVxdWlyZWRcIiBkaWFsb2cgKi9cbnZhciBhdXRoRGlhbG9nID0gbnVsbDtcbi8qIExvb3AgcmV0cnkgSUQgdGhhdCB3aXRzIGZvciBvdGhlciB1c2VyIHRvIGNyZWF0ZSB0aGUgcm9vbSAqL1xudmFyIGF1dGhSZXRyeUlkID0gbnVsbDtcbnZhciBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG5cbnZhciBBdXRoZW50aWNhdGlvbiA9IHtcbiAgICBvcGVuQXV0aGVudGljYXRpb25EaWFsb2c6IGZ1bmN0aW9uIChyb29tTmFtZSwgaW50ZXJ2YWxDYWxsYmFjaywgY2FsbGJhY2spIHtcbiAgICAgICAgLy8gVGhpcyBpcyB0aGUgbG9vcCB0aGF0IHdpbGwgd2FpdCBmb3IgdGhlIHJvb20gdG8gYmUgY3JlYXRlZCBieVxuICAgICAgICAvLyBzb21lb25lIGVsc2UuICdhdXRoX3JlcXVpcmVkLm1vZGVyYXRvcicgd2lsbCBicmluZyB1cyBiYWNrIGhlcmUuXG4gICAgICAgIGF1dGhSZXRyeUlkID0gd2luZG93LnNldFRpbWVvdXQoaW50ZXJ2YWxDYWxsYmFjaywgNTAwMCk7XG4gICAgICAgIC8vIFNob3cgcHJvbXB0IG9ubHkgaWYgaXQncyBub3Qgb3BlblxuICAgICAgICBpZiAoYXV0aERpYWxvZyAhPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIGV4dHJhY3Qgcm9vbSBuYW1lIGZyb20gJ3Jvb21AbXVjLnNlcnZlci5uZXQnXG4gICAgICAgIHZhciByb29tID0gcm9vbU5hbWUuc3Vic3RyKDAsIHJvb21OYW1lLmluZGV4T2YoJ0AnKSk7XG5cbiAgICAgICAgdmFyIHRpdGxlID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXCJkaWFsb2cuU3RvcFwiKTtcbiAgICAgICAgdmFyIG1zZyA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwiZGlhbG9nLkF1dGhNc2dcIixcbiAgICAgICAgICAgIHtyb29tOiByb29tfSk7XG5cbiAgICAgICAgdmFyIGJ1dHRvblR4dFxuICAgICAgICAgICAgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcImRpYWxvZy5BdXRoZW50aWNhdGVcIik7XG4gICAgICAgIHZhciBidXR0b25zID0gW107XG4gICAgICAgIGJ1dHRvbnMucHVzaCh7dGl0bGU6IGJ1dHRvblR4dCwgdmFsdWU6IFwiYXV0aE5vd1wifSk7XG5cbiAgICAgICAgYXV0aERpYWxvZyA9IEFQUC5VSS5tZXNzYWdlSGFuZGxlci5vcGVuRGlhbG9nKFxuICAgICAgICAgICAgdGl0bGUsXG4gICAgICAgICAgICBtc2csXG4gICAgICAgICAgICB0cnVlLFxuICAgICAgICAgICAgYnV0dG9ucyxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChvblN1Ym1pdEV2ZW50LCBzdWJtaXRWYWx1ZSkge1xuXG4gICAgICAgICAgICAgICAgLy8gRG8gbm90IGNsb3NlIHRoZSBkaWFsb2cgeWV0XG4gICAgICAgICAgICAgICAgb25TdWJtaXRFdmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG4gICAgICAgICAgICAgICAgLy8gT3BlbiBsb2dpbiBwb3B1cFxuICAgICAgICAgICAgICAgIGlmIChzdWJtaXRWYWx1ZSA9PT0gJ2F1dGhOb3cnKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sXG4gICAgY2xvc2VBdXRoZW50aWNhdGlvbldpbmRvdzogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoYXV0aGVudGljYXRpb25XaW5kb3cpIHtcbiAgICAgICAgICAgIGF1dGhlbnRpY2F0aW9uV2luZG93LmNsb3NlKCk7XG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHhtcHBBdXRoZW50aWNhdGU6IGZ1bmN0aW9uICgpIHtcblxuICAgICAgICB2YXIgbG9naW5EaWFsb2cgPSBMb2dpbkRpYWxvZy5zaG93KFxuICAgICAgICAgICAgZnVuY3Rpb24gKGNvbm5lY3Rpb24sIHN0YXRlKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFzdGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBVc2VyIGNhbmNlbGxlZFxuICAgICAgICAgICAgICAgICAgICBsb2dpbkRpYWxvZy5jbG9zZSgpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PSBBUFAueG1wcC5TdGF0dXMuQ09OTkVDVEVEKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgbG9naW5EaWFsb2cuY2xvc2UoKTtcblxuICAgICAgICAgICAgICAgICAgICBBdXRoZW50aWNhdGlvbi5zdG9wSW50ZXJ2YWwoKTtcbiAgICAgICAgICAgICAgICAgICAgQXV0aGVudGljYXRpb24uY2xvc2VBdXRoZW50aWNhdGlvbkRpYWxvZygpO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIENsb3NlIHRoZSBjb25uZWN0aW9uIGFzIGFub255bW91cyBvbmUgd2lsbCBiZSB1c2VkXG4gICAgICAgICAgICAgICAgICAgIC8vIHRvIGNyZWF0ZSB0aGUgY29uZmVyZW5jZS4gU2Vzc2lvbi1pZCB3aWxsIGF1dGhvcml6ZVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgcmVxdWVzdC5cbiAgICAgICAgICAgICAgICAgICAgY29ubmVjdGlvbi5kaXNjb25uZWN0KCk7XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHJvb21OYW1lID0gQVBQLlVJLmdlbmVyYXRlUm9vbU5hbWUoKTtcbiAgICAgICAgICAgICAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBpdCdzIG5vdCBcIm9uIHRoZSBmbHlcIiBhdXRoZW50aWNhdGlvbiBub3cgam9pblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGNvbmZlcmVuY2Ugcm9vbVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFBUFAueG1wcC5nZXRNVUNKb2luZWQoKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5VSS5jaGVja0Zvck5pY2tuYW1lQW5kSm9pbigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCB0cnVlKTtcbiAgICB9LFxuICAgIGZvY3VzQXV0aGVudGljYXRpb25XaW5kb3c6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gSWYgYXV0aCB3aW5kb3cgZXhpc3RzIGp1c3QgYnJpbmcgaXQgdG8gdGhlIGZyb250XG4gICAgICAgIGlmIChhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgYXV0aGVudGljYXRpb25XaW5kb3cuZm9jdXMoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgY2xvc2VBdXRoZW50aWNhdGlvbkRpYWxvZzogZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBDbG9zZSBhdXRoZW50aWNhdGlvbiBkaWFsb2cgaWYgb3BlbmVkXG4gICAgICAgIGlmIChhdXRoRGlhbG9nKSB7XG4gICAgICAgICAgICBhdXRoRGlhbG9nLmNsb3NlKCk7XG4gICAgICAgICAgICBhdXRoRGlhbG9nID0gbnVsbDtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgY3JlYXRlQXV0aGVudGljYXRpb25XaW5kb3c6IGZ1bmN0aW9uIChjYWxsYmFjaywgdXJsKSB7XG4gICAgICAgIGF1dGhlbnRpY2F0aW9uV2luZG93ID0gQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLm9wZW5DZW50ZXJlZFBvcHVwKFxuICAgICAgICAgICAgdXJsLCA5MTAsIDY2MCxcbiAgICAgICAgICAgIC8vIE9uIGNsb3NlZFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIC8vIENsb3NlIGF1dGhlbnRpY2F0aW9uIGRpYWxvZyBpZiBvcGVuZWRcbiAgICAgICAgICAgICAgICBBdXRoZW50aWNhdGlvbi5jbG9zZUF1dGhlbnRpY2F0aW9uRGlhbG9nKCk7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGF1dGhlbnRpY2F0aW9uV2luZG93O1xuICAgIH0sXG4gICAgc3RvcEludGVydmFsOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIENsZWFyIHJldHJ5IGludGVydmFsLCBzbyB0aGF0IHdlIGRvbid0IGNhbGwgJ2RvSm9pbkFmdGVyRm9jdXMnIHR3aWNlXG4gICAgICAgIGlmIChhdXRoUmV0cnlJZCkge1xuICAgICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dChhdXRoUmV0cnlJZCk7XG4gICAgICAgICAgICBhdXRoUmV0cnlJZCA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEF1dGhlbnRpY2F0aW9uOyIsIi8qIGdsb2JhbCAkLCBBUFAsIGNvbmZpZyovXG5cbnZhciBYTVBQID0gcmVxdWlyZSgnLi4vLi4veG1wcC94bXBwJyk7XG52YXIgTW9kZXJhdG9yID0gcmVxdWlyZSgnLi4vLi4veG1wcC9tb2RlcmF0b3InKTtcblxuLy9GSVhNRTogdXNlIExvZ2luRGlhbG9nIHRvIGFkZCByZXRyaWVzIHRvIFhNUFAuY29ubmVjdCBtZXRob2QgdXNlZCB3aGVuXG4vLyBhbm9ueW1vdXMgZG9tYWluIGlzIG5vdCBlbmFibGVkXG5cbi8qKlxuICogQ3JlYXRlcyBuZXcgPHR0PkRpYWxvZzwvdHQ+IGluc3RhbmNlLlxuICogQHBhcmFtIGNhbGxiYWNrIDx0dD5mdW5jdGlvbihTdHJvcGhlLkNvbm5lY3Rpb24sIFN0cm9waGUuU3RhdHVzKTwvdHQ+IGNhbGxlZFxuICogICAgICAgIHdoZW4gd2UgZWl0aGVyIGZhaWwgdG8gY29ubmVjdCBvciBzdWNjZWVkKGNoZWNrIFN0cm9waGUuU3RhdHVzKS5cbiAqIEBwYXJhbSBvYnRhaW5TZXNzaW9uIDx0dD50cnVlPC90dD4gaWYgd2Ugd2FudCB0byBzZW5kIENvbmZlcmVuY2VJUSB0byBKaWNvZm9cbiAqICAgICAgICBpbiBvcmRlciB0byBjcmVhdGUgc2Vzc2lvbi1pZCBhZnRlciB0aGUgY29ubmVjdGlvbiBpcyBlc3RhYmxpc2hlZC5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBEaWFsb2coY2FsbGJhY2ssIG9idGFpblNlc3Npb24pIHtcblxuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIHZhciBzdG9wID0gZmFsc2U7XG5cbiAgICB2YXIgY29ubmVjdGlvbiA9IEFQUC54bXBwLmNyZWF0ZUNvbm5lY3Rpb24oKTtcblxuICAgIHZhciBtZXNzYWdlID0gJzxoMiBkYXRhLWkxOG49XCJkaWFsb2cucGFzc3dvcmRSZXF1aXJlZFwiPic7XG4gICAgbWVzc2FnZSArPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFwiZGlhbG9nLnBhc3N3b3JkUmVxdWlyZWRcIik7XG4gICAgbWVzc2FnZSArPSAnPC9oMj4nICtcbiAgICAgICAgJzxpbnB1dCBuYW1lPVwidXNlcm5hbWVcIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAncGxhY2Vob2xkZXI9XCJ1c2VyQGRvbWFpbi5uZXRcIiBhdXRvZm9jdXM+JyArXG4gICAgICAgICc8aW5wdXQgbmFtZT1cInBhc3N3b3JkXCIgJyArXG4gICAgICAgICd0eXBlPVwicGFzc3dvcmRcIiBkYXRhLWkxOG49XCJbcGxhY2Vob2xkZXJdZGlhbG9nLnVzZXJQYXNzd29yZFwiJyArXG4gICAgICAgICcgcGxhY2Vob2xkZXI9XCJ1c2VyIHBhc3N3b3JkXCI+JztcblxuICAgIHZhciBva0J1dHRvbiA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwiZGlhbG9nLk9rXCIpO1xuXG4gICAgdmFyIGNhbmNlbEJ1dHRvbiA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwiZGlhbG9nLkNhbmNlbFwiKTtcblxuICAgIHZhciBzdGF0ZXMgPSB7XG4gICAgICAgIGxvZ2luOiB7XG4gICAgICAgICAgICBodG1sOiBtZXNzYWdlLFxuICAgICAgICAgICAgYnV0dG9uczogW1xuICAgICAgICAgICAgICAgIHsgdGl0bGU6IG9rQnV0dG9uLCB2YWx1ZTogdHJ1ZX0sXG4gICAgICAgICAgICAgICAgeyB0aXRsZTogY2FuY2VsQnV0dG9uLCB2YWx1ZTogZmFsc2V9XG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgZm9jdXM6ICc6aW5wdXQ6Zmlyc3QnLFxuICAgICAgICAgICAgc3VibWl0OiBmdW5jdGlvbiAoZSwgdiwgbSwgZikge1xuICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICB2YXIgamlkID0gZi51c2VybmFtZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhc3N3b3JkID0gZi5wYXNzd29yZDtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGppZCAmJiBwYXNzd29yZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc3RvcCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29ubmVjdGlvbi5yZXNldCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29ubkRpYWxvZy5nb1RvU3RhdGUoJ2Nvbm5lY3RpbmcnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uY29ubmVjdChqaWQsIHBhc3N3b3JkLCBzdGF0ZUhhbmRsZXIpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVXNlciBjYW5jZWxsZWRcbiAgICAgICAgICAgICAgICAgICAgc3RvcCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBjb25uZWN0aW5nOiB7XG4gICAgICAgICAgICB0aXRsZTogQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZygnZGlhbG9nLmNvbm5lY3RpbmcnKSxcbiAgICAgICAgICAgIGh0bWw6ICAgJzxkaXYgaWQ9XCJjb25uZWN0aW9uU3RhdHVzXCI+PC9kaXY+JyxcbiAgICAgICAgICAgIGJ1dHRvbnM6IFtdLFxuICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMFxuICAgICAgICB9LFxuICAgICAgICBmaW5pc2hlZDoge1xuICAgICAgICAgICAgdGl0bGU6IEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVTdHJpbmcoJ2RpYWxvZy5lcnJvcicpLFxuICAgICAgICAgICAgaHRtbDogICAnPGRpdiBpZD1cImVycm9yTWVzc2FnZVwiPjwvZGl2PicsXG4gICAgICAgICAgICBidXR0b25zOiBbXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB0aXRsZTogQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZygnZGlhbG9nLnJldHJ5JyksXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlOiAncmV0cnknXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHRpdGxlOiBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKCdkaWFsb2cuQ2FuY2VsJyksXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlOiAnY2FuY2VsJ1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMCxcbiAgICAgICAgICAgIHN1Ym1pdDogZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgaWYgKHYgPT09ICdyZXRyeScpXG4gICAgICAgICAgICAgICAgICAgIGNvbm5EaWFsb2cuZ29Ub1N0YXRlKCdsb2dpbicpO1xuICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB2YXIgY29ubkRpYWxvZ1xuICAgICAgICA9IEFQUC5VSS5tZXNzYWdlSGFuZGxlci5vcGVuRGlhbG9nV2l0aFN0YXRlcyhzdGF0ZXMsXG4gICAgICAgICAgICAgICAgeyBwZXJzaXN0ZW50OiB0cnVlLCBjbG9zZVRleHQ6ICcnIH0sIG51bGwpO1xuXG4gICAgdmFyIHN0YXRlSGFuZGxlciA9IGZ1bmN0aW9uIChzdGF0dXMsIG1lc3NhZ2UpIHtcbiAgICAgICAgaWYgKHN0b3ApIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciB0cmFuc2xhdGVLZXkgPSBcImNvbm5lY3Rpb24uXCIgKyBYTVBQLmdldFN0YXR1c1N0cmluZyhzdGF0dXMpO1xuICAgICAgICB2YXIgc3RhdHVzU3RyID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyh0cmFuc2xhdGVLZXkpO1xuXG4gICAgICAgIC8vIERpc3BsYXkgY3VycmVudCBzdGF0ZVxuICAgICAgICB2YXIgY29ubmVjdGlvblN0YXR1cyA9XG4gICAgICAgICAgICBjb25uRGlhbG9nLmdldFN0YXRlKCdjb25uZWN0aW5nJykuZmluZCgnI2Nvbm5lY3Rpb25TdGF0dXMnKTtcblxuICAgICAgICBjb25uZWN0aW9uU3RhdHVzLnRleHQoc3RhdHVzU3RyKTtcblxuICAgICAgICBzd2l0Y2ggKHN0YXR1cykge1xuICAgICAgICAgICAgY2FzZSBYTVBQLlN0YXR1cy5DT05ORUNURUQ6XG5cbiAgICAgICAgICAgICAgICBzdG9wID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAoIW9idGFpblNlc3Npb24pIHtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soY29ubmVjdGlvbiwgc3RhdHVzKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBPYnRhaW5pbmcgc2Vzc2lvbi1pZCBzdGF0dXNcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uU3RhdHVzLnRleHQoXG4gICAgICAgICAgICAgICAgICAgIEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVTdHJpbmcoXG4gICAgICAgICAgICAgICAgICAgICAgICAnY29ubmVjdGlvbi5GRVRDSF9TRVNTSU9OX0lEJykpO1xuXG4gICAgICAgICAgICAgICAgLy8gQXV0aGVudGljYXRlIHdpdGggSmljb2ZvIGFuZCBvYnRhaW4gc2Vzc2lvbi1pZFxuICAgICAgICAgICAgICAgIHZhciByb29tTmFtZSA9IEFQUC5VSS5nZW5lcmF0ZVJvb21OYW1lKCk7XG5cbiAgICAgICAgICAgICAgICAvLyBKaWNvZm8gd2lsbCByZXR1cm4gbmV3IHNlc3Npb24taWQgd2hlbiBjb25uZWN0ZWRcbiAgICAgICAgICAgICAgICAvLyBmcm9tIGF1dGhlbnRpY2F0ZWQgZG9tYWluXG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5jcmVhdGVDb25mZXJlbmNlSXEocm9vbU5hbWUpLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25TdGF0dXMudGV4dChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY29ubmVjdGlvbi5HT1RfU0VTU0lPTl9JRCcpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgc3RvcCA9IHRydWU7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBhcnNlIHNlc3Npb24taWRcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5wYXJzZVNlc3Npb25JZChyZXN1bHQpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhjb25uZWN0aW9uLCBzdGF0dXMpO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJBdXRoIG9uIHRoZSBmbHkgZmFpbGVkXCIsIGVycm9yKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgc3RvcCA9IHRydWU7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBlcnJvck1zZyA9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Nvbm5lY3Rpb24uR0VUX1NFU1NJT05fSURfRVJST1InKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQoZXJyb3IpLmZpbmQoJz5lcnJvcicpLmF0dHIoJ2NvZGUnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5kaXNwbGF5RXJyb3IoZXJyb3JNc2cpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgWE1QUC5TdGF0dXMuQVVUSEZBSUw6XG4gICAgICAgICAgICBjYXNlIFhNUFAuU3RhdHVzLkNPTk5GQUlMOlxuICAgICAgICAgICAgY2FzZSBYTVBQLlN0YXR1cy5ESVNDT05ORUNURUQ6XG5cbiAgICAgICAgICAgICAgICBzdG9wID0gdHJ1ZTtcblxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGNvbm5lY3Rpb24sIHN0YXR1cyk7XG5cbiAgICAgICAgICAgICAgICB2YXIgZXJyb3JNZXNzYWdlID0gc3RhdHVzU3RyO1xuXG4gICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2UpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlcnJvck1lc3NhZ2UgKz0gJzogJyArIG1lc3NhZ2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGxheUVycm9yKGVycm9yTWVzc2FnZSk7XG5cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGlzcGxheXMgZXJyb3IgbWVzc2FnZSBpbiAnZmluaXNoZWQnIHN0YXRlIHdoaWNoIGFsbG93cyBlaXRoZXIgdG8gY2FuY2VsXG4gICAgICogb3IgcmV0cnkuXG4gICAgICogQHBhcmFtIG1lc3NhZ2UgdGhlIGZpbmFsIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxuICAgICAqL1xuICAgIHRoaXMuZGlzcGxheUVycm9yID0gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcblxuICAgICAgICB2YXIgZmluaXNoZWRTdGF0ZSA9IGNvbm5EaWFsb2cuZ2V0U3RhdGUoJ2ZpbmlzaGVkJyk7XG5cbiAgICAgICAgdmFyIGVycm9yTWVzc2FnZUVsZW0gPSBmaW5pc2hlZFN0YXRlLmZpbmQoJyNlcnJvck1lc3NhZ2UnKTtcbiAgICAgICAgZXJyb3JNZXNzYWdlRWxlbS50ZXh0KG1lc3NhZ2UpO1xuXG4gICAgICAgIGNvbm5EaWFsb2cuZ29Ub1N0YXRlKCdmaW5pc2hlZCcpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZXMgTG9naW5EaWFsb2cuXG4gICAgICovXG4gICAgdGhpcy5jbG9zZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgc3RvcCA9IHRydWU7XG4gICAgICAgIGNvbm5EaWFsb2cuY2xvc2UoKTtcbiAgICB9O1xufVxuXG52YXIgTG9naW5EaWFsb2cgPSB7XG5cbiAgICAvKipcbiAgICAgKiBEaXNwbGF5cyBsb2dpbiBwcm9tcHQgdXNlZCB0byBlc3RhYmxpc2ggbmV3IFhNUFAgY29ubmVjdGlvbi4gR2l2ZW5cbiAgICAgKiA8dHQ+Y2FsbGJhY2soU3Ryb3BoZS5Db25uZWN0aW9uLCBTdHJvcGhlLlN0YXR1cyk8L3R0PiBmdW5jdGlvbiB3aWxsIGJlXG4gICAgICogY2FsbGVkIHdoZW4gd2UgY29ubmVjdCBzdWNjZXNzZnVsbHkoc3RhdHVzID09PSBDT05ORUNURUQpIG9yIHdoZW4gd2UgZmFpbFxuICAgICAqIHRvIGRvIHNvLiBPbiBjb25uZWN0aW9uIGZhaWx1cmUgcHJvZ3JhbSBjYW4gY2FsbCBEaWFsb2cuY2xvc2UoKSBtZXRob2QgaW5cbiAgICAgKiBvcmRlciB0byBjYW5jZWwgb3IgZG8gbm90aGluZyB0byBsZXQgdGhlIHVzZXIgcmV0cnkuXG4gICAgICogQHBhcmFtIGNhbGxiYWNrIDx0dD5mdW5jdGlvbihTdHJvcGhlLkNvbm5lY3Rpb24sIFN0cm9waGUuU3RhdHVzKTwvdHQ+XG4gICAgICogICAgICAgIGNhbGxlZCB3aGVuIHdlIGVpdGhlciBmYWlsIHRvIGNvbm5lY3Qgb3Igc3VjY2VlZChjaGVja1xuICAgICAqICAgICAgICBTdHJvcGhlLlN0YXR1cykuXG4gICAgICogQHBhcmFtIG9idGFpblNlc3Npb24gPHR0PnRydWU8L3R0PiBpZiB3ZSB3YW50IHRvIHNlbmQgQ29uZmVyZW5jZUlRIHRvXG4gICAgICogICAgICAgIEppY29mbyBpbiBvcmRlciB0byBjcmVhdGUgc2Vzc2lvbi1pZCBhZnRlciB0aGUgY29ubmVjdGlvbiBpc1xuICAgICAqICAgICAgICBlc3RhYmxpc2hlZC5cbiAgICAgKiBAcmV0dXJucyB7RGlhbG9nfVxuICAgICAqL1xuICAgIHNob3c6IGZ1bmN0aW9uIChjYWxsYmFjaywgb2J0YWluU2Vzc2lvbikge1xuICAgICAgICByZXR1cm4gbmV3IERpYWxvZyhjYWxsYmFjaywgb2J0YWluU2Vzc2lvbik7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBMb2dpbkRpYWxvZzsiLCJ2YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NcIik7XG52YXIgTWVkaWFTdHJlYW1UeXBlID0gcmVxdWlyZShcIi4uLy4uLy4uL3NlcnZpY2UvUlRDL01lZGlhU3RyZWFtVHlwZXNcIik7XG5cbnZhciB1c2VycyA9IHt9O1xudmFyIGFjdGl2ZVNwZWFrZXJKaWQ7XG5cbmZ1bmN0aW9uIHNldFZpc2liaWxpdHkoc2VsZWN0b3IsIHNob3cpIHtcbiAgICBpZiAoc2VsZWN0b3IgJiYgc2VsZWN0b3IubGVuZ3RoID4gMCkge1xuICAgICAgICBzZWxlY3Rvci5jc3MoXCJ2aXNpYmlsaXR5XCIsIHNob3cgPyBcInZpc2libGVcIiA6IFwiaGlkZGVuXCIpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gaXNVc2VyTXV0ZWQoamlkKSB7XG4gICAgLy8gWFhYKGdwKSB3ZSBtYXkgd2FudCB0byByZW5hbWUgdGhpcyBtZXRob2QgdG8gc29tZXRoaW5nIGxpa2VcbiAgICAvLyBpc1VzZXJTdHJlYW1pbmcsIGZvciBleGFtcGxlLlxuICAgIGlmIChqaWQgJiYgamlkICE9IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgdmFyIHJlc291cmNlID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgaWYgKCFyZXF1aXJlKFwiLi4vdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXRcIikuaXNJbkxhc3ROKHJlc291cmNlKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIUFQUC5SVEMucmVtb3RlU3RyZWFtc1tqaWRdIHx8ICFBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV0pIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiBBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV0ubXV0ZWQ7XG59XG5cbmZ1bmN0aW9uIGdldEdyYXZhdGFyVXJsKGlkLCBzaXplKSB7XG4gICAgaWYoaWQgPT09IEFQUC54bXBwLm15SmlkKCkgfHwgIWlkKSB7XG4gICAgICAgIGlkID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKS51aWQ7XG4gICAgfVxuICAgIHJldHVybiAnaHR0cHM6Ly93d3cuZ3JhdmF0YXIuY29tL2F2YXRhci8nICtcbiAgICAgICAgTUQ1LmhleGRpZ2VzdChpZC50cmltKCkudG9Mb3dlckNhc2UoKSkgK1xuICAgICAgICBcIj9kPXdhdmF0YXImc2l6ZT1cIiArIChzaXplIHx8IFwiMzBcIik7XG59XG5cbnZhciBBdmF0YXIgPSB7XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSB1c2VyJ3MgYXZhdGFyIGluIHRoZSBzZXR0aW5ncyBtZW51KGlmIGxvY2FsIHVzZXIpLCBjb250YWN0IGxpc3RcbiAgICAgKiBhbmQgdGh1bWJuYWlsXG4gICAgICogQHBhcmFtIGppZCBqaWQgb2YgdGhlIHVzZXJcbiAgICAgKiBAcGFyYW0gaWQgZW1haWwgb3IgdXNlcklEIHRvIGJlIHVzZWQgYXMgYSBoYXNoXG4gICAgICovXG4gICAgc2V0VXNlckF2YXRhcjogZnVuY3Rpb24gKGppZCwgaWQpIHtcbiAgICAgICAgaWYgKGlkKSB7XG4gICAgICAgICAgICBpZiAodXNlcnNbamlkXSA9PT0gaWQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB1c2Vyc1tqaWRdID0gaWQ7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHRodW1iVXJsID0gZ2V0R3JhdmF0YXJVcmwodXNlcnNbamlkXSB8fCBqaWQsIDEwMCk7XG4gICAgICAgIHZhciBjb250YWN0TGlzdFVybCA9IGdldEdyYXZhdGFyVXJsKHVzZXJzW2ppZF0gfHwgamlkKTtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgdmFyIHRodW1ibmFpbCA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpO1xuICAgICAgICB2YXIgYXZhdGFyID0gJCgnI2F2YXRhcl8nICsgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgIC8vIHNldCB0aGUgYXZhdGFyIGluIHRoZSBzZXR0aW5ncyBtZW51IGlmIGl0IGlzIGxvY2FsIHVzZXIgYW5kIGdldCB0aGVcbiAgICAgICAgLy8gbG9jYWwgdmlkZW8gY29udGFpbmVyXG4gICAgICAgIGlmIChqaWQgPT09IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgICQoJyNhdmF0YXInKS5nZXQoMCkuc3JjID0gdGh1bWJVcmw7XG4gICAgICAgICAgICB0aHVtYm5haWwgPSAkKCcjbG9jYWxWaWRlb0NvbnRhaW5lcicpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2V0IHRoZSBhdmF0YXIgaW4gdGhlIGNvbnRhY3QgbGlzdFxuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyMnICsgcmVzb3VyY2VKaWQgKyAnPmltZycpO1xuICAgICAgICBpZiAoY29udGFjdCAmJiBjb250YWN0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnRhY3QuZ2V0KDApLnNyYyA9IGNvbnRhY3RMaXN0VXJsO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2V0IHRoZSBhdmF0YXIgaW4gdGhlIHRodW1ibmFpbFxuICAgICAgICBpZiAoYXZhdGFyICYmIGF2YXRhci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBhdmF0YXJbMF0uc3JjID0gdGh1bWJVcmw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGh1bWJuYWlsICYmIHRodW1ibmFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgYXZhdGFyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gICAgICAgICAgICAgICAgYXZhdGFyLmlkID0gJ2F2YXRhcl8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICAgICAgYXZhdGFyLmNsYXNzTmFtZSA9ICd1c2VyQXZhdGFyJztcbiAgICAgICAgICAgICAgICBhdmF0YXIuc3JjID0gdGh1bWJVcmw7XG4gICAgICAgICAgICAgICAgdGh1bWJuYWlsLmFwcGVuZChhdmF0YXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy9pZiB0aGUgdXNlciBpcyB0aGUgY3VycmVudCBhY3RpdmUgc3BlYWtlciAtIHVwZGF0ZSB0aGUgYWN0aXZlIHNwZWFrZXJcbiAgICAgICAgLy8gYXZhdGFyXG4gICAgICAgIGlmIChqaWQgPT09IGFjdGl2ZVNwZWFrZXJKaWQpIHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlQWN0aXZlU3BlYWtlckF2YXRhclNyYyhqaWQpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIG9yIHNob3dzIHRoZSB1c2VyJ3MgYXZhdGFyXG4gICAgICogQHBhcmFtIGppZCBqaWQgb2YgdGhlIHVzZXJcbiAgICAgKiBAcGFyYW0gc2hvdyB3aGV0aGVyIHdlIHNob3VsZCBzaG93IHRoZSBhdmF0YXIgb3Igbm90XG4gICAgICogdmlkZW8gYmVjYXVzZSB0aGVyZSBpcyBubyBkb21pbmFudCBzcGVha2VyIGFuZCBubyBmb2N1c2VkIHNwZWFrZXJcbiAgICAgKi9cbiAgICBzaG93VXNlckF2YXRhcjogZnVuY3Rpb24gKGppZCwgc2hvdykge1xuICAgICAgICBpZiAodXNlcnNbamlkXSkge1xuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgICAgIHZhciB2aWRlbyA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQgKyAnPnZpZGVvJyk7XG4gICAgICAgICAgICB2YXIgYXZhdGFyID0gJCgnI2F2YXRhcl8nICsgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgICAgICBpZiAoamlkID09PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAgICAgdmlkZW8gPSAkKCcjbG9jYWxWaWRlb1dyYXBwZXI+dmlkZW8nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChzaG93ID09PSB1bmRlZmluZWQgfHwgc2hvdyA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHNob3cgPSBpc1VzZXJNdXRlZChqaWQpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvL2lmIHRoZSB1c2VyIGlzIHRoZSBjdXJyZW50bHkgZm9jdXNlZCwgdGhlIGRvbWluYW50IHNwZWFrZXIgb3IgaWZcbiAgICAgICAgICAgIC8vdGhlcmUgaXMgbm8gZm9jdXNlZCBhbmQgbm8gZG9taW5hbnQgc3BlYWtlciBhbmQgdGhlIGxhcmdlIHZpZGVvIGlzXG4gICAgICAgICAgICAvL2N1cnJlbnRseSBzaG93blxuICAgICAgICAgICAgaWYgKGFjdGl2ZVNwZWFrZXJKaWQgPT09IGppZCAmJiByZXF1aXJlKFwiLi4vdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXRcIikuaXNMYXJnZVZpZGVvT25Ub3AoKSkge1xuICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkoJChcIiNsYXJnZVZpZGVvXCIpLCAhc2hvdyk7XG4gICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eSgkKCcjYWN0aXZlU3BlYWtlcicpLCBzaG93KTtcbiAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KGF2YXRhciwgZmFsc2UpO1xuICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkodmlkZW8sIGZhbHNlKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKHZpZGVvICYmIHZpZGVvLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eSh2aWRlbywgIXNob3cpO1xuICAgICAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KGF2YXRhciwgc2hvdyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIHNyYyBvZiB0aGUgYWN0aXZlIHNwZWFrZXIgYXZhdGFyXG4gICAgICogQHBhcmFtIGppZCBvZiB0aGUgY3VycmVudCBhY3RpdmUgc3BlYWtlclxuICAgICAqL1xuICAgIHVwZGF0ZUFjdGl2ZVNwZWFrZXJBdmF0YXJTcmM6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgaWYgKCFqaWQpIHtcbiAgICAgICAgICAgIGppZCA9IEFQUC54bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UoXG4gICAgICAgICAgICAgICAgcmVxdWlyZShcIi4uL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0XCIpLmdldExhcmdlVmlkZW9TdGF0ZSgpLnVzZXJSZXNvdXJjZUppZCk7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGF2YXRhciA9ICQoXCIjYWN0aXZlU3BlYWtlckF2YXRhclwiKVswXTtcbiAgICAgICAgdmFyIHVybCA9IGdldEdyYXZhdGFyVXJsKHVzZXJzW2ppZF0sXG4gICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuQUNUSVZFX1NQRUFLRVJfQVZBVEFSX1NJWkUpO1xuICAgICAgICBpZiAoamlkID09PSBhY3RpdmVTcGVha2VySmlkICYmIGF2YXRhci5zcmMgPT09IHVybCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGFjdGl2ZVNwZWFrZXJKaWQgPSBqaWQ7XG4gICAgICAgIHZhciBpc011dGVkID0gaXNVc2VyTXV0ZWQoamlkKTtcbiAgICAgICAgaWYgKGppZCAmJiBpc011dGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICBhdmF0YXIuc3JjID0gdXJsO1xuICAgICAgICAgICAgc2V0VmlzaWJpbGl0eSgkKFwiI2xhcmdlVmlkZW9cIiksICFpc011dGVkKTtcbiAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihqaWQsIGlzTXV0ZWQpO1xuICAgICAgICB9XG4gICAgfVxuXG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gQXZhdGFyOyIsIi8qIGdsb2JhbCAkLCBjb25maWcsXG4gICBzZXRMYXJnZVZpZGVvVmlzaWJsZSwgVXRpbCAqL1xuXG52YXIgVmlkZW9MYXlvdXQgPSByZXF1aXJlKFwiLi4vdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXRcIik7XG52YXIgUHJlemkgPSByZXF1aXJlKFwiLi4vcHJlemkvUHJlemlcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uL3V0aWwvVUlVdGlsXCIpO1xuXG52YXIgZXRoZXJwYWROYW1lID0gbnVsbDtcbnZhciBldGhlcnBhZElGcmFtZSA9IG51bGw7XG52YXIgZG9tYWluID0gbnVsbDtcbnZhciBvcHRpb25zID0gXCI/c2hvd0NvbnRyb2xzPXRydWUmc2hvd0NoYXQ9ZmFsc2Umc2hvd0xpbmVOdW1iZXJzPXRydWUmdXNlTW9ub3NwYWNlRm9udD1mYWxzZVwiO1xuXG5cbi8qKlxuICogUmVzaXplcyB0aGUgZXRoZXJwYWQuXG4gKi9cbmZ1bmN0aW9uIHJlc2l6ZSgpIHtcbiAgICBpZiAoJCgnI2V0aGVycGFkPmlmcmFtZScpLmxlbmd0aCkge1xuICAgICAgICB2YXIgcmVtb3RlVmlkZW9zID0gJCgnI3JlbW90ZVZpZGVvcycpO1xuICAgICAgICB2YXIgYXZhaWxhYmxlSGVpZ2h0XG4gICAgICAgICAgICA9IHdpbmRvdy5pbm5lckhlaWdodCAtIHJlbW90ZVZpZGVvcy5vdXRlckhlaWdodCgpO1xuICAgICAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBVSVV0aWwuZ2V0QXZhaWxhYmxlVmlkZW9XaWR0aCgpO1xuXG4gICAgICAgICQoJyNldGhlcnBhZD5pZnJhbWUnKS53aWR0aChhdmFpbGFibGVXaWR0aCk7XG4gICAgICAgICQoJyNldGhlcnBhZD5pZnJhbWUnKS5oZWlnaHQoYXZhaWxhYmxlSGVpZ2h0KTtcbiAgICB9XG59XG5cbi8qKlxuICogU2hhcmVzIHRoZSBFdGhlcnBhZCBuYW1lIHdpdGggb3RoZXIgcGFydGljaXBhbnRzLlxuICovXG5mdW5jdGlvbiBzaGFyZUV0aGVycGFkKCkge1xuICAgIEFQUC54bXBwLmFkZFRvUHJlc2VuY2UoXCJldGhlcnBhZFwiLCBldGhlcnBhZE5hbWUpO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIEV0aGVycGFkIGJ1dHRvbiBhbmQgYWRkcyBpdCB0byB0aGUgdG9vbGJhci5cbiAqL1xuZnVuY3Rpb24gZW5hYmxlRXRoZXJwYWRCdXR0b24oKSB7XG4gICAgaWYgKCEkKCcjZXRoZXJwYWRCdXR0b24nKS5pcyhcIjp2aXNpYmxlXCIpKVxuICAgICAgICAkKCcjZXRoZXJwYWRCdXR0b24nKS5jc3Moe2Rpc3BsYXk6ICdpbmxpbmUtYmxvY2snfSk7XG59XG5cbi8qKlxuICogQ3JlYXRlcyB0aGUgSUZyYW1lIGZvciB0aGUgZXRoZXJwYWQuXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUlGcmFtZSgpIHtcbiAgICBldGhlcnBhZElGcmFtZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lmcmFtZScpO1xuICAgIGV0aGVycGFkSUZyYW1lLnNyYyA9IGRvbWFpbiArIGV0aGVycGFkTmFtZSArIG9wdGlvbnM7XG4gICAgZXRoZXJwYWRJRnJhbWUuZnJhbWVCb3JkZXIgPSAwO1xuICAgIGV0aGVycGFkSUZyYW1lLnNjcm9sbGluZyA9IFwibm9cIjtcbiAgICBldGhlcnBhZElGcmFtZS53aWR0aCA9ICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykud2lkdGgoKSB8fCA2NDA7XG4gICAgZXRoZXJwYWRJRnJhbWUuaGVpZ2h0ID0gJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS5oZWlnaHQoKSB8fCA0ODA7XG4gICAgZXRoZXJwYWRJRnJhbWUuc2V0QXR0cmlidXRlKCdzdHlsZScsICd2aXNpYmlsaXR5OiBoaWRkZW47Jyk7XG5cbiAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZXRoZXJwYWQnKS5hcHBlbmRDaGlsZChldGhlcnBhZElGcmFtZSk7XG5cbiAgICBldGhlcnBhZElGcmFtZS5vbmxvYWQgPSBmdW5jdGlvbigpIHtcblxuICAgICAgICBkb2N1bWVudC5kb21haW4gPSBkb2N1bWVudC5kb21haW47XG4gICAgICAgIGJ1YmJsZUlmcmFtZU1vdXNlTW92ZShldGhlcnBhZElGcmFtZSk7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAvLyB0aGUgaWZyYW1lcyBpbnNpZGUgb2YgdGhlIGV0aGVycGFkIGFyZVxuICAgICAgICAgICAgLy8gbm90IHlldCBsb2FkZWQgd2hlbiB0aGUgZXRoZXJwYWQgaWZyYW1lIGlzIGxvYWRlZFxuICAgICAgICAgICAgdmFyIG91dGVyID0gZXRoZXJwYWRJRnJhbWUuXG4gICAgICAgICAgICAgICAgY29udGVudERvY3VtZW50LmdldEVsZW1lbnRzQnlOYW1lKFwiYWNlX291dGVyXCIpWzBdO1xuICAgICAgICAgICAgYnViYmxlSWZyYW1lTW91c2VNb3ZlKG91dGVyKTtcbiAgICAgICAgICAgIHZhciBpbm5lciA9IG91dGVyLlxuICAgICAgICAgICAgICAgIGNvbnRlbnREb2N1bWVudC5nZXRFbGVtZW50c0J5TmFtZShcImFjZV9pbm5lclwiKVswXTtcbiAgICAgICAgICAgIGJ1YmJsZUlmcmFtZU1vdXNlTW92ZShpbm5lcik7XG4gICAgICAgIH0sIDIwMDApO1xuICAgIH07XG59XG5cbmZ1bmN0aW9uIGJ1YmJsZUlmcmFtZU1vdXNlTW92ZShpZnJhbWUpe1xuICAgIHZhciBleGlzdGluZ09uTW91c2VNb3ZlID0gaWZyYW1lLmNvbnRlbnRXaW5kb3cub25tb3VzZW1vdmU7XG4gICAgaWZyYW1lLmNvbnRlbnRXaW5kb3cub25tb3VzZW1vdmUgPSBmdW5jdGlvbihlKXtcbiAgICAgICAgaWYoZXhpc3RpbmdPbk1vdXNlTW92ZSkgZXhpc3RpbmdPbk1vdXNlTW92ZShlKTtcbiAgICAgICAgdmFyIGV2dCA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KFwiTW91c2VFdmVudHNcIik7XG4gICAgICAgIHZhciBib3VuZGluZ0NsaWVudFJlY3QgPSBpZnJhbWUuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGV2dC5pbml0TW91c2VFdmVudChcbiAgICAgICAgICAgIFwibW91c2Vtb3ZlXCIsXG4gICAgICAgICAgICB0cnVlLCAvLyBidWJibGVzXG4gICAgICAgICAgICBmYWxzZSwgLy8gbm90IGNhbmNlbGFibGVcbiAgICAgICAgICAgIHdpbmRvdyxcbiAgICAgICAgICAgIGUuZGV0YWlsLFxuICAgICAgICAgICAgZS5zY3JlZW5YLFxuICAgICAgICAgICAgZS5zY3JlZW5ZLFxuICAgICAgICAgICAgICAgIGUuY2xpZW50WCArIGJvdW5kaW5nQ2xpZW50UmVjdC5sZWZ0LFxuICAgICAgICAgICAgICAgIGUuY2xpZW50WSArIGJvdW5kaW5nQ2xpZW50UmVjdC50b3AsXG4gICAgICAgICAgICBlLmN0cmxLZXksXG4gICAgICAgICAgICBlLmFsdEtleSxcbiAgICAgICAgICAgIGUuc2hpZnRLZXksXG4gICAgICAgICAgICBlLm1ldGFLZXksXG4gICAgICAgICAgICBlLmJ1dHRvbixcbiAgICAgICAgICAgIG51bGwgLy8gbm8gcmVsYXRlZCBlbGVtZW50XG4gICAgICAgICk7XG4gICAgICAgIGlmcmFtZS5kaXNwYXRjaEV2ZW50KGV2dCk7XG4gICAgfTtcbn1cblxuXG4vKipcbiAqIE9uIHZpZGVvIHNlbGVjdGVkIGV2ZW50LlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCd2aWRlby5zZWxlY3RlZCcsIGZ1bmN0aW9uIChldmVudCwgaXNQcmVzZW50YXRpb24pIHtcbiAgICBpZiAoY29uZmlnLmV0aGVycGFkX2Jhc2UgJiYgZXRoZXJwYWRJRnJhbWUgJiYgZXRoZXJwYWRJRnJhbWUuc3R5bGUudmlzaWJpbGl0eSAhPT0gJ2hpZGRlbicpXG4gICAgICAgIEV0aGVycGFkLnRvZ2dsZUV0aGVycGFkKGlzUHJlc2VudGF0aW9uKTtcbn0pO1xuXG5cbnZhciBFdGhlcnBhZCA9IHtcbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplcyB0aGUgZXRoZXJwYWQuXG4gICAgICovXG4gICAgaW5pdDogZnVuY3Rpb24gKG5hbWUpIHtcblxuICAgICAgICBpZiAoY29uZmlnLmV0aGVycGFkX2Jhc2UgJiYgIWV0aGVycGFkTmFtZSkge1xuXG4gICAgICAgICAgICBkb21haW4gPSBjb25maWcuZXRoZXJwYWRfYmFzZTtcblxuICAgICAgICAgICAgaWYgKCFuYW1lKSB7XG4gICAgICAgICAgICAgICAgLy8gSW4gY2FzZSB3ZSdyZSB0aGUgZm9jdXMgd2UgZ2VuZXJhdGUgdGhlIG5hbWUuXG4gICAgICAgICAgICAgICAgZXRoZXJwYWROYW1lID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyaW5nKDcpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ18nICsgKG5ldyBEYXRlKCkuZ2V0VGltZSgpKS50b1N0cmluZygpO1xuICAgICAgICAgICAgICAgIHNoYXJlRXRoZXJwYWQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICBldGhlcnBhZE5hbWUgPSBuYW1lO1xuXG4gICAgICAgICAgICBlbmFibGVFdGhlcnBhZEJ1dHRvbigpO1xuXG4gICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAqIFJlc2l6ZXMgdGhlIGV0aGVycGFkLCB3aGVuIHRoZSB3aW5kb3cgaXMgcmVzaXplZC5cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgJCh3aW5kb3cpLnJlc2l6ZShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmVzaXplKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBPcGVucy9oaWRlcyB0aGUgRXRoZXJwYWQuXG4gICAgICovXG4gICAgdG9nZ2xlRXRoZXJwYWQ6IGZ1bmN0aW9uIChpc1ByZXNlbnRhdGlvbikge1xuICAgICAgICBpZiAoIWV0aGVycGFkSUZyYW1lKVxuICAgICAgICAgICAgY3JlYXRlSUZyYW1lKCk7XG5cbiAgICAgICAgdmFyIGxhcmdlVmlkZW8gPSBudWxsO1xuICAgICAgICBpZiAoUHJlemkuaXNQcmVzZW50YXRpb25WaXNpYmxlKCkpXG4gICAgICAgICAgICBsYXJnZVZpZGVvID0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKTtcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgbGFyZ2VWaWRlbyA9ICQoJyNsYXJnZVZpZGVvJyk7XG5cbiAgICAgICAgaWYgKCQoJyNldGhlcnBhZD5pZnJhbWUnKS5jc3MoJ3Zpc2liaWxpdHknKSA9PT0gJ2hpZGRlbicpIHtcbiAgICAgICAgICAgICQoJyNhY3RpdmVTcGVha2VyJykuY3NzKCd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xuICAgICAgICAgICAgbGFyZ2VWaWRlby5mYWRlT3V0KDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGlmIChQcmV6aS5pc1ByZXNlbnRhdGlvblZpc2libGUoKSkge1xuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvLmNzcyh7b3BhY2l0eTogJzAnfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2V0TGFyZ2VWaWRlb1Zpc2libGUoZmFsc2UpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykuZmFkZUluKDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuYmFja2dyb3VuZCA9ICcjZWVlZWVlJztcbiAgICAgICAgICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgICAgICAkKCcjZXRoZXJwYWQnKS5jc3Moe3pJbmRleDogMn0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoJCgnI2V0aGVycGFkPmlmcmFtZScpKSB7XG4gICAgICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykuZmFkZU91dCgzMDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykuY3NzKHt2aXNpYmlsaXR5OiAnaGlkZGVuJ30pO1xuICAgICAgICAgICAgICAgICQoJyNldGhlcnBhZCcpLmNzcyh7ekluZGV4OiAwfSk7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kID0gJ2JsYWNrJztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBpZiAoIWlzUHJlc2VudGF0aW9uKSB7XG4gICAgICAgICAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5mYWRlSW4oMzAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldExhcmdlVmlkZW9WaXNpYmxlKHRydWUpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJlc2l6ZSgpO1xuICAgIH0sXG5cbiAgICBpc1Zpc2libGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgZXRoZXJwYWRJZnJhbWUgPSAkKCcjZXRoZXJwYWQ+aWZyYW1lJyk7XG4gICAgICAgIHJldHVybiBldGhlcnBhZElmcmFtZSAmJiBldGhlcnBhZElmcmFtZS5pcygnOnZpc2libGUnKTtcbiAgICB9XG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gRXRoZXJwYWQ7XG4iLCJ2YXIgVG9vbGJhclRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXJcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uL3V0aWwvVUlVdGlsXCIpO1xudmFyIFZpZGVvTGF5b3V0ID0gcmVxdWlyZShcIi4uL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0XCIpO1xudmFyIG1lc3NhZ2VIYW5kbGVyID0gcmVxdWlyZShcIi4uL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgUHJlemlQbGF5ZXIgPSByZXF1aXJlKFwiLi9QcmV6aVBsYXllclwiKTtcblxudmFyIHByZXppUGxheWVyID0gbnVsbDtcblxudmFyIFByZXppID0ge1xuXG5cbiAgICAvKipcbiAgICAgKiBSZWxvYWRzIHRoZSBjdXJyZW50IHByZXNlbnRhdGlvbi5cbiAgICAgKi9cbiAgICByZWxvYWRQcmVzZW50YXRpb246IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgaWZyYW1lID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQocHJlemlQbGF5ZXIub3B0aW9ucy5wcmV6aUlkKTtcbiAgICAgICAgaWZyYW1lLnNyYyA9IGlmcmFtZS5zcmM7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgPHR0PnRydWU8L3R0PiBpZiB0aGUgcHJlc2VudGF0aW9uIGlzIHZpc2libGUsIDx0dD5mYWxzZTwvdHQ+IC1cbiAgICAgKiBvdGhlcndpc2UuXG4gICAgICovXG4gICAgaXNQcmVzZW50YXRpb25WaXNpYmxlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAoJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKSAhPSBudWxsXG4gICAgICAgICAgICAgICAgJiYgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5jc3MoJ29wYWNpdHknKSA9PSAxKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIFByZXppIGRpYWxvZywgZnJvbSB3aGljaCB0aGUgdXNlciBjb3VsZCBjaG9vc2UgYSBwcmVzZW50YXRpb25cbiAgICAgKiB0byBsb2FkLlxuICAgICAqL1xuICAgIG9wZW5QcmV6aURpYWxvZzogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBteXByZXppID0gQVBQLnhtcHAuZ2V0UHJlemkoKTtcbiAgICAgICAgaWYgKG15cHJlemkpIHtcbiAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXCJkaWFsb2cucmVtb3ZlUHJlemlUaXRsZVwiLFxuICAgICAgICAgICAgICAgIG51bGwsXG4gICAgICAgICAgICAgICAgXCJkaWFsb2cucmVtb3ZlUHJlemlNc2dcIixcbiAgICAgICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgIFwiZGlhbG9nLlJlbW92ZVwiLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGUsdixtLGYpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYodikge1xuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAocHJlemlQbGF5ZXIgIT0gbnVsbCkge1xuICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhcImRpYWxvZy5zaGFyZVByZXppVGl0bGVcIixcbiAgICAgICAgICAgICAgICBudWxsLCBcImRpYWxvZy5zaGFyZVByZXppTXNnXCIsXG4gICAgICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICBcImRpYWxvZy5Pa1wiLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGUsdixtLGYpIHtcbiAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgdmFyIGh0bWwgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgICAgICAgICBcImRpYWxvZy5zaGFyZVByZXppVGl0bGVcIik7XG4gICAgICAgICAgICB2YXIgY2FuY2VsQnV0dG9uID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICAgICAgXCJkaWFsb2cuQ2FuY2VsXCIpO1xuICAgICAgICAgICAgdmFyIHNoYXJlQnV0dG9uID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICAgICAgXCJkaWFsb2cuU2hhcmVcIik7XG4gICAgICAgICAgICB2YXIgYmFja0J1dHRvbiA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICAgICAgICAgIFwiZGlhbG9nLkJhY2tcIik7XG4gICAgICAgICAgICB2YXIgYnV0dG9ucyA9IFtdO1xuICAgICAgICAgICAgdmFyIGJ1dHRvbnMxID0gW107XG4gICAgICAgICAgICAvLyBDYW5jZWwgYnV0dG9uIHRvIGJvdGggc3RhdGVzXG4gICAgICAgICAgICBidXR0b25zLnB1c2goe3RpdGxlOiBjYW5jZWxCdXR0b24sIHZhbHVlOiBmYWxzZX0pO1xuICAgICAgICAgICAgYnV0dG9uczEucHVzaCh7dGl0bGU6IGNhbmNlbEJ1dHRvbiwgdmFsdWU6IGZhbHNlfSk7XG4gICAgICAgICAgICAvLyBTaGFyZSBidXR0b25cbiAgICAgICAgICAgIGJ1dHRvbnMucHVzaCh7dGl0bGU6IHNoYXJlQnV0dG9uLCB2YWx1ZTogdHJ1ZX0pO1xuICAgICAgICAgICAgLy8gQmFjayBidXR0b25cbiAgICAgICAgICAgIGJ1dHRvbnMxLnB1c2goe3RpdGxlOiBiYWNrQnV0dG9uLCB2YWx1ZTogdHJ1ZX0pO1xuICAgICAgICAgICAgdmFyIGxpbmtFcnJvciA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICAgICAgICAgIFwiZGlhbG9nLnByZXppTGlua0Vycm9yXCIpO1xuICAgICAgICAgICAgdmFyIGRlZmF1bHRVcmwgPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFwiZGVmYXVsdFByZXppTGlua1wiLFxuICAgICAgICAgICAgICAgIHt1cmw6IFwiaHR0cDovL3ByZXppLmNvbS93ejd2aGp5Y2w3ZTYvbXktcHJlemlcIn0pO1xuICAgICAgICAgICAgdmFyIG9wZW5QcmV6aVN0YXRlID0ge1xuICAgICAgICAgICAgICAgIHN0YXRlMDoge1xuICAgICAgICAgICAgICAgICAgICBodG1sOiAgICc8aDI+JyArIGh0bWwgKyAnPC9oMj4nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGlucHV0IG5hbWU9XCJwcmV6aVVybFwiIHR5cGU9XCJ0ZXh0XCIgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtaTE4bj1cIltwbGFjZWhvbGRlcl1kZWZhdWx0UHJlemlMaW5rXCIgZGF0YS1pMThuLW9wdGlvbnM9XFwnJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe1widXJsXCI6IFwiaHR0cDovL3ByZXppLmNvbS93ejd2aGp5Y2w3ZTYvbXktcHJlemlcIn0pICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnXFwnIHBsYWNlaG9sZGVyPVwiJyArIGRlZmF1bHRVcmwgKyAnXCIgYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICAgICAgICAgIHBlcnNpc3RlbnQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBidXR0b25zOiBidXR0b25zLFxuICAgICAgICAgICAgICAgICAgICBmb2N1czogJzppbnB1dDpmaXJzdCcsXG4gICAgICAgICAgICAgICAgICAgIGRlZmF1bHRCdXR0b246IDAsXG4gICAgICAgICAgICAgICAgICAgIHN1Ym1pdDogZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHYpXG4gICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHByZXppVXJsID0gZi5wcmV6aVVybDtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwcmV6aVVybClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciB1cmxWYWx1ZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBlbmNvZGVVUkkoVUlVdGlsLmVzY2FwZUh0bWwocHJlemlVcmwpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodXJsVmFsdWUuaW5kZXhPZignaHR0cDovL3ByZXppLmNvbS8nKSAhPSAwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmJiB1cmxWYWx1ZS5pbmRleE9mKCdodHRwczovL3ByZXppLmNvbS8nKSAhPSAwKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5nb1RvU3RhdGUoJ3N0YXRlMScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHByZXNJZFRtcCA9IHVybFZhbHVlLnN1YnN0cmluZyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsVmFsdWUuaW5kZXhPZihcInByZXppLmNvbS9cIikgKyAxMCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWlzQWxwaGFudW1lcmljKHByZXNJZFRtcClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHwgcHJlc0lkVG1wLmluZGV4T2YoJy8nKSA8IDIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5nb1RvU3RhdGUoJ3N0YXRlMScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFQUC54bXBwLmFkZFRvUHJlc2VuY2UoXCJwcmV6aVwiLCB1cmxWYWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5jbG9zZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBzdGF0ZTE6IHtcbiAgICAgICAgICAgICAgICAgICAgaHRtbDogICAnPGgyPicgKyBodG1sICsgJzwvaDI+JyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlua0Vycm9yLFxuICAgICAgICAgICAgICAgICAgICBwZXJzaXN0ZW50OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgYnV0dG9uczogYnV0dG9uczEsXG4gICAgICAgICAgICAgICAgICAgIGZvY3VzOiAnOmlucHV0OmZpcnN0JyxcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMSxcbiAgICAgICAgICAgICAgICAgICAgc3VibWl0OiBmdW5jdGlvbiAoZSwgdiwgbSwgZikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYgPT09IDApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5nb1RvU3RhdGUoJ3N0YXRlMCcpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5EaWFsb2dXaXRoU3RhdGVzKG9wZW5QcmV6aVN0YXRlKTtcbiAgICAgICAgfVxuICAgIH1cblxufTtcblxuLyoqXG4gKiBBIG5ldyBwcmVzZW50YXRpb24gaGFzIGJlZW4gYWRkZWQuXG4gKlxuICogQHBhcmFtIGV2ZW50IHRoZSBldmVudCBpbmRpY2F0aW5nIHRoZSBhZGQgb2YgYSBwcmVzZW50YXRpb25cbiAqIEBwYXJhbSBqaWQgdGhlIGppZCBmcm9tIHdoaWNoIHRoZSBwcmVzZW50YXRpb24gd2FzIGFkZGVkXG4gKiBAcGFyYW0gcHJlc1VybCB1cmwgb2YgdGhlIHByZXNlbnRhdGlvblxuICogQHBhcmFtIGN1cnJlbnRTbGlkZSB0aGUgY3VycmVudCBzbGlkZSB0byB3aGljaCB3ZSBzaG91bGQgbW92ZVxuICovXG5mdW5jdGlvbiBwcmVzZW50YXRpb25BZGRlZChldmVudCwgamlkLCBwcmVzVXJsLCBjdXJyZW50U2xpZGUpIHtcbiAgICBjb25zb2xlLmxvZyhcInByZXNlbnRhdGlvbiBhZGRlZFwiLCBwcmVzVXJsKTtcblxuICAgIHZhciBwcmVzSWQgPSBnZXRQcmVzZW50YXRpb25JZChwcmVzVXJsKTtcblxuICAgIHZhciBlbGVtZW50SWQgPSAncGFydGljaXBhbnRfJ1xuICAgICAgICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZClcbiAgICAgICAgKyAnXycgKyBwcmVzSWQ7XG5cbiAgICAvLyBXZSBleHBsaWNpdGx5IGRvbid0IHNwZWNpZnkgdGhlIHBlZXIgamlkIGhlcmUsIGJlY2F1c2Ugd2UgZG9uJ3Qgd2FudFxuICAgIC8vIHRoaXMgdmlkZW8gdG8gYmUgZGVhbHQgd2l0aCBhcyBhIHBlZXIgcmVsYXRlZCBvbmUgKGZvciBleGFtcGxlIHdlXG4gICAgLy8gZG9uJ3Qgd2FudCB0byBzaG93IGEgbXV0ZS9raWNrIG1lbnUgZm9yIHRoaXMgb25lLCBldGMuKS5cbiAgICBWaWRlb0xheW91dC5hZGRSZW1vdGVWaWRlb0NvbnRhaW5lcihudWxsLCBlbGVtZW50SWQpO1xuICAgIFZpZGVvTGF5b3V0LnJlc2l6ZVRodW1ibmFpbHMoKTtcblxuICAgIHZhciBjb250cm9sc0VuYWJsZWQgPSBmYWxzZTtcbiAgICBpZiAoamlkID09PSBBUFAueG1wcC5teUppZCgpKVxuICAgICAgICBjb250cm9sc0VuYWJsZWQgPSB0cnVlO1xuXG4gICAgc2V0UHJlc2VudGF0aW9uVmlzaWJsZSh0cnVlKTtcbiAgICAkKCcjbGFyZ2VWaWRlb0NvbnRhaW5lcicpLmhvdmVyKFxuICAgICAgICBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChQcmV6aS5pc1ByZXNlbnRhdGlvblZpc2libGUoKSkge1xuICAgICAgICAgICAgICAgIHZhciByZWxvYWRCdXR0b25SaWdodCA9IHdpbmRvdy5pbm5lcldpZHRoXG4gICAgICAgICAgICAgICAgICAgIC0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5vZmZzZXQoKS5sZWZ0XG4gICAgICAgICAgICAgICAgICAgIC0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS53aWR0aCgpO1xuXG4gICAgICAgICAgICAgICAgJCgnI3JlbG9hZFByZXNlbnRhdGlvbicpLmNzcyh7ICByaWdodDogcmVsb2FkQnV0dG9uUmlnaHQsXG4gICAgICAgICAgICAgICAgICAgIGRpc3BsYXk6J2lubGluZS1ibG9jayd9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoIVByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKVxuICAgICAgICAgICAgICAgICQoJyNyZWxvYWRQcmVzZW50YXRpb24nKS5jc3Moe2Rpc3BsYXk6J25vbmUnfSk7XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB2YXIgZSA9IGV2ZW50LnRvRWxlbWVudCB8fCBldmVudC5yZWxhdGVkVGFyZ2V0O1xuXG4gICAgICAgICAgICAgICAgaWYgKGUgJiYgZS5pZCAhPSAncmVsb2FkUHJlc2VudGF0aW9uJyAmJiBlLmlkICE9ICdoZWFkZXInKVxuICAgICAgICAgICAgICAgICAgICAkKCcjcmVsb2FkUHJlc2VudGF0aW9uJykuY3NzKHtkaXNwbGF5Oidub25lJ30pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgIHByZXppUGxheWVyID0gbmV3IFByZXppUGxheWVyKFxuICAgICAgICAncHJlc2VudGF0aW9uJyxcbiAgICAgICAge3ByZXppSWQ6IHByZXNJZCxcbiAgICAgICAgICAgIHdpZHRoOiBnZXRQcmVzZW50YXRpb25XaWR0aCgpLFxuICAgICAgICAgICAgaGVpZ2h0OiBnZXRQcmVzZW50YXRpb25IZWloZ3QoKSxcbiAgICAgICAgICAgIGNvbnRyb2xzOiBjb250cm9sc0VuYWJsZWQsXG4gICAgICAgICAgICBkZWJ1ZzogdHJ1ZVxuICAgICAgICB9KTtcblxuICAgICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykuYXR0cignaWQnLCBwcmV6aVBsYXllci5vcHRpb25zLnByZXppSWQpO1xuXG4gICAgcHJlemlQbGF5ZXIub24oUHJlemlQbGF5ZXIuRVZFTlRfU1RBVFVTLCBmdW5jdGlvbihldmVudCkge1xuICAgICAgICBjb25zb2xlLmxvZyhcInByZXppIHN0YXR1c1wiLCBldmVudC52YWx1ZSk7XG4gICAgICAgIGlmIChldmVudC52YWx1ZSA9PSBQcmV6aVBsYXllci5TVEFUVVNfQ09OVEVOVF9SRUFEWSkge1xuICAgICAgICAgICAgaWYgKGppZCAhPSBBUFAueG1wcC5teUppZCgpKVxuICAgICAgICAgICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50U2xpZGUpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICBwcmV6aVBsYXllci5vbihQcmV6aVBsYXllci5FVkVOVF9DVVJSRU5UX1NURVAsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiZXZlbnQgdmFsdWVcIiwgZXZlbnQudmFsdWUpO1xuICAgICAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwicHJlemlTbGlkZVwiLCBldmVudC52YWx1ZSk7XG4gICAgfSk7XG5cbiAgICAkKFwiI1wiICsgZWxlbWVudElkKS5jc3MoICdiYWNrZ3JvdW5kLWltYWdlJyxcbiAgICAgICAgJ3VybCguLi9pbWFnZXMvYXZhdGFycHJlemkucG5nKScpO1xuICAgICQoXCIjXCIgKyBlbGVtZW50SWQpLmNsaWNrKFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZXRQcmVzZW50YXRpb25WaXNpYmxlKHRydWUpO1xuICAgICAgICB9XG4gICAgKTtcbn07XG5cbi8qKlxuICogQSBwcmVzZW50YXRpb24gaGFzIGJlZW4gcmVtb3ZlZC5cbiAqXG4gKiBAcGFyYW0gZXZlbnQgdGhlIGV2ZW50IGluZGljYXRpbmcgdGhlIHJlbW92ZSBvZiBhIHByZXNlbnRhdGlvblxuICogQHBhcmFtIGppZCB0aGUgamlkIGZvciB3aGljaCB0aGUgcHJlc2VudGF0aW9uIHdhcyByZW1vdmVkXG4gKiBAcGFyYW0gdGhlIHVybCBvZiB0aGUgcHJlc2VudGF0aW9uXG4gKi9cbmZ1bmN0aW9uIHByZXNlbnRhdGlvblJlbW92ZWQoZXZlbnQsIGppZCwgcHJlc1VybCkge1xuICAgIGNvbnNvbGUubG9nKCdwcmVzZW50YXRpb24gcmVtb3ZlZCcsIHByZXNVcmwpO1xuICAgIHZhciBwcmVzSWQgPSBnZXRQcmVzZW50YXRpb25JZChwcmVzVXJsKTtcbiAgICBzZXRQcmVzZW50YXRpb25WaXNpYmxlKGZhbHNlKTtcbiAgICAkKCcjcGFydGljaXBhbnRfJ1xuICAgICAgICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZClcbiAgICAgICAgKyAnXycgKyBwcmVzSWQpLnJlbW92ZSgpO1xuICAgICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykucmVtb3ZlKCk7XG4gICAgaWYgKHByZXppUGxheWVyICE9IG51bGwpIHtcbiAgICAgICAgcHJlemlQbGF5ZXIuZGVzdHJveSgpO1xuICAgICAgICBwcmV6aVBsYXllciA9IG51bGw7XG4gICAgfVxufTtcblxuLyoqXG4gKiBJbmRpY2F0ZXMgaWYgdGhlIGdpdmVuIHN0cmluZyBpcyBhbiBhbHBoYW51bWVyaWMgc3RyaW5nLlxuICogTm90ZSB0aGF0IHNvbWUgc3BlY2lhbCBjaGFyYWN0ZXJzIGFyZSBhbHNvIGFsbG93ZWQgKC0sIF8gLCAvLCAmLCA/LCA9LCA7KSBmb3IgdGhlXG4gKiBwdXJwb3NlIG9mIGNoZWNraW5nIFVSSXMuXG4gKi9cbmZ1bmN0aW9uIGlzQWxwaGFudW1lcmljKHVuc2FmZVRleHQpIHtcbiAgICB2YXIgcmVnZXggPSAvXlthLXowLTktX1xcLyZcXD89O10rJC9pO1xuICAgIHJldHVybiByZWdleC50ZXN0KHVuc2FmZVRleHQpO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHByZXNlbnRhdGlvbiBpZCBmcm9tIHRoZSBnaXZlbiB1cmwuXG4gKi9cbmZ1bmN0aW9uIGdldFByZXNlbnRhdGlvbklkIChwcmVzVXJsKSB7XG4gICAgdmFyIHByZXNJZFRtcCA9IHByZXNVcmwuc3Vic3RyaW5nKHByZXNVcmwuaW5kZXhPZihcInByZXppLmNvbS9cIikgKyAxMCk7XG4gICAgcmV0dXJuIHByZXNJZFRtcC5zdWJzdHJpbmcoMCwgcHJlc0lkVG1wLmluZGV4T2YoJy8nKSk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgcHJlc2VudGF0aW9uIHdpZHRoLlxuICovXG5mdW5jdGlvbiBnZXRQcmVzZW50YXRpb25XaWR0aCgpIHtcbiAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBVSVV0aWwuZ2V0QXZhaWxhYmxlVmlkZW9XaWR0aCgpO1xuICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSBnZXRQcmVzZW50YXRpb25IZWloZ3QoKTtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IDE2LjAgLyA5LjA7XG4gICAgaWYgKGF2YWlsYWJsZUhlaWdodCA8IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW8pIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBNYXRoLmZsb29yKGF2YWlsYWJsZUhlaWdodCAqIGFzcGVjdFJhdGlvKTtcbiAgICB9XG4gICAgcmV0dXJuIGF2YWlsYWJsZVdpZHRoO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHByZXNlbnRhdGlvbiBoZWlnaHQuXG4gKi9cbmZ1bmN0aW9uIGdldFByZXNlbnRhdGlvbkhlaWhndCgpIHtcbiAgICB2YXIgcmVtb3RlVmlkZW9zID0gJCgnI3JlbW90ZVZpZGVvcycpO1xuICAgIHJldHVybiB3aW5kb3cuaW5uZXJIZWlnaHQgLSByZW1vdGVWaWRlb3Mub3V0ZXJIZWlnaHQoKTtcbn1cblxuLyoqXG4gKiBSZXNpemVzIHRoZSBwcmVzZW50YXRpb24gaWZyYW1lLlxuICovXG5mdW5jdGlvbiByZXNpemUoKSB7XG4gICAgaWYgKCQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykpIHtcbiAgICAgICAgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS53aWR0aChnZXRQcmVzZW50YXRpb25XaWR0aCgpKTtcbiAgICAgICAgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5oZWlnaHQoZ2V0UHJlc2VudGF0aW9uSGVpaGd0KCkpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBTaG93cy9oaWRlcyBhIHByZXNlbnRhdGlvbi5cbiAqL1xuZnVuY3Rpb24gc2V0UHJlc2VudGF0aW9uVmlzaWJsZSh2aXNpYmxlKSB7XG4gICAgdmFyIHByZXppID0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKTtcbiAgICBpZiAodmlzaWJsZSkge1xuICAgICAgICAvLyBUcmlnZ2VyIHRoZSB2aWRlby5zZWxlY3RlZCBldmVudCB0byBpbmRpY2F0ZSBhIGNoYW5nZSBpbiB0aGVcbiAgICAgICAgLy8gbGFyZ2UgdmlkZW8uXG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoXCJ2aWRlby5zZWxlY3RlZFwiLCBbdHJ1ZV0pO1xuXG4gICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZU91dCgzMDApO1xuICAgICAgICBwcmV6aS5mYWRlSW4oMzAwLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHByZXppLmNzcyh7b3BhY2l0eTonMSd9KTtcbiAgICAgICAgICAgIFRvb2xiYXJUb2dnbGVyLmRvY2tUb29sYmFyKHRydWUpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2V0TGFyZ2VWaWRlb1Zpc2libGUoZmFsc2UpO1xuICAgICAgICB9KTtcbiAgICAgICAgJCgnI2FjdGl2ZVNwZWFrZXInKS5jc3MoJ3Zpc2liaWxpdHknLCAnaGlkZGVuJyk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBpZiAocHJlemkuY3NzKCdvcGFjaXR5JykgPT0gJzEnKSB7XG4gICAgICAgICAgICBwcmV6aS5mYWRlT3V0KDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHByZXppLmNzcyh7b3BhY2l0eTonMCd9KTtcbiAgICAgICAgICAgICAgICAkKCcjcmVsb2FkUHJlc2VudGF0aW9uJykuY3NzKHtkaXNwbGF5Oidub25lJ30pO1xuICAgICAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZUluKDMwMCwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldExhcmdlVmlkZW9WaXNpYmxlKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcihmYWxzZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBQcmVzZW50YXRpb24gaGFzIGJlZW4gcmVtb3ZlZC5cbiAqL1xuJChkb2N1bWVudCkuYmluZCgncHJlc2VudGF0aW9ucmVtb3ZlZC5tdWMnLCBwcmVzZW50YXRpb25SZW1vdmVkKTtcblxuLyoqXG4gKiBQcmVzZW50YXRpb24gaGFzIGJlZW4gYWRkZWQuXG4gKi9cbiQoZG9jdW1lbnQpLmJpbmQoJ3ByZXNlbnRhdGlvbmFkZGVkLm11YycsIHByZXNlbnRhdGlvbkFkZGVkKTtcblxuLypcbiAqIEluZGljYXRlcyBwcmVzZW50YXRpb24gc2xpZGUgY2hhbmdlLlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCdnb3Rvc2xpZGUubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIHByZXNVcmwsIGN1cnJlbnQpIHtcbiAgICBpZiAocHJlemlQbGF5ZXIgJiYgcHJlemlQbGF5ZXIuZ2V0Q3VycmVudFN0ZXAoKSAhPSBjdXJyZW50KSB7XG4gICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50KTtcblxuICAgICAgICB2YXIgYW5pbWF0aW9uU3RlcHNBcnJheSA9IHByZXppUGxheWVyLmdldEFuaW1hdGlvbkNvdW50T25TdGVwcygpO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHBhcnNlSW50KGFuaW1hdGlvblN0ZXBzQXJyYXlbY3VycmVudF0pOyBpKyspIHtcbiAgICAgICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50LCBpKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG4vKipcbiAqIE9uIHZpZGVvIHNlbGVjdGVkIGV2ZW50LlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCd2aWRlby5zZWxlY3RlZCcsIGZ1bmN0aW9uIChldmVudCwgaXNQcmVzZW50YXRpb24pIHtcbiAgICBpZiAoIWlzUHJlc2VudGF0aW9uICYmICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykpIHtcbiAgICAgICAgc2V0UHJlc2VudGF0aW9uVmlzaWJsZShmYWxzZSk7XG4gICAgfVxufSk7XG5cbiQod2luZG93KS5yZXNpemUoZnVuY3Rpb24gKCkge1xuICAgIHJlc2l6ZSgpO1xufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gUHJlemk7XG4iLCIoZnVuY3Rpb24oKSB7XG4gICAgXCJ1c2Ugc3RyaWN0XCI7XG4gICAgdmFyIF9fYmluZCA9IGZ1bmN0aW9uKGZuLCBtZSl7IHJldHVybiBmdW5jdGlvbigpeyByZXR1cm4gZm4uYXBwbHkobWUsIGFyZ3VtZW50cyk7IH07IH07XG5cbiAgICB3aW5kb3cuUHJlemlQbGF5ZXIgPSAoZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgUHJlemlQbGF5ZXIuQVBJX1ZFUlNJT04gPSAxO1xuICAgICAgICBQcmV6aVBsYXllci5DVVJSRU5UX1NURVAgPSAnY3VycmVudFN0ZXAnO1xuICAgICAgICBQcmV6aVBsYXllci5DVVJSRU5UX0FOSU1BVElPTl9TVEVQID0gJ2N1cnJlbnRBbmltYXRpb25TdGVwJztcbiAgICAgICAgUHJlemlQbGF5ZXIuQ1VSUkVOVF9PQkpFQ1QgPSAnY3VycmVudE9iamVjdCc7XG4gICAgICAgIFByZXppUGxheWVyLlNUQVRVU19MT0FESU5HID0gJ2xvYWRpbmcnO1xuICAgICAgICBQcmV6aVBsYXllci5TVEFUVVNfUkVBRFkgPSAncmVhZHknO1xuICAgICAgICBQcmV6aVBsYXllci5TVEFUVVNfQ09OVEVOVF9SRUFEWSA9ICdjb250ZW50cmVhZHknO1xuICAgICAgICBQcmV6aVBsYXllci5FVkVOVF9DVVJSRU5UX1NURVAgPSBcImN1cnJlbnRTdGVwQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX0NVUlJFTlRfQU5JTUFUSU9OX1NURVAgPSBcImN1cnJlbnRBbmltYXRpb25TdGVwQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX0NVUlJFTlRfT0JKRUNUID0gXCJjdXJyZW50T2JqZWN0Q2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX1NUQVRVUyA9IFwic3RhdHVzQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX1BMQVlJTkcgPSBcImlzQXV0b1BsYXlpbmdDaGFuZ2VcIjtcbiAgICAgICAgUHJlemlQbGF5ZXIuRVZFTlRfSVNfTU9WSU5HID0gXCJpc01vdmluZ0NoYW5nZVwiO1xuICAgICAgICBQcmV6aVBsYXllci5kb21haW4gPSBcImh0dHBzOi8vcHJlemkuY29tXCI7XG4gICAgICAgIFByZXppUGxheWVyLnBhdGggPSBcIi9wbGF5ZXIvXCI7XG4gICAgICAgIFByZXppUGxheWVyLnBsYXllcnMgPSB7fTtcbiAgICAgICAgUHJlemlQbGF5ZXIuYmluZGVkX21ldGhvZHMgPSBbJ2NoYW5nZXNIYW5kbGVyJ107XG5cbiAgICAgICAgUHJlemlQbGF5ZXIuY3JlYXRlTXVsdGlwbGVQbGF5ZXJzID0gZnVuY3Rpb24ob3B0aW9uQXJyYXkpe1xuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8b3B0aW9uQXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgb3B0aW9uU2V0ID0gb3B0aW9uQXJyYXlbaV07XG4gICAgICAgICAgICAgICAgbmV3IFByZXppUGxheWVyKG9wdGlvblNldC5pZCwgb3B0aW9uU2V0KTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIubWVzc2FnZVJlY2VpdmVkID0gZnVuY3Rpb24oZXZlbnQpe1xuICAgICAgICAgICAgdmFyIG1lc3NhZ2UsIGl0ZW0sIHBsYXllcjtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZSA9IEpTT04ucGFyc2UoZXZlbnQuZGF0YSk7XG4gICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2UuaWQgJiYgKHBsYXllciA9IFByZXppUGxheWVyLnBsYXllcnNbbWVzc2FnZS5pZF0pKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChwbGF5ZXIub3B0aW9ucy5kZWJ1ZyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvbnNvbGUgJiYgY29uc29sZS5sb2cpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3JlY2VpdmVkJywgbWVzc2FnZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2UudHlwZSA9PT0gXCJjaGFuZ2VzXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYXllci5jaGFuZ2VzSGFuZGxlcihtZXNzYWdlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHBsYXllci5jYWxsYmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGl0ZW0gPSBwbGF5ZXIuY2FsbGJhY2tzW2ldO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGl0ZW0gJiYgbWVzc2FnZS50eXBlID09PSBpdGVtLmV2ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbS5jYWxsYmFjayhtZXNzYWdlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGZ1bmN0aW9uIFByZXppUGxheWVyKGlkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGFyYW1zLCBwYXJhbVN0cmluZyA9IFwiXCIsIF90aGlzID0gdGhpcztcbiAgICAgICAgICAgIGlmIChQcmV6aVBsYXllci5wbGF5ZXJzW2lkXSl7XG4gICAgICAgICAgICAgICAgUHJlemlQbGF5ZXIucGxheWVyc1tpZF0uZGVzdHJveSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8UHJlemlQbGF5ZXIuYmluZGVkX21ldGhvZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgbWV0aG9kX25hbWUgPSBQcmV6aVBsYXllci5iaW5kZWRfbWV0aG9kc1tpXTtcbiAgICAgICAgICAgICAgICBfdGhpc1ttZXRob2RfbmFtZV0gPSBfX2JpbmQoX3RoaXNbbWV0aG9kX25hbWVdLCBfdGhpcyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXMgPSB7J3N0YXR1cyc6IFByZXppUGxheWVyLlNUQVRVU19MT0FESU5HfTtcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW1ByZXppUGxheWVyLkNVUlJFTlRfU1RFUF0gPSAwO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXNbUHJlemlQbGF5ZXIuQ1VSUkVOVF9BTklNQVRJT05fU1RFUF0gPSAwO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXNbUHJlemlQbGF5ZXIuQ1VSUkVOVF9PQkpFQ1RdID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG4gICAgICAgICAgICB0aGlzLmlkID0gaWQ7XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG4gICAgICAgICAgICBpZiAoIXRoaXMuZW1iZWRUbykge1xuICAgICAgICAgICAgICAgIHRocm93IFwiVGhlIGVsZW1lbnQgaWQgaXMgbm90IGF2YWlsYWJsZS5cIjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuaWZyYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaWZyYW1lJyk7XG4gICAgICAgICAgICBwYXJhbXMgPSBbXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnb2lkJywgdmFsdWU6IG9wdGlvbnMucHJlemlJZCB9LFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2V4cGxvcmFibGUnLCB2YWx1ZTogb3B0aW9ucy5leHBsb3JhYmxlID8gMSA6IDAgfSxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjb250cm9scycsIHZhbHVlOiBvcHRpb25zLmNvbnRyb2xzID8gMSA6IDAgfVxuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIGZvcih2YXIgaT0wOyBpPHBhcmFtcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBwYXJhbSA9IHBhcmFtc1tpXTtcbiAgICAgICAgICAgICAgICBwYXJhbVN0cmluZyArPSAoaT09PTAgPyBcIj9cIiA6IFwiJlwiKSArIHBhcmFtLm5hbWUgKyBcIj1cIiArIHBhcmFtLnZhbHVlO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLnNyYyA9IFByZXppUGxheWVyLmRvbWFpbiArIFByZXppUGxheWVyLnBhdGggKyBwYXJhbVN0cmluZztcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLmZyYW1lQm9yZGVyID0gMDtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLnNjcm9sbGluZyA9IFwibm9cIjtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLndpZHRoID0gb3B0aW9ucy53aWR0aCB8fCA2NDA7XG4gICAgICAgICAgICB0aGlzLmlmcmFtZS5oZWlnaHQgPSBvcHRpb25zLmhlaWdodCB8fCA0ODA7XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8uaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICAvLyBKSVRTSTogSU4gQ0FTRSBTT01FVEhJTkcgR09FUyBXUk9ORy5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lbWJlZFRvLmFwcGVuZENoaWxkKHRoaXMuaWZyYW1lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIkNBVENIIEVSUk9SXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBKSVRTSTogSW5jcmVhc2UgaW50ZXJ2YWwgZnJvbSAyMDAgdG8gNTAwLCB3aGljaCBmaXhlcyBwcmV6aVxuICAgICAgICAgICAgLy8gY3Jhc2hlcyBmb3IgdXMuXG4gICAgICAgICAgICB0aGlzLmluaXRQb2xsSW50ZXJ2YWwgPSBzZXRJbnRlcnZhbChmdW5jdGlvbigpe1xuICAgICAgICAgICAgICAgIF90aGlzLnNlbmRNZXNzYWdlKHsnYWN0aW9uJzogJ2luaXQnfSk7XG4gICAgICAgICAgICB9LCA1MDApO1xuICAgICAgICAgICAgUHJlemlQbGF5ZXIucGxheWVyc1tpZF0gPSB0aGlzO1xuICAgICAgICB9XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmNoYW5nZXNIYW5kbGVyID0gZnVuY3Rpb24obWVzc2FnZSkge1xuICAgICAgICAgICAgdmFyIGtleSwgdmFsdWUsIGosIGl0ZW07XG4gICAgICAgICAgICBpZiAodGhpcy5pbml0UG9sbEludGVydmFsKSB7XG4gICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmluaXRQb2xsSW50ZXJ2YWwpO1xuICAgICAgICAgICAgICAgIHRoaXMuaW5pdFBvbGxJbnRlcnZhbCA9IGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yIChrZXkgaW4gbWVzc2FnZS5kYXRhKSB7XG4gICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2UuZGF0YS5oYXNPd25Qcm9wZXJ0eShrZXkpKXtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBtZXNzYWdlLmRhdGFba2V5XTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNba2V5XSA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGo9MDsgajx0aGlzLmNhbGxiYWNrcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgaXRlbSA9IHRoaXMuY2FsbGJhY2tzW2pdO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGl0ZW0gJiYgaXRlbS5ldmVudCA9PT0ga2V5ICsgXCJDaGFuZ2VcIil7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbS5jYWxsYmFjayh7dHlwZTogaXRlbS5ldmVudCwgdmFsdWU6IHZhbHVlfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmluaXRQb2xsSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHRoaXMuaW5pdFBvbGxJbnRlcnZhbCk7XG4gICAgICAgICAgICAgICAgdGhpcy5pbml0UG9sbEludGVydmFsID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8uaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnNlbmRNZXNzYWdlID0gZnVuY3Rpb24obWVzc2FnZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5kZWJ1ZyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgIGlmIChjb25zb2xlICYmIGNvbnNvbGUubG9nKSBjb25zb2xlLmxvZygnc2VudCcsIG1lc3NhZ2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbWVzc2FnZS52ZXJzaW9uID0gUHJlemlQbGF5ZXIuQVBJX1ZFUlNJT047XG4gICAgICAgICAgICBtZXNzYWdlLmlkID0gdGhpcy5pZDtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmlmcmFtZS5jb250ZW50V2luZG93LnBvc3RNZXNzYWdlKEpTT04uc3RyaW5naWZ5KG1lc3NhZ2UpLCAnKicpO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5uZXh0U3RlcCA9IC8qIG5leHRTdGVwIGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvTmV4dFN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9OZXh0U3RlcCddXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUucHJldmlvdXNTdGVwID0gLyogcHJldmlvdXNTdGVwIGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvUHJldmlvdXNTdGVwID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnbW92ZVRvUHJldlN0ZXAnXVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnRvU3RlcCA9IC8qIHRvU3RlcCBpcyBERVBSRUNBVEVEICovXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5mbHlUb1N0ZXAgPSBmdW5jdGlvbihzdGVwLCBhbmltYXRpb25fc3RlcCkge1xuICAgICAgICAgICAgdmFyIG9iaiA9IHRoaXM7XG4gICAgICAgICAgICAvLyBjaGVjayBhbmltYXRpb25fc3RlcFxuICAgICAgICAgICAgaWYgKGFuaW1hdGlvbl9zdGVwID4gMCAmJlxuICAgICAgICAgICAgICAgIG9iai52YWx1ZXMuYW5pbWF0aW9uQ291bnRPblN0ZXBzICYmXG4gICAgICAgICAgICAgICAgb2JqLnZhbHVlcy5hbmltYXRpb25Db3VudE9uU3RlcHNbc3RlcF0gPD0gYW5pbWF0aW9uX3N0ZXApIHtcbiAgICAgICAgICAgICAgICBhbmltYXRpb25fc3RlcCA9IG9iai52YWx1ZXMuYW5pbWF0aW9uQ291bnRPblN0ZXBzW3N0ZXBdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8ganVtcCB0byBhbmltYXRpb24gc3RlcHMgYnkgY2FsbGluZyBmbHlUb05leHRTdGVwKClcbiAgICAgICAgICAgIGZ1bmN0aW9uIGRvQW5pbWF0aW9uU3RlcHMoKSB7XG4gICAgICAgICAgICAgICAgaWYgKG9iai52YWx1ZXMuaXNNb3ZpbmcgPT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGRvQW5pbWF0aW9uU3RlcHMsIDEwMCk7IC8vIHdhaXQgdW50aWwgdGhlIGZsaWdodCBlbmRzXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgd2hpbGUgKGFuaW1hdGlvbl9zdGVwLS0gPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIG9iai5mbHlUb05leHRTdGVwKCk7IC8vIGRvIHRoZSBhbmltYXRpb24gc3RlcHNcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZXRUaW1lb3V0KGRvQW5pbWF0aW9uU3RlcHMsIDIwMCk7IC8vIDIwMG1zIGlzIHRoZSBpbnRlcm5hbCBcInJlcG9ydGluZ1wiIHRpbWVcbiAgICAgICAgICAgIC8vIGp1bXAgdG8gdGhlIHN0ZXBcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9TdGVwJywgc3RlcF1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS50b09iamVjdCA9IC8qIHRvT2JqZWN0IGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvT2JqZWN0ID0gZnVuY3Rpb24ob2JqZWN0SWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9PYmplY3QnLCBvYmplY3RJZF1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5wbGF5ID0gZnVuY3Rpb24oZGVmYXVsdERlbGF5KSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnc3RhcnRBdXRvUGxheScsIGRlZmF1bHREZWxheV1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5zdG9wID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnc3RvcEF1dG9QbGF5J11cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5wYXVzZSA9IGZ1bmN0aW9uKGRlZmF1bHREZWxheSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2VuZE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgICdhY3Rpb24nOiAncHJlc2VudCcsXG4gICAgICAgICAgICAgICAgJ2RhdGEnOiBbJ3BhdXNlQXV0b1BsYXknLCBkZWZhdWx0RGVsYXldXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudFN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5jdXJyZW50U3RlcDtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudEFuaW1hdGlvblN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5jdXJyZW50QW5pbWF0aW9uU3RlcDtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudE9iamVjdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzLmN1cnJlbnRPYmplY3Q7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmdldFN0YXR1cyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzLnN0YXR1cztcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuaXNQbGF5aW5nID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMuaXNBdXRvUGxheWluZztcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0U3RlcENvdW50ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMuc3RlcENvdW50O1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5nZXRBbmltYXRpb25Db3VudE9uU3RlcHMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5hbmltYXRpb25Db3VudE9uU3RlcHM7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmdldFRpdGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMudGl0bGU7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnNldERpbWVuc2lvbnMgPSBmdW5jdGlvbihkaW1zKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBwYXJhbWV0ZXIgaW4gZGltcykge1xuICAgICAgICAgICAgICAgIHRoaXMuaWZyYW1lW3BhcmFtZXRlcl0gPSBkaW1zW3BhcmFtZXRlcl07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0RGltZW5zaW9ucyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICB3aWR0aDogcGFyc2VJbnQodGhpcy5pZnJhbWUud2lkdGgsIDEwKSxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHBhcnNlSW50KHRoaXMuaWZyYW1lLmhlaWdodCwgMTApXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldmVudCwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgIHRoaXMuY2FsbGJhY2tzLnB1c2goe1xuICAgICAgICAgICAgICAgIGV2ZW50OiBldmVudCxcbiAgICAgICAgICAgICAgICBjYWxsYmFjazogY2FsbGJhY2tcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5vZmYgPSBmdW5jdGlvbihldmVudCwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgIHZhciBqLCBpdGVtO1xuICAgICAgICAgICAgaWYgKGV2ZW50ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNhbGxiYWNrcyA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaiA9IHRoaXMuY2FsbGJhY2tzLmxlbmd0aDtcbiAgICAgICAgICAgIHdoaWxlIChqLS0pIHtcbiAgICAgICAgICAgICAgICBpdGVtID0gdGhpcy5jYWxsYmFja3Nbal07XG4gICAgICAgICAgICAgICAgaWYgKGl0ZW0gJiYgaXRlbS5ldmVudCA9PT0gZXZlbnQgJiYgKGNhbGxiYWNrID09PSB1bmRlZmluZWQgfHwgaXRlbS5jYWxsYmFjayA9PT0gY2FsbGJhY2spKXtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jYWxsYmFja3Muc3BsaWNlKGosIDEpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBpZiAod2luZG93LmFkZEV2ZW50TGlzdGVuZXIpIHtcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgUHJlemlQbGF5ZXIubWVzc2FnZVJlY2VpdmVkLCBmYWxzZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB3aW5kb3cuYXR0YWNoRXZlbnQoJ29ubWVzc2FnZScsIFByZXppUGxheWVyLm1lc3NhZ2VSZWNlaXZlZCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gUHJlemlQbGF5ZXI7XG5cbiAgICB9KSgpO1xuXG59KSgpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFByZXppUGxheWVyO1xuIiwidmFyIENoYXQgPSByZXF1aXJlKFwiLi9jaGF0L0NoYXRcIik7XG52YXIgQ29udGFjdExpc3QgPSByZXF1aXJlKFwiLi9jb250YWN0bGlzdC9Db250YWN0TGlzdFwiKTtcbnZhciBTZXR0aW5ncyA9IHJlcXVpcmUoXCIuLy4uLy4uL3NldHRpbmdzL1NldHRpbmdzXCIpO1xudmFyIFNldHRpbmdzTWVudSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzL1NldHRpbmdzTWVudVwiKTtcbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKTtcbnZhciBUb29sYmFyVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi90b29sYmFycy9Ub29sYmFyVG9nZ2xlclwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vdXRpbC9VSVV0aWxcIik7XG5cbi8qKlxuICogVG9nZ2xlciBmb3IgdGhlIGNoYXQsIGNvbnRhY3QgbGlzdCwgc2V0dGluZ3MgbWVudSwgZXRjLi5cbiAqL1xudmFyIFBhbmVsVG9nZ2xlciA9IChmdW5jdGlvbihteSkge1xuXG4gICAgdmFyIGN1cnJlbnRseU9wZW4gPSBudWxsO1xuICAgIHZhciBidXR0b25zID0ge1xuICAgICAgICAnI2NoYXRzcGFjZSc6ICcjY2hhdEJvdHRvbUJ1dHRvbicsXG4gICAgICAgICcjY29udGFjdGxpc3QnOiAnI2NvbnRhY3RMaXN0QnV0dG9uJyxcbiAgICAgICAgJyNzZXR0aW5nc21lbnUnOiAnI3NldHRpbmdzQnV0dG9uJ1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRoZSB2aWRlbyBhcmVhXG4gICAgICogQHBhcmFtIGlzQ2xvc2luZyB3aGV0aGVyIHRoZSBzaWRlIHBhbmVsIGlzIGdvaW5nIHRvIGJlIGNsb3NlZCBvciBpcyBnb2luZyB0byBvcGVuIC8gcmVtYWluIG9wZW5lZFxuICAgICAqIEBwYXJhbSBjb21wbGV0ZUZ1bmN0aW9uIGEgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW4gdGhlIHZpZGVvIHNwYWNlIGlzIHJlc2l6ZWRcbiAgICAgKi9cbiAgICB2YXIgcmVzaXplVmlkZW9BcmVhID0gZnVuY3Rpb24oaXNDbG9zaW5nLCBjb21wbGV0ZUZ1bmN0aW9uKSB7XG4gICAgICAgIHZhciB2aWRlb3NwYWNlID0gJCgnI3ZpZGVvc3BhY2UnKTtcblxuICAgICAgICB2YXIgcGFuZWxTaXplID0gaXNDbG9zaW5nID8gWzAsIDBdIDogUGFuZWxUb2dnbGVyLmdldFBhbmVsU2l6ZSgpO1xuICAgICAgICB2YXIgdmlkZW9zcGFjZVdpZHRoID0gd2luZG93LmlubmVyV2lkdGggLSBwYW5lbFNpemVbMF07XG4gICAgICAgIHZhciB2aWRlb3NwYWNlSGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuICAgICAgICB2YXIgdmlkZW9TaXplXG4gICAgICAgICAgICA9IFZpZGVvTGF5b3V0LmdldFZpZGVvU2l6ZShudWxsLCBudWxsLCB2aWRlb3NwYWNlV2lkdGgsIHZpZGVvc3BhY2VIZWlnaHQpO1xuICAgICAgICB2YXIgdmlkZW9XaWR0aCA9IHZpZGVvU2l6ZVswXTtcbiAgICAgICAgdmFyIHZpZGVvSGVpZ2h0ID0gdmlkZW9TaXplWzFdO1xuICAgICAgICB2YXIgdmlkZW9Qb3NpdGlvbiA9IFZpZGVvTGF5b3V0LmdldFZpZGVvUG9zaXRpb24odmlkZW9XaWR0aCxcbiAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgdmlkZW9zcGFjZUhlaWdodCk7XG4gICAgICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblswXTtcbiAgICAgICAgdmFyIHZlcnRpY2FsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblsxXTtcblxuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9zcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHRodW1ibmFpbHNXaWR0aCA9IHRodW1ibmFpbFNpemVbMF07XG4gICAgICAgIHZhciB0aHVtYm5haWxzSGVpZ2h0ID0gdGh1bWJuYWlsU2l6ZVsxXTtcbiAgICAgICAgLy9mb3IgY2hhdFxuXG4gICAgICAgIHZpZGVvc3BhY2UuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgcmlnaHQ6IHBhbmVsU2l6ZVswXSxcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgIGhlaWdodDogdmlkZW9zcGFjZUhlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMCxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZTogY29tcGxldGVGdW5jdGlvblxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcycpLmFuaW1hdGUoe1xuICAgICAgICAgICAgICAgIGhlaWdodDogdGh1bWJuYWlsc0hlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcz5zcGFuJykuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgaGVpZ2h0OiB0aHVtYm5haWxzSGVpZ2h0LFxuICAgICAgICAgICAgICAgIHdpZHRoOiB0aHVtYm5haWxzV2lkdGhcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDAsXG4gICAgICAgICAgICAgICAgY29tcGxldGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwicmVtb3RldmlkZW8ucmVzaXplZFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3RodW1ibmFpbHNXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxzSGVpZ2h0XSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgIGhlaWdodDogdmlkZW9zcGFjZUhlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9XaWR0aCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgIHRvcDogdmVydGljYWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgYm90dG9tOiB2ZXJ0aWNhbEluZGVudCxcbiAgICAgICAgICAgICAgICBsZWZ0OiBob3Jpem9udGFsSW5kZW50LFxuICAgICAgICAgICAgICAgIHJpZ2h0OiBob3Jpem9udGFsSW5kZW50XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVG9nZ2xlcyB0aGUgd2luZG93cyBpbiB0aGUgc2lkZSBwYW5lbFxuICAgICAqIEBwYXJhbSBvYmplY3QgdGhlIHdpbmRvdyB0aGF0IHNob3VsZCBiZSBzaG93blxuICAgICAqIEBwYXJhbSBzZWxlY3RvciB0aGUgc2VsZWN0b3IgZm9yIHRoZSBlbGVtZW50IGNvbnRhaW5pbmcgdGhlIHBhbmVsXG4gICAgICogQHBhcmFtIG9uT3BlbkNvbXBsZXRlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBwYW5lbCBpcyBvcGVuZWRcbiAgICAgKiBAcGFyYW0gb25PcGVuIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBpZiB0aGUgd2luZG93IGlzIGdvaW5nIHRvIGJlIG9wZW5lZFxuICAgICAqIEBwYXJhbSBvbkNsb3NlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBpZiB0aGUgd2luZG93IGlzIGdvaW5nIHRvIGJlIGNsb3NlZFxuICAgICAqL1xuICAgIHZhciB0b2dnbGUgPSBmdW5jdGlvbihvYmplY3QsIHNlbGVjdG9yLCBvbk9wZW5Db21wbGV0ZSwgb25PcGVuLCBvbkNsb3NlKSB7XG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhidXR0b25zW3NlbGVjdG9yXSwgXCJhY3RpdmVcIik7XG5cbiAgICAgICAgaWYgKG9iamVjdC5pc1Zpc2libGUoKSkge1xuICAgICAgICAgICAgJChcIiN0b2FzdC1jb250YWluZXJcIikuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIHJpZ2h0OiAnNXB4J1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICQoc2VsZWN0b3IpLmhpZGUoXCJzbGlkZVwiLCB7XG4gICAgICAgICAgICAgICAgZGlyZWN0aW9uOiBcInJpZ2h0XCIsXG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaWYodHlwZW9mIG9uQ2xvc2UgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgIG9uQ2xvc2UoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY3VycmVudGx5T3BlbiA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBVbmRvY2sgdGhlIHRvb2xiYXIgd2hlbiB0aGUgY2hhdCBpcyBzaG93biBhbmQgaWYgd2UncmUgaW4gYVxuICAgICAgICAgICAgLy8gdmlkZW8gbW9kZS5cbiAgICAgICAgICAgIGlmIChWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9WaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcihmYWxzZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKGN1cnJlbnRseU9wZW4pIHtcbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudCA9ICQoY3VycmVudGx5T3Blbik7XG4gICAgICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKGJ1dHRvbnNbY3VycmVudGx5T3Blbl0sIFwiYWN0aXZlXCIpO1xuICAgICAgICAgICAgICAgIGN1cnJlbnQuY3NzKCd6LWluZGV4JywgNCk7XG4gICAgICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY3NzKCdkaXNwbGF5JywgJ25vbmUnKTtcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jc3MoJ3otaW5kZXgnLCA1KTtcbiAgICAgICAgICAgICAgICB9LCA1MDApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICAgICAgcmlnaHQ6IChQYW5lbFRvZ2dsZXIuZ2V0UGFuZWxTaXplKClbMF0gKyA1KSArICdweCdcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAkKHNlbGVjdG9yKS5zaG93KFwic2xpZGVcIiwge1xuICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogXCJyaWdodFwiLFxuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlOiBvbk9wZW5Db21wbGV0ZVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZih0eXBlb2Ygb25PcGVuID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICAgICAgICBvbk9wZW4oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY3VycmVudGx5T3BlbiA9IHNlbGVjdG9yO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIC8gY2xvc2VzIHRoZSBjaGF0IGFyZWEuXG4gICAgICovXG4gICAgbXkudG9nZ2xlQ2hhdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgY2hhdENvbXBsZXRlRnVuY3Rpb24gPSBDaGF0LmlzVmlzaWJsZSgpID9cbiAgICAgICAgICAgIGZ1bmN0aW9uKCkge30gOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBDaGF0LnNjcm9sbENoYXRUb0JvdHRvbSgpO1xuICAgICAgICAgICAgJCgnI2NoYXRzcGFjZScpLnRyaWdnZXIoJ3Nob3duJyk7XG4gICAgICAgIH07XG5cbiAgICAgICAgcmVzaXplVmlkZW9BcmVhKENoYXQuaXNWaXNpYmxlKCksIGNoYXRDb21wbGV0ZUZ1bmN0aW9uKTtcblxuICAgICAgICB0b2dnbGUoQ2hhdCxcbiAgICAgICAgICAgICcjY2hhdHNwYWNlJyxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAvLyBSZXF1ZXN0IHRoZSBmb2N1cyBpbiB0aGUgbmlja25hbWUgZmllbGQgb3IgdGhlIGNoYXQgaW5wdXQgZmllbGQuXG4gICAgICAgICAgICAgICAgaWYgKCQoJyNuaWNrbmFtZScpLmNzcygndmlzaWJpbGl0eScpID09PSAndmlzaWJsZScpIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI25pY2tpbnB1dCcpLmZvY3VzKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI3VzZXJtc2cnKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgQ2hhdC5yZXNpemVDaGF0LFxuICAgICAgICAgICAgbnVsbCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIC8gY2xvc2VzIHRoZSBjb250YWN0IGxpc3QgYXJlYS5cbiAgICAgKi9cbiAgICBteS50b2dnbGVDb250YWN0TGlzdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGNvbXBsZXRlRnVuY3Rpb24gPSBDb250YWN0TGlzdC5pc1Zpc2libGUoKSA/XG4gICAgICAgICAgICBmdW5jdGlvbigpIHt9IDogZnVuY3Rpb24gKCkgeyAkKCcjY29udGFjdGxpc3QnKS50cmlnZ2VyKCdzaG93bicpO307XG4gICAgICAgIHJlc2l6ZVZpZGVvQXJlYShDb250YWN0TGlzdC5pc1Zpc2libGUoKSwgY29tcGxldGVGdW5jdGlvbik7XG5cbiAgICAgICAgdG9nZ2xlKENvbnRhY3RMaXN0LFxuICAgICAgICAgICAgJyNjb250YWN0bGlzdCcsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgQ29udGFjdExpc3Quc2V0VmlzdWFsTm90aWZpY2F0aW9uKGZhbHNlKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgLyBjbG9zZXMgdGhlIHNldHRpbmdzIG1lbnVcbiAgICAgKi9cbiAgICBteS50b2dnbGVTZXR0aW5nc01lbnUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmVzaXplVmlkZW9BcmVhKFNldHRpbmdzTWVudS5pc1Zpc2libGUoKSwgZnVuY3Rpb24gKCl7fSk7XG4gICAgICAgIHRvZ2dsZShTZXR0aW5nc01lbnUsXG4gICAgICAgICAgICAnI3NldHRpbmdzbWVudScsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICAgICAgICAgICAgICAkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUgPSBzZXR0aW5ncy5kaXNwbGF5TmFtZTtcbiAgICAgICAgICAgICAgICAkKCcjc2V0RW1haWwnKS5nZXQoMCkudmFsdWUgPSBzZXR0aW5ncy5lbWFpbDtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2l6ZSBvZiB0aGUgc2lkZSBwYW5lbC5cbiAgICAgKi9cbiAgICBteS5nZXRQYW5lbFNpemUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG4gICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuXG4gICAgICAgIHZhciBwYW5lbFdpZHRoID0gMjAwO1xuICAgICAgICBpZiAoYXZhaWxhYmxlV2lkdGggKiAwLjIgPCAyMDApIHtcbiAgICAgICAgICAgIHBhbmVsV2lkdGggPSBhdmFpbGFibGVXaWR0aCAqIDAuMjtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBbcGFuZWxXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbiAgICB9O1xuXG4gICAgbXkuaXNWaXNpYmxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAoQ2hhdC5pc1Zpc2libGUoKSB8fCBDb250YWN0TGlzdC5pc1Zpc2libGUoKSB8fCBTZXR0aW5nc01lbnUuaXNWaXNpYmxlKCkpO1xuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG5cbn0oUGFuZWxUb2dnbGVyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gUGFuZWxUb2dnbGVyOyIsIi8qIGdsb2JhbCAkLCBVdGlsLCBuaWNrbmFtZTp0cnVlICovXG52YXIgUmVwbGFjZW1lbnQgPSByZXF1aXJlKFwiLi9SZXBsYWNlbWVudFwiKTtcbnZhciBDb21tYW5kc1Byb2Nlc3NvciA9IHJlcXVpcmUoXCIuL0NvbW1hbmRzXCIpO1xudmFyIFRvb2xiYXJUb2dnbGVyID0gcmVxdWlyZShcIi4uLy4uL3Rvb2xiYXJzL1Rvb2xiYXJUb2dnbGVyXCIpO1xudmFyIHNtaWxleXMgPSByZXF1aXJlKFwiLi9zbWlsZXlzLmpzb25cIikuc21pbGV5cztcbnZhciBOaWNrbmFtZUhhbmRsZXIgPSByZXF1aXJlKFwiLi4vLi4vdXRpbC9OaWNrbmFtZUhhbmRsZXJcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uLy4uL3V0aWwvVUlVdGlsXCIpO1xudmFyIFVJRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uLy4uLy4uL3NlcnZpY2UvVUkvVUlFdmVudHNcIik7XG5cbnZhciBub3RpZmljYXRpb25JbnRlcnZhbCA9IGZhbHNlO1xudmFyIHVucmVhZE1lc3NhZ2VzID0gMDtcblxuXG4vKipcbiAqIFNob3dzL2hpZGVzIGEgdmlzdWFsIG5vdGlmaWNhdGlvbiwgaW5kaWNhdGluZyB0aGF0IGEgbWVzc2FnZSBoYXMgYXJyaXZlZC5cbiAqL1xuZnVuY3Rpb24gc2V0VmlzdWFsTm90aWZpY2F0aW9uKHNob3cpIHtcbiAgICB2YXIgdW5yZWFkTXNnRWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1bnJlYWRNZXNzYWdlcycpO1xuICAgIHZhciB1bnJlYWRNc2dCb3R0b21FbGVtZW50XG4gICAgICAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2JvdHRvbVVucmVhZE1lc3NhZ2VzJyk7XG5cbiAgICB2YXIgZ2xvd2VyID0gJCgnI2NoYXRCdXR0b24nKTtcbiAgICB2YXIgYm90dG9tR2xvd2VyID0gJCgnI2NoYXRCb3R0b21CdXR0b24nKTtcblxuICAgIGlmICh1bnJlYWRNZXNzYWdlcykge1xuICAgICAgICB1bnJlYWRNc2dFbGVtZW50LmlubmVySFRNTCA9IHVucmVhZE1lc3NhZ2VzLnRvU3RyaW5nKCk7XG4gICAgICAgIHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQuaW5uZXJIVE1MID0gdW5yZWFkTWVzc2FnZXMudG9TdHJpbmcoKTtcblxuICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcih0cnVlKTtcblxuICAgICAgICB2YXIgY2hhdEJ1dHRvbkVsZW1lbnRcbiAgICAgICAgICAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NoYXRCdXR0b24nKS5wYXJlbnROb2RlO1xuICAgICAgICB2YXIgbGVmdEluZGVudCA9IChVSVV0aWwuZ2V0VGV4dFdpZHRoKGNoYXRCdXR0b25FbGVtZW50KSAtXG4gICAgICAgICAgICBVSVV0aWwuZ2V0VGV4dFdpZHRoKHVucmVhZE1zZ0VsZW1lbnQpKSAvIDI7XG4gICAgICAgIHZhciB0b3BJbmRlbnQgPSAoVUlVdGlsLmdldFRleHRIZWlnaHQoY2hhdEJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFVJVXRpbC5nZXRUZXh0SGVpZ2h0KHVucmVhZE1zZ0VsZW1lbnQpKSAvIDIgLSAzO1xuXG4gICAgICAgIHVucmVhZE1zZ0VsZW1lbnQuc2V0QXR0cmlidXRlKFxuICAgICAgICAgICAgJ3N0eWxlJyxcbiAgICAgICAgICAgICAgICAndG9wOicgKyB0b3BJbmRlbnQgK1xuICAgICAgICAgICAgICAgICc7IGxlZnQ6JyArIGxlZnRJbmRlbnQgKyAnOycpO1xuXG4gICAgICAgIHZhciBjaGF0Qm90dG9tQnV0dG9uRWxlbWVudFxuICAgICAgICAgICAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2hhdEJvdHRvbUJ1dHRvbicpLnBhcmVudE5vZGU7XG4gICAgICAgIHZhciBib3R0b21MZWZ0SW5kZW50ID0gKFVJVXRpbC5nZXRUZXh0V2lkdGgoY2hhdEJvdHRvbUJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFVJVXRpbC5nZXRUZXh0V2lkdGgodW5yZWFkTXNnQm90dG9tRWxlbWVudCkpIC8gMjtcbiAgICAgICAgdmFyIGJvdHRvbVRvcEluZGVudCA9IChVSVV0aWwuZ2V0VGV4dEhlaWdodChjaGF0Qm90dG9tQnV0dG9uRWxlbWVudCkgLVxuICAgICAgICAgICAgVUlVdGlsLmdldFRleHRIZWlnaHQodW5yZWFkTXNnQm90dG9tRWxlbWVudCkpIC8gMiAtIDI7XG5cbiAgICAgICAgdW5yZWFkTXNnQm90dG9tRWxlbWVudC5zZXRBdHRyaWJ1dGUoXG4gICAgICAgICAgICAnc3R5bGUnLFxuICAgICAgICAgICAgICAgICd0b3A6JyArIGJvdHRvbVRvcEluZGVudCArXG4gICAgICAgICAgICAgICAgJzsgbGVmdDonICsgYm90dG9tTGVmdEluZGVudCArICc7Jyk7XG5cblxuICAgICAgICBpZiAoIWdsb3dlci5oYXNDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpKSB7XG4gICAgICAgICAgICBnbG93ZXIucmVtb3ZlQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgICAgICAgICAgZ2xvd2VyLmFkZENsYXNzKCdpY29uLWNoYXQtc2ltcGxlJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHVucmVhZE1zZ0VsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpO1xuICAgICAgICBnbG93ZXIuYWRkQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgIH1cblxuICAgIGlmIChzaG93ICYmICFub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBnbG93ZXIudG9nZ2xlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgYm90dG9tR2xvd2VyLnRvZ2dsZUNsYXNzKCdhY3RpdmUgZ2xvd2luZycpO1xuICAgICAgICB9LCA4MDApO1xuICAgIH1cbiAgICBlbHNlIGlmICghc2hvdyAmJiBub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChub3RpZmljYXRpb25JbnRlcnZhbCk7XG4gICAgICAgIG5vdGlmaWNhdGlvbkludGVydmFsID0gZmFsc2U7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgIGJvdHRvbUdsb3dlci5yZW1vdmVDbGFzcygnZ2xvd2luZycpO1xuICAgICAgICBib3R0b21HbG93ZXIuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgIH1cbn1cblxuXG4vKipcbiAqIFJldHVybnMgdGhlIGN1cnJlbnQgdGltZSBpbiB0aGUgZm9ybWF0IGl0IGlzIHNob3duIHRvIHRoZSB1c2VyXG4gKiBAcmV0dXJucyB7c3RyaW5nfVxuICovXG5mdW5jdGlvbiBnZXRDdXJyZW50VGltZSgpIHtcbiAgICB2YXIgbm93ICAgICA9IG5ldyBEYXRlKCk7XG4gICAgdmFyIGhvdXIgICAgPSBub3cuZ2V0SG91cnMoKTtcbiAgICB2YXIgbWludXRlICA9IG5vdy5nZXRNaW51dGVzKCk7XG4gICAgdmFyIHNlY29uZCAgPSBub3cuZ2V0U2Vjb25kcygpO1xuICAgIGlmKGhvdXIudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgaG91ciA9ICcwJytob3VyO1xuICAgIH1cbiAgICBpZihtaW51dGUudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgbWludXRlID0gJzAnK21pbnV0ZTtcbiAgICB9XG4gICAgaWYoc2Vjb25kLnRvU3RyaW5nKCkubGVuZ3RoID09PSAxKSB7XG4gICAgICAgIHNlY29uZCA9ICcwJytzZWNvbmQ7XG4gICAgfVxuICAgIHJldHVybiBob3VyKyc6JyttaW51dGUrJzonK3NlY29uZDtcbn1cblxuZnVuY3Rpb24gdG9nZ2xlU21pbGV5cygpXG57XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c0NvbnRhaW5lcicpO1xuICAgIGlmKCFzbWlsZXlzLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgIHNtaWxleXMuc2hvdyhcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHNtaWxleXMuaGlkZShcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH1cbiAgICAkKCcjdXNlcm1zZycpLmZvY3VzKCk7XG59XG5cbmZ1bmN0aW9uIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBudW1iZXIpIHtcbiAgICBzbWlsZXkub25jbGljayA9IGZ1bmN0aW9uIGFkZFNtaWxleVRvTWVzc2FnZSgpIHtcbiAgICAgICAgdmFyIHVzZXJtc2cgPSAkKCcjdXNlcm1zZycpO1xuICAgICAgICB2YXIgbWVzc2FnZSA9IHVzZXJtc2cudmFsKCk7XG4gICAgICAgIG1lc3NhZ2UgKz0gc21pbGV5c1snc21pbGV5JyArIG51bWJlcl07XG4gICAgICAgIHVzZXJtc2cudmFsKG1lc3NhZ2UpO1xuICAgICAgICB1c2VybXNnLmdldCgwKS5zZXRTZWxlY3Rpb25SYW5nZShtZXNzYWdlLmxlbmd0aCwgbWVzc2FnZS5sZW5ndGgpO1xuICAgICAgICB0b2dnbGVTbWlsZXlzKCk7XG4gICAgICAgIHVzZXJtc2cuZm9jdXMoKTtcbiAgICB9O1xufVxuXG4vKipcbiAqIEFkZHMgdGhlIHNtaWxleXMgY29udGFpbmVyIHRvIHRoZSBjaGF0XG4gKi9cbmZ1bmN0aW9uIGFkZFNtaWxleXMoKSB7XG4gICAgdmFyIHNtaWxleXNDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBzbWlsZXlzQ29udGFpbmVyLmlkID0gJ3NtaWxleXNDb250YWluZXInO1xuICAgIGZvcih2YXIgaSA9IDE7IGkgPD0gMjE7IGkrKykge1xuICAgICAgICB2YXIgc21pbGV5Q29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNtaWxleUNvbnRhaW5lci5pZCA9ICdzbWlsZXknICsgaTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmNsYXNzTmFtZSA9ICdzbWlsZXlDb250YWluZXInO1xuICAgICAgICB2YXIgc21pbGV5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gICAgICAgIHNtaWxleS5zcmMgPSAnaW1hZ2VzL3NtaWxleXMvc21pbGV5JyArIGkgKyAnLnN2Zyc7XG4gICAgICAgIHNtaWxleS5jbGFzc05hbWUgPSAgJ3NtaWxleSc7XG4gICAgICAgIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBpKTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmFwcGVuZENoaWxkKHNtaWxleSk7XG4gICAgICAgIHNtaWxleXNDb250YWluZXIuYXBwZW5kQ2hpbGQoc21pbGV5Q29udGFpbmVyKTtcbiAgICB9XG5cbiAgICAkKFwiI2NoYXRzcGFjZVwiKS5hcHBlbmQoc21pbGV5c0NvbnRhaW5lcik7XG59XG5cbi8qKlxuICogUmVzaXplcyB0aGUgY2hhdCBjb252ZXJzYXRpb24uXG4gKi9cbmZ1bmN0aW9uIHJlc2l6ZUNoYXRDb252ZXJzYXRpb24oKSB7XG4gICAgdmFyIG1zZ2FyZWFIZWlnaHQgPSAkKCcjdXNlcm1zZycpLm91dGVySGVpZ2h0KCk7XG4gICAgdmFyIGNoYXRzcGFjZSA9ICQoJyNjaGF0c3BhY2UnKTtcbiAgICB2YXIgd2lkdGggPSBjaGF0c3BhY2Uud2lkdGgoKTtcbiAgICB2YXIgY2hhdCA9ICQoJyNjaGF0Y29udmVyc2F0aW9uJyk7XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c2FyZWEnKTtcblxuICAgIHNtaWxleXMuaGVpZ2h0KG1zZ2FyZWFIZWlnaHQpO1xuICAgICQoXCIjc21pbGV5c1wiKS5jc3MoJ2JvdHRvbScsIChtc2dhcmVhSGVpZ2h0IC0gMjYpIC8gMik7XG4gICAgJCgnI3NtaWxleXNDb250YWluZXInKS5jc3MoJ2JvdHRvbScsIG1zZ2FyZWFIZWlnaHQpO1xuICAgIGNoYXQud2lkdGgod2lkdGggLSAxMCk7XG4gICAgY2hhdC5oZWlnaHQod2luZG93LmlubmVySGVpZ2h0IC0gMTUgLSBtc2dhcmVhSGVpZ2h0KTtcbn1cblxuLyoqXG4gKiBDaGF0IHJlbGF0ZWQgdXNlciBpbnRlcmZhY2UuXG4gKi9cbnZhciBDaGF0ID0gKGZ1bmN0aW9uIChteSkge1xuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemVzIGNoYXQgcmVsYXRlZCBpbnRlcmZhY2UuXG4gICAgICovXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYoTmlja25hbWVIYW5kbGVyLmdldE5pY2tuYW1lKCkpXG4gICAgICAgICAgICBDaGF0LnNldENoYXRDb252ZXJzYXRpb25Nb2RlKHRydWUpO1xuICAgICAgICBOaWNrbmFtZUhhbmRsZXIuYWRkTGlzdGVuZXIoVUlFdmVudHMuTklDS05BTUVfQ0hBTkdFRCxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChuaWNrbmFtZSkge1xuICAgICAgICAgICAgICAgIENoYXQuc2V0Q2hhdENvbnZlcnNhdGlvbk1vZGUodHJ1ZSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAkKCcjbmlja2lucHV0Jykua2V5ZG93bihmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5rZXlDb2RlID09PSAxMykge1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgdmFyIHZhbCA9IFVJVXRpbC5lc2NhcGVIdG1sKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUgPSAnJztcbiAgICAgICAgICAgICAgICBpZiAoIU5pY2tuYW1lSGFuZGxlci5nZXROaWNrbmFtZSgpKSB7XG4gICAgICAgICAgICAgICAgICAgIE5pY2tuYW1lSGFuZGxlci5zZXROaWNrbmFtZSh2YWwpO1xuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgICQoJyN1c2VybXNnJykua2V5ZG93bihmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5rZXlDb2RlID09PSAxMykge1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgdmFyIHZhbHVlID0gdGhpcy52YWx1ZTtcbiAgICAgICAgICAgICAgICAkKCcjdXNlcm1zZycpLnZhbCgnJykudHJpZ2dlcignYXV0b3NpemUucmVzaXplJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5mb2N1cygpO1xuICAgICAgICAgICAgICAgIHZhciBjb21tYW5kID0gbmV3IENvbW1hbmRzUHJvY2Vzc29yKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBpZihjb21tYW5kLmlzQ29tbWFuZCgpKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgY29tbWFuZC5wcm9jZXNzQ29tbWFuZCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWVzc2FnZSA9IFVJVXRpbC5lc2NhcGVIdG1sKHZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuc2VuZENoYXRNZXNzYWdlKG1lc3NhZ2UsIE5pY2tuYW1lSGFuZGxlci5nZXROaWNrbmFtZSgpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBvblRleHRBcmVhUmVzaXplID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmVzaXplQ2hhdENvbnZlcnNhdGlvbigpO1xuICAgICAgICAgICAgQ2hhdC5zY3JvbGxDaGF0VG9Cb3R0b20oKTtcbiAgICAgICAgfTtcbiAgICAgICAgJCgnI3VzZXJtc2cnKS5hdXRvc2l6ZSh7Y2FsbGJhY2s6IG9uVGV4dEFyZWFSZXNpemV9KTtcblxuICAgICAgICAkKFwiI2NoYXRzcGFjZVwiKS5iaW5kKFwic2hvd25cIixcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB1bnJlYWRNZXNzYWdlcyA9IDA7XG4gICAgICAgICAgICAgICAgc2V0VmlzdWFsTm90aWZpY2F0aW9uKGZhbHNlKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIGFkZFNtaWxleXMoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwZW5kcyB0aGUgZ2l2ZW4gbWVzc2FnZSB0byB0aGUgY2hhdCBjb252ZXJzYXRpb24uXG4gICAgICovXG4gICAgbXkudXBkYXRlQ2hhdENvbnZlcnNhdGlvbiA9IGZ1bmN0aW9uIChmcm9tLCBkaXNwbGF5TmFtZSwgbWVzc2FnZSkge1xuICAgICAgICB2YXIgZGl2Q2xhc3NOYW1lID0gJyc7XG5cbiAgICAgICAgaWYgKEFQUC54bXBwLm15SmlkKCkgPT09IGZyb20pIHtcbiAgICAgICAgICAgIGRpdkNsYXNzTmFtZSA9IFwibG9jYWx1c2VyXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBkaXZDbGFzc05hbWUgPSBcInJlbW90ZXVzZXJcIjtcblxuICAgICAgICAgICAgaWYgKCFDaGF0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgICAgICAgICAgdW5yZWFkTWVzc2FnZXMrKztcbiAgICAgICAgICAgICAgICBVSVV0aWwucGxheVNvdW5kTm90aWZpY2F0aW9uKCdjaGF0Tm90aWZpY2F0aW9uJyk7XG4gICAgICAgICAgICAgICAgc2V0VmlzdWFsTm90aWZpY2F0aW9uKHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVwbGFjZSBsaW5rcyBhbmQgc21pbGV5c1xuICAgICAgICAvLyBTdHJvcGhlIGFscmVhZHkgZXNjYXBlcyBzcGVjaWFsIHN5bWJvbHMgb24gc2VuZGluZyxcbiAgICAgICAgLy8gc28gd2UgZXNjYXBlIGhlcmUgb25seSB0YWdzIHRvIGF2b2lkIGRvdWJsZSAmYW1wO1xuICAgICAgICB2YXIgZXNjTWVzc2FnZSA9IG1lc3NhZ2UucmVwbGFjZSgvPC9nLCAnJmx0OycpLlxuICAgICAgICAgICAgcmVwbGFjZSgvPi9nLCAnJmd0OycpLnJlcGxhY2UoL1xcbi9nLCAnPGJyLz4nKTtcbiAgICAgICAgdmFyIGVzY0Rpc3BsYXlOYW1lID0gVUlVdGlsLmVzY2FwZUh0bWwoZGlzcGxheU5hbWUpO1xuICAgICAgICBtZXNzYWdlID0gUmVwbGFjZW1lbnQucHJvY2Vzc1JlcGxhY2VtZW50cyhlc2NNZXNzYWdlKTtcblxuICAgICAgICB2YXIgbWVzc2FnZUNvbnRhaW5lciA9XG4gICAgICAgICAgICAnPGRpdiBjbGFzcz1cImNoYXRtZXNzYWdlXCI+JytcbiAgICAgICAgICAgICAgICAnPGltZyBzcmM9XCIuLi9pbWFnZXMvY2hhdEFycm93LnN2Z1wiIGNsYXNzPVwiY2hhdEFycm93XCI+JyArXG4gICAgICAgICAgICAgICAgJzxkaXYgY2xhc3M9XCJ1c2VybmFtZSAnICsgZGl2Q2xhc3NOYW1lICsnXCI+JyArIGVzY0Rpc3BsYXlOYW1lICtcbiAgICAgICAgICAgICAgICAnPC9kaXY+JyArICc8ZGl2IGNsYXNzPVwidGltZXN0YW1wXCI+JyArIGdldEN1cnJlbnRUaW1lKCkgK1xuICAgICAgICAgICAgICAgICc8L2Rpdj4nICsgJzxkaXYgY2xhc3M9XCJ1c2VybWVzc2FnZVwiPicgKyBtZXNzYWdlICsgJzwvZGl2PicgK1xuICAgICAgICAgICAgJzwvZGl2Pic7XG5cbiAgICAgICAgJCgnI2NoYXRjb252ZXJzYXRpb24nKS5hcHBlbmQobWVzc2FnZUNvbnRhaW5lcik7XG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYW5pbWF0ZShcbiAgICAgICAgICAgICAgICB7IHNjcm9sbFRvcDogJCgnI2NoYXRjb252ZXJzYXRpb24nKVswXS5zY3JvbGxIZWlnaHR9LCAxMDAwKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwZW5kcyBlcnJvciBtZXNzYWdlIHRvIHRoZSBjb252ZXJzYXRpb25cbiAgICAgKiBAcGFyYW0gZXJyb3JNZXNzYWdlIHRoZSByZWNlaXZlZCBlcnJvciBtZXNzYWdlLlxuICAgICAqIEBwYXJhbSBvcmlnaW5hbFRleHQgdGhlIG9yaWdpbmFsIG1lc3NhZ2UuXG4gICAgICovXG4gICAgbXkuY2hhdEFkZEVycm9yID0gZnVuY3Rpb24oZXJyb3JNZXNzYWdlLCBvcmlnaW5hbFRleHQpXG4gICAge1xuICAgICAgICBlcnJvck1lc3NhZ2UgPSBVSVV0aWwuZXNjYXBlSHRtbChlcnJvck1lc3NhZ2UpO1xuICAgICAgICBvcmlnaW5hbFRleHQgPSBVSVV0aWwuZXNjYXBlSHRtbChvcmlnaW5hbFRleHQpO1xuXG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYXBwZW5kKFxuICAgICAgICAgICAgJzxkaXYgY2xhc3M9XCJlcnJvck1lc3NhZ2VcIj48Yj5FcnJvcjogPC9iPicgKyAnWW91ciBtZXNzYWdlJyArXG4gICAgICAgICAgICAob3JpZ2luYWxUZXh0PyAoJyBcXFwiJysgb3JpZ2luYWxUZXh0ICsgJ1xcXCInKSA6IFwiXCIpICtcbiAgICAgICAgICAgICcgd2FzIG5vdCBzZW50LicgK1xuICAgICAgICAgICAgKGVycm9yTWVzc2FnZT8gKCcgUmVhc29uOiAnICsgZXJyb3JNZXNzYWdlKSA6ICcnKSArICAnPC9kaXY+Jyk7XG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYW5pbWF0ZShcbiAgICAgICAgICAgIHsgc2Nyb2xsVG9wOiAkKCcjY2hhdGNvbnZlcnNhdGlvbicpWzBdLnNjcm9sbEhlaWdodH0sIDEwMDApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBzdWJqZWN0IHRvIHRoZSBVSVxuICAgICAqIEBwYXJhbSBzdWJqZWN0IHRoZSBzdWJqZWN0XG4gICAgICovXG4gICAgbXkuY2hhdFNldFN1YmplY3QgPSBmdW5jdGlvbihzdWJqZWN0KVxuICAgIHtcbiAgICAgICAgaWYoc3ViamVjdClcbiAgICAgICAgICAgIHN1YmplY3QgPSBzdWJqZWN0LnRyaW0oKTtcbiAgICAgICAgJCgnI3N1YmplY3QnKS5odG1sKFJlcGxhY2VtZW50LmxpbmtpZnkoVUlVdGlsLmVzY2FwZUh0bWwoc3ViamVjdCkpKTtcbiAgICAgICAgaWYoc3ViamVjdCA9PT0gXCJcIilcbiAgICAgICAge1xuICAgICAgICAgICAgJChcIiNzdWJqZWN0XCIpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgICQoXCIjc3ViamVjdFwiKS5jc3Moe2Rpc3BsYXk6IFwiYmxvY2tcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuXG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBjaGF0IGNvbnZlcnNhdGlvbiBtb2RlLlxuICAgICAqL1xuICAgIG15LnNldENoYXRDb252ZXJzYXRpb25Nb2RlID0gZnVuY3Rpb24gKGlzQ29udmVyc2F0aW9uTW9kZSkge1xuICAgICAgICBpZiAoaXNDb252ZXJzYXRpb25Nb2RlKSB7XG4gICAgICAgICAgICAkKCcjbmlja25hbWUnKS5jc3Moe3Zpc2liaWxpdHk6ICdoaWRkZW4nfSk7XG4gICAgICAgICAgICAkKCcjY2hhdGNvbnZlcnNhdGlvbicpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcjdXNlcm1zZycpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcjc21pbGV5c2FyZWEnKS5jc3Moe3Zpc2liaWxpdHk6ICd2aXNpYmxlJ30pO1xuICAgICAgICAgICAgJCgnI3VzZXJtc2cnKS5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIGNoYXQgYXJlYS5cbiAgICAgKi9cbiAgICBteS5yZXNpemVDaGF0ID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgY2hhdFNpemUgPSByZXF1aXJlKFwiLi4vU2lkZVBhbmVsVG9nZ2xlclwiKS5nZXRQYW5lbFNpemUoKTtcblxuICAgICAgICAkKCcjY2hhdHNwYWNlJykud2lkdGgoY2hhdFNpemVbMF0pO1xuICAgICAgICAkKCcjY2hhdHNwYWNlJykuaGVpZ2h0KGNoYXRTaXplWzFdKTtcblxuICAgICAgICByZXNpemVDaGF0Q29udmVyc2F0aW9uKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEluZGljYXRlcyBpZiB0aGUgY2hhdCBpcyBjdXJyZW50bHkgdmlzaWJsZS5cbiAgICAgKi9cbiAgICBteS5pc1Zpc2libGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAkKCcjY2hhdHNwYWNlJykuaXMoXCI6dmlzaWJsZVwiKTtcbiAgICB9O1xuICAgIC8qKlxuICAgICAqIFNob3dzIGFuZCBoaWRlcyB0aGUgd2luZG93IHdpdGggdGhlIHNtaWxleXNcbiAgICAgKi9cbiAgICBteS50b2dnbGVTbWlsZXlzID0gdG9nZ2xlU21pbGV5cztcblxuICAgIC8qKlxuICAgICAqIFNjcm9sbHMgY2hhdCB0byB0aGUgYm90dG9tLlxuICAgICAqL1xuICAgIG15LnNjcm9sbENoYXRUb0JvdHRvbSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuc2Nyb2xsVG9wKFxuICAgICAgICAgICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJylbMF0uc2Nyb2xsSGVpZ2h0KTtcbiAgICAgICAgfSwgNSk7XG4gICAgfTtcblxuXG4gICAgcmV0dXJuIG15O1xufShDaGF0IHx8IHt9KSk7XG5tb2R1bGUuZXhwb3J0cyA9IENoYXQ7IiwidmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi8uLi91dGlsL1VJVXRpbFwiKTtcblxuLyoqXG4gKiBMaXN0IHdpdGggc3VwcG9ydGVkIGNvbW1hbmRzLiBUaGUga2V5cyBhcmUgdGhlIG5hbWVzIG9mIHRoZSBjb21tYW5kcyBhbmRcbiAqIHRoZSB2YWx1ZSBpcyB0aGUgZnVuY3Rpb24gdGhhdCBwcm9jZXNzZXMgdGhlIG1lc3NhZ2UuXG4gKiBAdHlwZSB7e1N0cmluZzogZnVuY3Rpb259fVxuICovXG52YXIgY29tbWFuZHMgPSB7XG4gICAgXCJ0b3BpY1wiIDogcHJvY2Vzc1RvcGljXG59O1xuXG4vKipcbiAqIEV4dHJhY3RzIHRoZSBjb21tYW5kIGZyb20gdGhlIG1lc3NhZ2UuXG4gKiBAcGFyYW0gbWVzc2FnZSB0aGUgcmVjZWl2ZWQgbWVzc2FnZVxuICogQHJldHVybnMge3N0cmluZ30gdGhlIGNvbW1hbmRcbiAqL1xuZnVuY3Rpb24gZ2V0Q29tbWFuZChtZXNzYWdlKVxue1xuICAgIGlmKG1lc3NhZ2UpXG4gICAge1xuICAgICAgICBmb3IodmFyIGNvbW1hbmQgaW4gY29tbWFuZHMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlmKG1lc3NhZ2UuaW5kZXhPZihcIi9cIiArIGNvbW1hbmQpID09IDApXG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbW1hbmQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFwiXCI7XG59O1xuXG4vKipcbiAqIFByb2Nlc3NlcyB0aGUgZGF0YSBmb3IgdG9waWMgY29tbWFuZC5cbiAqIEBwYXJhbSBjb21tYW5kQXJndW1lbnRzIHRoZSBhcmd1bWVudHMgb2YgdGhlIHRvcGljIGNvbW1hbmQuXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NUb3BpYyhjb21tYW5kQXJndW1lbnRzKVxue1xuICAgIHZhciB0b3BpYyA9IFVJVXRpbC5lc2NhcGVIdG1sKGNvbW1hbmRBcmd1bWVudHMpO1xuICAgIEFQUC54bXBwLnNldFN1YmplY3QodG9waWMpO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdHMgbmV3IENvbW1hbmRQcm9jY2Vzc29yIGluc3RhbmNlIGZyb20gYSBtZXNzYWdlIHRoYXRcbiAqIGhhbmRsZXMgY29tbWFuZHMgcmVjZWl2ZWQgdmlhIGNoYXQgbWVzc2FnZXMuXG4gKiBAcGFyYW0gbWVzc2FnZSB0aGUgbWVzc2FnZVxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIENvbW1hbmRzUHJvY2Vzc29yKG1lc3NhZ2UpXG57XG5cblxuICAgIHZhciBjb21tYW5kID0gZ2V0Q29tbWFuZChtZXNzYWdlKTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG5hbWUgb2YgdGhlIGNvbW1hbmQuXG4gICAgICogQHJldHVybnMge1N0cmluZ30gdGhlIGNvbW1hbmRcbiAgICAgKi9cbiAgICB0aGlzLmdldENvbW1hbmQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICByZXR1cm4gY29tbWFuZDtcbiAgICB9O1xuXG5cbiAgICB2YXIgbWVzc2FnZUFyZ3VtZW50ID0gbWVzc2FnZS5zdWJzdHIoY29tbWFuZC5sZW5ndGggKyAyKTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGFyZ3VtZW50cyBvZiB0aGUgY29tbWFuZC5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAqL1xuICAgIHRoaXMuZ2V0QXJndW1lbnQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICByZXR1cm4gbWVzc2FnZUFyZ3VtZW50O1xuICAgIH07XG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgdGhpcyBpbnN0YW5jZSBpcyB2YWxpZCBjb21tYW5kIG9yIG5vdC5cbiAqIEByZXR1cm5zIHtib29sZWFufVxuICovXG5Db21tYW5kc1Byb2Nlc3Nvci5wcm90b3R5cGUuaXNDb21tYW5kID0gZnVuY3Rpb24oKVxue1xuICAgIGlmKHRoaXMuZ2V0Q29tbWFuZCgpKVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICByZXR1cm4gZmFsc2U7XG59O1xuXG4vKipcbiAqIFByb2Nlc3NlcyB0aGUgY29tbWFuZC5cbiAqL1xuQ29tbWFuZHNQcm9jZXNzb3IucHJvdG90eXBlLnByb2Nlc3NDb21tYW5kID0gZnVuY3Rpb24oKVxue1xuICAgIGlmKCF0aGlzLmlzQ29tbWFuZCgpKVxuICAgICAgICByZXR1cm47XG5cbiAgICBjb21tYW5kc1t0aGlzLmdldENvbW1hbmQoKV0odGhpcy5nZXRBcmd1bWVudCgpKTtcblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb21tYW5kc1Byb2Nlc3NvcjsiLCJ2YXIgU21pbGV5cyA9IHJlcXVpcmUoXCIuL3NtaWxleXMuanNvblwiKTtcbi8qKlxuICogUHJvY2Vzc2VzIGxpbmtzIGFuZCBzbWlsZXlzIGluIFwiYm9keVwiXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NSZXBsYWNlbWVudHMoYm9keSlcbntcbiAgICAvL21ha2UgbGlua3MgY2xpY2thYmxlXG4gICAgYm9keSA9IGxpbmtpZnkoYm9keSk7XG5cbiAgICAvL2FkZCBzbWlsZXlzXG4gICAgYm9keSA9IHNtaWxpZnkoYm9keSk7XG5cbiAgICByZXR1cm4gYm9keTtcbn1cblxuLyoqXG4gKiBGaW5kcyBhbmQgcmVwbGFjZXMgYWxsIGxpbmtzIGluIHRoZSBsaW5rcyBpbiBcImJvZHlcIlxuICogd2l0aCB0aGVpciA8YSBocmVmPVwiXCI+PC9hPlxuICovXG5mdW5jdGlvbiBsaW5raWZ5KGlucHV0VGV4dClcbntcbiAgICB2YXIgcmVwbGFjZWRUZXh0LCByZXBsYWNlUGF0dGVybjEsIHJlcGxhY2VQYXR0ZXJuMiwgcmVwbGFjZVBhdHRlcm4zO1xuXG4gICAgLy9VUkxzIHN0YXJ0aW5nIHdpdGggaHR0cDovLywgaHR0cHM6Ly8sIG9yIGZ0cDovL1xuICAgIHJlcGxhY2VQYXR0ZXJuMSA9IC8oXFxiKGh0dHBzP3xmdHApOlxcL1xcL1stQS1aMC05KyZAI1xcLyU/PX5ffCE6LC47XSpbLUEtWjAtOSsmQCNcXC8lPX5ffF0pL2dpbTtcbiAgICByZXBsYWNlZFRleHQgPSBpbnB1dFRleHQucmVwbGFjZShyZXBsYWNlUGF0dGVybjEsICc8YSBocmVmPVwiJDFcIiB0YXJnZXQ9XCJfYmxhbmtcIj4kMTwvYT4nKTtcblxuICAgIC8vVVJMcyBzdGFydGluZyB3aXRoIFwid3d3LlwiICh3aXRob3V0IC8vIGJlZm9yZSBpdCwgb3IgaXQnZCByZS1saW5rIHRoZSBvbmVzIGRvbmUgYWJvdmUpLlxuICAgIHJlcGxhY2VQYXR0ZXJuMiA9IC8oXnxbXlxcL10pKHd3d1xcLltcXFNdKyhcXGJ8JCkpL2dpbTtcbiAgICByZXBsYWNlZFRleHQgPSByZXBsYWNlZFRleHQucmVwbGFjZShyZXBsYWNlUGF0dGVybjIsICckMTxhIGhyZWY9XCJodHRwOi8vJDJcIiB0YXJnZXQ9XCJfYmxhbmtcIj4kMjwvYT4nKTtcblxuICAgIC8vQ2hhbmdlIGVtYWlsIGFkZHJlc3NlcyB0byBtYWlsdG86OiBsaW5rcy5cbiAgICByZXBsYWNlUGF0dGVybjMgPSAvKChbYS16QS1aMC05XFwtXFxfXFwuXSkrQFthLXpBLVpcXF9dKz8oXFwuW2EtekEtWl17Miw2fSkrKS9naW07XG4gICAgcmVwbGFjZWRUZXh0ID0gcmVwbGFjZWRUZXh0LnJlcGxhY2UocmVwbGFjZVBhdHRlcm4zLCAnPGEgaHJlZj1cIm1haWx0bzokMVwiPiQxPC9hPicpO1xuXG4gICAgcmV0dXJuIHJlcGxhY2VkVGV4dDtcbn1cblxuLyoqXG4gKiBSZXBsYWNlcyBjb21tb24gc21pbGV5IHN0cmluZ3Mgd2l0aCBpbWFnZXNcbiAqL1xuZnVuY3Rpb24gc21pbGlmeShib2R5KVxue1xuICAgIGlmKCFib2R5KSB7XG4gICAgICAgIHJldHVybiBib2R5O1xuICAgIH1cblxuICAgIHZhciByZWdleHMgPSBTbWlsZXlzW1wicmVnZXhzXCJdO1xuICAgIGZvcih2YXIgc21pbGV5IGluIHJlZ2V4cykge1xuICAgICAgICBpZihyZWdleHMuaGFzT3duUHJvcGVydHkoc21pbGV5KSkge1xuICAgICAgICAgICAgYm9keSA9IGJvZHkucmVwbGFjZShyZWdleHNbc21pbGV5XSxcbiAgICAgICAgICAgICAgICAgICAgJzxpbWcgY2xhc3M9XCJzbWlsZXlcIiBzcmM9XCJpbWFnZXMvc21pbGV5cy8nICsgc21pbGV5ICsgJy5zdmdcIj4nKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBib2R5O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBwcm9jZXNzUmVwbGFjZW1lbnRzOiBwcm9jZXNzUmVwbGFjZW1lbnRzLFxuICAgIGxpbmtpZnk6IGxpbmtpZnlcbn07XG4iLCJtb2R1bGUuZXhwb3J0cz17XG4gICAgXCJzbWlsZXlzXCI6IHtcbiAgICAgICAgXCJzbWlsZXkxXCI6IFwiOilcIixcbiAgICAgICAgXCJzbWlsZXkyXCI6IFwiOihcIixcbiAgICAgICAgXCJzbWlsZXkzXCI6IFwiOkRcIixcbiAgICAgICAgXCJzbWlsZXk0XCI6IFwiKHkpXCIsXG4gICAgICAgIFwic21pbGV5NVwiOiBcIiA6UFwiLFxuICAgICAgICBcInNtaWxleTZcIjogXCIod2F2ZSlcIixcbiAgICAgICAgXCJzbWlsZXk3XCI6IFwiKGJsdXNoKVwiLFxuICAgICAgICBcInNtaWxleThcIjogXCIoY2h1Y2tsZSlcIixcbiAgICAgICAgXCJzbWlsZXk5XCI6IFwiKHNob2NrZWQpXCIsXG4gICAgICAgIFwic21pbGV5MTBcIjogXCI6KlwiLFxuICAgICAgICBcInNtaWxleTExXCI6IFwiKG4pXCIsXG4gICAgICAgIFwic21pbGV5MTJcIjogXCIoc2VhcmNoKVwiLFxuICAgICAgICBcInNtaWxleTEzXCI6IFwiIDwzXCIsXG4gICAgICAgIFwic21pbGV5MTRcIjogXCIob29wcylcIixcbiAgICAgICAgXCJzbWlsZXkxNVwiOiBcIihhbmdyeSlcIixcbiAgICAgICAgXCJzbWlsZXkxNlwiOiBcIihhbmdlbClcIixcbiAgICAgICAgXCJzbWlsZXkxN1wiOiBcIihzaWNrKVwiLFxuICAgICAgICBcInNtaWxleTE4XCI6IFwiOyhcIixcbiAgICAgICAgXCJzbWlsZXkxOVwiOiBcIihib21iKVwiLFxuICAgICAgICBcInNtaWxleTIwXCI6IFwiKGNsYXApXCIsXG4gICAgICAgIFwic21pbGV5MjFcIjogXCIgOylcIlxuICAgIH0sXG4gICAgXCJyZWdleHNcIjoge1xuICAgICAgICBcInNtaWxleTJcIjogLyg6LVxcKFxcKHw6LVxcKHw6XFwoXFwofDpcXCh8XFwoc2FkXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkzXCI6IC8oOi1cXClcXCl8OlxcKVxcKXxcXChsb2xcXCl8Oi1EfDpEKS9naSxcbiAgICAgICAgXCJzbWlsZXkxXCI6IC8oOi1cXCl8OlxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5NFwiOiAvKFxcKHlcXCl8XFwoWVxcKXxcXChva1xcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5NVwiOiAvKDotUHw6UHw6LXB8OnApL2dpLFxuICAgICAgICBcInNtaWxleTZcIjogLyhcXCh3YXZlXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk3XCI6IC8oXFwoYmx1c2hcXCkpL2dpLFxuICAgICAgICBcInNtaWxleThcIjogLyhcXChjaHVja2xlXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk5XCI6IC8oOi0wfFxcKHNob2NrZWRcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTEwXCI6IC8oOi1cXCp8OlxcKnxcXChraXNzXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxMVwiOiAvKFxcKG5cXCkpL2dpLFxuICAgICAgICBcInNtaWxleTEyXCI6IC8oXFwoc2VhcmNoXFwpKS9nLFxuICAgICAgICBcInNtaWxleTEzXCI6IC8oPDN8Jmx0OzN8JmFtcDtsdDszfFxcKExcXCl8XFwobFxcKXxcXChIXFwpfFxcKGhcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTE0XCI6IC8oXFwob29wc1xcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTVcIjogLyhcXChhbmdyeVxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTZcIjogLyhcXChhbmdlbFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTdcIjogLyhcXChzaWNrXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxOFwiOiAvKDstXFwoXFwofDtcXChcXCh8Oy1cXCh8O1xcKHw6XCJcXCh8OlwiLVxcKHw6fi1cXCh8On5cXCh8XFwodXBzZXRcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTE5XCI6IC8oXFwoYm9tYlxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MjBcIjogLyhcXChjbGFwXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkyMVwiOiAvKDstXFwpfDtcXCl8Oy1cXClcXCl8O1xcKVxcKXw7LUR8O0R8XFwod2lua1xcKSkvZ2lcbiAgICB9XG59XG4iLCJcbnZhciBudW1iZXJPZkNvbnRhY3RzID0gMDtcbnZhciBub3RpZmljYXRpb25JbnRlcnZhbDtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBudW1iZXIgb2YgcGFydGljaXBhbnRzIGluIHRoZSBjb250YWN0IGxpc3QgYnV0dG9uIGFuZCBzZXRzXG4gKiB0aGUgZ2xvd1xuICogQHBhcmFtIGRlbHRhIGluZGljYXRlcyB3aGV0aGVyIGEgbmV3IHVzZXIgaGFzIGpvaW5lZCAoMSkgb3Igc29tZW9uZSBoYXNcbiAqIGxlZnQoLTEpXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZU51bWJlck9mUGFydGljaXBhbnRzKGRlbHRhKSB7XG4gICAgLy93aGVuIHRoZSB1c2VyIGlzIGFsb25lIHdlIGRvbid0IHNob3cgdGhlIG51bWJlciBvZiBwYXJ0aWNpcGFudHNcbiAgICBpZihudW1iZXJPZkNvbnRhY3RzID09PSAwKSB7XG4gICAgICAgICQoXCIjbnVtYmVyT2ZQYXJ0aWNpcGFudHNcIikudGV4dCgnJyk7XG4gICAgICAgIG51bWJlck9mQ29udGFjdHMgKz0gZGVsdGE7XG4gICAgfSBlbHNlIGlmKG51bWJlck9mQ29udGFjdHMgIT09IDAgJiYgIUNvbnRhY3RMaXN0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgIENvbnRhY3RMaXN0LnNldFZpc3VhbE5vdGlmaWNhdGlvbih0cnVlKTtcbiAgICAgICAgbnVtYmVyT2ZDb250YWN0cyArPSBkZWx0YTtcbiAgICAgICAgJChcIiNudW1iZXJPZlBhcnRpY2lwYW50c1wiKS50ZXh0KG51bWJlck9mQ29udGFjdHMpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBhdmF0YXIgZWxlbWVudC5cbiAqXG4gKiBAcmV0dXJuIHRoZSBuZXdseSBjcmVhdGVkIGF2YXRhciBlbGVtZW50XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUF2YXRhcihpZCkge1xuICAgIHZhciBhdmF0YXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTtcbiAgICBhdmF0YXIuY2xhc3NOYW1lID0gXCJpY29uLWF2YXRhciBhdmF0YXJcIjtcbiAgICBhdmF0YXIuc3JjID0gXCJodHRwczovL3d3dy5ncmF2YXRhci5jb20vYXZhdGFyL1wiICsgaWQgKyBcIj9kPXdhdmF0YXImc2l6ZT0zMFwiO1xuXG4gICAgcmV0dXJuIGF2YXRhcjtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBkaXNwbGF5IG5hbWUgcGFyYWdyYXBoLlxuICpcbiAqIEBwYXJhbSBkaXNwbGF5TmFtZSB0aGUgZGlzcGxheSBuYW1lIHRvIHNldFxuICovXG5mdW5jdGlvbiBjcmVhdGVEaXNwbGF5TmFtZVBhcmFncmFwaChrZXksIGRpc3BsYXlOYW1lKSB7XG4gICAgdmFyIHAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdwJyk7XG4gICAgaWYoZGlzcGxheU5hbWUpXG4gICAgICAgIHAuaW5uZXJUZXh0ID0gZGlzcGxheU5hbWU7XG4gICAgZWxzZSBpZihrZXkpXG4gICAge1xuICAgICAgICBwLnNldEF0dHJpYnV0ZShcImRhdGEtaTE4blwiLGtleSk7XG4gICAgICAgIHAuaW5uZXJUZXh0ID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhrZXkpO1xuICAgIH1cblxuICAgIHJldHVybiBwO1xufVxuXG5cbmZ1bmN0aW9uIHN0b3BHbG93aW5nKGdsb3dlcikge1xuICAgIHdpbmRvdy5jbGVhckludGVydmFsKG5vdGlmaWNhdGlvbkludGVydmFsKTtcbiAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IGZhbHNlO1xuICAgIGdsb3dlci5yZW1vdmVDbGFzcygnZ2xvd2luZycpO1xuICAgIGlmICghQ29udGFjdExpc3QuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgZ2xvd2VyLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICB9XG59XG5cblxuLyoqXG4gKiBDb250YWN0IGxpc3QuXG4gKi9cbnZhciBDb250YWN0TGlzdCA9IHtcbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgaWYgdGhlIGNoYXQgaXMgY3VycmVudGx5IHZpc2libGUuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIDx0dD50cnVlPC90dD4gaWYgdGhlIGNoYXQgaXMgY3VycmVudGx5IHZpc2libGUsIDx0dD5mYWxzZTwvdHQ+IC1cbiAgICAgKiBvdGhlcndpc2VcbiAgICAgKi9cbiAgICBpc1Zpc2libGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuICQoJyNjb250YWN0bGlzdCcpLmlzKFwiOnZpc2libGVcIik7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEFkZHMgYSBjb250YWN0IGZvciB0aGUgZ2l2ZW4gcGVlckppZCBpZiBzdWNoIGRvZXNuJ3QgeWV0IGV4aXN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHBlZXJKaWQgdGhlIHBlZXJKaWQgY29ycmVzcG9uZGluZyB0byB0aGUgY29udGFjdFxuICAgICAqIEBwYXJhbSBpZCB0aGUgdXNlcidzIGVtYWlsIG9yIHVzZXJJZCB1c2VkIHRvIGdldCB0aGUgdXNlcidzIGF2YXRhclxuICAgICAqL1xuICAgIGVuc3VyZUFkZENvbnRhY3Q6IGZ1bmN0aW9uIChwZWVySmlkLCBpZCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyNjb250YWN0bGlzdD51bD5saVtpZD1cIicgKyByZXNvdXJjZUppZCArICdcIl0nKTtcblxuICAgICAgICBpZiAoIWNvbnRhY3QgfHwgY29udGFjdC5sZW5ndGggPD0gMClcbiAgICAgICAgICAgIENvbnRhY3RMaXN0LmFkZENvbnRhY3QocGVlckppZCwgaWQpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgY29udGFjdCBmb3IgdGhlIGdpdmVuIHBlZXIgamlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHBlZXJKaWQgdGhlIGppZCBvZiB0aGUgY29udGFjdCB0byBhZGRcbiAgICAgKiBAcGFyYW0gaWQgdGhlIGVtYWlsIG9yIHVzZXJJZCBvZiB0aGUgdXNlclxuICAgICAqL1xuICAgIGFkZENvbnRhY3Q6IGZ1bmN0aW9uIChwZWVySmlkLCBpZCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdGxpc3QgPSAkKCcjY29udGFjdGxpc3Q+dWwnKTtcblxuICAgICAgICB2YXIgbmV3Q29udGFjdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7XG4gICAgICAgIG5ld0NvbnRhY3QuaWQgPSByZXNvdXJjZUppZDtcbiAgICAgICAgbmV3Q29udGFjdC5jbGFzc05hbWUgPSBcImNsaWNrYWJsZVwiO1xuICAgICAgICBuZXdDb250YWN0Lm9uY2xpY2sgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5jdXJyZW50VGFyZ2V0LmNsYXNzTmFtZSA9PT0gXCJjbGlja2FibGVcIikge1xuICAgICAgICAgICAgICAgICQoQ29udGFjdExpc3QpLnRyaWdnZXIoJ2NvbnRhY3RjbGlja2VkJywgW3BlZXJKaWRdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBuZXdDb250YWN0LmFwcGVuZENoaWxkKGNyZWF0ZUF2YXRhcihpZCkpO1xuICAgICAgICBuZXdDb250YWN0LmFwcGVuZENoaWxkKGNyZWF0ZURpc3BsYXlOYW1lUGFyYWdyYXBoKFwicGFydGljaXBhbnRcIikpO1xuXG4gICAgICAgIHZhciBjbEVsZW1lbnQgPSBjb250YWN0bGlzdC5nZXQoMCk7XG5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkID09PSBBUFAueG1wcC5teVJlc291cmNlKClcbiAgICAgICAgICAgICYmICQoJyNjb250YWN0bGlzdD51bCAudGl0bGUnKVswXS5uZXh0U2libGluZy5uZXh0U2libGluZykge1xuICAgICAgICAgICAgY2xFbGVtZW50Lmluc2VydEJlZm9yZShuZXdDb250YWN0LFxuICAgICAgICAgICAgICAgICQoJyNjb250YWN0bGlzdD51bCAudGl0bGUnKVswXS5uZXh0U2libGluZy5uZXh0U2libGluZyk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjbEVsZW1lbnQuYXBwZW5kQ2hpbGQobmV3Q29udGFjdCk7XG4gICAgICAgIH1cbiAgICAgICAgdXBkYXRlTnVtYmVyT2ZQYXJ0aWNpcGFudHMoMSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJlbW92ZXMgYSBjb250YWN0IGZvciB0aGUgZ2l2ZW4gcGVlciBqaWQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcGVlckppZCB0aGUgcGVlckppZCBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb250YWN0IHRvIHJlbW92ZVxuICAgICAqL1xuICAgIHJlbW92ZUNvbnRhY3Q6IGZ1bmN0aW9uIChwZWVySmlkKSB7XG4gICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHBlZXJKaWQpO1xuXG4gICAgICAgIHZhciBjb250YWN0ID0gJCgnI2NvbnRhY3RsaXN0PnVsPmxpW2lkPVwiJyArIHJlc291cmNlSmlkICsgJ1wiXScpO1xuXG4gICAgICAgIGlmIChjb250YWN0ICYmIGNvbnRhY3QubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgdmFyIGNvbnRhY3RsaXN0ID0gJCgnI2NvbnRhY3RsaXN0PnVsJyk7XG5cbiAgICAgICAgICAgIGNvbnRhY3RsaXN0LmdldCgwKS5yZW1vdmVDaGlsZChjb250YWN0LmdldCgwKSk7XG5cbiAgICAgICAgICAgIHVwZGF0ZU51bWJlck9mUGFydGljaXBhbnRzKC0xKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBzZXRWaXN1YWxOb3RpZmljYXRpb246IGZ1bmN0aW9uIChzaG93LCBzdG9wR2xvd2luZ0luKSB7XG4gICAgICAgIHZhciBnbG93ZXIgPSAkKCcjY29udGFjdExpc3RCdXR0b24nKTtcblxuICAgICAgICBpZiAoc2hvdyAmJiAhbm90aWZpY2F0aW9uSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgIG5vdGlmaWNhdGlvbkludGVydmFsID0gd2luZG93LnNldEludGVydmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBnbG93ZXIudG9nZ2xlQ2xhc3MoJ2FjdGl2ZSBnbG93aW5nJyk7XG4gICAgICAgICAgICB9LCA4MDApO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKCFzaG93ICYmIG5vdGlmaWNhdGlvbkludGVydmFsKSB7XG4gICAgICAgICAgICBzdG9wR2xvd2luZyhnbG93ZXIpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChzdG9wR2xvd2luZ0luKSB7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBzdG9wR2xvd2luZyhnbG93ZXIpO1xuICAgICAgICAgICAgfSwgc3RvcEdsb3dpbmdJbik7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc2V0Q2xpY2thYmxlOiBmdW5jdGlvbiAocmVzb3VyY2VKaWQsIGlzQ2xpY2thYmxlKSB7XG4gICAgICAgIHZhciBjb250YWN0ID0gJCgnI2NvbnRhY3RsaXN0PnVsPmxpW2lkPVwiJyArIHJlc291cmNlSmlkICsgJ1wiXScpO1xuICAgICAgICBpZiAoaXNDbGlja2FibGUpIHtcbiAgICAgICAgICAgIGNvbnRhY3QuYWRkQ2xhc3MoJ2NsaWNrYWJsZScpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29udGFjdC5yZW1vdmVDbGFzcygnY2xpY2thYmxlJyk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgb25EaXNwbGF5TmFtZUNoYW5nZTogZnVuY3Rpb24gKHBlZXJKaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgICAgIGlmIChwZWVySmlkID09PSAnbG9jYWxWaWRlb0NvbnRhaW5lcicpXG4gICAgICAgICAgICBwZWVySmlkID0gQVBQLnhtcHAubXlKaWQoKTtcblxuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdE5hbWUgPSAkKCcjY29udGFjdGxpc3QgIycgKyByZXNvdXJjZUppZCArICc+cCcpO1xuXG4gICAgICAgIGlmIChjb250YWN0TmFtZSAmJiBkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGggPiAwKVxuICAgICAgICAgICAgY29udGFjdE5hbWUuaHRtbChkaXNwbGF5TmFtZSk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb250YWN0TGlzdDsiLCJ2YXIgQXZhdGFyID0gcmVxdWlyZShcIi4uLy4uL2F2YXRhci9BdmF0YXJcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi8uLi8uLi8uLi9zZXR0aW5ncy9TZXR0aW5nc1wiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vLi4vdXRpbC9VSVV0aWxcIik7XG52YXIgbGFuZ3VhZ2VzID0gcmVxdWlyZShcIi4uLy4uLy4uLy4uL3NlcnZpY2UvdHJhbnNsYXRpb24vbGFuZ3VhZ2VzXCIpO1xuXG5mdW5jdGlvbiBnZW5lcmF0ZUxhbmd1YWdlc1NlbGVjdEJveCgpXG57XG4gICAgdmFyIGN1cnJlbnRMYW5nID0gQVBQLnRyYW5zbGF0aW9uLmdldEN1cnJlbnRMYW5ndWFnZSgpO1xuICAgIHZhciBodG1sID0gXCI8c2VsZWN0IGlkPVxcXCJsYW5ndWFnZXNfc2VsZWN0Ym94XFxcIj5cIjtcbiAgICB2YXIgbGFuZ0FycmF5ID0gbGFuZ3VhZ2VzLmdldExhbmd1YWdlcygpO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBsYW5nQXJyYXkubGVuZ3RoOyBpKyspXG4gICAge1xuICAgICAgICB2YXIgbGFuZyA9IGxhbmdBcnJheVtpXTtcbiAgICAgICAgaHRtbCArPSBcIjxvcHRpb24gXCI7XG4gICAgICAgIGlmKGxhbmcgPT09IGN1cnJlbnRMYW5nKVxuICAgICAgICAgICAgaHRtbCArPSBcInNlbGVjdGVkIFwiO1xuICAgICAgICBodG1sICs9IFwidmFsdWU9XFxcIlwiICsgbGFuZyArIFwiXFxcIiBkYXRhLWkxOG49J2xhbmd1YWdlczpcIiArIGxhbmcgKyBcIic+XCI7XG4gICAgICAgIGh0bWwgKz0gXCI8L29wdGlvbj5cIjtcblxuICAgIH1cblxuICAgIHJldHVybiBodG1sICsgXCI8L3NlbGVjdD5cIjtcbn1cblxuXG52YXIgU2V0dGluZ3NNZW51ID0ge1xuXG4gICAgaW5pdDogZnVuY3Rpb24gKCkge1xuICAgICAgICAkKFwiI3VwZGF0ZVNldHRpbmdzXCIpLmJlZm9yZShnZW5lcmF0ZUxhbmd1YWdlc1NlbGVjdEJveCgpKTtcbiAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJChcIiNsYW5ndWFnZXNfc2VsZWN0Ym94XCIpKTtcbiAgICAgICAgJCgnI3NldHRpbmdzbWVudT5pbnB1dCcpLmtleXVwKGZ1bmN0aW9uKGV2ZW50KXtcbiAgICAgICAgICAgIGlmKGV2ZW50LmtleUNvZGUgPT09IDEzKSB7Ly9lbnRlclxuICAgICAgICAgICAgICAgIFNldHRpbmdzTWVudS51cGRhdGUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgJChcIiN1cGRhdGVTZXR0aW5nc1wiKS5jbGljayhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBTZXR0aW5nc01lbnUudXBkYXRlKCk7XG4gICAgICAgIH0pO1xuICAgIH0sXG5cbiAgICB1cGRhdGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgbmV3RGlzcGxheU5hbWUgPSBVSVV0aWwuZXNjYXBlSHRtbCgkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUpO1xuICAgICAgICB2YXIgbmV3RW1haWwgPSBVSVV0aWwuZXNjYXBlSHRtbCgkKCcjc2V0RW1haWwnKS5nZXQoMCkudmFsdWUpO1xuXG4gICAgICAgIGlmKG5ld0Rpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSBTZXR0aW5ncy5zZXREaXNwbGF5TmFtZShuZXdEaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwiZGlzcGxheU5hbWVcIiwgZGlzcGxheU5hbWUsIHRydWUpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGxhbmd1YWdlID0gJChcIiNsYW5ndWFnZXNfc2VsZWN0Ym94XCIpLnZhbCgpO1xuICAgICAgICBBUFAudHJhbnNsYXRpb24uc2V0TGFuZ3VhZ2UobGFuZ3VhZ2UpO1xuICAgICAgICBTZXR0aW5ncy5zZXRMYW5ndWFnZShsYW5ndWFnZSk7XG5cbiAgICAgICAgQVBQLnhtcHAuYWRkVG9QcmVzZW5jZShcImVtYWlsXCIsIG5ld0VtYWlsKTtcbiAgICAgICAgdmFyIGVtYWlsID0gU2V0dGluZ3Muc2V0RW1haWwobmV3RW1haWwpO1xuXG5cbiAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIoQVBQLnhtcHAubXlKaWQoKSwgZW1haWwpO1xuICAgIH0sXG5cbiAgICBpc1Zpc2libGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJCgnI3NldHRpbmdzbWVudScpLmlzKCc6dmlzaWJsZScpO1xuICAgIH0sXG5cbiAgICBzZXREaXNwbGF5TmFtZTogZnVuY3Rpb24obmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgdmFyIGRpc3BsYXlOYW1lID0gU2V0dGluZ3Muc2V0RGlzcGxheU5hbWUobmV3RGlzcGxheU5hbWUpO1xuICAgICAgICAkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUgPSBkaXNwbGF5TmFtZTtcbiAgICB9LFxuXG4gICAgb25EaXNwbGF5TmFtZUNoYW5nZTogZnVuY3Rpb24ocGVlckppZCwgbmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgaWYocGVlckppZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInIHx8XG4gICAgICAgICAgICBwZWVySmlkID09PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICB0aGlzLnNldERpc3BsYXlOYW1lKG5ld0Rpc3BsYXlOYW1lKTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cblxubW9kdWxlLmV4cG9ydHMgPSBTZXR0aW5nc01lbnU7IiwidmFyIFBhbmVsVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvU2lkZVBhbmVsVG9nZ2xlclwiKTtcblxudmFyIGJ1dHRvbkhhbmRsZXJzID0ge1xuICAgIFwiYm90dG9tX3Rvb2xiYXJfY29udGFjdF9saXN0XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQm90dG9tVG9vbGJhci50b2dnbGVDb250YWN0TGlzdCgpO1xuICAgIH0sXG4gICAgXCJib3R0b21fdG9vbGJhcl9maWxtX3N0cmlwXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQm90dG9tVG9vbGJhci50b2dnbGVGaWxtU3RyaXAoKTtcbiAgICB9LFxuICAgIFwiYm90dG9tX3Rvb2xiYXJfY2hhdFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ2hhdCgpO1xuICAgIH1cbn07XG5cbnZhciBCb3R0b21Ub29sYmFyID0gKGZ1bmN0aW9uIChteSkge1xuICAgIG15LmluaXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvcih2YXIgayBpbiBidXR0b25IYW5kbGVycylcbiAgICAgICAgICAgICQoXCIjXCIgKyBrKS5jbGljayhidXR0b25IYW5kbGVyc1trXSk7XG4gICAgfTtcblxuICAgIG15LnRvZ2dsZUNoYXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZUNoYXQoKTtcbiAgICB9O1xuXG4gICAgbXkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZUNvbnRhY3RMaXN0KCk7XG4gICAgfTtcblxuICAgIG15LnRvZ2dsZUZpbG1TdHJpcCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgZmlsbXN0cmlwID0gJChcIiNyZW1vdGVWaWRlb3NcIik7XG4gICAgICAgIGZpbG1zdHJpcC50b2dnbGVDbGFzcyhcImhpZGRlblwiKTtcbiAgICB9O1xuXG4gICAgJChkb2N1bWVudCkuYmluZChcInJlbW90ZXZpZGVvLnJlc2l6ZWRcIiwgZnVuY3Rpb24gKGV2ZW50LCB3aWR0aCwgaGVpZ2h0KSB7XG4gICAgICAgIHZhciBib3R0b20gPSAoaGVpZ2h0IC0gJCgnI2JvdHRvbVRvb2xiYXInKS5vdXRlckhlaWdodCgpKS8yICsgMTg7XG5cbiAgICAgICAgJCgnI2JvdHRvbVRvb2xiYXInKS5jc3Moe2JvdHRvbTogYm90dG9tICsgJ3B4J30pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIG15O1xufShCb3R0b21Ub29sYmFyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gQm90dG9tVG9vbGJhcjtcbiIsIi8qIGdsb2JhbCBBUFAsJCwgYnV0dG9uQ2xpY2ssIGNvbmZpZywgbG9ja1Jvb20sXG4gICBzZXRTaGFyZWRLZXksIFV0aWwgKi9cbnZhciBtZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuLi91dGlsL01lc3NhZ2VIYW5kbGVyXCIpO1xudmFyIEJvdHRvbVRvb2xiYXIgPSByZXF1aXJlKFwiLi9Cb3R0b21Ub29sYmFyXCIpO1xudmFyIFByZXppID0gcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpO1xudmFyIEV0aGVycGFkID0gcmVxdWlyZShcIi4uL2V0aGVycGFkL0V0aGVycGFkXCIpO1xudmFyIFBhbmVsVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvU2lkZVBhbmVsVG9nZ2xlclwiKTtcbnZhciBBdXRoZW50aWNhdGlvbiA9IHJlcXVpcmUoXCIuLi9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvblwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vdXRpbC9VSVV0aWxcIik7XG52YXIgQXV0aGVudGljYXRpb25FdmVudHNcbiAgICA9IHJlcXVpcmUoXCIuLi8uLi8uLi9zZXJ2aWNlL2F1dGhlbnRpY2F0aW9uL0F1dGhlbnRpY2F0aW9uRXZlbnRzXCIpO1xuXG52YXIgcm9vbVVybCA9IG51bGw7XG52YXIgc2hhcmVkS2V5ID0gJyc7XG52YXIgVUkgPSBudWxsO1xuXG52YXIgYnV0dG9uSGFuZGxlcnMgPVxue1xuICAgIFwidG9vbGJhcl9idXR0b25fbXV0ZVwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBBUFAuVUkudG9nZ2xlQXVkaW8oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fY2FtZXJhXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIEFQUC5VSS50b2dnbGVWaWRlbygpO1xuICAgIH0sXG4gICAgLypcInRvb2xiYXJfYnV0dG9uX2F1dGhlbnRpY2F0aW9uXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXIuYXV0aGVudGljYXRlQ2xpY2tlZCgpO1xuICAgIH0sKi9cbiAgICBcInRvb2xiYXJfYnV0dG9uX3JlY29yZFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0b2dnbGVSZWNvcmRpbmcoKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fc2VjdXJpdHlcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gVG9vbGJhci5vcGVuTG9ja0RpYWxvZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9saW5rXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXIub3BlbkxpbmtEaWFsb2coKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fY2hhdFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBCb3R0b21Ub29sYmFyLnRvZ2dsZUNoYXQoKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fcHJlemlcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gUHJlemkub3BlblByZXppRGlhbG9nKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2V0aGVycGFkXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIEV0aGVycGFkLnRvZ2dsZUV0aGVycGFkKDApO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9kZXNrdG9wc2hhcmluZ1wiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBBUFAuZGVza3RvcHNoYXJpbmcudG9nZ2xlU2NyZWVuU2hhcmluZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9mdWxsU2NyZWVuXCI6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNmdWxsU2NyZWVuXCIsIFwiaWNvbi1mdWxsLXNjcmVlbiBpY29uLWV4aXQtZnVsbC1zY3JlZW5cIik7XG4gICAgICAgIHJldHVybiBUb29sYmFyLnRvZ2dsZUZ1bGxTY3JlZW4oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fc2lwXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNhbGxTaXBCdXR0b25DbGlja2VkKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3NldHRpbmdzXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZVNldHRpbmdzTWVudSgpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9oYW5ndXBcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gaGFuZ3VwKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2xvZ2luXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgVG9vbGJhci5hdXRoZW50aWNhdGVDbGlja2VkKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2xvZ291dFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIEFzayBmb3IgY29uZmlybWF0aW9uXG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXG4gICAgICAgICAgICBcImRpYWxvZy5sb2dvdXRUaXRsZVwiLFxuICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgIFwiZGlhbG9nLmxvZ291dFF1ZXN0aW9uXCIsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcImRpYWxvZy5ZZXNcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uIChldnQsIHllcykge1xuICAgICAgICAgICAgICAgIGlmICh5ZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAubG9nb3V0KGZ1bmN0aW9uICh1cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3cubG9jYXRpb24uaHJlZiA9IHVybDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFuZ3VwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgIH1cbn07XG5cbmZ1bmN0aW9uIGhhbmd1cCgpIHtcbiAgICBBUFAueG1wcC5kaXNwb3NlQ29uZmVyZW5jZSgpO1xuICAgIGlmKGNvbmZpZy5lbmFibGVXZWxjb21lUGFnZSlcbiAgICB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLndlbGNvbWVQYWdlRGlzYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSA9IFwiL1wiO1xuICAgICAgICB9LCAxMDAwMCk7XG5cbiAgICB9XG5cbiAgICB2YXIgdGl0bGUgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgXCJkaWFsb2cuc2Vzc1Rlcm1pbmF0ZWRcIik7XG4gICAgdmFyIG1zZyA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICBcImRpYWxvZy5odW5nVXBcIik7XG4gICAgdmFyIGJ1dHRvbiA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICBcImRpYWxvZy5qb2luQWdhaW5cIik7XG4gICAgdmFyIGJ1dHRvbnMgPSBbXTtcbiAgICBidXR0b25zLnB1c2goe3RpdGxlOiBidXR0b24sIHZhbHVlOiB0cnVlfSk7XG5cbiAgICBVSS5tZXNzYWdlSGFuZGxlci5vcGVuRGlhbG9nKFxuICAgICAgICB0aXRsZSxcbiAgICAgICAgbXNnLFxuICAgICAgICB0cnVlLFxuICAgICAgICBidXR0b25zLFxuICAgICAgICBmdW5jdGlvbihldmVudCwgdmFsdWUsIG1lc3NhZ2UsIGZvcm1WYWxzKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICApO1xufVxuXG4vKipcbiAqIFN0YXJ0cyBvciBzdG9wcyB0aGUgcmVjb3JkaW5nIGZvciB0aGUgY29uZmVyZW5jZS5cbiAqL1xuXG5mdW5jdGlvbiB0b2dnbGVSZWNvcmRpbmcoKSB7XG4gICAgQVBQLnhtcHAudG9nZ2xlUmVjb3JkaW5nKGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICB2YXIgbXNnID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICBcImRpYWxvZy5yZWNvcmRpbmdUb2tlblwiKTtcbiAgICAgICAgdmFyIHRva2VuID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcImRpYWxvZy50b2tlblwiKTtcbiAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCwgbnVsbCwgbnVsbCxcbiAgICAgICAgICAgICAgICAnPGgyPicgKyBtc2cgKyAnPC9oMj4nICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IG5hbWU9XCJyZWNvcmRpbmdUb2tlblwiIHR5cGU9XCJ0ZXh0XCIgJyArXG4gICAgICAgICAgICAgICAgJyBkYXRhLWkxOG49XCJbcGxhY2Vob2xkZXJdZGlhbG9nLnRva2VuXCIgJyArXG4gICAgICAgICAgICAgICAgJ3BsYWNlaG9sZGVyPVwiJyArIHRva2VuICsgJ1wiIGF1dG9mb2N1cz4nLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcImRpYWxvZy5TYXZlXCIsXG4gICAgICAgICAgICBmdW5jdGlvbiAoZSwgdiwgbSwgZikge1xuICAgICAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB0b2tlbiA9IGYucmVjb3JkaW5nVG9rZW47XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHRva2VuKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhVSVV0aWwuZXNjYXBlSHRtbCh0b2tlbikpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIG51bGwsXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7IH0sXG4gICAgICAgICAgICAnOmlucHV0OmZpcnN0J1xuICAgICAgICApO1xuICAgIH0sIFRvb2xiYXIuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUsIFRvb2xiYXIuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUpO1xufVxuXG4vKipcbiAqIExvY2tzIC8gdW5sb2NrcyB0aGUgcm9vbS5cbiAqL1xuZnVuY3Rpb24gbG9ja1Jvb20obG9jaykge1xuICAgIHZhciBjdXJyZW50U2hhcmVkS2V5ID0gJyc7XG4gICAgaWYgKGxvY2spXG4gICAgICAgIGN1cnJlbnRTaGFyZWRLZXkgPSBzaGFyZWRLZXk7XG5cbiAgICBBUFAueG1wcC5sb2NrUm9vbShjdXJyZW50U2hhcmVkS2V5LCBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgIC8vIHBhc3N3b3JkIGlzIHJlcXVpcmVkXG4gICAgICAgIGlmIChzaGFyZWRLZXkpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzZXQgcm9vbSBwYXNzd29yZCcpO1xuICAgICAgICAgICAgVG9vbGJhci5sb2NrTG9ja0J1dHRvbigpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ3JlbW92ZWQgcm9vbSBwYXNzd29yZCcpO1xuICAgICAgICAgICAgVG9vbGJhci51bmxvY2tMb2NrQnV0dG9uKCk7XG4gICAgICAgIH1cbiAgICB9LCBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgIGNvbnNvbGUud2Fybignc2V0dGluZyBwYXNzd29yZCBmYWlsZWQnLCBlcnIpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoXCJkaWFsb2cubG9ja1RpdGxlXCIsXG4gICAgICAgICAgICBcImRpYWxvZy5sb2NrTWVzc2FnZVwiKTtcbiAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkoJycpO1xuICAgIH0sIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdyb29tIHBhc3N3b3JkcyBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcihcImRpYWxvZy53YXJuaW5nXCIsXG4gICAgICAgICAgICBcImRpYWxvZy5wYXNzd29yZE5vdFN1cHBvcnRlZFwiKTtcbiAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkoJycpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBJbnZpdGUgcGFydGljaXBhbnRzIHRvIGNvbmZlcmVuY2UuXG4gKi9cbmZ1bmN0aW9uIGludml0ZVBhcnRpY2lwYW50cygpIHtcbiAgICBpZiAocm9vbVVybCA9PT0gbnVsbClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgdmFyIHNoYXJlZEtleVRleHQgPSBcIlwiO1xuICAgIGlmIChzaGFyZWRLZXkgJiYgc2hhcmVkS2V5Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgc2hhcmVkS2V5VGV4dCA9XG4gICAgICAgICAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFwiZW1haWwuc2hhcmVkS2V5XCIsXG4gICAgICAgICAgICAgICAge3NoYXJlZEtleTogc2hhcmVkS2V5fSk7XG4gICAgICAgIHNoYXJlZEtleVRleHQgPSBzaGFyZWRLZXlUZXh0LnJlcGxhY2UoL1xcbi9nLCBcIiUwRCUwQVwiKTtcbiAgICB9XG5cbiAgICB2YXIgc3VwcG9ydGVkQnJvd3NlcnMgPSBcIkNocm9taXVtLCBHb29nbGUgQ2hyb21lIFwiICtcbiAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcImVtYWlsLmFuZFwiKSArIFwiIE9wZXJhXCI7XG4gICAgdmFyIGNvbmZlcmVuY2VOYW1lID0gcm9vbVVybC5zdWJzdHJpbmcocm9vbVVybC5sYXN0SW5kZXhPZignLycpICsgMSk7XG4gICAgdmFyIHN1YmplY3QgPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFwiZW1haWwuc3ViamVjdFwiLFxuICAgICAgICB7YXBwTmFtZTppbnRlcmZhY2VDb25maWcuQVBQX05BTUUsIGNvbmZlcmVuY2VOYW1lOiBjb25mZXJlbmNlTmFtZX0pO1xuICAgIHZhciBib2R5ID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcImVtYWlsLmJvZHlcIixcbiAgICAgICAge2FwcE5hbWU6aW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FLCBzaGFyZWRLZXlUZXh0OiBzaGFyZWRLZXlUZXh0LFxuICAgICAgICAgICAgcm9vbVVybDogcm9vbVVybCwgc3VwcG9ydGVkQnJvd3NlcnM6IHN1cHBvcnRlZEJyb3dzZXJzfSk7XG4gICAgYm9keSA9IGJvZHkucmVwbGFjZSgvXFxuL2csIFwiJTBEJTBBXCIpO1xuXG4gICAgaWYgKHdpbmRvdy5sb2NhbFN0b3JhZ2UuZGlzcGxheW5hbWUpIHtcbiAgICAgICAgYm9keSArPSBcIiUwRCUwQSUwRCUwQVwiICsgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZTtcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLklOVklUQVRJT05fUE9XRVJFRF9CWSkge1xuICAgICAgICBib2R5ICs9IFwiJTBEJTBBJTBEJTBBLS0lMEQlMEFwb3dlcmVkIGJ5IGppdHNpLm9yZ1wiO1xuICAgIH1cblxuICAgIHdpbmRvdy5vcGVuKFwibWFpbHRvOj9zdWJqZWN0PVwiICsgc3ViamVjdCArIFwiJmJvZHk9XCIgKyBib2R5LCAnX2JsYW5rJyk7XG59XG5cbmZ1bmN0aW9uIGNhbGxTaXBCdXR0b25DbGlja2VkKClcbntcbiAgICB2YXIgZGVmYXVsdE51bWJlclxuICAgICAgICA9IGNvbmZpZy5kZWZhdWx0U2lwTnVtYmVyID8gY29uZmlnLmRlZmF1bHRTaXBOdW1iZXIgOiAnJztcblxuICAgIHZhciBzaXBNc2cgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgXCJkaWFsb2cuc2lwTXNnXCIpO1xuICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCwgbnVsbCwgbnVsbCxcbiAgICAgICAgJzxoMj4nICsgc2lwTXNnICsgJzwvaDI+JyArXG4gICAgICAgICc8aW5wdXQgbmFtZT1cInNpcE51bWJlclwiIHR5cGU9XCJ0ZXh0XCInICtcbiAgICAgICAgJyB2YWx1ZT1cIicgKyBkZWZhdWx0TnVtYmVyICsgJ1wiIGF1dG9mb2N1cz4nLFxuICAgICAgICBmYWxzZSxcbiAgICAgICAgXCJkaWFsb2cuRGlhbFwiLFxuICAgICAgICBmdW5jdGlvbiAoZSwgdiwgbSwgZikge1xuICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICB2YXIgbnVtYmVySW5wdXQgPSBmLnNpcE51bWJlcjtcbiAgICAgICAgICAgICAgICBpZiAobnVtYmVySW5wdXQpIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuZGlhbChcbiAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcklucHV0LCAnZnJvbW51bWJlcicsIFVJLmdldFJvb21OYW1lKCksIHNoYXJlZEtleSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBudWxsLCBudWxsLCAnOmlucHV0OmZpcnN0J1xuICAgICk7XG59XG5cbnZhciBUb29sYmFyID0gKGZ1bmN0aW9uIChteSkge1xuXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICh1aSkge1xuICAgICAgICBmb3IodmFyIGsgaW4gYnV0dG9uSGFuZGxlcnMpXG4gICAgICAgICAgICAkKFwiI1wiICsgaykuY2xpY2soYnV0dG9uSGFuZGxlcnNba10pO1xuICAgICAgICBVSSA9IHVpO1xuICAgICAgICAvLyBVcGRhdGUgbG9naW4gaW5mb1xuICAgICAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihcbiAgICAgICAgICAgIEF1dGhlbnRpY2F0aW9uRXZlbnRzLklERU5USVRZX1VQREFURUQsXG4gICAgICAgICAgICBmdW5jdGlvbiAoYXV0aGVudGljYXRpb25FbmFibGVkLCB1c2VySWRlbnRpdHkpIHtcblxuICAgICAgICAgICAgICAgIHZhciBsb2dnZWRJbiA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIGlmICh1c2VySWRlbnRpdHkpIHtcbiAgICAgICAgICAgICAgICAgICAgbG9nZ2VkSW4gPSB0cnVlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbihhdXRoZW50aWNhdGlvbkVuYWJsZWQpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGF1dGhlbnRpY2F0aW9uRW5hYmxlZCkge1xuICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldEF1dGhlbnRpY2F0ZWRJZGVudGl0eSh1c2VySWRlbnRpdHkpO1xuXG4gICAgICAgICAgICAgICAgICAgIFRvb2xiYXIuc2hvd0xvZ2luQnV0dG9uKCFsb2dnZWRJbik7XG4gICAgICAgICAgICAgICAgICAgIFRvb2xiYXIuc2hvd0xvZ291dEJ1dHRvbihsb2dnZWRJbik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHNoYXJlZCBrZXlcbiAgICAgKiBAcGFyYW0gc0tleSB0aGUgc2hhcmVkIGtleVxuICAgICAqL1xuICAgIG15LnNldFNoYXJlZEtleSA9IGZ1bmN0aW9uIChzS2V5KSB7XG4gICAgICAgIHNoYXJlZEtleSA9IHNLZXk7XG4gICAgfTtcblxuICAgIG15LmF1dGhlbnRpY2F0ZUNsaWNrZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEF1dGhlbnRpY2F0aW9uLmZvY3VzQXV0aGVudGljYXRpb25XaW5kb3coKTtcbiAgICAgICAgaWYgKCFBUFAueG1wcC5pc0V4dGVybmFsQXV0aEVuYWJsZWQoKSkge1xuICAgICAgICAgICAgQXV0aGVudGljYXRpb24ueG1wcEF1dGhlbnRpY2F0ZSgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIEdldCBhdXRoZW50aWNhdGlvbiBVUkxcbiAgICAgICAgaWYgKCFBUFAueG1wcC5nZXRNVUNKb2luZWQoKSkge1xuICAgICAgICAgICAgQVBQLnhtcHAuZ2V0TG9naW5VcmwoVUkuZ2V0Um9vbU5hbWUoKSwgZnVuY3Rpb24gKHVybCkge1xuICAgICAgICAgICAgICAgIC8vIElmIGNvbmZlcmVuY2UgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQgeWV0IC0gcmVkaXJlY3QgdG8gbG9naW4gcGFnZVxuICAgICAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gdXJsO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBBUFAueG1wcC5nZXRQb3B1cExvZ2luVXJsKFVJLmdldFJvb21OYW1lKCksIGZ1bmN0aW9uICh1cmwpIHtcbiAgICAgICAgICAgICAgICAvLyBPdGhlcndpc2UgLSBvcGVuIHBvcHVwIHdpdGggYXV0aGVudGljYXRpb24gVVJMXG4gICAgICAgICAgICAgICAgdmFyIGF1dGhlbnRpY2F0aW9uV2luZG93ID0gQXV0aGVudGljYXRpb24uY3JlYXRlQXV0aGVudGljYXRpb25XaW5kb3coXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9uIHBvcHVwIGNsb3NlZCAtIHJldHJ5IHJvb20gYWxsb2NhdGlvblxuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlVJLmdldFJvb21OYW1lKCksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkgeyBjb25zb2xlLmluZm8oXCJBVVRIIERPTkVcIik7IH1cbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH0sIHVybCk7XG4gICAgICAgICAgICAgICAgaWYgKCFhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgIG51bGwsIFwiZGlhbG9nLnBvcHVwRXJyb3JcIik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgcm9vbSBpbnZpdGUgdXJsLlxuICAgICAqL1xuICAgIG15LnVwZGF0ZVJvb21VcmwgPSBmdW5jdGlvbiAobmV3Um9vbVVybCkge1xuICAgICAgICByb29tVXJsID0gbmV3Um9vbVVybDtcblxuICAgICAgICAvLyBJZiB0aGUgaW52aXRlIGRpYWxvZyBoYXMgYmVlbiBhbHJlYWR5IG9wZW5lZCB3ZSB1cGRhdGUgdGhlIGluZm9ybWF0aW9uLlxuICAgICAgICB2YXIgaW52aXRlTGluayA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdpbnZpdGVMaW5rUmVmJyk7XG4gICAgICAgIGlmIChpbnZpdGVMaW5rKSB7XG4gICAgICAgICAgICBpbnZpdGVMaW5rLnZhbHVlID0gcm9vbVVybDtcbiAgICAgICAgICAgIGludml0ZUxpbmsuc2VsZWN0KCk7XG4gICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnanFpX3N0YXRlMF9idXR0b25JbnZpdGUnKS5kaXNhYmxlZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERpc2FibGVzIGFuZCBlbmFibGVzIHNvbWUgb2YgdGhlIGJ1dHRvbnMuXG4gICAgICovXG4gICAgbXkuc2V0dXBCdXR0b25zRnJvbUNvbmZpZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKGNvbmZpZy5kaXNhYmxlUHJlemkpXG4gICAgICAgIHtcbiAgICAgICAgICAgICQoXCIjcHJlemlfYnV0dG9uXCIpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB0aGUgbG9jayByb29tIGRpYWxvZy5cbiAgICAgKi9cbiAgICBteS5vcGVuTG9ja0RpYWxvZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gT25seSB0aGUgZm9jdXMgaXMgYWJsZSB0byBzZXQgYSBzaGFyZWQga2V5LlxuICAgICAgICBpZiAoIUFQUC54bXBwLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgIGlmIChzaGFyZWRLZXkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICBcImRpYWxvZy5wYXNzd29yZEVycm9yXCIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhudWxsLCBcImRpYWxvZy5wYXNzd29yZEVycm9yMlwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChzaGFyZWRLZXkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsIG51bGwsXG4gICAgICAgICAgICAgICAgICAgIFwiZGlhbG9nLnBhc3N3b3JkQ2hlY2tcIixcbiAgICAgICAgICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIFwiZGlhbG9nLlJlbW92ZVwiLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZSwgdikge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldFNoYXJlZEtleSgnJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9ja1Jvb20oZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zZyA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICAgICAgICAgICAgICBcImRpYWxvZy5wYXNzd29yZE1zZ1wiKTtcbiAgICAgICAgICAgICAgICB2YXIgeW91clBhc3N3b3JkID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcbiAgICAgICAgICAgICAgICAgICAgXCJkaWFsb2cueW91clBhc3N3b3JkXCIpO1xuICAgICAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCwgbnVsbCwgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgJzxoMj4nICsgbXNnICsgJzwvaDI+JyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnPGlucHV0IG5hbWU9XCJsb2NrS2V5XCIgdHlwZT1cInRleHRcIicgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyBkYXRhLWkxOG49XCJbcGxhY2Vob2xkZXJdZGlhbG9nLnlvdXJQYXNzd29yZFwiICcgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJ3BsYWNlaG9sZGVyPVwiJyArIHlvdXJQYXNzd29yZCArICdcIiBhdXRvZm9jdXM+JyxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIFwiZGlhbG9nLlNhdmVcIixcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxvY2tLZXkgPSBmLmxvY2tLZXk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9ja0tleSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldFNoYXJlZEtleShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVJVXRpbC5lc2NhcGVIdG1sKGxvY2tLZXkpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9ja1Jvb20odHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBudWxsLCBudWxsLCAnaW5wdXQ6Zmlyc3QnXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB0aGUgaW52aXRlIGxpbmsgZGlhbG9nLlxuICAgICAqL1xuICAgIG15Lm9wZW5MaW5rRGlhbG9nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW52aXRlQXR0cmVpYnV0ZXM7XG5cbiAgICAgICAgaWYgKHJvb21VcmwgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGludml0ZUF0dHJlaWJ1dGVzID0gJ2RhdGEtaTE4bj1cIlt2YWx1ZV1yb29tVXJsRGVmYXVsdE1zZ1wiIHZhbHVlPVwiJyArXG4gICAgICAgICAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nKFwicm9vbVVybERlZmF1bHRNc2dcIikgKyAnXCInO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW52aXRlQXR0cmVpYnV0ZXMgPSBcInZhbHVlPVxcXCJcIiArIGVuY29kZVVSSShyb29tVXJsKSArIFwiXFxcIlwiO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXCJkaWFsb2cuc2hhcmVMaW5rXCIsXG4gICAgICAgICAgICBudWxsLCBudWxsLFxuICAgICAgICAgICAgJzxpbnB1dCBpZD1cImludml0ZUxpbmtSZWZcIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAgICAgICAgIGludml0ZUF0dHJlaWJ1dGVzICsgJyBvbmNsaWNrPVwidGhpcy5zZWxlY3QoKTtcIiByZWFkb25seT4nLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcImRpYWxvZy5JbnZpdGVcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlLCB2KSB7XG4gICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJvb21VcmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGludml0ZVBhcnRpY2lwYW50cygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocm9vbVVybCkge1xuICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52aXRlTGlua1JlZicpLnNlbGVjdCgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdqcWlfc3RhdGUwX2J1dHRvbkludml0ZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIHNldHRpbmdzIGRpYWxvZy5cbiAgICAgKiBGSVhNRTogbm90IHVzZWQgP1xuICAgICAqL1xuICAgIG15Lm9wZW5TZXR0aW5nc0RpYWxvZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNldHRpbmdzMSA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFxuICAgICAgICAgICAgXCJkaWFsb2cuc2V0dGluZ3MxXCIpO1xuICAgICAgICB2YXIgc2V0dGluZ3MyID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICBcImRpYWxvZy5zZXR0aW5nczJcIik7XG4gICAgICAgIHZhciBzZXR0aW5nczMgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgICAgIFwiZGlhbG9nLnNldHRpbmdzM1wiKTtcblxuICAgICAgICB2YXIgeW91clBhc3N3b3JkID0gQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcbiAgICAgICAgICAgIFwiZGlhbG9nLnlvdXJQYXNzd29yZFwiKTtcblxuICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsXG4gICAgICAgICAgICAnPGgyPicgKyBzZXR0aW5nczEgKyAnPC9oMj4nICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIGlkPVwiaW5pdE11dGVkXCI+JyArXG4gICAgICAgICAgICAgICAgc2V0dGluZ3MyICsgJzxici8+JyArXG4gICAgICAgICAgICAgICAgJzxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiBpZD1cInJlcXVpcmVOaWNrbmFtZXNcIj4nICtcbiAgICAgICAgICAgICAgICAgc2V0dGluZ3MzICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwibG9ja0tleVwiIHR5cGU9XCJ0ZXh0XCIgcGxhY2Vob2xkZXI9XCInICsgeW91clBhc3N3b3JkICtcbiAgICAgICAgICAgICAgICAnXCIgZGF0YS1pMThuPVwiW3BsYWNlaG9sZGVyXWRpYWxvZy55b3VyUGFzc3dvcmRcIiBhdXRvZm9jdXM+JyxcbiAgICAgICAgICAgIG51bGwsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcImRpYWxvZy5TYXZlXCIsXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2tLZXknKS5mb2N1cygpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlLCB2KSB7XG4gICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCQoJyNpbml0TXV0ZWQnKS5pcyhcIjpjaGVja2VkXCIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpdCBpcyBjaGVja2VkXG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoJCgnI3JlcXVpcmVOaWNrbmFtZXMnKS5pcyhcIjpjaGVja2VkXCIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpdCBpcyBjaGVja2VkXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLypcbiAgICAgICAgICAgICAgICAgICAgdmFyIGxvY2tLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9ja0tleScpO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChsb2NrS2V5LnZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZXRTaGFyZWRLZXkobG9ja0tleS52YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBsb2NrUm9vbSh0cnVlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVG9nZ2xlcyB0aGUgYXBwbGljYXRpb24gaW4gYW5kIG91dCBvZiBmdWxsIHNjcmVlbiBtb2RlXG4gICAgICogKGEuay5hLiBwcmVzZW50YXRpb24gbW9kZSBpbiBDaHJvbWUpLlxuICAgICAqL1xuICAgIG15LnRvZ2dsZUZ1bGxTY3JlZW4gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBmc0VsZW1lbnQgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQ7XG5cbiAgICAgICAgaWYgKCFkb2N1bWVudC5tb3pGdWxsU2NyZWVuICYmICFkb2N1bWVudC53ZWJraXRJc0Z1bGxTY3JlZW4pIHtcbiAgICAgICAgICAgIC8vRW50ZXIgRnVsbCBTY3JlZW5cbiAgICAgICAgICAgIGlmIChmc0VsZW1lbnQubW96UmVxdWVzdEZ1bGxTY3JlZW4pIHtcbiAgICAgICAgICAgICAgICBmc0VsZW1lbnQubW96UmVxdWVzdEZ1bGxTY3JlZW4oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIGZzRWxlbWVudC53ZWJraXRSZXF1ZXN0RnVsbFNjcmVlbihFbGVtZW50LkFMTE9XX0tFWUJPQVJEX0lOUFVUKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vRXhpdCBGdWxsIFNjcmVlblxuICAgICAgICAgICAgaWYgKGRvY3VtZW50Lm1vekNhbmNlbEZ1bGxTY3JlZW4pIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5tb3pDYW5jZWxGdWxsU2NyZWVuKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LndlYmtpdENhbmNlbEZ1bGxTY3JlZW4oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG4gICAgLyoqXG4gICAgICogVW5sb2NrcyB0aGUgbG9jayBidXR0b24gc3RhdGUuXG4gICAgICovXG4gICAgbXkudW5sb2NrTG9ja0J1dHRvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKCQoXCIjbG9ja0ljb25cIikuaGFzQ2xhc3MoXCJpY29uLXNlY3VyaXR5LWxvY2tlZFwiKSlcbiAgICAgICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNsb2NrSWNvblwiLCBcImljb24tc2VjdXJpdHkgaWNvbi1zZWN1cml0eS1sb2NrZWRcIik7XG4gICAgfTtcbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBsb2NrIGJ1dHRvbiBzdGF0ZSB0byBsb2NrZWQuXG4gICAgICovXG4gICAgbXkubG9ja0xvY2tCdXR0b24gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmICgkKFwiI2xvY2tJY29uXCIpLmhhc0NsYXNzKFwiaWNvbi1zZWN1cml0eVwiKSlcbiAgICAgICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNsb2NrSWNvblwiLCBcImljb24tc2VjdXJpdHkgaWNvbi1zZWN1cml0eS1sb2NrZWRcIik7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIG9yIGhpZGVzIGF1dGhlbnRpY2F0aW9uIGJ1dHRvblxuICAgICAqIEBwYXJhbSBzaG93IDx0dD50cnVlPC90dD4gdG8gc2hvdyBvciA8dHQ+ZmFsc2U8L3R0PiB0byBoaWRlXG4gICAgICovXG4gICAgbXkuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbiA9IGZ1bmN0aW9uIChzaG93KSB7XG4gICAgICAgIGlmIChzaG93KSB7XG4gICAgICAgICAgICAkKCcjYXV0aGVudGljYXRpb24nKS5jc3Moe2Rpc3BsYXk6IFwiaW5saW5lXCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICQoJyNhdXRoZW50aWNhdGlvbicpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvLyBTaG93cyBvciBoaWRlcyB0aGUgJ3JlY29yZGluZycgYnV0dG9uLlxuICAgIG15LnNob3dSZWNvcmRpbmdCdXR0b24gPSBmdW5jdGlvbiAoc2hvdykge1xuICAgICAgICBpZiAoIWNvbmZpZy5lbmFibGVSZWNvcmRpbmcpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChzaG93KSB7XG4gICAgICAgICAgICAkKCcjcmVjb3JkaW5nJykuY3NzKHtkaXNwbGF5OiBcImlubGluZVwifSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAkKCcjcmVjb3JkaW5nJykuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8vIFNldHMgdGhlIHN0YXRlIG9mIHRoZSByZWNvcmRpbmcgYnV0dG9uXG4gICAgbXkuc2V0UmVjb3JkaW5nQnV0dG9uU3RhdGUgPSBmdW5jdGlvbiAoaXNSZWNvcmRpbmcpIHtcbiAgICAgICAgaWYgKGlzUmVjb3JkaW5nKSB7XG4gICAgICAgICAgICAkKCcjcmVjb3JkQnV0dG9uJykucmVtb3ZlQ2xhc3MoXCJpY29uLXJlY0VuYWJsZVwiKTtcbiAgICAgICAgICAgICQoJyNyZWNvcmRCdXR0b24nKS5hZGRDbGFzcyhcImljb24tcmVjRW5hYmxlIGFjdGl2ZVwiKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRCdXR0b24nKS5yZW1vdmVDbGFzcyhcImljb24tcmVjRW5hYmxlIGFjdGl2ZVwiKTtcbiAgICAgICAgICAgICQoJyNyZWNvcmRCdXR0b24nKS5hZGRDbGFzcyhcImljb24tcmVjRW5hYmxlXCIpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8vIFNob3dzIG9yIGhpZGVzIFNJUCBjYWxscyBidXR0b25cbiAgICBteS5zaG93U2lwQ2FsbEJ1dHRvbiA9IGZ1bmN0aW9uIChzaG93KSB7XG4gICAgICAgIGlmIChBUFAueG1wcC5pc1NpcEdhdGV3YXlFbmFibGVkKCkgJiYgc2hvdykge1xuICAgICAgICAgICAgJCgnI3NpcENhbGxCdXR0b24nKS5jc3Moe2Rpc3BsYXk6IFwiaW5saW5lLWJsb2NrXCJ9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICQoJyNzaXBDYWxsQnV0dG9uJykuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIERpc3BsYXlzIHVzZXIgYXV0aGVudGljYXRlZCBpZGVudGl0eSBuYW1lKGxvZ2luKS5cbiAgICAgKiBAcGFyYW0gYXV0aElkZW50aXR5IGlkZW50aXR5IG5hbWUgdG8gYmUgZGlzcGxheWVkLlxuICAgICAqL1xuICAgIG15LnNldEF1dGhlbnRpY2F0ZWRJZGVudGl0eSA9IGZ1bmN0aW9uIChhdXRoSWRlbnRpdHkpIHtcbiAgICAgICAgaWYgKGF1dGhJZGVudGl0eSkge1xuICAgICAgICAgICAgJCgnI3Rvb2xiYXJfYXV0aF9pZGVudGl0eScpLmNzcyh7ZGlzcGxheTogXCJsaXN0LWl0ZW1cIn0pO1xuICAgICAgICAgICAgJCgnI3Rvb2xiYXJfYXV0aF9pZGVudGl0eScpLnRleHQoYXV0aElkZW50aXR5KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICQoJyN0b29sYmFyX2F1dGhfaWRlbnRpdHknKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MvaGlkZXMgbG9naW4gYnV0dG9uLlxuICAgICAqIEBwYXJhbSBzaG93IDx0dD50cnVlPC90dD4gdG8gc2hvd1xuICAgICAqL1xuICAgIG15LnNob3dMb2dpbkJ1dHRvbiA9IGZ1bmN0aW9uIChzaG93KSB7XG4gICAgICAgIGlmIChzaG93KSB7XG4gICAgICAgICAgICAkKCcjdG9vbGJhcl9idXR0b25fbG9naW4nKS5jc3Moe2Rpc3BsYXk6IFwibGlzdC1pdGVtXCJ9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICQoJyN0b29sYmFyX2J1dHRvbl9sb2dpbicpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cy9oaWRlcyBsb2dvdXQgYnV0dG9uLlxuICAgICAqIEBwYXJhbSBzaG93IDx0dD50cnVlPC90dD4gdG8gc2hvd1xuICAgICAqL1xuICAgIG15LnNob3dMb2dvdXRCdXR0b24gPSBmdW5jdGlvbiAoc2hvdykge1xuICAgICAgICBpZiAoc2hvdykge1xuICAgICAgICAgICAgJCgnI3Rvb2xiYXJfYnV0dG9uX2xvZ291dCcpLmNzcyh7ZGlzcGxheTogXCJsaXN0LWl0ZW1cIn0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgJCgnI3Rvb2xiYXJfYnV0dG9uX2xvZ291dCcpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBzdGF0ZSBvZiB0aGUgYnV0dG9uLiBUaGUgYnV0dG9uIGhhcyBibHVlIGdsb3cgaWYgZGVza3RvcFxuICAgICAqIHN0cmVhbWluZyBpcyBhY3RpdmUuXG4gICAgICogQHBhcmFtIGFjdGl2ZSB0aGUgc3RhdGUgb2YgdGhlIGRlc2t0b3Agc3RyZWFtaW5nLlxuICAgICAqL1xuICAgIG15LmNoYW5nZURlc2t0b3BTaGFyaW5nQnV0dG9uU3RhdGUgPSBmdW5jdGlvbiAoYWN0aXZlKSB7XG4gICAgICAgIHZhciBidXR0b24gPSAkKFwiI2Rlc2t0b3BzaGFyaW5nID4gYVwiKTtcbiAgICAgICAgaWYgKGFjdGl2ZSlcbiAgICAgICAge1xuICAgICAgICAgICAgYnV0dG9uLmFkZENsYXNzKFwiZ2xvd1wiKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ1dHRvbi5yZW1vdmVDbGFzcyhcImdsb3dcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIG15O1xufShUb29sYmFyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gVG9vbGJhcjsiLCIvKiBnbG9iYWwgJCwgaW50ZXJmYWNlQ29uZmlnLCBNb2RlcmF0b3IsIERlc2t0b3BTdHJlYW1pbmcuc2hvd0Rlc2t0b3BTaGFyaW5nQnV0dG9uICovXG5cbnZhciB0b29sYmFyVGltZW91dE9iamVjdCxcbiAgICB0b29sYmFyVGltZW91dCA9IGludGVyZmFjZUNvbmZpZy5JTklUSUFMX1RPT0xCQVJfVElNRU9VVDtcblxuZnVuY3Rpb24gc2hvd0Rlc2t0b3BTaGFyaW5nQnV0dG9uKCkge1xuICAgIGlmIChBUFAuZGVza3RvcHNoYXJpbmcuaXNEZXNrdG9wU2hhcmluZ0VuYWJsZWQoKSkge1xuICAgICAgICAkKCcjZGVza3RvcHNoYXJpbmcnKS5jc3Moe2Rpc3BsYXk6IFwiaW5saW5lXCJ9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAkKCcjZGVza3RvcHNoYXJpbmcnKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgfVxufVxuXG4vKipcbiAqIEhpZGVzIHRoZSB0b29sYmFyLlxuICovXG5mdW5jdGlvbiBoaWRlVG9vbGJhcigpIHtcbiAgICB2YXIgaGVhZGVyID0gJChcIiNoZWFkZXJcIiksXG4gICAgICAgIGJvdHRvbVRvb2xiYXIgPSAkKFwiI2JvdHRvbVRvb2xiYXJcIik7XG4gICAgdmFyIGlzVG9vbGJhckhvdmVyID0gZmFsc2U7XG4gICAgaGVhZGVyLmZpbmQoJyonKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGlkID0gJCh0aGlzKS5hdHRyKCdpZCcpO1xuICAgICAgICBpZiAoJChcIiNcIiArIGlkICsgXCI6aG92ZXJcIikubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgaXNUb29sYmFySG92ZXIgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSk7XG4gICAgaWYgKCQoXCIjYm90dG9tVG9vbGJhcjpob3ZlclwiKS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGlzVG9vbGJhckhvdmVyID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjbGVhclRpbWVvdXQodG9vbGJhclRpbWVvdXRPYmplY3QpO1xuICAgIHRvb2xiYXJUaW1lb3V0T2JqZWN0ID0gbnVsbDtcblxuICAgIGlmICghaXNUb29sYmFySG92ZXIpIHtcbiAgICAgICAgaGVhZGVyLmhpZGUoXCJzbGlkZVwiLCB7IGRpcmVjdGlvbjogXCJ1cFwiLCBkdXJhdGlvbjogMzAwfSk7XG4gICAgICAgICQoJyNzdWJqZWN0JykuYW5pbWF0ZSh7dG9wOiBcIi09NDBcIn0sIDMwMCk7XG4gICAgICAgIGlmICgkKFwiI3JlbW90ZVZpZGVvc1wiKS5oYXNDbGFzcyhcImhpZGRlblwiKSkge1xuICAgICAgICAgICAgYm90dG9tVG9vbGJhci5oaWRlKFxuICAgICAgICAgICAgICAgIFwic2xpZGVcIiwge2RpcmVjdGlvbjogXCJyaWdodFwiLCBkdXJhdGlvbjogMzAwfSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHRvb2xiYXJUaW1lb3V0T2JqZWN0ID0gc2V0VGltZW91dChoaWRlVG9vbGJhciwgdG9vbGJhclRpbWVvdXQpO1xuICAgIH1cbn1cblxudmFyIFRvb2xiYXJUb2dnbGVyID0ge1xuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBtYWluIHRvb2xiYXIuXG4gICAgICovXG4gICAgc2hvd1Rvb2xiYXI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGhlYWRlciA9ICQoXCIjaGVhZGVyXCIpLFxuICAgICAgICAgICAgYm90dG9tVG9vbGJhciA9ICQoXCIjYm90dG9tVG9vbGJhclwiKTtcbiAgICAgICAgaWYgKCFoZWFkZXIuaXMoJzp2aXNpYmxlJykgfHwgIWJvdHRvbVRvb2xiYXIuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICAgICAgaGVhZGVyLnNob3coXCJzbGlkZVwiLCB7IGRpcmVjdGlvbjogXCJ1cFwiLCBkdXJhdGlvbjogMzAwfSk7XG4gICAgICAgICAgICAkKCcjc3ViamVjdCcpLmFuaW1hdGUoe3RvcDogXCIrPTQwXCJ9LCAzMDApO1xuICAgICAgICAgICAgaWYgKCFib3R0b21Ub29sYmFyLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgICAgICAgICBib3R0b21Ub29sYmFyLnNob3coXG4gICAgICAgICAgICAgICAgICAgIFwic2xpZGVcIiwge2RpcmVjdGlvbjogXCJyaWdodFwiLCBkdXJhdGlvbjogMzAwfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0b29sYmFyVGltZW91dE9iamVjdCkge1xuICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0b29sYmFyVGltZW91dE9iamVjdCk7XG4gICAgICAgICAgICAgICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBzZXRUaW1lb3V0KGhpZGVUb29sYmFyLCB0b29sYmFyVGltZW91dCk7XG4gICAgICAgICAgICB0b29sYmFyVGltZW91dCA9IGludGVyZmFjZUNvbmZpZy5UT09MQkFSX1RJTUVPVVQ7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoQVBQLnhtcHAuaXNNb2RlcmF0b3IoKSlcbiAgICAgICAge1xuLy8gICAgICAgICAgICBUT0RPOiBFbmFibGUgc2V0dGluZ3MgZnVuY3Rpb25hbGl0eS5cbi8vICAgICAgICAgICAgICAgICAgTmVlZCB0byB1bmNvbW1lbnQgdGhlIHNldHRpbmdzIGJ1dHRvbiBpbiBpbmRleC5odG1sLlxuLy8gICAgICAgICAgICAkKCcjc2V0dGluZ3NCdXR0b24nKS5jc3Moe3Zpc2liaWxpdHk6XCJ2aXNpYmxlXCJ9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFNob3cvaGlkZSBkZXNrdG9wIHNoYXJpbmcgYnV0dG9uXG4gICAgICAgIHNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbigpO1xuICAgIH0sXG5cblxuICAgIC8qKlxuICAgICAqIERvY2tzL3VuZG9ja3MgdGhlIHRvb2xiYXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gaXNEb2NrIGluZGljYXRlcyB3aGF0IG9wZXJhdGlvbiB0byBwZXJmb3JtXG4gICAgICovXG4gICAgZG9ja1Rvb2xiYXI6IGZ1bmN0aW9uIChpc0RvY2spIHtcbiAgICAgICAgaWYgKGlzRG9jaykge1xuICAgICAgICAgICAgLy8gRmlyc3QgbWFrZSBzdXJlIHRoZSB0b29sYmFyIGlzIHNob3duLlxuICAgICAgICAgICAgaWYgKCEkKCcjaGVhZGVyJykuaXMoJzp2aXNpYmxlJykpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNob3dUb29sYmFyKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFRoZW4gY2xlYXIgdGhlIHRpbWUgb3V0LCB0byBkb2NrIHRoZSB0b29sYmFyLlxuICAgICAgICAgICAgaWYgKHRvb2xiYXJUaW1lb3V0T2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRvb2xiYXJUaW1lb3V0T2JqZWN0KTtcbiAgICAgICAgICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBpZiAoISQoJyNoZWFkZXInKS5pcygnOnZpc2libGUnKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2hvd1Rvb2xiYXIoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHRvb2xiYXJUaW1lb3V0T2JqZWN0ID0gc2V0VGltZW91dChoaWRlVG9vbGJhciwgdG9vbGJhclRpbWVvdXQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcblxuICAgIHNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbjogc2hvd0Rlc2t0b3BTaGFyaW5nQnV0dG9uXG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gVG9vbGJhclRvZ2dsZXI7IiwidmFyIEppdHNpUG9wb3ZlciA9IChmdW5jdGlvbiAoKSB7XG4gICAgLyoqXG4gICAgICogQ29uc3RydWN0cyBuZXcgSml0c2lQb3BvdmVyIGFuZCBhdHRhY2hlcyBpdCB0byB0aGUgZWxlbWVudFxuICAgICAqIEBwYXJhbSBlbGVtZW50IGpxdWVyeSBzZWxlY3RvclxuICAgICAqIEBwYXJhbSBvcHRpb25zIHRoZSBvcHRpb25zIGZvciB0aGUgcG9wb3Zlci5cbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBKaXRzaVBvcG92ZXIoZWxlbWVudCwgb3B0aW9ucylcbiAgICB7XG4gICAgICAgIHRoaXMub3B0aW9ucyA9IHtcbiAgICAgICAgICAgIHNraW46IFwid2hpdGVcIixcbiAgICAgICAgICAgIGNvbnRlbnQ6IFwiXCJcbiAgICAgICAgfTtcbiAgICAgICAgaWYob3B0aW9ucylcbiAgICAgICAge1xuICAgICAgICAgICAgaWYob3B0aW9ucy5za2luKVxuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucy5za2luID0gb3B0aW9ucy5za2luO1xuXG4gICAgICAgICAgICBpZihvcHRpb25zLmNvbnRlbnQpXG4gICAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLmNvbnRlbnQgPSBvcHRpb25zLmNvbnRlbnQ7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmVsZW1lbnRJc0hvdmVyZWQgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5wb3BvdmVySXNIb3ZlcmVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMucG9wb3ZlclNob3duID0gZmFsc2U7XG5cbiAgICAgICAgZWxlbWVudC5kYXRhKFwiaml0c2lfcG9wb3ZlclwiLCB0aGlzKTtcbiAgICAgICAgdGhpcy5lbGVtZW50ID0gZWxlbWVudDtcbiAgICAgICAgdGhpcy50ZW1wbGF0ZSA9ICcgPGRpdiBjbGFzcz1cImppdHNpcG9wb3ZlciAnICsgdGhpcy5vcHRpb25zLnNraW4gK1xuICAgICAgICAgICAgJ1wiPjxkaXYgY2xhc3M9XCJhcnJvd1wiPjwvZGl2PjxkaXYgY2xhc3M9XCJqaXRzaXBvcG92ZXItY29udGVudFwiPjwvZGl2PicgK1xuICAgICAgICAgICAgJzxkaXYgY2xhc3M9XCJqaXRzaVBvcHVwbWVudVBhZGRpbmdcIj48L2Rpdj48L2Rpdj4nO1xuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgIHRoaXMuZWxlbWVudC5vbihcIm1vdXNlZW50ZXJcIiwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5lbGVtZW50SXNIb3ZlcmVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHNlbGYuc2hvdygpO1xuICAgICAgICB9KS5vbihcIm1vdXNlbGVhdmVcIiwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5lbGVtZW50SXNIb3ZlcmVkID0gZmFsc2U7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmhpZGUoKTtcbiAgICAgICAgICAgIH0sIDEwKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2hvd3MgdGhlIHBvcG92ZXJcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLnNob3cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoaXMuY3JlYXRlUG9wb3ZlcigpO1xuICAgICAgICB0aGlzLnBvcG92ZXJTaG93biA9IHRydWU7XG5cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIHBvcG92ZXJcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLmhpZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKCF0aGlzLmVsZW1lbnRJc0hvdmVyZWQgJiYgIXRoaXMucG9wb3ZlcklzSG92ZXJlZCAmJiB0aGlzLnBvcG92ZXJTaG93bilcbiAgICAgICAge1xuICAgICAgICAgICAgdGhpcy5mb3JjZUhpZGUoKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyB0aGUgcG9wb3ZlclxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUuZm9yY2VIaWRlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAkKFwiLmppdHNpcG9wb3ZlclwiKS5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5wb3BvdmVyU2hvd24gPSBmYWxzZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyB0aGUgcG9wb3ZlciBodG1sXG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS5jcmVhdGVQb3BvdmVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAkKFwiYm9keVwiKS5hcHBlbmQodGhpcy50ZW1wbGF0ZSk7XG4gICAgICAgICQoXCIuaml0c2lwb3BvdmVyID4gLmppdHNpcG9wb3Zlci1jb250ZW50XCIpLmh0bWwodGhpcy5vcHRpb25zLmNvbnRlbnQpO1xuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICQoXCIuaml0c2lwb3BvdmVyXCIpLm9uKFwibW91c2VlbnRlclwiLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZWxmLnBvcG92ZXJJc0hvdmVyZWQgPSB0cnVlO1xuICAgICAgICB9KS5vbihcIm1vdXNlbGVhdmVcIiwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5wb3BvdmVySXNIb3ZlcmVkID0gZmFsc2U7XG4gICAgICAgICAgICBzZWxmLmhpZGUoKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yZWZyZXNoUG9zaXRpb24oKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVmcmVzaGVzIHRoZSBwb3NpdGlvbiBvZiB0aGUgcG9wb3ZlclxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUucmVmcmVzaFBvc2l0aW9uID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAkKFwiLmppdHNpcG9wb3ZlclwiKS5wb3NpdGlvbih7XG4gICAgICAgICAgICBteTogXCJib3R0b21cIixcbiAgICAgICAgICAgIGF0OiBcInRvcFwiLFxuICAgICAgICAgICAgY29sbGlzaW9uOiBcImZpdFwiLFxuICAgICAgICAgICAgb2Y6IHRoaXMuZWxlbWVudCxcbiAgICAgICAgICAgIHVzaW5nOiBmdW5jdGlvbiAocG9zaXRpb24sIGVsZW1lbnRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNhbGNMZWZ0ID0gZWxlbWVudHMudGFyZ2V0LmxlZnQgLSBlbGVtZW50cy5lbGVtZW50LmxlZnQgKyBlbGVtZW50cy50YXJnZXQud2lkdGgvMjtcbiAgICAgICAgICAgICAgICAkKFwiLmppdHNpcG9wb3ZlclwiKS5jc3Moe3RvcDogcG9zaXRpb24udG9wLCBsZWZ0OiBwb3NpdGlvbi5sZWZ0LCBkaXNwbGF5OiBcInRhYmxlXCJ9KTtcbiAgICAgICAgICAgICAgICAkKFwiLmppdHNpcG9wb3ZlciA+IC5hcnJvd1wiKS5jc3Moe2xlZnQ6IGNhbGNMZWZ0fSk7XG4gICAgICAgICAgICAgICAgJChcIi5qaXRzaXBvcG92ZXIgPiAuaml0c2lQb3B1cG1lbnVQYWRkaW5nXCIpLmNzcyh7bGVmdDogY2FsY0xlZnQgLSA1MH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgY29udGVudCBvZiBwb3BvdmVyLlxuICAgICAqIEBwYXJhbSBjb250ZW50IG5ldyBjb250ZW50XG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS51cGRhdGVDb250ZW50ID0gZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgdGhpcy5vcHRpb25zLmNvbnRlbnQgPSBjb250ZW50O1xuICAgICAgICBpZighdGhpcy5wb3BvdmVyU2hvd24pXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICQoXCIuaml0c2lwb3BvdmVyXCIpLnJlbW92ZSgpO1xuICAgICAgICB0aGlzLmNyZWF0ZVBvcG92ZXIoKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEppdHNpUG9wb3ZlcjtcblxuXG59KSgpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEppdHNpUG9wb3ZlcjsiLCIvKiBnbG9iYWwgJCwgQVBQLCBqUXVlcnksIHRvYXN0ciAqL1xudmFyIG1lc3NhZ2VIYW5kbGVyID0gKGZ1bmN0aW9uKG15KSB7XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIG1lc3NhZ2UgdG8gdGhlIHVzZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGl0bGVTdHJpbmcgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1lc3NhZ2VTdHJpbmcgdGhlIHRleHQgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKi9cbiAgICBteS5vcGVuTWVzc2FnZURpYWxvZyA9IGZ1bmN0aW9uKHRpdGxlS2V5LCBtZXNzYWdlS2V5KSB7XG4gICAgICAgIHZhciB0aXRsZSA9IG51bGw7XG4gICAgICAgIGlmKHRpdGxlS2V5KVxuICAgICAgICB7XG4gICAgICAgICAgICB0aXRsZSA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKHRpdGxlS2V5KTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgbWVzc2FnZSA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKG1lc3NhZ2VLZXkpO1xuICAgICAgICAkLnByb21wdChtZXNzYWdlLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHRpdGxlOiB0aXRsZSxcbiAgICAgICAgICAgICAgICBwZXJzaXN0ZW50OiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIG1lc3NhZ2UgdG8gdGhlIHVzZXIgd2l0aCB0d28gYnV0dG9uczogZmlyc3QgaXMgZ2l2ZW4gYXMgYSBwYXJhbWV0ZXIgYW5kIHRoZSBzZWNvbmQgaXMgQ2FuY2VsLlxuICAgICAqXG4gICAgICogQHBhcmFtIHRpdGxlU3RyaW5nIHRoZSB0aXRsZSBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBtc2dTdHJpbmcgdGhlIHRleHQgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gcGVyc2lzdGVudCBib29sZWFuIHZhbHVlIHdoaWNoIGRldGVybWluZXMgd2hldGhlciB0aGUgbWVzc2FnZSBpcyBwZXJzaXN0ZW50IG9yIG5vdFxuICAgICAqIEBwYXJhbSBsZWZ0QnV0dG9uIHRoZSBmaXN0IGJ1dHRvbidzIHRleHRcbiAgICAgKiBAcGFyYW0gc3VibWl0RnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIG9uIHN1Ym1pdFxuICAgICAqIEBwYXJhbSBsb2FkZWRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBmdWxseSBsb2FkZWRcbiAgICAgKiBAcGFyYW0gY2xvc2VGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBjbG9zZWRcbiAgICAgKiBAcGFyYW0gZm9jdXMgb3B0aW9uYWwgZm9jdXMgc2VsZWN0b3Igb3IgYnV0dG9uIGluZGV4IHRvIGJlIGZvY3VzZWQgYWZ0ZXJcbiAgICAgKiAgICAgICAgdGhlIGRpYWxvZyBpcyBvcGVuZWRcbiAgICAgKiBAcGFyYW0gZGVmYXVsdEJ1dHRvbiBpbmRleCBvZiBkZWZhdWx0IGJ1dHRvbiB3aGljaCB3aWxsIGJlIGFjdGl2YXRlZCB3aGVuXG4gICAgICogICAgICAgIHRoZSB1c2VyIHByZXNzICdlbnRlcicuIEluZGV4ZWQgZnJvbSAwLlxuICAgICAqL1xuICAgIG15Lm9wZW5Ud29CdXR0b25EaWFsb2cgPSBmdW5jdGlvbih0aXRsZUtleSwgdGl0bGVTdHJpbmcsIG1zZ0tleSwgbXNnU3RyaW5nLFxuICAgICAgICBwZXJzaXN0ZW50LCBsZWZ0QnV0dG9uS2V5LCBzdWJtaXRGdW5jdGlvbiwgbG9hZGVkRnVuY3Rpb24sXG4gICAgICAgIGNsb3NlRnVuY3Rpb24sIGZvY3VzLCBkZWZhdWx0QnV0dG9uKVxuICAgIHtcbiAgICAgICAgdmFyIGJ1dHRvbnMgPSBbXTtcblxuICAgICAgICB2YXIgbGVmdEJ1dHRvbiA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKGxlZnRCdXR0b25LZXkpO1xuICAgICAgICBidXR0b25zLnB1c2goeyB0aXRsZTogbGVmdEJ1dHRvbiwgdmFsdWU6IHRydWV9KTtcblxuICAgICAgICB2YXIgY2FuY2VsQnV0dG9uXG4gICAgICAgICAgICA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKFwiZGlhbG9nLkNhbmNlbFwiKTtcbiAgICAgICAgYnV0dG9ucy5wdXNoKHt0aXRsZTogY2FuY2VsQnV0dG9uLCB2YWx1ZTogZmFsc2V9KTtcblxuICAgICAgICB2YXIgbWVzc2FnZSA9IG1zZ1N0cmluZywgdGl0bGUgPSB0aXRsZVN0cmluZztcbiAgICAgICAgaWYgKHRpdGxlS2V5KVxuICAgICAgICB7XG4gICAgICAgICAgICB0aXRsZSA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKHRpdGxlS2V5KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobXNnS2V5KSB7XG4gICAgICAgICAgICBtZXNzYWdlID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwobXNnS2V5KTtcbiAgICAgICAgfVxuICAgICAgICAkLnByb21wdChtZXNzYWdlLCB7XG4gICAgICAgICAgICB0aXRsZTogdGl0bGUsXG4gICAgICAgICAgICBwZXJzaXN0ZW50OiBmYWxzZSxcbiAgICAgICAgICAgIGJ1dHRvbnM6IGJ1dHRvbnMsXG4gICAgICAgICAgICBkZWZhdWx0QnV0dG9uOiBkZWZhdWx0QnV0dG9uLFxuICAgICAgICAgICAgZm9jdXM6IGZvY3VzLFxuICAgICAgICAgICAgbG9hZGVkOiBsb2FkZWRGdW5jdGlvbixcbiAgICAgICAgICAgIHN1Ym1pdDogc3VibWl0RnVuY3Rpb24sXG4gICAgICAgICAgICBjbG9zZTogY2xvc2VGdW5jdGlvblxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MgYSBtZXNzYWdlIHRvIHRoZSB1c2VyIHdpdGggdHdvIGJ1dHRvbnM6IGZpcnN0IGlzIGdpdmVuIGFzIGEgcGFyYW1ldGVyIGFuZCB0aGUgc2Vjb25kIGlzIENhbmNlbC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB0aXRsZVN0cmluZyB0aGUgdGl0bGUgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gbXNnU3RyaW5nIHRoZSB0ZXh0IG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIHBlcnNpc3RlbnQgYm9vbGVhbiB2YWx1ZSB3aGljaCBkZXRlcm1pbmVzIHdoZXRoZXIgdGhlIG1lc3NhZ2UgaXMgcGVyc2lzdGVudCBvciBub3RcbiAgICAgKiBAcGFyYW0gYnV0dG9ucyBvYmplY3Qgd2l0aCB0aGUgYnV0dG9ucy4gVGhlIGtleXMgbXVzdCBiZSB0aGUgbmFtZSBvZiB0aGUgYnV0dG9uIGFuZCB2YWx1ZSBpcyB0aGUgdmFsdWVcbiAgICAgKiB0aGF0IHdpbGwgYmUgcGFzc2VkIHRvIHN1Ym1pdEZ1bmN0aW9uXG4gICAgICogQHBhcmFtIHN1Ym1pdEZ1bmN0aW9uIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBvbiBzdWJtaXRcbiAgICAgKiBAcGFyYW0gbG9hZGVkRnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIGFmdGVyIHRoZSBwcm9tcHQgaXMgZnVsbHkgbG9hZGVkXG4gICAgICovXG4gICAgbXkub3BlbkRpYWxvZyA9IGZ1bmN0aW9uICh0aXRsZVN0cmluZywgbXNnU3RyaW5nLCBwZXJzaXN0ZW50LCBidXR0b25zLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VibWl0RnVuY3Rpb24sIGxvYWRlZEZ1bmN0aW9uKSB7XG4gICAgICAgIHZhciBhcmdzID0ge1xuICAgICAgICAgICAgdGl0bGU6IHRpdGxlU3RyaW5nLFxuICAgICAgICAgICAgcGVyc2lzdGVudDogcGVyc2lzdGVudCxcbiAgICAgICAgICAgIGJ1dHRvbnM6IGJ1dHRvbnMsXG4gICAgICAgICAgICBkZWZhdWx0QnV0dG9uOiAxLFxuICAgICAgICAgICAgbG9hZGVkOiBsb2FkZWRGdW5jdGlvbixcbiAgICAgICAgICAgIHN1Ym1pdDogc3VibWl0RnVuY3Rpb25cbiAgICAgICAgfTtcbiAgICAgICAgaWYgKHBlcnNpc3RlbnQpIHtcbiAgICAgICAgICAgIGFyZ3MuY2xvc2VUZXh0ID0gJyc7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBJbXByb21wdHUobXNnU3RyaW5nLCBhcmdzKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xvc2VzIGN1cnJlbnRseSBvcGVuZWQgZGlhbG9nLlxuICAgICAqL1xuICAgIG15LmNsb3NlRGlhbG9nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAkLnByb21wdC5jbG9zZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIGRpYWxvZyB3aXRoIGRpZmZlcmVudCBzdGF0ZXMgdG8gdGhlIHVzZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gc3RhdGVzT2JqZWN0IG9iamVjdCBjb250YWluaW5nIGFsbCB0aGUgc3RhdGVzIG9mIHRoZSBkaWFsb2dcbiAgICAgKi9cbiAgICBteS5vcGVuRGlhbG9nV2l0aFN0YXRlcyA9IGZ1bmN0aW9uIChzdGF0ZXNPYmplY3QsIG9wdGlvbnMpIHtcblxuICAgICAgICByZXR1cm4gbmV3IEltcHJvbXB0dShzdGF0ZXNPYmplY3QsIG9wdGlvbnMpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyBuZXcgcG9wdXAgd2luZG93IGZvciBnaXZlbiA8dHQ+dXJsPC90dD4gY2VudGVyZWQgb3ZlciBjdXJyZW50XG4gICAgICogd2luZG93LlxuICAgICAqXG4gICAgICogQHBhcmFtIHVybCB0aGUgVVJMIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgcG9wdXAgd2luZG93XG4gICAgICogQHBhcmFtIHcgdGhlIHdpZHRoIG9mIHRoZSBwb3B1cCB3aW5kb3dcbiAgICAgKiBAcGFyYW0gaCB0aGUgaGVpZ2h0IG9mIHRoZSBwb3B1cCB3aW5kb3dcbiAgICAgKiBAcGFyYW0gb25Qb3B1cENsb3NlZCBvcHRpb25hbCBjYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiBwb3B1cCB3aW5kb3dcbiAgICAgKiAgICAgICAgaGFzIGJlZW4gY2xvc2VkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgcG9wdXAgd2luZG93IG9iamVjdCBpZiBvcGVuZWQgc3VjY2Vzc2Z1bGx5IG9yIHVuZGVmaW5lZFxuICAgICAqICAgICAgICAgIGluIGNhc2Ugd2UgZmFpbGVkIHRvIG9wZW4gaXQocG9wdXAgYmxvY2tlZClcbiAgICAgKi9cbiAgICBteS5vcGVuQ2VudGVyZWRQb3B1cCA9IGZ1bmN0aW9uICh1cmwsIHcsIGgsIG9uUG9wdXBDbG9zZWQpIHtcbiAgICAgICAgdmFyIGwgPSB3aW5kb3cuc2NyZWVuWCArICh3aW5kb3cuaW5uZXJXaWR0aCAvIDIpIC0gKHcgLyAyKTtcbiAgICAgICAgdmFyIHQgPSB3aW5kb3cuc2NyZWVuWSArICh3aW5kb3cuaW5uZXJIZWlnaHQgLyAyKSAtIChoIC8gMik7XG4gICAgICAgIHZhciBwb3B1cCA9IHdpbmRvdy5vcGVuKFxuICAgICAgICAgICAgdXJsLCAnX2JsYW5rJyxcbiAgICAgICAgICAgICd0b3A9JyArIHQgKyAnLCBsZWZ0PScgKyBsICsgJywgd2lkdGg9JyArIHcgKyAnLCBoZWlnaHQ9JyArIGggKyAnJyk7XG4gICAgICAgIGlmIChwb3B1cCAmJiBvblBvcHVwQ2xvc2VkKSB7XG4gICAgICAgICAgICB2YXIgcG9sbFRpbWVyID0gd2luZG93LnNldEludGVydmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocG9wdXAuY2xvc2VkICE9PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChwb2xsVGltZXIpO1xuICAgICAgICAgICAgICAgICAgICBvblBvcHVwQ2xvc2VkKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgMjAwKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcG9wdXA7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgZGlhbG9nIHByb21wdGluZyB0aGUgdXNlciB0byBzZW5kIGFuIGVycm9yIHJlcG9ydC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB0aXRsZVN0cmluZyB0aGUgdGl0bGUgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gbXNnU3RyaW5nIHRoZSB0ZXh0IG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIGVycm9yIHRoZSBlcnJvciB0aGF0IGlzIGJlaW5nIHJlcG9ydGVkXG4gICAgICovXG4gICAgbXkub3BlblJlcG9ydERpYWxvZyA9IGZ1bmN0aW9uKHRpdGxlS2V5LCBtc2dLZXksIGVycm9yKSB7XG4gICAgICAgIG15Lm9wZW5NZXNzYWdlRGlhbG9nKHRpdGxlS2V5LCBtc2dLZXkpO1xuICAgICAgICBjb25zb2xlLmxvZyhlcnJvcik7XG4gICAgICAgIC8vRklYTUUgc2VuZCB0aGUgZXJyb3IgdG8gdGhlIHNlcnZlclxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiAgU2hvd3MgYW4gZXJyb3IgZGlhbG9nIHRvIHRoZSB1c2VyLlxuICAgICAqIEBwYXJhbSB0aXRsZSB0aGUgdGl0bGUgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gbWVzc2FnZSB0aGUgdGV4dCBvZiB0aGUgbWVzc2FmZVxuICAgICAqL1xuICAgIG15LnNob3dFcnJvciA9IGZ1bmN0aW9uKHRpdGxlS2V5LCBtc2dLZXkpIHtcblxuICAgICAgICBpZighdGl0bGVLZXkpIHtcbiAgICAgICAgICAgIHRpdGxlS2V5ID0gXCJkaWFsb2cub29wc1wiO1xuICAgICAgICB9XG4gICAgICAgIGlmKCFtc2dLZXkpXG4gICAgICAgIHtcbiAgICAgICAgICAgIG1zZ0tleSA9IFwiZGlhbG9nLmRlZmF1bHRFcnJvclwiO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5NZXNzYWdlRGlhbG9nKHRpdGxlS2V5LCBtc2dLZXkpO1xuICAgIH07XG5cbiAgICBteS5ub3RpZnkgPSBmdW5jdGlvbihkaXNwbGF5TmFtZSwgZGlzcGxheU5hbWVLZXksXG4gICAgICAgICAgICAgICAgICAgICAgICAgY2xzLCBtZXNzYWdlS2V5LCBtZXNzYWdlQXJndW1lbnRzKSB7XG4gICAgICAgIHZhciBkaXNwbGF5TmFtZVNwYW4gPSAnPHNwYW4gY2xhc3M9XCJuaWNrbmFtZVwiICc7XG4gICAgICAgIGlmKGRpc3BsYXlOYW1lKVxuICAgICAgICB7XG4gICAgICAgICAgICBkaXNwbGF5TmFtZVNwYW4gKz0gXCI+XCIgKyBkaXNwbGF5TmFtZTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGRpc3BsYXlOYW1lU3BhbiArPSBcImRhdGEtaTE4bj0nXCIgKyBkaXNwbGF5TmFtZUtleSArXG4gICAgICAgICAgICAgICAgXCInPlwiICsgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhkaXNwbGF5TmFtZUtleSk7XG4gICAgICAgIH1cbiAgICAgICAgZGlzcGxheU5hbWVTcGFuICs9IFwiPC9zcGFuPlwiO1xuICAgICAgICB0b2FzdHIuaW5mbyhcbiAgICAgICAgICAgIGRpc3BsYXlOYW1lU3BhbiArICc8YnI+JyArXG4gICAgICAgICAgICAnPHNwYW4gY2xhc3M9JyArIGNscyArICcgZGF0YS1pMThuPVwiJyArIG1lc3NhZ2VLZXkgKyAnXCInICtcbiAgICAgICAgICAgICAgICAobWVzc2FnZUFyZ3VtZW50cz9cbiAgICAgICAgICAgICAgICAgICAgXCIgZGF0YS1pMThuLW9wdGlvbnM9J1wiICsgSlNPTi5zdHJpbmdpZnkobWVzc2FnZUFyZ3VtZW50cykgKyBcIidcIlxuICAgICAgICAgICAgICAgICAgICA6IFwiXCIpICsgXCI+XCIgK1xuICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhtZXNzYWdlS2V5LFxuICAgICAgICAgICAgICAgIG1lc3NhZ2VBcmd1bWVudHMpICtcbiAgICAgICAgICAgICc8L3NwYW4+Jyk7XG4gICAgfTtcblxuICAgIHJldHVybiBteTtcbn0obWVzc2FnZUhhbmRsZXIgfHwge30pKTtcblxubW9kdWxlLmV4cG9ydHMgPSBtZXNzYWdlSGFuZGxlcjtcblxuXG4iLCJ2YXIgVUlFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vLi4vc2VydmljZS9VSS9VSUV2ZW50c1wiKTtcblxudmFyIG5pY2tuYW1lID0gbnVsbDtcbnZhciBldmVudEVtaXR0ZXIgPSBudWxsO1xuXG52YXIgTmlja2FuYW1lSGFuZGxlciA9IHtcbiAgICBpbml0OiBmdW5jdGlvbiAoZW1pdHRlcikge1xuICAgICAgICBldmVudEVtaXR0ZXIgPSBlbWl0dGVyO1xuICAgICAgICB2YXIgc3RvcmVkRGlzcGxheU5hbWUgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmRpc3BsYXluYW1lO1xuICAgICAgICBpZiAoc3RvcmVkRGlzcGxheU5hbWUpIHtcbiAgICAgICAgICAgIG5pY2tuYW1lID0gc3RvcmVkRGlzcGxheU5hbWU7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHNldE5pY2tuYW1lOiBmdW5jdGlvbiAobmV3Tmlja25hbWUpIHtcbiAgICAgICAgaWYgKCFuZXdOaWNrbmFtZSB8fCBuaWNrbmFtZSA9PT0gbmV3Tmlja25hbWUpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgbmlja25hbWUgPSBuZXdOaWNrbmFtZTtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSA9IG5pY2tuYW1lO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5OSUNLTkFNRV9DSEFOR0VELCBuZXdOaWNrbmFtZSk7XG4gICAgfSxcbiAgICBnZXROaWNrbmFtZTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmlja25hbWU7XG4gICAgfSxcbiAgICBhZGRMaXN0ZW5lcjogZnVuY3Rpb24gKHR5cGUsIGxpc3RlbmVyKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbih0eXBlLCBsaXN0ZW5lcik7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBOaWNrYW5hbWVIYW5kbGVyOyIsIi8qKlxuICogQ3JlYXRlZCBieSBocmlzdG8gb24gMTIvMjIvMTQuXG4gKi9cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGF2YWlsYWJsZSB2aWRlbyB3aWR0aC5cbiAgICAgKi9cbiAgICBnZXRBdmFpbGFibGVWaWRlb1dpZHRoOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBQYW5lbFRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG4gICAgICAgIHZhciByaWdodFBhbmVsV2lkdGhcbiAgICAgICAgICAgID0gUGFuZWxUb2dnbGVyLmlzVmlzaWJsZSgpID8gUGFuZWxUb2dnbGVyLmdldFBhbmVsU2l6ZSgpWzBdIDogMDtcblxuICAgICAgICByZXR1cm4gd2luZG93LmlubmVyV2lkdGggLSByaWdodFBhbmVsV2lkdGg7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBDaGFuZ2VzIHRoZSBzdHlsZSBjbGFzcyBvZiB0aGUgZWxlbWVudCBnaXZlbiBieSBpZC5cbiAgICAgKi9cbiAgICBidXR0b25DbGljazogZnVuY3Rpb24oaWQsIGNsYXNzbmFtZSkge1xuICAgICAgICAkKGlkKS50b2dnbGVDbGFzcyhjbGFzc25hbWUpOyAvLyBhZGQgdGhlIGNsYXNzIHRvIHRoZSBjbGlja2VkIGVsZW1lbnRcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHRleHQgd2lkdGggZm9yIHRoZSBnaXZlbiBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGVsIHRoZSBlbGVtZW50XG4gICAgICovXG4gICAgZ2V0VGV4dFdpZHRoOiBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgcmV0dXJuIChlbC5jbGllbnRXaWR0aCArIDEpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSB0ZXh0IGhlaWdodCBmb3IgdGhlIGdpdmVuIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZWwgdGhlIGVsZW1lbnRcbiAgICAgKi9cbiAgICBnZXRUZXh0SGVpZ2h0OiBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgcmV0dXJuIChlbC5jbGllbnRIZWlnaHQgKyAxKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUGxheXMgdGhlIHNvdW5kIGdpdmVuIGJ5IGlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIHRoZSBpZGVudGlmaWVyIG9mIHRoZSBhdWRpbyBlbGVtZW50LlxuICAgICAqL1xuICAgIHBsYXlTb3VuZE5vdGlmaWNhdGlvbjogZnVuY3Rpb24gKGlkKSB7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKS5wbGF5KCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEVzY2FwZXMgdGhlIGdpdmVuIHRleHQuXG4gICAgICovXG4gICAgZXNjYXBlSHRtbDogZnVuY3Rpb24gKHVuc2FmZVRleHQpIHtcbiAgICAgICAgcmV0dXJuICQoJzxkaXYvPicpLnRleHQodW5zYWZlVGV4dCkuaHRtbCgpO1xuICAgIH0sXG5cbiAgICBpbWFnZVRvR3JheVNjYWxlOiBmdW5jdGlvbiAoY2FudmFzKSB7XG4gICAgICAgIHZhciBjb250ZXh0ID0gY2FudmFzLmdldENvbnRleHQoJzJkJyk7XG4gICAgICAgIHZhciBpbWdEYXRhID0gY29udGV4dC5nZXRJbWFnZURhdGEoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KTtcbiAgICAgICAgdmFyIHBpeGVscyAgPSBpbWdEYXRhLmRhdGE7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIG4gPSBwaXhlbHMubGVuZ3RoOyBpIDwgbjsgaSArPSA0KSB7XG4gICAgICAgICAgICB2YXIgZ3JheXNjYWxlXG4gICAgICAgICAgICAgICAgPSBwaXhlbHNbaV0gKiAuMyArIHBpeGVsc1tpKzFdICogLjU5ICsgcGl4ZWxzW2krMl0gKiAuMTE7XG4gICAgICAgICAgICBwaXhlbHNbaSAgXSA9IGdyYXlzY2FsZTsgICAgICAgIC8vIHJlZFxuICAgICAgICAgICAgcGl4ZWxzW2krMV0gPSBncmF5c2NhbGU7ICAgICAgICAvLyBncmVlblxuICAgICAgICAgICAgcGl4ZWxzW2krMl0gPSBncmF5c2NhbGU7ICAgICAgICAvLyBibHVlXG4gICAgICAgICAgICAvLyBwaXhlbHNbaSszXSAgICAgICAgICAgICAgaXMgYWxwaGFcbiAgICAgICAgfVxuICAgICAgICAvLyByZWRyYXcgdGhlIGltYWdlIGluIGJsYWNrICYgd2hpdGVcbiAgICAgICAgY29udGV4dC5wdXRJbWFnZURhdGEoaW1nRGF0YSwgMCwgMCk7XG4gICAgfSxcblxuICAgIHNldFRvb2x0aXA6IGZ1bmN0aW9uIChlbGVtZW50LCBrZXksIHBvc2l0aW9uKSB7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS1pMThuXCIsIFwiW2RhdGEtY29udGVudF1cIiArIGtleSk7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS10b2dnbGVcIiwgXCJwb3BvdmVyXCIpO1xuICAgICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShcImRhdGEtcGxhY2VtZW50XCIsIHBvc2l0aW9uKTtcbiAgICAgICAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJkYXRhLWh0bWxcIiwgdHJ1ZSk7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS1jb250YWluZXJcIiwgXCJib2R5XCIpO1xuICAgIH1cblxuXG59OyIsInZhciBKaXRzaVBvcG92ZXIgPSByZXF1aXJlKFwiLi4vdXRpbC9KaXRzaVBvcG92ZXJcIik7XG5cbi8qKlxuICogQ29uc3RydWN0cyBuZXcgY29ubmVjdGlvbiBpbmRpY2F0b3IuXG4gKiBAcGFyYW0gdmlkZW9Db250YWluZXIgdGhlIHZpZGVvIGNvbnRhaW5lciBhc3NvY2lhdGVkIHdpdGggdGhlIGluZGljYXRvci5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBDb25uZWN0aW9uSW5kaWNhdG9yKHZpZGVvQ29udGFpbmVyLCBqaWQsIFZpZGVvTGF5b3V0KVxue1xuICAgIHRoaXMudmlkZW9Db250YWluZXIgPSB2aWRlb0NvbnRhaW5lcjtcbiAgICB0aGlzLmJhbmR3aWR0aCA9IG51bGw7XG4gICAgdGhpcy5wYWNrZXRMb3NzID0gbnVsbDtcbiAgICB0aGlzLmJpdHJhdGUgPSBudWxsO1xuICAgIHRoaXMuc2hvd01vcmVWYWx1ZSA9IGZhbHNlO1xuICAgIHRoaXMucmVzb2x1dGlvbiA9IG51bGw7XG4gICAgdGhpcy50cmFuc3BvcnQgPSBbXTtcbiAgICB0aGlzLnBvcG92ZXIgPSBudWxsO1xuICAgIHRoaXMuamlkID0gamlkO1xuICAgIHRoaXMuY3JlYXRlKCk7XG4gICAgdGhpcy52aWRlb0xheW91dCA9IFZpZGVvTGF5b3V0O1xufVxuXG4vKipcbiAqIFZhbHVlcyBmb3IgdGhlIGNvbm5lY3Rpb24gcXVhbGl0eVxuICogQHR5cGUge3s5ODogc3RyaW5nLFxuICogICAgICAgICA4MTogc3RyaW5nLFxuICogICAgICAgICA2NDogc3RyaW5nLFxuICogICAgICAgICA0Nzogc3RyaW5nLFxuICogICAgICAgICAzMDogc3RyaW5nLFxuICogICAgICAgICAwOiBzdHJpbmd9fVxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLmNvbm5lY3Rpb25RdWFsaXR5VmFsdWVzID0ge1xuICAgIDk4OiBcIjE4cHhcIiwgLy9mdWxsXG4gICAgODE6IFwiMTVweFwiLC8vNCBiYXJzXG4gICAgNjQ6IFwiMTFweFwiLC8vMyBiYXJzXG4gICAgNDc6IFwiN3B4XCIsLy8yIGJhcnNcbiAgICAzMDogXCIzcHhcIiwvLzEgYmFyXG4gICAgMDogXCIwcHhcIi8vZW1wdHlcbn07XG5cbkNvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0SVAgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKDAsIHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSArIDEsIHZhbHVlLmxlbmd0aCk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFN0cmluZ0Zyb21BcnJheSA9IGZ1bmN0aW9uIChhcnJheSkge1xuICAgIHZhciByZXMgPSBcIlwiO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKylcbiAgICB7XG4gICAgICAgIHJlcyArPSAoaSA9PT0gMD8gXCJcIiA6IFwiLCBcIikgKyBhcnJheVtpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlcztcbn07XG5cbi8qKlxuICogR2VuZXJhdGVzIHRoZSBodG1sIGNvbnRlbnQuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSB0aGUgaHRtbCBjb250ZW50LlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5nZW5lcmF0ZVRleHQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGRvd25sb2FkQml0cmF0ZSwgdXBsb2FkQml0cmF0ZSwgcGFja2V0TG9zcywgcmVzb2x1dGlvbiwgaTtcblxuICAgIHZhciB0cmFuc2xhdGUgPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nO1xuXG4gICAgaWYodGhpcy5iaXRyYXRlID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgZG93bmxvYWRCaXRyYXRlID0gXCJOL0FcIjtcbiAgICAgICAgdXBsb2FkQml0cmF0ZSA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIGRvd25sb2FkQml0cmF0ZSA9XG4gICAgICAgICAgICB0aGlzLmJpdHJhdGUuZG93bmxvYWQ/IHRoaXMuYml0cmF0ZS5kb3dubG9hZCArIFwiIEticHNcIiA6IFwiTi9BXCI7XG4gICAgICAgIHVwbG9hZEJpdHJhdGUgPVxuICAgICAgICAgICAgdGhpcy5iaXRyYXRlLnVwbG9hZD8gdGhpcy5iaXRyYXRlLnVwbG9hZCArIFwiIEticHNcIiA6IFwiTi9BXCI7XG4gICAgfVxuXG4gICAgaWYodGhpcy5wYWNrZXRMb3NzID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgcGFja2V0TG9zcyA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG5cbiAgICAgICAgcGFja2V0TG9zcyA9IFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ncmVlbic+JmRhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgICh0aGlzLnBhY2tldExvc3MuZG93bmxvYWQgIT09IG51bGw/IHRoaXMucGFja2V0TG9zcy5kb3dubG9hZCA6IFwiTi9BXCIpICtcbiAgICAgICAgICAgIFwiJSA8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX29yYW5nZSc+JnVhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgICh0aGlzLnBhY2tldExvc3MudXBsb2FkICE9PSBudWxsPyB0aGlzLnBhY2tldExvc3MudXBsb2FkIDogXCJOL0FcIikgKyBcIiVcIjtcbiAgICB9XG5cbiAgICB2YXIgcmVzb2x1dGlvblZhbHVlID0gbnVsbDtcbiAgICBpZih0aGlzLnJlc29sdXRpb24gJiYgdGhpcy5qaWQgIT0gbnVsbClcbiAgICB7XG4gICAgICAgIHZhciBrZXlzID0gT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKTtcbiAgICAgICAgaWYoa2V5cy5sZW5ndGggPT0gMSlcbiAgICAgICAge1xuICAgICAgICAgICAgZm9yKHZhciBzc3JjIGluIHRoaXMucmVzb2x1dGlvbilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uVmFsdWUgPSB0aGlzLnJlc29sdXRpb25bc3NyY107XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZihrZXlzLmxlbmd0aCA+IDEpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBkaXNwbGF5ZWRTc3JjID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdTU1JDKHRoaXMuamlkKTtcbiAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZSA9IHRoaXMucmVzb2x1dGlvbltkaXNwbGF5ZWRTc3JjXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmKHRoaXMuamlkID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiXCI7XG4gICAgICAgIGlmKHRoaXMucmVzb2x1dGlvbiA9PT0gbnVsbCB8fCAhT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKSB8fFxuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKS5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc29sdXRpb24gPSBcIk4vQVwiO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGZvcihpIGluIHRoaXMucmVzb2x1dGlvbilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uVmFsdWUgPSB0aGlzLnJlc29sdXRpb25baV07XG4gICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlLmhlaWdodCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uICs9IChyZXNvbHV0aW9uID09PSBcIlwiPyBcIlwiIDogXCIsIFwiKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoICsgXCJ4XCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgfVxuICAgIGVsc2UgaWYoIXJlc29sdXRpb25WYWx1ZSB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLmhlaWdodCB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHJlc29sdXRpb24gPSByZXNvbHV0aW9uVmFsdWUud2lkdGggKyBcInhcIiArIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgfVxuXG4gICAgdmFyIHJlc3VsdCA9IFwiPHRhYmxlIHN0eWxlPSd3aWR0aDoxMDAlJz5cIiArXG4gICAgICAgIFwiPHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IuYml0cmF0ZSc+XCIgK1xuICAgICAgICB0cmFuc2xhdGUoXCJjb25uZWN0aW9uaW5kaWNhdG9yLmJpdHJhdGVcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ncmVlbic+JmRhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgZG93bmxvYWRCaXRyYXRlICsgXCIgPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9vcmFuZ2UnPiZ1YXJyOzwvc3Bhbj5cIiArXG4gICAgICAgIHVwbG9hZEJpdHJhdGUgKyBcIjwvdGQ+XCIgK1xuICAgICAgICBcIjwvdHI+PHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IucGFja2V0bG9zcyc+XCIgK1xuICAgICAgICB0cmFuc2xhdGUoXCJjb25uZWN0aW9uaW5kaWNhdG9yLnBhY2tldGxvc3NcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgXCI8dGQ+XCIgKyBwYWNrZXRMb3NzICArIFwiPC90ZD5cIiArXG4gICAgICAgIFwiPC90cj48dHI+XCIgK1xuICAgICAgICBcIjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5yZXNvbHV0aW9uJz5cIiArXG4gICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IucmVzb2x1dGlvblwiKSArIFwiPC9zcGFuPjwvdGQ+XCIgK1xuICAgICAgICBcIjx0ZD5cIiArIHJlc29sdXRpb24gKyBcIjwvdGQ+PC90cj48L3RhYmxlPlwiO1xuXG4gICAgaWYodGhpcy52aWRlb0NvbnRhaW5lci5pZCA9PSBcImxvY2FsVmlkZW9Db250YWluZXJcIikge1xuICAgICAgICByZXN1bHQgKz0gXCI8ZGl2IGNsYXNzPVxcXCJqaXRzaXBvcG92ZXJfc2hvd21vcmVcXFwiIFwiICtcbiAgICAgICAgICAgIFwib25jbGljayA9IFxcXCJBUFAuVUkuY29ubmVjdGlvbkluZGljYXRvclNob3dNb3JlKCdcIiArXG4gICAgICAgICAgICB0aGlzLnZpZGVvQ29udGFpbmVyLmlkICsgXCInKVxcXCIgIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5cIiArXG4gICAgICAgICAgICAgICAgKHRoaXMuc2hvd01vcmVWYWx1ZSA/IFwibGVzc1wiIDogXCJtb3JlXCIpICsgXCInPlwiICtcbiAgICAgICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IuXCIgKyAodGhpcy5zaG93TW9yZVZhbHVlID8gXCJsZXNzXCIgOiBcIm1vcmVcIikpICtcbiAgICAgICAgICAgIFwiPC9kaXY+PGJyIC8+XCI7XG4gICAgfVxuXG4gICAgaWYodGhpcy5zaG93TW9yZVZhbHVlKVxuICAgIHtcbiAgICAgICAgdmFyIGRvd25sb2FkQmFuZHdpZHRoLCB1cGxvYWRCYW5kd2lkdGgsIHRyYW5zcG9ydDtcbiAgICAgICAgaWYodGhpcy5iYW5kd2lkdGggPT09IG51bGwpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGRvd25sb2FkQmFuZHdpZHRoID0gXCJOL0FcIjtcbiAgICAgICAgICAgIHVwbG9hZEJhbmR3aWR0aCA9IFwiTi9BXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBkb3dubG9hZEJhbmR3aWR0aCA9IHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkICsgXCIgS2Jwc1wiIDpcbiAgICAgICAgICAgICAgICBcIk4vQVwiO1xuICAgICAgICAgICAgdXBsb2FkQmFuZHdpZHRoID0gdGhpcy5iYW5kd2lkdGgudXBsb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLnVwbG9hZCArIFwiIEticHNcIiA6XG4gICAgICAgICAgICAgICAgXCJOL0FcIjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCF0aGlzLnRyYW5zcG9ydCB8fCB0aGlzLnRyYW5zcG9ydC5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRyYW5zcG9ydCA9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIFwiICtcbiAgICAgICAgICAgICAgICBcImRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5hZGRyZXNzJz5cIiArXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlKFwiY29ubmVjdGlvbmluZGljYXRvci5hZGRyZXNzXCIpICsgXCI8L3NwYW4+PC90ZD5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+IE4vQTwvdGQ+PC90cj5cIjtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBkYXRhID0ge3JlbW90ZUlQOiBbXSwgbG9jYWxJUDpbXSwgcmVtb3RlUG9ydDpbXSwgbG9jYWxQb3J0OltdfTtcbiAgICAgICAgICAgIGZvcihpID0gMDsgaSA8IHRoaXMudHJhbnNwb3J0Lmxlbmd0aDsgaSsrKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHZhciBpcCA9ICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldElQKHRoaXMudHJhbnNwb3J0W2ldLmlwKTtcbiAgICAgICAgICAgICAgICB2YXIgcG9ydCA9IENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0UG9ydCh0aGlzLnRyYW5zcG9ydFtpXS5pcCk7XG4gICAgICAgICAgICAgICAgdmFyIGxvY2FsSVAgPVxuICAgICAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldElQKHRoaXMudHJhbnNwb3J0W2ldLmxvY2FsaXApO1xuICAgICAgICAgICAgICAgIHZhciBsb2NhbFBvcnQgPVxuICAgICAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQodGhpcy50cmFuc3BvcnRbaV0ubG9jYWxpcCk7XG4gICAgICAgICAgICAgICAgaWYoZGF0YS5yZW1vdGVJUC5pbmRleE9mKGlwKSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEucmVtb3RlSVAucHVzaChpcCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYoZGF0YS5yZW1vdGVQb3J0LmluZGV4T2YocG9ydCkgPT0gLTEpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnJlbW90ZVBvcnQucHVzaChwb3J0KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZihkYXRhLmxvY2FsSVAuaW5kZXhPZihsb2NhbElQKSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEubG9jYWxJUC5wdXNoKGxvY2FsSVApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmKGRhdGEubG9jYWxQb3J0LmluZGV4T2YobG9jYWxQb3J0KSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEubG9jYWxQb3J0LnB1c2gobG9jYWxQb3J0KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGxvY2FsX2FkZHJlc3Nfa2V5ID0gXCJjb25uZWN0aW9uaW5kaWNhdG9yLmxvY2FsYWRkcmVzc1wiO1xuICAgICAgICAgICAgdmFyIHJlbW90ZV9hZGRyZXNzX2tleSA9IFwiY29ubmVjdGlvbmluZGljYXRvci5yZW1vdGVhZGRyZXNzXCI7XG4gICAgICAgICAgICB2YXIgbG9jYWxUcmFuc3BvcnQgPVxuICAgICAgICAgICAgICAgIFwiPHRyPjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nXCIgK1xuICAgICAgICAgICAgICAgIGxvY2FsX2FkZHJlc3Nfa2V5ICtcIicgZGF0YS1pMThuLW9wdGlvbnM9J1wiICtcbiAgICAgICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe2NvdW50OiBkYXRhLmxvY2FsSVAubGVuZ3RofSkgKyBcIic+XCIgK1xuICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGUobG9jYWxfYWRkcmVzc19rZXksIHtjb3VudDogZGF0YS5sb2NhbElQLmxlbmd0aH0pICtcbiAgICAgICAgICAgICAgICAgICAgXCI8L3NwYW4+PC90ZD48dGQ+IFwiICtcbiAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldFN0cmluZ0Zyb21BcnJheShkYXRhLmxvY2FsSVApICtcbiAgICAgICAgICAgICAgICBcIjwvdGQ+PC90cj5cIjtcbiAgICAgICAgICAgIHRyYW5zcG9ydCA9XG4gICAgICAgICAgICAgICAgXCI8dHI+PHRkPjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZScgZGF0YS1pMThuPSdcIiArXG4gICAgICAgICAgICAgICAgcmVtb3RlX2FkZHJlc3Nfa2V5ICsgXCInIGRhdGEtaTE4bi1vcHRpb25zPSdcIiArXG4gICAgICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHtjb3VudDogZGF0YS5yZW1vdGVJUC5sZW5ndGh9KSArIFwiJz5cIiArXG4gICAgICAgICAgICAgICAgICAgIHRyYW5zbGF0ZShyZW1vdGVfYWRkcmVzc19rZXksXG4gICAgICAgICAgICAgICAgICAgICAgICB7Y291bnQ6IGRhdGEucmVtb3RlSVAubGVuZ3RofSkgK1xuICAgICAgICAgICAgICAgICAgICBcIjwvc3Bhbj48L3RkPjx0ZD4gXCIgK1xuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0U3RyaW5nRnJvbUFycmF5KGRhdGEucmVtb3RlSVApICtcbiAgICAgICAgICAgICAgICBcIjwvdGQ+PC90cj5cIjtcblxuICAgICAgICAgICAgdmFyIGtleV9yZW1vdGUgPSBcImNvbm5lY3Rpb25pbmRpY2F0b3IucmVtb3RlcG9ydFwiLFxuICAgICAgICAgICAgICAgIGtleV9sb2NhbCA9IFwiY29ubmVjdGlvbmluZGljYXRvci5sb2NhbHBvcnRcIjtcblxuICAgICAgICAgICAgdHJhbnNwb3J0ICs9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD5cIiArXG4gICAgICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nXCIgKyBrZXlfcmVtb3RlICtcbiAgICAgICAgICAgICAgICBcIicgZGF0YS1pMThuLW9wdGlvbnM9J1wiICtcbiAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeSh7Y291bnQ6IHRoaXMudHJhbnNwb3J0Lmxlbmd0aH0pICsgXCInPlwiICtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGUoa2V5X3JlbW90ZSwge2NvdW50OiB0aGlzLnRyYW5zcG9ydC5sZW5ndGh9KSArXG4gICAgICAgICAgICAgICAgXCI8L3NwYW4+PC90ZD48dGQ+XCI7XG4gICAgICAgICAgICBsb2NhbFRyYW5zcG9ydCArPSBcIjx0cj5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgICAgIFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J1wiICsga2V5X2xvY2FsICtcbiAgICAgICAgICAgICAgICBcIicgZGF0YS1pMThuLW9wdGlvbnM9J1wiICtcbiAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeSh7Y291bnQ6IHRoaXMudHJhbnNwb3J0Lmxlbmd0aH0pICsgXCInPlwiICtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGUoa2V5X2xvY2FsLCB7Y291bnQ6IHRoaXMudHJhbnNwb3J0Lmxlbmd0aH0pICtcbiAgICAgICAgICAgICAgICBcIjwvc3Bhbj48L3RkPjx0ZD5cIjtcblxuICAgICAgICAgICAgdHJhbnNwb3J0ICs9XG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRTdHJpbmdGcm9tQXJyYXkoZGF0YS5yZW1vdGVQb3J0KTtcbiAgICAgICAgICAgIGxvY2FsVHJhbnNwb3J0ICs9XG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRTdHJpbmdGcm9tQXJyYXkoZGF0YS5sb2NhbFBvcnQpO1xuICAgICAgICAgICAgdHJhbnNwb3J0ICs9IFwiPC90ZD48L3RyPlwiO1xuICAgICAgICAgICAgdHJhbnNwb3J0ICs9IGxvY2FsVHJhbnNwb3J0ICsgXCI8L3RkPjwvdHI+XCI7XG4gICAgICAgICAgICB0cmFuc3BvcnQgKz1cIjx0cj5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IudHJhbnNwb3J0Jz5cIiArXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlKFwiY29ubmVjdGlvbmluZGljYXRvci50cmFuc3BvcnRcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD5cIiArIHRoaXMudHJhbnNwb3J0WzBdLnR5cGUgKyBcIjwvdGQ+PC90cj5cIjtcblxuICAgICAgICB9XG5cbiAgICAgICAgcmVzdWx0ICs9IFwiPHRhYmxlICBzdHlsZT0nd2lkdGg6MTAwJSc+XCIgK1xuICAgICAgICAgICAgXCI8dHI+XCIgK1xuICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5iYW5kd2lkdGgnPlwiICtcbiAgICAgICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IuYmFuZHdpZHRoXCIpICsgXCI8L3NwYW4+XCIgK1xuICAgICAgICAgICAgXCI8L3RkPjx0ZD5cIiArXG4gICAgICAgICAgICBcIjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfZ3JlZW4nPiZkYXJyOzwvc3Bhbj5cIiArXG4gICAgICAgICAgICBkb3dubG9hZEJhbmR3aWR0aCArXG4gICAgICAgICAgICBcIiA8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX29yYW5nZSc+JnVhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgIHVwbG9hZEJhbmR3aWR0aCArIFwiPC90ZD48L3RyPlwiO1xuXG4gICAgICAgIHJlc3VsdCArPSB0cmFuc3BvcnQgKyBcIjwvdGFibGU+XCI7XG5cbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyoqXG4gKiBTaG93cyBvciBoaWRlIHRoZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uLlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5zaG93TW9yZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnNob3dNb3JlVmFsdWUgPSAhdGhpcy5zaG93TW9yZVZhbHVlO1xuICAgIHRoaXMudXBkYXRlUG9wb3ZlckRhdGEoKTtcbn07XG5cblxuZnVuY3Rpb24gY3JlYXRlSWNvbihjbGFzc2VzKVxue1xuICAgIHZhciBpY29uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIik7XG4gICAgZm9yKHZhciBpIGluIGNsYXNzZXMpXG4gICAge1xuICAgICAgICBpY29uLmNsYXNzTGlzdC5hZGQoY2xhc3Nlc1tpXSk7XG4gICAgfVxuICAgIGljb24uYXBwZW5kQ2hpbGQoXG4gICAgICAgIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJpXCIpKS5jbGFzc0xpc3QuYWRkKFwiaWNvbi1jb25uZWN0aW9uXCIpO1xuICAgIHJldHVybiBpY29uO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGluZGljYXRvclxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5jcmVhdGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuY2xhc3NOYW1lID0gXCJjb25uZWN0aW9uaW5kaWNhdG9yXCI7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICB0aGlzLnZpZGVvQ29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lcik7XG4gICAgdGhpcy5wb3BvdmVyID0gbmV3IEppdHNpUG9wb3ZlcihcbiAgICAgICAgJChcIiNcIiArIHRoaXMudmlkZW9Db250YWluZXIuaWQgKyBcIiA+IC5jb25uZWN0aW9uaW5kaWNhdG9yXCIpLFxuICAgICAgICB7Y29udGVudDogXCI8ZGl2IGNsYXNzPVxcXCJjb25uZWN0aW9uX2luZm9cXFwiIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5uYSc+XCIgK1xuICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcImNvbm5lY3Rpb25pbmRpY2F0b3IubmFcIikgKyBcIjwvZGl2PlwiLFxuICAgICAgICAgICAgc2tpbjogXCJibGFja1wifSk7XG5cbiAgICB0aGlzLmVtcHR5SWNvbiA9IHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5hcHBlbmRDaGlsZChcbiAgICAgICAgY3JlYXRlSWNvbihbXCJjb25uZWN0aW9uXCIsIFwiY29ubmVjdGlvbl9lbXB0eVwiXSkpO1xuICAgIHRoaXMuZnVsbEljb24gPSB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuYXBwZW5kQ2hpbGQoXG4gICAgICAgIGNyZWF0ZUljb24oW1wiY29ubmVjdGlvblwiLCBcImNvbm5lY3Rpb25fZnVsbFwiXSkpO1xuXG59O1xuXG4vKipcbiAqIFJlbW92ZXMgdGhlIGluZGljYXRvclxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5yZW1vdmUgPSBmdW5jdGlvbigpXG57XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnJlbW92ZSgpO1xuICAgIHRoaXMucG9wb3Zlci5mb3JjZUhpZGUoKTtcblxufTtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBkYXRhIG9mIHRoZSBpbmRpY2F0b3JcbiAqIEBwYXJhbSBwZXJjZW50IHRoZSBwZXJjZW50IG9mIGNvbm5lY3Rpb24gcXVhbGl0eVxuICogQHBhcmFtIG9iamVjdCB0aGUgc3RhdGlzdGljcyBkYXRhLlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS51cGRhdGVDb25uZWN0aW9uUXVhbGl0eSA9XG5mdW5jdGlvbiAocGVyY2VudCwgb2JqZWN0KSB7XG5cbiAgICBpZihwZXJjZW50ID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICAgICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIGlmKHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID09IFwibm9uZVwiKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwiYmxvY2tcIjtcbiAgICAgICAgICAgIHRoaXMudmlkZW9MYXlvdXQudXBkYXRlTXV0ZVBvc2l0aW9uKHRoaXMudmlkZW9Db250YWluZXIuaWQpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRoaXMuYmFuZHdpZHRoID0gb2JqZWN0LmJhbmR3aWR0aDtcbiAgICB0aGlzLmJpdHJhdGUgPSBvYmplY3QuYml0cmF0ZTtcbiAgICB0aGlzLnBhY2tldExvc3MgPSBvYmplY3QucGFja2V0TG9zcztcbiAgICB0aGlzLnRyYW5zcG9ydCA9IG9iamVjdC50cmFuc3BvcnQ7XG4gICAgaWYob2JqZWN0LnJlc29sdXRpb24pXG4gICAge1xuICAgICAgICB0aGlzLnJlc29sdXRpb24gPSBvYmplY3QucmVzb2x1dGlvbjtcbiAgICB9XG4gICAgZm9yKHZhciBxdWFsaXR5IGluIENvbm5lY3Rpb25JbmRpY2F0b3IuY29ubmVjdGlvblF1YWxpdHlWYWx1ZXMpXG4gICAge1xuICAgICAgICBpZihwZXJjZW50ID49IHF1YWxpdHkpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuZnVsbEljb24uc3R5bGUud2lkdGggPVxuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuY29ubmVjdGlvblF1YWxpdHlWYWx1ZXNbcXVhbGl0eV07XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhpcy51cGRhdGVQb3BvdmVyRGF0YSgpO1xufTtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSByZXNvbHV0aW9uXG4gKiBAcGFyYW0gcmVzb2x1dGlvbiB0aGUgbmV3IHJlc29sdXRpb25cbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUudXBkYXRlUmVzb2x1dGlvbiA9IGZ1bmN0aW9uIChyZXNvbHV0aW9uKSB7XG4gICAgdGhpcy5yZXNvbHV0aW9uID0gcmVzb2x1dGlvbjtcbiAgICB0aGlzLnVwZGF0ZVBvcG92ZXJEYXRhKCk7XG59O1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGNvbnRlbnQgb2YgdGhlIHBvcG92ZXJcbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUudXBkYXRlUG9wb3ZlckRhdGEgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5wb3BvdmVyLnVwZGF0ZUNvbnRlbnQoXG4gICAgICAgIFwiPGRpdiBjbGFzcz1cXFwiY29ubmVjdGlvbl9pbmZvXFxcIj5cIiArIHRoaXMuZ2VuZXJhdGVUZXh0KCkgKyBcIjwvZGl2PlwiKTtcbiAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlRWxlbWVudCgkKFwiLmNvbm5lY3Rpb25faW5mb1wiKSk7XG59O1xuXG4vKipcbiAqIEhpZGVzIHRoZSBwb3BvdmVyXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLmhpZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xufTtcblxuLyoqXG4gKiBIaWRlcyB0aGUgaW5kaWNhdG9yXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLmhpZGVJbmRpY2F0b3IgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICBpZih0aGlzLnBvcG92ZXIpXG4gICAgICAgIHRoaXMucG9wb3Zlci5mb3JjZUhpZGUoKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29ubmVjdGlvbkluZGljYXRvcjsiLCJ2YXIgQXVkaW9MZXZlbHMgPSByZXF1aXJlKFwiLi4vYXVkaW9fbGV2ZWxzL0F1ZGlvTGV2ZWxzXCIpO1xudmFyIEF2YXRhciA9IHJlcXVpcmUoXCIuLi9hdmF0YXIvQXZhdGFyXCIpO1xudmFyIENoYXQgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL2NoYXQvQ2hhdFwiKTtcbnZhciBDb250YWN0TGlzdCA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvY29udGFjdGxpc3QvQ29udGFjdExpc3RcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uL3V0aWwvVUlVdGlsXCIpO1xudmFyIENvbm5lY3Rpb25JbmRpY2F0b3IgPSByZXF1aXJlKFwiLi9Db25uZWN0aW9uSW5kaWNhdG9yXCIpO1xudmFyIE5pY2tuYW1lSGFuZGxlciA9IHJlcXVpcmUoXCIuLi91dGlsL05pY2tuYW1lSGFuZGxlclwiKTtcbnZhciBNZWRpYVN0cmVhbVR5cGUgPSByZXF1aXJlKFwiLi4vLi4vLi4vc2VydmljZS9SVEMvTWVkaWFTdHJlYW1UeXBlc1wiKTtcbnZhciBVSUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi8uLi9zZXJ2aWNlL1VJL1VJRXZlbnRzXCIpO1xuXG52YXIgY3VycmVudERvbWluYW50U3BlYWtlciA9IG51bGw7XG52YXIgbGFzdE5Db3VudCA9IGNvbmZpZy5jaGFubmVsTGFzdE47XG52YXIgbG9jYWxMYXN0TkNvdW50ID0gY29uZmlnLmNoYW5uZWxMYXN0TjtcbnZhciBsb2NhbExhc3ROU2V0ID0gW107XG52YXIgbGFzdE5FbmRwb2ludHNDYWNoZSA9IFtdO1xudmFyIGxhc3ROUGlja3VwSmlkID0gbnVsbDtcbnZhciBsYXJnZVZpZGVvU3RhdGUgPSB7XG4gICAgdXBkYXRlSW5Qcm9ncmVzczogZmFsc2UsXG4gICAgbmV3U3JjOiAnJ1xufTtcblxudmFyIGV2ZW50RW1pdHRlciA9IG51bGw7XG5cbi8qKlxuICogQ3VycmVudGx5IGZvY3VzZWQgdmlkZW8gXCJzcmNcIihkaXNwbGF5ZWQgaW4gbGFyZ2UgdmlkZW8pLlxuICogQHR5cGUge1N0cmluZ31cbiAqL1xudmFyIGZvY3VzZWRWaWRlb0luZm8gPSBudWxsO1xuXG4vKipcbiAqIEluZGljYXRlcyBpZiB3ZSBoYXZlIG11dGVkIG91ciBhdWRpbyBiZWZvcmUgdGhlIGNvbmZlcmVuY2UgaGFzIHN0YXJ0ZWQuXG4gKiBAdHlwZSB7Ym9vbGVhbn1cbiAqL1xudmFyIHByZU11dGVkID0gZmFsc2U7XG5cbnZhciBtdXRlZEF1ZGlvcyA9IHt9O1xuXG52YXIgZmxpcFhMb2NhbFZpZGVvID0gdHJ1ZTtcbnZhciBjdXJyZW50VmlkZW9XaWR0aCA9IG51bGw7XG52YXIgY3VycmVudFZpZGVvSGVpZ2h0ID0gbnVsbDtcblxudmFyIGxvY2FsVmlkZW9TcmMgPSBudWxsO1xuXG5mdW5jdGlvbiB2aWRlb2FjdGl2ZSggdmlkZW9lbGVtKSB7XG4gICAgaWYgKHZpZGVvZWxlbS5hdHRyKCdpZCcpLmluZGV4T2YoJ21peGVkbXNsYWJlbCcpID09PSAtMSkge1xuICAgICAgICAvLyBpZ25vcmUgbWl4ZWRtc2xhYmVsYTAgYW5kIHYwXG5cbiAgICAgICAgdmlkZW9lbGVtLnNob3coKTtcbiAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuXG4gICAgICAgIHZhciB2aWRlb1BhcmVudCA9IHZpZGVvZWxlbS5wYXJlbnQoKTtcbiAgICAgICAgdmFyIHBhcmVudFJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgaWYgKHZpZGVvUGFyZW50KVxuICAgICAgICAgICAgcGFyZW50UmVzb3VyY2VKaWRcbiAgICAgICAgICAgICAgICA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZCh2aWRlb1BhcmVudFswXSk7XG5cbiAgICAgICAgLy8gVXBkYXRlIHRoZSBsYXJnZSB2aWRlbyB0byB0aGUgbGFzdCBhZGRlZCB2aWRlbyBvbmx5IGlmIHRoZXJlJ3Mgbm9cbiAgICAgICAgLy8gY3VycmVudCBkb21pbmFudCwgZm9jdXNlZCBzcGVha2VyIG9yIHByZXppIHBsYXlpbmcgb3IgdXBkYXRlIGl0IHRvXG4gICAgICAgIC8vIHRoZSBjdXJyZW50IGRvbWluYW50IHNwZWFrZXIuXG4gICAgICAgIGlmICgoIWZvY3VzZWRWaWRlb0luZm8gJiZcbiAgICAgICAgICAgICFWaWRlb0xheW91dC5nZXREb21pbmFudFNwZWFrZXJSZXNvdXJjZUppZCgpICYmXG4gICAgICAgICAgICAhcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKSB8fFxuICAgICAgICAgICAgKHBhcmVudFJlc291cmNlSmlkICYmXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0RG9taW5hbnRTcGVha2VyUmVzb3VyY2VKaWQoKSA9PT0gcGFyZW50UmVzb3VyY2VKaWQpKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKFxuICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmModmlkZW9lbGVtWzBdKSxcbiAgICAgICAgICAgICAgICAxLFxuICAgICAgICAgICAgICAgIHBhcmVudFJlc291cmNlSmlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFZpZGVvTGF5b3V0LnNob3dNb2RlcmF0b3JJbmRpY2F0b3IoKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWxlY3Rvciwgc3NyYywgc3RyZWFtLCBqaWQpIHtcbiAgICAvLyBYWFgoZ3ApIHNvLCBldmVyeSBjYWxsIHRvIHRoaXMgZnVuY3Rpb24gaXMgKmFsd2F5cyogcHJlY2VkZWQgYnkgYSBjYWxsXG4gICAgLy8gdG8gdGhlIFJUQy5hdHRhY2hNZWRpYVN0cmVhbSgpIGZ1bmN0aW9uIGJ1dCB0aGF0IGNhbGwgaXMgKm5vdCogZm9sbG93ZWRcbiAgICAvLyBieSBhbiB1cGRhdGUgdG8gdGhlIHZpZGVvU3JjVG9Tc3JjIG1hcCFcbiAgICAvL1xuICAgIC8vIFRoZSBhYm92ZSB3YXkgb2YgZG9pbmcgdGhpbmdzIHJlc3VsdHMgaW4gdmlkZW8gU1JDcyB0aGF0IGRvbid0IGNvcnJlc3BvbmRcbiAgICAvLyB0byBhbnkgU1NSQyBmb3IgYSBzaG9ydCBwZXJpb2Qgb2YgdGltZSAodG8gYmUgbW9yZSBwcmVjaXNlLCBmb3IgYXMgbG9uZ1xuICAgIC8vIHRoZSB3YWl0Rm9yUmVtb3RlVmlkZW8gdGFrZXMgdG8gY29tcGxldGUpLiBUaGlzIGNhdXNlcyBwcm9ibGVtcyAoc2VlXG4gICAgLy8gYmVsbG93KS5cbiAgICAvL1xuICAgIC8vIEknbSB3b25kZXJpbmcgd2h5IHdlIG5lZWQgdG8gZG8gdGhhdDsgaS5lLiB3aHkgY2FsbCBSVEMuYXR0YWNoTWVkaWFTdHJlYW0oKVxuICAgIC8vIGEgc2Vjb25kIHRpbWUgaW4gaGVyZSBhbmQgb25seSB0aGVuIHVwZGF0ZSB0aGUgdmlkZW9TcmNUb1NzcmMgbWFwPyBXaHlcbiAgICAvLyBub3Qgc2ltcGx5IHVwZGF0ZSB0aGUgdmlkZW9TcmNUb1NzcmMgbWFwIHdoZW4gdGhlIFJUQy5hdHRhY2hNZWRpYVN0cmVhbSgpXG4gICAgLy8gaXMgY2FsbGVkIHRoZSBmaXJzdCB0aW1lPyBJIGFjdHVhbGx5IGRvIHRoYXQgaW4gdGhlIGxhc3ROIGNoYW5nZWQgZXZlbnRcbiAgICAvLyBoYW5kbGVyIGJlY2F1c2UgdGhlIFwib3JwaGFuXCIgdmlkZW8gU1JDIGlzIGNhdXNpbmcgdHJvdWJsZXMgdGhlcmUuIFRoZVxuICAgIC8vIHB1cnBvc2Ugb2YgdGhpcyBtZXRob2Qgd291bGQgdGhlbiBiZSB0byBmaXJlIHRoZSBcInZpZGVvYWN0aXZlLmppbmdsZVwiLlxuICAgIC8vXG4gICAgLy8gRm9vZCBmb3IgdGhvdWdoIEkgZ3Vlc3MgOi0pXG5cbiAgICBpZiAoc2VsZWN0b3IucmVtb3ZlZCB8fCAhc2VsZWN0b3IucGFyZW50KCkuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJNZWRpYSByZW1vdmVkIGJlZm9yZSBoYWQgc3RhcnRlZFwiLCBzZWxlY3Rvcik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoc3RyZWFtLmlkID09PSAnbWl4ZWRtc2xhYmVsJykgcmV0dXJuO1xuXG4gICAgaWYgKHNlbGVjdG9yWzBdLmN1cnJlbnRUaW1lID4gMCkge1xuICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBBUFAuc2ltdWxjYXN0LmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKHN0cmVhbSk7XG4gICAgICAgIEFQUC5SVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsZWN0b3IsIHZpZGVvU3RyZWFtKTsgLy8gRklYTUU6IHdoeSBkbyBpIGhhdmUgdG8gZG8gdGhpcyBmb3IgRkY/XG4gICAgICAgIHZpZGVvYWN0aXZlKHNlbGVjdG9yKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWxlY3Rvciwgc3NyYywgc3RyZWFtLCBqaWQpO1xuICAgICAgICB9LCAyNTApO1xuICAgIH1cbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIHRoZSB2aWRlbyBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBpbmRlbnRzLFxuICogc28gdGhhdCBpZiBmaXRzIGl0cyBwYXJlbnQuXG4gKlxuICogQHJldHVybiBhbiBhcnJheSB3aXRoIDIgZWxlbWVudHMsIHRoZSBob3Jpem9udGFsIGluZGVudCBhbmQgdGhlIHZlcnRpY2FsXG4gKiBpbmRlbnRcbiAqL1xuZnVuY3Rpb24gZ2V0Q2FtZXJhVmlkZW9Qb3NpdGlvbih2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG4gICAgLy8gUGFyZW50IGhlaWdodCBpc24ndCBjb21wbGV0ZWx5IGNhbGN1bGF0ZWQgd2hlbiB3ZSBwb3NpdGlvbiB0aGUgdmlkZW8gaW5cbiAgICAvLyBmdWxsIHNjcmVlbiBtb2RlIGFuZCB0aGlzIGlzIHdoeSB3ZSB1c2UgdGhlIHNjcmVlbiBoZWlnaHQgaW4gdGhpcyBjYXNlLlxuICAgIC8vIE5lZWQgdG8gdGhpbmsgaXQgZnVydGhlciBhdCBzb21lIHBvaW50IGFuZCBpbXBsZW1lbnQgaXQgcHJvcGVybHkuXG4gICAgdmFyIGlzRnVsbFNjcmVlbiA9IGRvY3VtZW50LmZ1bGxTY3JlZW4gfHxcbiAgICAgICAgZG9jdW1lbnQubW96RnVsbFNjcmVlbiB8fFxuICAgICAgICBkb2N1bWVudC53ZWJraXRJc0Z1bGxTY3JlZW47XG4gICAgaWYgKGlzRnVsbFNjcmVlbilcbiAgICAgICAgdmlkZW9TcGFjZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcblxuICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gKHZpZGVvU3BhY2VXaWR0aCAtIHZpZGVvV2lkdGgpIC8gMjtcbiAgICB2YXIgdmVydGljYWxJbmRlbnQgPSAodmlkZW9TcGFjZUhlaWdodCAtIHZpZGVvSGVpZ2h0KSAvIDI7XG5cbiAgICByZXR1cm4gW2hvcml6b250YWxJbmRlbnQsIHZlcnRpY2FsSW5kZW50XTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIHRoZSB2aWRlbyBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBpbmRlbnRzLlxuICogQ2VudGVycyBob3Jpem9udGFsbHkgYW5kIHRvcCBhbGlnbnMgdmVydGljYWxseS5cbiAqXG4gKiBAcmV0dXJuIGFuIGFycmF5IHdpdGggMiBlbGVtZW50cywgdGhlIGhvcml6b250YWwgaW5kZW50IGFuZCB0aGUgdmVydGljYWxcbiAqIGluZGVudFxuICovXG5mdW5jdGlvbiBnZXREZXNrdG9wVmlkZW9Qb3NpdGlvbih2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG5cbiAgICB2YXIgaG9yaXpvbnRhbEluZGVudCA9ICh2aWRlb1NwYWNlV2lkdGggLSB2aWRlb1dpZHRoKSAvIDI7XG5cbiAgICB2YXIgdmVydGljYWxJbmRlbnQgPSAwOy8vIFRvcCBhbGlnbmVkXG5cbiAgICByZXR1cm4gW2hvcml6b250YWxJbmRlbnQsIHZlcnRpY2FsSW5kZW50XTtcbn1cblxuXG4vKipcbiAqIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHZpZGVvIGRpbWVuc2lvbnMsIHNvIHRoYXQgaXQgY292ZXJzIHRoZSBzY3JlZW4uXG4gKiBJdCBsZWF2ZXMgbm8gZW1wdHkgYXJlYXMsIGJ1dCBzb21lIHBhcnRzIG9mIHRoZSB2aWRlbyBtaWdodCBub3QgYmUgdmlzaWJsZS5cbiAqXG4gKiBAcmV0dXJuIGFuIGFycmF5IHdpdGggMiBlbGVtZW50cywgdGhlIHZpZGVvIHdpZHRoIGFuZCB0aGUgdmlkZW8gaGVpZ2h0XG4gKi9cbmZ1bmN0aW9uIGdldENhbWVyYVZpZGVvU2l6ZSh2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG4gICAgaWYgKCF2aWRlb1dpZHRoKVxuICAgICAgICB2aWRlb1dpZHRoID0gY3VycmVudFZpZGVvV2lkdGg7XG4gICAgaWYgKCF2aWRlb0hlaWdodClcbiAgICAgICAgdmlkZW9IZWlnaHQgPSBjdXJyZW50VmlkZW9IZWlnaHQ7XG5cbiAgICB2YXIgYXNwZWN0UmF0aW8gPSB2aWRlb1dpZHRoIC8gdmlkZW9IZWlnaHQ7XG5cbiAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBNYXRoLm1heCh2aWRlb1dpZHRoLCB2aWRlb1NwYWNlV2lkdGgpO1xuICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSBNYXRoLm1heCh2aWRlb0hlaWdodCwgdmlkZW9TcGFjZUhlaWdodCk7XG5cbiAgICBpZiAoYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyA8IHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gdmlkZW9TcGFjZUhlaWdodDtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8gPCB2aWRlb1NwYWNlV2lkdGgpIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSB2aWRlb1NwYWNlV2lkdGg7XG4gICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW87XG4gICAgfVxuXG4gICAgcmV0dXJuIFthdmFpbGFibGVXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBkaXNwbGF5IG5hbWUgZm9yIHRoZSBnaXZlbiB2aWRlbyBzcGFuIGlkLlxuICovXG5mdW5jdGlvbiBzZXREaXNwbGF5TmFtZSh2aWRlb1NwYW5JZCwgZGlzcGxheU5hbWUsIGtleSkge1xuICAgIHZhciBuYW1lU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uZGlzcGxheW5hbWUnKTtcbiAgICB2YXIgZGVmYXVsdExvY2FsRGlzcGxheU5hbWUgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcbiAgICAgICAgaW50ZXJmYWNlQ29uZmlnLkRFRkFVTFRfTE9DQUxfRElTUExBWV9OQU1FKTtcblxuICAgIC8vIElmIHdlIGFscmVhZHkgaGF2ZSBhIGRpc3BsYXkgbmFtZSBmb3IgdGhpcyB2aWRlby5cbiAgICBpZiAobmFtZVNwYW4ubGVuZ3RoID4gMCkge1xuICAgICAgICB2YXIgbmFtZVNwYW5FbGVtZW50ID0gbmFtZVNwYW4uZ2V0KDApO1xuXG4gICAgICAgIGlmIChuYW1lU3BhbkVsZW1lbnQuaWQgPT09ICdsb2NhbERpc3BsYXlOYW1lJyAmJlxuICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKS50ZXh0KCkgIT09IGRpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB2YXIgbWVIVE1MID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXCJtZVwiKTtcbiAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmh0bWwoZGlzcGxheU5hbWUgKyAnICgnICsgbWVIVE1MICsgJyknKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmh0bWwoZGVmYXVsdExvY2FsRGlzcGxheU5hbWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCA+IDApXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZCArICdfbmFtZScpLmh0bWwoZGlzcGxheU5hbWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAoa2V5ICYmIGtleS5sZW5ndGggPiAwKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHZhciBuYW1lSHRtbCA9IEFQUC50cmFuc2xhdGlvbi5nZW5lcmF0ZVRyYW5zbGF0b25IVE1MKGtleSk7XG4gICAgICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZCArICdfbmFtZScpLmh0bWwobmFtZUh0bWwpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnX25hbWUnKS50ZXh0KFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9SRU1PVEVfRElTUExBWV9OQU1FKTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBlZGl0QnV0dG9uID0gbnVsbDtcblxuICAgICAgICBuYW1lU3BhbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgbmFtZVNwYW4uY2xhc3NOYW1lID0gJ2Rpc3BsYXluYW1lJztcbiAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQobmFtZVNwYW4pO1xuXG4gICAgICAgIGlmICh2aWRlb1NwYW5JZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInKSB7XG4gICAgICAgICAgICBlZGl0QnV0dG9uID0gY3JlYXRlRWRpdERpc3BsYXlOYW1lQnV0dG9uKCk7XG4gICAgICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHZhciBtZUhUTUwgPSBBUFAudHJhbnNsYXRpb24uZ2VuZXJhdGVUcmFuc2xhdG9uSFRNTChcIm1lXCIpO1xuICAgICAgICAgICAgICAgIG5hbWVTcGFuLmlubmVySFRNTCA9IGRpc3BsYXlOYW1lICsgbWVIVE1MO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIG5hbWVTcGFuLmlubmVySFRNTCA9IGRlZmF1bHRMb2NhbERpc3BsYXlOYW1lO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCA+IDApIHtcblxuICAgICAgICAgICAgICAgIG5hbWVTcGFuLmlubmVyVGV4dCA9IGRpc3BsYXlOYW1lO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIG5hbWVTcGFuLmlubmVyVGV4dCA9IGludGVyZmFjZUNvbmZpZy5ERUZBVUxUX1JFTU9URV9ESVNQTEFZX05BTUU7XG4gICAgICAgIH1cblxuXG4gICAgICAgIGlmICghZWRpdEJ1dHRvbikge1xuICAgICAgICAgICAgbmFtZVNwYW4uaWQgPSB2aWRlb1NwYW5JZCArICdfbmFtZSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBuYW1lU3Bhbi5pZCA9ICdsb2NhbERpc3BsYXlOYW1lJztcbiAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKGVkaXRCdXR0b24pO1xuICAgICAgICAgICAgLy90cmFuc2xhdGVzIHBvcG92ZXIgb2YgZWRpdCBidXR0b25cbiAgICAgICAgICAgIEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVFbGVtZW50KCQoXCJhLmRpc3BsYXluYW1lXCIpKTtcblxuICAgICAgICAgICAgdmFyIGVkaXRhYmxlVGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XG4gICAgICAgICAgICBlZGl0YWJsZVRleHQuY2xhc3NOYW1lID0gJ2Rpc3BsYXluYW1lJztcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC50eXBlID0gJ3RleHQnO1xuICAgICAgICAgICAgZWRpdGFibGVUZXh0LmlkID0gJ2VkaXREaXNwbGF5TmFtZSc7XG5cbiAgICAgICAgICAgIGlmIChkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBlZGl0YWJsZVRleHQudmFsdWVcbiAgICAgICAgICAgICAgICAgICAgPSBkaXNwbGF5TmFtZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGRlZmF1bHROaWNrbmFtZSA9IEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVTdHJpbmcoXG4gICAgICAgICAgICAgICAgXCJkZWZhdWx0Tmlja25hbWVcIiwge25hbWU6IFwiSmFuZSBQaW5rXCJ9KTtcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgJ2Rpc3BsYXk6bm9uZTsnKTtcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC5zZXRBdHRyaWJ1dGUoJ2RhdGEtMThuJyxcbiAgICAgICAgICAgICAgICAnW3BsYWNlaG9sZGVyXWRlZmF1bHROaWNrbmFtZScpO1xuICAgICAgICAgICAgZWRpdGFibGVUZXh0LnNldEF0dHJpYnV0ZShcImRhdGEtaTE4bi1vcHRpb25zXCIsXG4gICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe25hbWU6IFwiSmFuZSBQaW5rXCJ9KSk7XG4gICAgICAgICAgICBlZGl0YWJsZVRleHQuc2V0QXR0cmlidXRlKFwicGxhY2Vob2xkZXJcIiwgZGVmYXVsdE5pY2tuYW1lKTtcblxuICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQoZWRpdGFibGVUZXh0KTtcblxuICAgICAgICAgICAgJCgnI2xvY2FsVmlkZW9Db250YWluZXIgLmRpc3BsYXluYW1lJylcbiAgICAgICAgICAgICAgICAuYmluZChcImNsaWNrXCIsIGZ1bmN0aW9uIChlKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmhpZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLmZvY3VzKCk7XG4gICAgICAgICAgICAgICAgICAgICQoJyNlZGl0RGlzcGxheU5hbWUnKS5zZWxlY3QoKTtcblxuICAgICAgICAgICAgICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykub25lKFwiZm9jdXNvdXRcIiwgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmlucHV0RGlzcGxheU5hbWVIYW5kbGVyKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykub24oJ2tleWRvd24nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUua2V5Q29kZSA9PT0gMTMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIodGhpcy52YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIEdldHMgdGhlIHNlbGVjdG9yIG9mIHZpZGVvIHRodW1ibmFpbCBjb250YWluZXIgZm9yIHRoZSB1c2VyIGlkZW50aWZpZWQgYnlcbiAqIGdpdmVuIDx0dD51c2VySmlkPC90dD5cbiAqIEBwYXJhbSByZXNvdXJjZUppZCB1c2VyJ3MgSmlkIGZvciB3aG9tIHdlIHdhbnQgdG8gZ2V0IHRoZSB2aWRlbyBjb250YWluZXIuXG4gKi9cbmZ1bmN0aW9uIGdldFBhcnRpY2lwYW50Q29udGFpbmVyKHJlc291cmNlSmlkKVxue1xuICAgIGlmICghcmVzb3VyY2VKaWQpXG4gICAgICAgIHJldHVybiBudWxsO1xuXG4gICAgaWYgKHJlc291cmNlSmlkID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpXG4gICAgICAgIHJldHVybiAkKFwiI2xvY2FsVmlkZW9Db250YWluZXJcIik7XG4gICAgZWxzZVxuICAgICAgICByZXR1cm4gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkKTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBzaXplIGFuZCBwb3NpdGlvbiBvZiB0aGUgZ2l2ZW4gdmlkZW8gZWxlbWVudC5cbiAqXG4gKiBAcGFyYW0gdmlkZW8gdGhlIHZpZGVvIGVsZW1lbnQgdG8gcG9zaXRpb25cbiAqIEBwYXJhbSB3aWR0aCB0aGUgZGVzaXJlZCB2aWRlbyB3aWR0aFxuICogQHBhcmFtIGhlaWdodCB0aGUgZGVzaXJlZCB2aWRlbyBoZWlnaHRcbiAqIEBwYXJhbSBob3Jpem9udGFsSW5kZW50IHRoZSBsZWZ0IGFuZCByaWdodCBpbmRlbnRcbiAqIEBwYXJhbSB2ZXJ0aWNhbEluZGVudCB0aGUgdG9wIGFuZCBib3R0b20gaW5kZW50XG4gKi9cbmZ1bmN0aW9uIHBvc2l0aW9uVmlkZW8odmlkZW8sXG4gICAgICAgICAgICAgICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgIGhvcml6b250YWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2FsSW5kZW50KSB7XG4gICAgdmlkZW8ud2lkdGgod2lkdGgpO1xuICAgIHZpZGVvLmhlaWdodChoZWlnaHQpO1xuICAgIHZpZGVvLmNzcyh7ICB0b3A6IHZlcnRpY2FsSW5kZW50ICsgJ3B4JyxcbiAgICAgICAgYm90dG9tOiB2ZXJ0aWNhbEluZGVudCArICdweCcsXG4gICAgICAgIGxlZnQ6IGhvcml6b250YWxJbmRlbnQgKyAncHgnLFxuICAgICAgICByaWdodDogaG9yaXpvbnRhbEluZGVudCArICdweCd9KTtcbn1cblxuLyoqXG4gKiBBZGRzIHRoZSByZW1vdGUgdmlkZW8gbWVudSBlbGVtZW50IGZvciB0aGUgZ2l2ZW4gPHR0PmppZDwvdHQ+IGluIHRoZVxuICogZ2l2ZW4gPHR0PnBhcmVudEVsZW1lbnQ8L3R0Pi5cbiAqXG4gKiBAcGFyYW0gamlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZm9yIHdoaWNoIHdlJ3JlIGFkZGluZyBhIG1lbnUuXG4gKiBAcGFyYW0gcGFyZW50RWxlbWVudCB0aGUgcGFyZW50IGVsZW1lbnQgd2hlcmUgdGhpcyBtZW51IHdpbGwgYmUgYWRkZWRcbiAqL1xuZnVuY3Rpb24gYWRkUmVtb3RlVmlkZW9NZW51KGppZCwgcGFyZW50RWxlbWVudCkge1xuICAgIHZhciBzcGFuRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICBzcGFuRWxlbWVudC5jbGFzc05hbWUgPSAncmVtb3RldmlkZW9tZW51JztcblxuICAgIHBhcmVudEVsZW1lbnQuYXBwZW5kQ2hpbGQoc3BhbkVsZW1lbnQpO1xuXG4gICAgdmFyIG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgIG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdmYSBmYS1hbmdsZS1kb3duJztcbiAgICBtZW51RWxlbWVudC50aXRsZSA9ICdSZW1vdGUgdXNlciBjb250cm9scyc7XG4gICAgc3BhbkVsZW1lbnQuYXBwZW5kQ2hpbGQobWVudUVsZW1lbnQpO1xuXG4vLyAgICAgICAgPHVsIGNsYXNzPVwicG9wdXBtZW51XCI+XG4vLyAgICAgICAgPGxpPjxhIGhyZWY9XCIjXCI+TXV0ZTwvYT48L2xpPlxuLy8gICAgICAgIDxsaT48YSBocmVmPVwiI1wiPkVqZWN0PC9hPjwvbGk+XG4vLyAgICAgICAgPC91bD5cblxuICAgIHZhciBwb3B1cG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndWwnKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdwb3B1cG1lbnUnO1xuICAgIHBvcHVwbWVudUVsZW1lbnQuaWRcbiAgICAgICAgPSAncmVtb3RlX3BvcHVwbWVudV8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICBzcGFuRWxlbWVudC5hcHBlbmRDaGlsZChwb3B1cG1lbnVFbGVtZW50KTtcblxuICAgIHZhciBtdXRlTWVudUl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuICAgIHZhciBtdXRlTGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cbiAgICB2YXIgbXV0ZWRJbmRpY2F0b3IgPSBcIjxpIHN0eWxlPSdmbG9hdDpsZWZ0OycgY2xhc3M9J2ljb24tbWljLWRpc2FibGVkJz48L2k+XCI7XG5cbiAgICBpZiAoIW11dGVkQXVkaW9zW2ppZF0pIHtcbiAgICAgICAgbXV0ZUxpbmtJdGVtLmlubmVySFRNTCA9IG11dGVkSW5kaWNhdG9yICtcbiAgICAgICAgICAgIFwiIDxkaXYgc3R5bGU9J3dpZHRoOiA5MHB4O21hcmdpbi1sZWZ0OiAyMHB4OycgZGF0YS1pMThuPSd2aWRlb3RodW1ibmFpbC5kb211dGUnPjwvZGl2PlwiO1xuICAgICAgICBtdXRlTGlua0l0ZW0uY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIG11dGVMaW5rSXRlbS5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArXG4gICAgICAgICAgICBcIiA8ZGl2IHN0eWxlPSd3aWR0aDogOTBweDttYXJnaW4tbGVmdDogMjBweDsnIGRhdGEtaTE4bj0ndmlkZW90aHVtYm5haWwubXV0ZWQnPjwvZGl2PlwiO1xuICAgICAgICBtdXRlTGlua0l0ZW0uY2xhc3NOYW1lID0gJ211dGVsaW5rIGRpc2FibGVkJztcbiAgICB9XG5cbiAgICBtdXRlTGlua0l0ZW0ub25jbGljayA9IGZ1bmN0aW9uKCl7XG4gICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ2Rpc2FibGVkJykgIT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBpc011dGUgPSBtdXRlZEF1ZGlvc1tqaWRdID09IHRydWU7XG4gICAgICAgIEFQUC54bXBwLnNldE11dGUoamlkLCAhaXNNdXRlKTtcblxuICAgICAgICBwb3B1cG1lbnVFbGVtZW50LnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAnZGlzcGxheTpub25lOycpO1xuXG4gICAgICAgIGlmIChpc011dGUpIHtcbiAgICAgICAgICAgIHRoaXMuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgK1xuICAgICAgICAgICAgICAgIFwiIDxkaXYgc3R5bGU9J3dpZHRoOiA5MHB4O21hcmdpbi1sZWZ0OiAyMHB4OycgZGF0YS1pMThuPSd2aWRlb3RodW1ibmFpbC5tdXRlZCc+PC9kaXY+XCI7XG4gICAgICAgICAgICB0aGlzLmNsYXNzTmFtZSA9ICdtdXRlbGluayBkaXNhYmxlZCc7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmlubmVySFRNTCA9IG11dGVkSW5kaWNhdG9yICtcbiAgICAgICAgICAgICAgICBcIiA8ZGl2IHN0eWxlPSd3aWR0aDogOTBweDttYXJnaW4tbGVmdDogMjBweDsnIGRhdGEtaTE4bj0ndmlkZW90aHVtYm5haWwuZG9tdXRlJz48L2Rpdj5cIjtcbiAgICAgICAgICAgIHRoaXMuY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBtdXRlTWVudUl0ZW0uYXBwZW5kQ2hpbGQobXV0ZUxpbmtJdGVtKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKG11dGVNZW51SXRlbSk7XG5cbiAgICB2YXIgZWplY3RJbmRpY2F0b3IgPSBcIjxpIHN0eWxlPSdmbG9hdDpsZWZ0OycgY2xhc3M9J2ZhIGZhLWVqZWN0Jz48L2k+XCI7XG5cbiAgICB2YXIgZWplY3RNZW51SXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7XG4gICAgdmFyIGVqZWN0TGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgdmFyIGVqZWN0VGV4dCA9IFwiPGRpdiBzdHlsZT0nd2lkdGg6IDkwcHg7bWFyZ2luLWxlZnQ6IDIwcHg7JyBkYXRhLWkxOG49J3ZpZGVvdGh1bWJuYWlsLmtpY2snPiZuYnNwOzwvZGl2PlwiO1xuICAgIGVqZWN0TGlua0l0ZW0uaW5uZXJIVE1MID0gZWplY3RJbmRpY2F0b3IgKyAnICcgKyBlamVjdFRleHQ7XG4gICAgZWplY3RMaW5rSXRlbS5vbmNsaWNrID0gZnVuY3Rpb24oKXtcbiAgICAgICAgQVBQLnhtcHAuZWplY3QoamlkKTtcbiAgICAgICAgcG9wdXBtZW51RWxlbWVudC5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgJ2Rpc3BsYXk6bm9uZTsnKTtcbiAgICB9O1xuXG4gICAgZWplY3RNZW51SXRlbS5hcHBlbmRDaGlsZChlamVjdExpbmtJdGVtKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKGVqZWN0TWVudUl0ZW0pO1xuXG4gICAgdmFyIHBhZGRpbmdTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgIHBhZGRpbmdTcGFuLmNsYXNzTmFtZSA9ICdwb3B1cG1lbnVQYWRkaW5nJztcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKHBhZGRpbmdTcGFuKTtcbiAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlRWxlbWVudCgkKFwiI1wiICsgcG9wdXBtZW51RWxlbWVudC5pZCArIFwiID4gbGkgPiBhID4gZGl2XCIpKTtcbn1cblxuLyoqXG4gKiBSZW1vdmVzIHJlbW90ZSB2aWRlbyBtZW51IGVsZW1lbnQgZnJvbSB2aWRlbyBlbGVtZW50IGlkZW50aWZpZWQgYnlcbiAqIGdpdmVuIDx0dD52aWRlb0VsZW1lbnRJZDwvdHQ+LlxuICpcbiAqIEBwYXJhbSB2aWRlb0VsZW1lbnRJZCB0aGUgaWQgb2YgbG9jYWwgb3IgcmVtb3RlIHZpZGVvIGVsZW1lbnQuXG4gKi9cbmZ1bmN0aW9uIHJlbW92ZVJlbW90ZVZpZGVvTWVudSh2aWRlb0VsZW1lbnRJZCkge1xuICAgIHZhciBtZW51U3BhbiA9ICQoJyMnICsgdmlkZW9FbGVtZW50SWQgKyAnPnNwYW4ucmVtb3RldmlkZW9tZW51Jyk7XG4gICAgaWYgKG1lbnVTcGFuLmxlbmd0aCkge1xuICAgICAgICBtZW51U3Bhbi5yZW1vdmUoKTtcbiAgICB9XG59XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgZGF0YSBmb3IgdGhlIGluZGljYXRvclxuICogQHBhcmFtIGlkIHRoZSBpZCBvZiB0aGUgaW5kaWNhdG9yXG4gKiBAcGFyYW0gcGVyY2VudCB0aGUgcGVyY2VudCBmb3IgY29ubmVjdGlvbiBxdWFsaXR5XG4gKiBAcGFyYW0gb2JqZWN0IHRoZSBkYXRhXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZVN0YXRzSW5kaWNhdG9yKGlkLCBwZXJjZW50LCBvYmplY3QpIHtcbiAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0pXG4gICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2lkXS51cGRhdGVDb25uZWN0aW9uUXVhbGl0eShwZXJjZW50LCBvYmplY3QpO1xufVxuXG5cbi8qKlxuICogUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgdmlkZW8gZGltZW5zaW9ucywgc28gdGhhdCBpdCBrZWVwcyBpdCdzIGFzcGVjdFxuICogcmF0aW8gYW5kIGZpdHMgYXZhaWxhYmxlIGFyZWEgd2l0aCBpdCdzIGxhcmdlciBkaW1lbnNpb24uIFRoaXMgbWV0aG9kXG4gKiBlbnN1cmVzIHRoYXQgd2hvbGUgdmlkZW8gd2lsbCBiZSB2aXNpYmxlIGFuZCBjYW4gbGVhdmUgZW1wdHkgYXJlYXMuXG4gKlxuICogQHJldHVybiBhbiBhcnJheSB3aXRoIDIgZWxlbWVudHMsIHRoZSB2aWRlbyB3aWR0aCBhbmQgdGhlIHZpZGVvIGhlaWdodFxuICovXG5mdW5jdGlvbiBnZXREZXNrdG9wVmlkZW9TaXplKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICBpZiAoIXZpZGVvV2lkdGgpXG4gICAgICAgIHZpZGVvV2lkdGggPSBjdXJyZW50VmlkZW9XaWR0aDtcbiAgICBpZiAoIXZpZGVvSGVpZ2h0KVxuICAgICAgICB2aWRlb0hlaWdodCA9IGN1cnJlbnRWaWRlb0hlaWdodDtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IHZpZGVvV2lkdGggLyB2aWRlb0hlaWdodDtcblxuICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KHZpZGVvV2lkdGgsIHZpZGVvU3BhY2VXaWR0aCk7XG4gICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IE1hdGgubWF4KHZpZGVvSGVpZ2h0LCB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgIHZpZGVvU3BhY2VIZWlnaHQgLT0gJCgnI3JlbW90ZVZpZGVvcycpLm91dGVySGVpZ2h0KCk7XG5cbiAgICBpZiAoYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyA+PSB2aWRlb1NwYWNlSGVpZ2h0KVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gdmlkZW9TcGFjZUhlaWdodDtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8gPj0gdmlkZW9TcGFjZVdpZHRoKVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSB2aWRlb1NwYWNlV2lkdGg7XG4gICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW87XG4gICAgfVxuXG4gICAgcmV0dXJuIFthdmFpbGFibGVXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBlZGl0IGRpc3BsYXkgbmFtZSBidXR0b24uXG4gKlxuICogQHJldHVybnMgdGhlIGVkaXQgYnV0dG9uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUVkaXREaXNwbGF5TmFtZUJ1dHRvbigpIHtcbiAgICB2YXIgZWRpdEJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICBlZGl0QnV0dG9uLmNsYXNzTmFtZSA9ICdkaXNwbGF5bmFtZSc7XG4gICAgVUlVdGlsLnNldFRvb2x0aXAoZWRpdEJ1dHRvbixcbiAgICAgICAgXCJ2aWRlb3RodW1ibmFpbC5lZGl0bmlja25hbWVcIixcbiAgICAgICAgXCJ0b3BcIik7XG4gICAgZWRpdEJ1dHRvbi5pbm5lckhUTUwgPSAnPGkgY2xhc3M9XCJmYSBmYS1wZW5jaWxcIj48L2k+JztcblxuICAgIHJldHVybiBlZGl0QnV0dG9uO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGVsZW1lbnQgaW5kaWNhdGluZyB0aGUgbW9kZXJhdG9yKG93bmVyKSBvZiB0aGUgY29uZmVyZW5jZS5cbiAqXG4gKiBAcGFyYW0gcGFyZW50RWxlbWVudCB0aGUgcGFyZW50IGVsZW1lbnQgd2hlcmUgdGhlIG93bmVyIGluZGljYXRvciB3aWxsXG4gKiBiZSBhZGRlZFxuICovXG5mdW5jdGlvbiBjcmVhdGVNb2RlcmF0b3JJbmRpY2F0b3JFbGVtZW50KHBhcmVudEVsZW1lbnQpIHtcbiAgICB2YXIgbW9kZXJhdG9ySW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgIG1vZGVyYXRvckluZGljYXRvci5jbGFzc05hbWUgPSAnZmEgZmEtc3Rhcic7XG4gICAgcGFyZW50RWxlbWVudC5hcHBlbmRDaGlsZChtb2RlcmF0b3JJbmRpY2F0b3IpO1xuXG4gICAgVUlVdGlsLnNldFRvb2x0aXAocGFyZW50RWxlbWVudCxcbiAgICAgICAgXCJ2aWRlb3RodW1ibmFpbC5tb2RlcmF0b3JcIixcbiAgICAgICAgXCJ0b3BcIik7XG59XG5cblxudmFyIFZpZGVvTGF5b3V0ID0gKGZ1bmN0aW9uIChteSkge1xuICAgIG15LmNvbm5lY3Rpb25JbmRpY2F0b3JzID0ge307XG5cbiAgICAvLyBCeSBkZWZhdWx0IHdlIHVzZSBjYW1lcmFcbiAgICBteS5nZXRWaWRlb1NpemUgPSBnZXRDYW1lcmFWaWRlb1NpemU7XG4gICAgbXkuZ2V0VmlkZW9Qb3NpdGlvbiA9IGdldENhbWVyYVZpZGVvUG9zaXRpb247XG5cbiAgICBteS5pbml0ID0gZnVuY3Rpb24gKGVtaXR0ZXIpIHtcbiAgICAgICAgLy8gTGlzdGVuIGZvciBsYXJnZSB2aWRlbyBzaXplIHVwZGF0ZXNcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKVxuICAgICAgICAgICAgLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWRlZG1ldGFkYXRhJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9XaWR0aCA9IHRoaXMudmlkZW9XaWR0aDtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9IZWlnaHQgPSB0aGlzLnZpZGVvSGVpZ2h0O1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoY3VycmVudFZpZGVvV2lkdGgsIGN1cnJlbnRWaWRlb0hlaWdodCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgZXZlbnRFbWl0dGVyID0gZW1pdHRlcjtcbiAgICB9O1xuXG4gICAgbXkuaXNJbkxhc3ROID0gZnVuY3Rpb24ocmVzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIGxhc3ROQ291bnQgPCAwIC8vIGxhc3ROIGlzIGRpc2FibGVkLCByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfHwgKGxhc3ROQ291bnQgPiAwICYmIGxhc3RORW5kcG9pbnRzQ2FjaGUubGVuZ3RoID09IDApIC8vIGxhc3RORW5kcG9pbnRzIGNhY2hlIG5vdCBidWlsdCB5ZXQsIHJldHVybiB0cnVlXG4gICAgICAgICAgICB8fCAobGFzdE5FbmRwb2ludHNDYWNoZSAmJiBsYXN0TkVuZHBvaW50c0NhY2hlLmluZGV4T2YocmVzb3VyY2UpICE9PSAtMSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFZpZGVvKHN0cmVhbSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsQXVkaW8gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbSgkKCcjbG9jYWxBdWRpbycpLCBzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKSk7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NhbEF1ZGlvJykuYXV0b3BsYXkgPSB0cnVlO1xuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9jYWxBdWRpbycpLnZvbHVtZSA9IDA7XG4gICAgICAgIGlmIChwcmVNdXRlZCkge1xuICAgICAgICAgICAgaWYoIUFQUC5VSS5zZXRBdWRpb011dGVkKHRydWUpKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHByZU11dGVkID0gbXV0ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHByZU11dGVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkuY2hhbmdlTG9jYWxWaWRlbyA9IGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgICB2YXIgZmxpcFggPSB0cnVlO1xuICAgICAgICBpZihzdHJlYW0udmlkZW9UeXBlID09IFwic2NyZWVuXCIpXG4gICAgICAgICAgICBmbGlwWCA9IGZhbHNlO1xuICAgICAgICB2YXIgbG9jYWxWaWRlbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJyk7XG4gICAgICAgIGxvY2FsVmlkZW8uaWQgPSAnbG9jYWxWaWRlb18nICtcbiAgICAgICAgICAgIEFQUC5SVEMuZ2V0U3RyZWFtSUQoc3RyZWFtLmdldE9yaWdpbmFsU3RyZWFtKCkpO1xuICAgICAgICBsb2NhbFZpZGVvLmF1dG9wbGF5ID0gdHJ1ZTtcbiAgICAgICAgbG9jYWxWaWRlby52b2x1bWUgPSAwOyAvLyBpcyBpdCByZXF1aXJlZCBpZiBhdWRpbyBpcyBzZXBhcmF0ZWQgP1xuICAgICAgICBsb2NhbFZpZGVvLm9uY29udGV4dG1lbnUgPSBmdW5jdGlvbiAoKSB7IHJldHVybiBmYWxzZTsgfTtcblxuICAgICAgICB2YXIgbG9jYWxWaWRlb0NvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NhbFZpZGVvV3JhcHBlcicpO1xuICAgICAgICBsb2NhbFZpZGVvQ29udGFpbmVyLmFwcGVuZENoaWxkKGxvY2FsVmlkZW8pO1xuXG4gICAgICAgIC8vIFNldCBkZWZhdWx0IGRpc3BsYXkgbmFtZS5cbiAgICAgICAgc2V0RGlzcGxheU5hbWUoJ2xvY2FsVmlkZW9Db250YWluZXInKTtcblxuICAgICAgICBpZighVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbXCJsb2NhbFZpZGVvQ29udGFpbmVyXCJdKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tcImxvY2FsVmlkZW9Db250YWluZXJcIl1cbiAgICAgICAgICAgICAgICA9IG5ldyBDb25uZWN0aW9uSW5kaWNhdG9yKCQoXCIjbG9jYWxWaWRlb0NvbnRhaW5lclwiKVswXSwgbnVsbCwgVmlkZW9MYXlvdXQpO1xuICAgICAgICB9XG5cbiAgICAgICAgQXVkaW9MZXZlbHMudXBkYXRlQXVkaW9MZXZlbENhbnZhcyhudWxsLCBWaWRlb0xheW91dCk7XG5cbiAgICAgICAgdmFyIGxvY2FsVmlkZW9TZWxlY3RvciA9ICQoJyMnICsgbG9jYWxWaWRlby5pZCk7XG5cbiAgICAgICAgZnVuY3Rpb24gbG9jYWxWaWRlb0NsaWNrKGV2ZW50KSB7XG4gICAgICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbyksXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAubXlSZXNvdXJjZSgpKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBZGQgY2xpY2sgaGFuZGxlciB0byBib3RoIHZpZGVvIGFuZCB2aWRlbyB3cmFwcGVyIGVsZW1lbnRzIGluIGNhc2VcbiAgICAgICAgLy8gdGhlcmUncyBubyB2aWRlby5cbiAgICAgICAgbG9jYWxWaWRlb1NlbGVjdG9yLmNsaWNrKGxvY2FsVmlkZW9DbGljayk7XG4gICAgICAgICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyJykuY2xpY2sobG9jYWxWaWRlb0NsaWNrKTtcblxuICAgICAgICAvLyBBZGQgaG92ZXIgaGFuZGxlclxuICAgICAgICAkKCcjbG9jYWxWaWRlb0NvbnRhaW5lcicpLmhvdmVyKFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd0Rpc3BsYXlOYW1lKCdsb2NhbFZpZGVvQ29udGFpbmVyJywgdHJ1ZSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9WaXNpYmxlKClcbiAgICAgICAgICAgICAgICAgICAgICAgIHx8IEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbykgIT09IEFQUC5SVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpXG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSgnbG9jYWxWaWRlb0NvbnRhaW5lcicsIGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgICAgLy8gQWRkIHN0cmVhbSBlbmRlZCBoYW5kbGVyXG4gICAgICAgIHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLm9uZW5kZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBsb2NhbFZpZGVvQ29udGFpbmVyLnJlbW92ZUNoaWxkKGxvY2FsVmlkZW8pO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3ZlZFZpZGVvKEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbykpO1xuICAgICAgICB9O1xuICAgICAgICAvLyBGbGlwIHZpZGVvIHggYXhpcyBpZiBuZWVkZWRcbiAgICAgICAgZmxpcFhMb2NhbFZpZGVvID0gZmxpcFg7XG4gICAgICAgIGlmIChmbGlwWCkge1xuICAgICAgICAgICAgbG9jYWxWaWRlb1NlbGVjdG9yLmFkZENsYXNzKFwiZmxpcFZpZGVvWFwiKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBdHRhY2ggV2ViUlRDIHN0cmVhbVxuICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBBUFAuc2ltdWxjYXN0LmdldExvY2FsVmlkZW9TdHJlYW0oKTtcbiAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShsb2NhbFZpZGVvU2VsZWN0b3IsIHZpZGVvU3RyZWFtKTtcblxuICAgICAgICBsb2NhbFZpZGVvU3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYyhsb2NhbFZpZGVvKTtcblxuICAgICAgICB2YXIgbXlSZXNvdXJjZUppZCA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcblxuICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKGxvY2FsVmlkZW9TcmMsIDAsXG4gICAgICAgICAgICBteVJlc291cmNlSmlkKTtcblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIG9yIHJlbW92ZXMgaWNvbnMgZm9yIG5vdCBhdmFpbGFibGUgY2FtZXJhIGFuZCBtaWNyb3Bob25lLlxuICAgICAqIEBwYXJhbSByZXNvdXJjZUppZCB0aGUgamlkIG9mIHVzZXJcbiAgICAgKiBAcGFyYW0gZGV2aWNlcyBhdmFpbGFibGUgZGV2aWNlc1xuICAgICAqL1xuICAgIG15LnNldERldmljZUF2YWlsYWJpbGl0eUljb25zID0gZnVuY3Rpb24gKHJlc291cmNlSmlkLCBkZXZpY2VzKSB7XG4gICAgICAgIGlmKCFkZXZpY2VzKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBjb250YWluZXIgPSBudWxsXG4gICAgICAgIGlmKCFyZXNvdXJjZUppZClcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGFpbmVyID0gJChcIiNsb2NhbFZpZGVvQ29udGFpbmVyXCIpWzBdO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGFpbmVyID0gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkKVswXTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCFjb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgJChcIiNcIiArIGNvbnRhaW5lci5pZCArIFwiID4gLm5vTWljXCIpLnJlbW92ZSgpO1xuICAgICAgICAkKFwiI1wiICsgY29udGFpbmVyLmlkICsgXCIgPiAubm9WaWRlb1wiKS5yZW1vdmUoKTtcbiAgICAgICAgaWYoIWRldmljZXMuYXVkaW8pXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpKS5zZXRBdHRyaWJ1dGUoXCJjbGFzc1wiLFwibm9NaWNcIik7XG4gICAgICAgIH1cblxuICAgICAgICBpZighZGV2aWNlcy52aWRlbylcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIikpLnNldEF0dHJpYnV0ZShcImNsYXNzXCIsXCJub1ZpZGVvXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoIWRldmljZXMuYXVkaW8gJiYgIWRldmljZXMudmlkZW8pXG4gICAgICAgIHtcbiAgICAgICAgICAgICQoXCIjXCIgKyBjb250YWluZXIuaWQgKyBcIiA+IC5ub01pY1wiKS5jc3MoXCJiYWNrZ3JvdW5kLXBvc2l0aW9uXCIsIFwiNzUlXCIpO1xuICAgICAgICAgICAgJChcIiNcIiArIGNvbnRhaW5lci5pZCArIFwiID4gLm5vVmlkZW9cIikuY3NzKFwiYmFja2dyb3VuZC1wb3NpdGlvblwiLCBcIjI1JVwiKTtcbiAgICAgICAgICAgICQoXCIjXCIgKyBjb250YWluZXIuaWQgKyBcIiA+IC5ub1ZpZGVvXCIpLmNzcyhcImJhY2tncm91bmQtY29sb3JcIiwgXCJ0cmFuc3BhcmVudFwiKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiByZW1vdmVkIHZpZGVvIGlzIGN1cnJlbnRseSBkaXNwbGF5ZWQgYW5kIHRyaWVzIHRvIGRpc3BsYXlcbiAgICAgKiBhbm90aGVyIG9uZSBpbnN0ZWFkLlxuICAgICAqIEBwYXJhbSByZW1vdmVkVmlkZW9TcmMgc3JjIHN0cmVhbSBpZGVudGlmaWVyIG9mIHRoZSB2aWRlby5cbiAgICAgKi9cbiAgICBteS51cGRhdGVSZW1vdmVkVmlkZW8gPSBmdW5jdGlvbihyZW1vdmVkVmlkZW9TcmMpIHtcbiAgICAgICAgaWYgKHJlbW92ZWRWaWRlb1NyYyA9PT0gQVBQLlJUQy5nZXRWaWRlb1NyYygkKCcjbGFyZ2VWaWRlbycpWzBdKSkge1xuICAgICAgICAgICAgLy8gdGhpcyBpcyBjdXJyZW50bHkgZGlzcGxheWVkIGFzIGxhcmdlXG4gICAgICAgICAgICAvLyBwaWNrIHRoZSBsYXN0IHZpc2libGUgdmlkZW8gaW4gdGhlIHJvd1xuICAgICAgICAgICAgLy8gaWYgbm9ib2R5IGVsc2UgaXMgbGVmdCwgdGhpcyBwaWNrcyB0aGUgbG9jYWwgdmlkZW9cbiAgICAgICAgICAgIHZhciBwaWNrXG4gICAgICAgICAgICAgICAgPSAkKCcjcmVtb3RlVmlkZW9zPnNwYW5baWQhPVwibWl4ZWRzdHJlYW1cIl06dmlzaWJsZTpsYXN0PnZpZGVvJylcbiAgICAgICAgICAgICAgICAgICAgLmdldCgwKTtcblxuICAgICAgICAgICAgaWYgKCFwaWNrKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiTGFzdCB2aXNpYmxlIHZpZGVvIG5vIGxvbmdlciBleGlzdHNcIik7XG4gICAgICAgICAgICAgICAgcGljayA9ICQoJyNyZW1vdGVWaWRlb3M+c3BhbltpZCE9XCJtaXhlZHN0cmVhbVwiXT52aWRlbycpLmdldCgwKTtcblxuICAgICAgICAgICAgICAgIGlmICghcGljayB8fCAhQVBQLlJUQy5nZXRWaWRlb1NyYyhwaWNrKSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUcnkgbG9jYWwgdmlkZW9cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiRmFsbGJhY2sgdG8gbG9jYWwgdmlkZW8uLi5cIik7XG4gICAgICAgICAgICAgICAgICAgIHBpY2sgPSAkKCcjcmVtb3RlVmlkZW9zPnNwYW4+c3Bhbj52aWRlbycpLmdldCgwKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG11dGUgaWYgbG9jYWx2aWRlb1xuICAgICAgICAgICAgaWYgKHBpY2spIHtcbiAgICAgICAgICAgICAgICB2YXIgY29udGFpbmVyID0gcGljay5wYXJlbnROb2RlO1xuICAgICAgICAgICAgICAgIHZhciBqaWQgPSBudWxsO1xuICAgICAgICAgICAgICAgIGlmKGNvbnRhaW5lcilcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGlmKGNvbnRhaW5lci5pZCA9PSBcImxvY2FsVmlkZW9XcmFwcGVyXCIpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGppZCA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGppZCA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZChjb250YWluZXIpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTGFyZ2VWaWRlbyhBUFAuUlRDLmdldFZpZGVvU3JjKHBpY2spLCBwaWNrLnZvbHVtZSwgamlkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiRmFpbGVkIHRvIGVsZWN0IGxhcmdlIHZpZGVvXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgICBcbiAgICBteS5vblJlbW90ZVN0cmVhbUFkZGVkID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICB2YXIgY29udGFpbmVyO1xuICAgICAgICB2YXIgcmVtb3RlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZW1vdGVWaWRlb3MnKTtcblxuICAgICAgICBpZiAoc3RyZWFtLnBlZXJqaWQpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMoc3RyZWFtLnBlZXJqaWQpO1xuXG4gICAgICAgICAgICBjb250YWluZXIgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXG4gICAgICAgICAgICAgICAgICAgICdwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoc3RyZWFtLnBlZXJqaWQpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHZhciBpZCA9IHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLmlkO1xuICAgICAgICAgICAgaWYgKGlkICE9PSAnbWl4ZWRtc2xhYmVsJ1xuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBkZWZhdWx0IHN0cmVhbSBpcyBhZGRlZCBhbHdheXMgd2l0aCBuZXcgZm9jdXNcbiAgICAgICAgICAgICAgICAvLyAodG8gYmUgaW52ZXN0aWdhdGVkKVxuICAgICAgICAgICAgICAgICYmIGlkICE9PSAnZGVmYXVsdCcpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdjYW4gbm90IGFzc29jaWF0ZSBzdHJlYW0nLFxuICAgICAgICAgICAgICAgICAgICBpZCxcbiAgICAgICAgICAgICAgICAgICAgJ3dpdGggYSBwYXJ0aWNpcGFudCcpO1xuICAgICAgICAgICAgICAgIC8vIFdlIGRvbid0IHdhbnQgdG8gYWRkIGl0IGhlcmUgc2luY2UgaXQgd2lsbCBjYXVzZSB0cm91Ymxlc1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEZJWE1FOiBmb3IgdGhlIG1peGVkIG1zIHdlIGRvbnQgbmVlZCBhIHZpZGVvIC0tIGN1cnJlbnRseVxuICAgICAgICAgICAgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgICAgICAgICAgY29udGFpbmVyLmlkID0gJ21peGVkc3RyZWFtJztcbiAgICAgICAgICAgIGNvbnRhaW5lci5jbGFzc05hbWUgPSAndmlkZW9jb250YWluZXInO1xuICAgICAgICAgICAgcmVtb3Rlcy5hcHBlbmRDaGlsZChjb250YWluZXIpO1xuICAgICAgICAgICAgVUlVdGlsLnBsYXlTb3VuZE5vdGlmaWNhdGlvbigndXNlckpvaW5lZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuYWRkUmVtb3RlU3RyZWFtRWxlbWVudCggY29udGFpbmVyLFxuICAgICAgICAgICAgICAgIHN0cmVhbS5zaWQsXG4gICAgICAgICAgICAgICAgc3RyZWFtLmdldE9yaWdpbmFsU3RyZWFtKCksXG4gICAgICAgICAgICAgICAgc3RyZWFtLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgc3RyZWFtLnNzcmMpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgbXkuZ2V0TGFyZ2VWaWRlb1N0YXRlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbGFyZ2VWaWRlb1N0YXRlO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBsYXJnZSB2aWRlbyB3aXRoIHRoZSBnaXZlbiBuZXcgdmlkZW8gc291cmNlLlxuICAgICAqL1xuICAgIG15LnVwZGF0ZUxhcmdlVmlkZW8gPSBmdW5jdGlvbihuZXdTcmMsIHZvbCwgcmVzb3VyY2VKaWQpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ2hvdmVyIGluJywgbmV3U3JjKTtcblxuICAgICAgICBpZiAoQVBQLlJUQy5nZXRWaWRlb1NyYygkKCcjbGFyZ2VWaWRlbycpWzBdKSAhPT0gbmV3U3JjKSB7XG5cbiAgICAgICAgICAgICQoJyNhY3RpdmVTcGVha2VyJykuY3NzKCd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xuICAgICAgICAgICAgLy8gRHVlIHRvIHRoZSBzaW11bGNhc3QgdGhlIGxvY2FsVmlkZW9TcmMgbWF5IGhhdmUgY2hhbmdlZCB3aGVuIHRoZVxuICAgICAgICAgICAgLy8gZmFkZU91dCBldmVudCB0cmlnZ2Vycy4gSW4gdGhhdCBjYXNlIHRoZSBnZXRKaWRGcm9tVmlkZW9TcmMgYW5kXG4gICAgICAgICAgICAvLyBpc1ZpZGVvU3JjRGVza3RvcCBtZXRob2RzIHdpbGwgbm90IGZ1bmN0aW9uIGNvcnJlY3RseS5cbiAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAvLyBBbHNvLCBhZ2FpbiBkdWUgdG8gdGhlIHNpbXVsY2FzdCwgdGhlIHVwZGF0ZUxhcmdlVmlkZW8gbWV0aG9kIGNhblxuICAgICAgICAgICAgLy8gYmUgY2FsbGVkIG11bHRpcGxlIHRpbWVzIGFsbW9zdCBzaW11bHRhbmVvdXNseS4gVGhlcmVmb3JlLCB3ZVxuICAgICAgICAgICAgLy8gc3RvcmUgdGhlIHN0YXRlIGhlcmUgYW5kIHVwZGF0ZSBvbmx5IG9uY2UuXG5cbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5uZXdTcmMgPSBuZXdTcmM7XG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUuaXNWaXNpYmxlID0gJCgnI2xhcmdlVmlkZW8nKS5pcygnOnZpc2libGUnKTtcbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5pc0Rlc2t0b3AgPSBBUFAuUlRDLmlzVmlkZW9TcmNEZXNrdG9wKFxuICAgICAgICAgICAgICAgIEFQUC54bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2VKaWQpKTtcblxuICAgICAgICAgICAgaWYobGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5vbGRSZXNvdXJjZUppZCA9IGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5vbGRSZXNvdXJjZUppZCA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkID0gcmVzb3VyY2VKaWQ7XG5cbiAgICAgICAgICAgIC8vIFNjcmVlbiBzdHJlYW0gaXMgYWxyZWFkeSByb3RhdGVkXG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUuZmxpcFggPSAobmV3U3JjID09PSBsb2NhbFZpZGVvU3JjKSAmJiBmbGlwWExvY2FsVmlkZW87XG5cbiAgICAgICAgICAgIHZhciB1c2VyQ2hhbmdlZCA9IGZhbHNlO1xuICAgICAgICAgICAgaWYgKGxhcmdlVmlkZW9TdGF0ZS5vbGRSZXNvdXJjZUppZCAhPT0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgIHVzZXJDaGFuZ2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAvLyB3ZSB3YW50IHRoZSBub3RpZmljYXRpb24gdG8gdHJpZ2dlciBldmVuIGlmIHVzZXJKaWQgaXMgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIC8vIG9yIG51bGwuXG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoVUlFdmVudHMuU0VMRUNURURfRU5EUE9JTlQsXG4gICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIWxhcmdlVmlkZW9TdGF0ZS51cGRhdGVJblByb2dyZXNzKSB7XG4gICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVwZGF0ZUluUHJvZ3Jlc3MgPSB0cnVlO1xuXG4gICAgICAgICAgICAgICAgdmFyIGRvVXBkYXRlID0gZnVuY3Rpb24gKCkge1xuXG4gICAgICAgICAgICAgICAgICAgIEF2YXRhci51cGRhdGVBY3RpdmVTcGVha2VyQXZhdGFyU3JjKFxuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCF1c2VyQ2hhbmdlZCAmJiBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgIT09IG51bGwgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmMoJChsYXJnZVZpZGVvU3RhdGUucHJlbG9hZClbMF0pID09PSBuZXdTcmMpXG4gICAgICAgICAgICAgICAgICAgIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdTd2l0Y2hpbmcgdG8gcHJlbG9hZGVkIHZpZGVvJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgYXR0cmlidXRlcyA9ICQoJyNsYXJnZVZpZGVvJykucHJvcChcImF0dHJpYnV0ZXNcIik7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvb3AgdGhyb3VnaCBsYXJnZVZpZGVvIGF0dHJpYnV0ZXMgYW5kIGFwcGx5IHRoZW0gb25cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHByZWxvYWQuXG4gICAgICAgICAgICAgICAgICAgICAgICAkLmVhY2goYXR0cmlidXRlcywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm5hbWUgIT09ICdpZCcgJiYgdGhpcy5uYW1lICE9PSAnc3JjJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZC5hdHRyKHRoaXMubmFtZSwgdGhpcy52YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkLmFwcGVuZFRvKCQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5hdHRyKCdpZCcsICdwcmV2aW91c0xhcmdlVmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkLmF0dHIoJ2lkJywgJ2xhcmdlVmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoJyNwcmV2aW91c0xhcmdlVmlkZW8nKS5yZW1vdmUoKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQub24oJ2xvYWRlZG1ldGFkYXRhJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9XaWR0aCA9IHRoaXMudmlkZW9XaWR0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9IZWlnaHQgPSB0aGlzLnZpZGVvSGVpZ2h0O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoY3VycmVudFZpZGVvV2lkdGgsIGN1cnJlbnRWaWRlb0hlaWdodCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkX3NzcmMgPSAwO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5zZXRWaWRlb1NyYygkKCcjbGFyZ2VWaWRlbycpWzBdLCBsYXJnZVZpZGVvU3RhdGUubmV3U3JjKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHZhciB2aWRlb1RyYW5zZm9ybSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsYXJnZVZpZGVvJylcbiAgICAgICAgICAgICAgICAgICAgICAgIC5zdHlsZS53ZWJraXRUcmFuc2Zvcm07XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGxhcmdlVmlkZW9TdGF0ZS5mbGlwWCAmJiB2aWRlb1RyYW5zZm9ybSAhPT0gJ3NjYWxlWCgtMSknKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGFyZ2VWaWRlbycpLnN0eWxlLndlYmtpdFRyYW5zZm9ybVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gXCJzY2FsZVgoLTEpXCI7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSBpZiAoIWxhcmdlVmlkZW9TdGF0ZS5mbGlwWCAmJiB2aWRlb1RyYW5zZm9ybSA9PT0gJ3NjYWxlWCgtMSknKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGFyZ2VWaWRlbycpLnN0eWxlLndlYmtpdFRyYW5zZm9ybVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gXCJub25lXCI7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBDaGFuZ2UgdGhlIHdheSB3ZSdsbCBiZSBtZWFzdXJpbmcgYW5kIHBvc2l0aW9uaW5nIGxhcmdlIHZpZGVvXG5cbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0VmlkZW9TaXplID0gbGFyZ2VWaWRlb1N0YXRlLmlzRGVza3RvcFxuICAgICAgICAgICAgICAgICAgICAgICAgPyBnZXREZXNrdG9wVmlkZW9TaXplXG4gICAgICAgICAgICAgICAgICAgICAgICA6IGdldENhbWVyYVZpZGVvU2l6ZTtcbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0VmlkZW9Qb3NpdGlvbiA9IGxhcmdlVmlkZW9TdGF0ZS5pc0Rlc2t0b3BcbiAgICAgICAgICAgICAgICAgICAgICAgID8gZ2V0RGVza3RvcFZpZGVvUG9zaXRpb25cbiAgICAgICAgICAgICAgICAgICAgICAgIDogZ2V0Q2FtZXJhVmlkZW9Qb3NpdGlvbjtcblxuXG4gICAgICAgICAgICAgICAgICAgIC8vIE9ubHkgaWYgdGhlIGxhcmdlIHZpZGVvIGlzIGN1cnJlbnRseSB2aXNpYmxlLlxuICAgICAgICAgICAgICAgICAgICAvLyBEaXNhYmxlIHByZXZpb3VzIGRvbWluYW50IHNwZWFrZXIgdmlkZW8uXG4gICAgICAgICAgICAgICAgICAgIGlmIChsYXJnZVZpZGVvU3RhdGUub2xkUmVzb3VyY2VKaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUub2xkUmVzb3VyY2VKaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gRW5hYmxlIG5ldyBkb21pbmFudCBzcGVha2VyIGluIHRoZSByZW1vdGUgdmlkZW9zIHNlY3Rpb24uXG4gICAgICAgICAgICAgICAgICAgIGlmIChsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5lbmFibGVEb21pbmFudFNwZWFrZXIoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICh1c2VyQ2hhbmdlZCAmJiBsYXJnZVZpZGVvU3RhdGUuaXNWaXNpYmxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyB1c2luZyBcInRoaXNcIiBzaG91bGQgYmUgb2sgYmVjYXVzZSB3ZSdyZSBjYWxsZWRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGZyb20gd2l0aGluIHRoZSBmYWRlT3V0IGV2ZW50LlxuICAgICAgICAgICAgICAgICAgICAgICAgJCh0aGlzKS5mYWRlSW4oMzAwKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmKHVzZXJDaGFuZ2VkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXBkYXRlSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBpZiAodXNlckNoYW5nZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5mYWRlT3V0KDMwMCwgZG9VcGRhdGUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRvVXBkYXRlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKFxuICAgICAgICAgICAgICAgIEFQUC54bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UoXG4gICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpKTtcbiAgICAgICAgfVxuXG4gICAgfTtcblxuICAgIG15LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkID0gZnVuY3Rpb24odmlkZW9TcmMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub1Bpbm5lZEVuZHBvaW50Q2hhbmdlZEV2ZW50LCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc291cmNlSmlkKSB7XG4gICAgICAgIC8vIFJlc3RvcmUgc3R5bGUgZm9yIHByZXZpb3VzbHkgZm9jdXNlZCB2aWRlb1xuICAgICAgICB2YXIgb2xkQ29udGFpbmVyID0gbnVsbDtcbiAgICAgICAgaWYoZm9jdXNlZFZpZGVvSW5mbykge1xuICAgICAgICAgICAgdmFyIGZvY3VzUmVzb3VyY2VKaWQgPSBmb2N1c2VkVmlkZW9JbmZvLnJlc291cmNlSmlkO1xuICAgICAgICAgICAgb2xkQ29udGFpbmVyID0gZ2V0UGFydGljaXBhbnRDb250YWluZXIoZm9jdXNSZXNvdXJjZUppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAob2xkQ29udGFpbmVyKSB7XG4gICAgICAgICAgICBvbGRDb250YWluZXIucmVtb3ZlQ2xhc3MoXCJ2aWRlb0NvbnRhaW5lckZvY3VzZWRcIik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVbmxvY2sgY3VycmVudCBmb2N1c2VkLlxuICAgICAgICBpZiAoZm9jdXNlZFZpZGVvSW5mbyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyA9PT0gdmlkZW9TcmMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGZvY3VzZWRWaWRlb0luZm8gPSBudWxsO1xuICAgICAgICAgICAgdmFyIGRvbWluYW50U3BlYWtlclZpZGVvID0gbnVsbDtcbiAgICAgICAgICAgIC8vIEVuYWJsZSB0aGUgY3VycmVudGx5IHNldCBkb21pbmFudCBzcGVha2VyLlxuICAgICAgICAgICAgaWYgKGN1cnJlbnREb21pbmFudFNwZWFrZXIpIHtcbiAgICAgICAgICAgICAgICBkb21pbmFudFNwZWFrZXJWaWRlb1xuICAgICAgICAgICAgICAgICAgICA9ICQoJyNwYXJ0aWNpcGFudF8nICsgY3VycmVudERvbWluYW50U3BlYWtlciArICc+dmlkZW8nKVxuICAgICAgICAgICAgICAgICAgICAgICAgLmdldCgwKTtcblxuICAgICAgICAgICAgICAgIGlmIChkb21pbmFudFNwZWFrZXJWaWRlbykge1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKFxuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5nZXRWaWRlb1NyYyhkb21pbmFudFNwZWFrZXJWaWRlbyksXG4gICAgICAgICAgICAgICAgICAgICAgICAxLFxuICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudERvbWluYW50U3BlYWtlcik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIW5vUGlubmVkRW5kcG9pbnRDaGFuZ2VkRXZlbnQpIHtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5QSU5ORURfRU5EUE9JTlQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gTG9jayBuZXcgdmlkZW9cbiAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IHtcbiAgICAgICAgICAgIHNyYzogdmlkZW9TcmMsXG4gICAgICAgICAgICByZXNvdXJjZUppZDogcmVzb3VyY2VKaWRcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBVcGRhdGUgZm9jdXNlZC9waW5uZWQgaW50ZXJmYWNlLlxuICAgICAgICBpZiAocmVzb3VyY2VKaWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBjb250YWluZXIgPSBnZXRQYXJ0aWNpcGFudENvbnRhaW5lcihyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICBjb250YWluZXIuYWRkQ2xhc3MoXCJ2aWRlb0NvbnRhaW5lckZvY3VzZWRcIik7XG5cbiAgICAgICAgICAgIGlmICghbm9QaW5uZWRFbmRwb2ludENoYW5nZWRFdmVudCkge1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFVJRXZlbnRzLlBJTk5FRF9FTkRQT0lOVCwgcmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCQoJyNsYXJnZVZpZGVvJykuYXR0cignc3JjJykgPT09IHZpZGVvU3JjICYmXG4gICAgICAgICAgICBWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9PblRvcCgpKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBUcmlnZ2VycyBhIFwidmlkZW8uc2VsZWN0ZWRcIiBldmVudC4gVGhlIFwiZmFsc2VcIiBwYXJhbWV0ZXIgaW5kaWNhdGVzXG4gICAgICAgIC8vIHRoaXMgaXNuJ3QgYSBwcmV6aS5cbiAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcInZpZGVvLnNlbGVjdGVkXCIsIFtmYWxzZV0pO1xuXG4gICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8odmlkZW9TcmMsIDEsIHJlc291cmNlSmlkKTtcblxuICAgICAgICAkKCdhdWRpbycpLmVhY2goZnVuY3Rpb24gKGlkeCwgZWwpIHtcbiAgICAgICAgICAgIGlmIChlbC5pZC5pbmRleE9mKCdtaXhlZG1zbGFiZWwnKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICBlbC52b2x1bWUgPSAwO1xuICAgICAgICAgICAgICAgIGVsLnZvbHVtZSA9IDE7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBQb3NpdGlvbnMgdGhlIGxhcmdlIHZpZGVvLlxuICAgICAqXG4gICAgICogQHBhcmFtIHZpZGVvV2lkdGggdGhlIHN0cmVhbSB2aWRlbyB3aWR0aFxuICAgICAqIEBwYXJhbSB2aWRlb0hlaWdodCB0aGUgc3RyZWFtIHZpZGVvIGhlaWdodFxuICAgICAqL1xuICAgIG15LnBvc2l0aW9uTGFyZ2UgPSBmdW5jdGlvbiAodmlkZW9XaWR0aCwgdmlkZW9IZWlnaHQpIHtcbiAgICAgICAgdmFyIHZpZGVvU3BhY2VXaWR0aCA9ICQoJyN2aWRlb3NwYWNlJykud2lkdGgoKTtcbiAgICAgICAgdmFyIHZpZGVvU3BhY2VIZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG5cbiAgICAgICAgdmFyIHZpZGVvU2l6ZSA9IFZpZGVvTGF5b3V0LmdldFZpZGVvU2l6ZSh2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgICAgICB2YXIgbGFyZ2VWaWRlb1dpZHRoID0gdmlkZW9TaXplWzBdO1xuICAgICAgICB2YXIgbGFyZ2VWaWRlb0hlaWdodCA9IHZpZGVvU2l6ZVsxXTtcblxuICAgICAgICB2YXIgdmlkZW9Qb3NpdGlvbiA9IFZpZGVvTGF5b3V0LmdldFZpZGVvUG9zaXRpb24obGFyZ2VWaWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpO1xuXG4gICAgICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblswXTtcbiAgICAgICAgdmFyIHZlcnRpY2FsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblsxXTtcblxuICAgICAgICBwb3NpdGlvblZpZGVvKCQoJyNsYXJnZVZpZGVvJyksXG4gICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgaG9yaXpvbnRhbEluZGVudCwgdmVydGljYWxJbmRlbnQpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cy9oaWRlcyB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICovXG4gICAgbXkuc2V0TGFyZ2VWaWRlb1Zpc2libGUgPSBmdW5jdGlvbihpc1Zpc2libGUpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcblxuICAgICAgICBpZiAoaXNWaXNpYmxlKSB7XG4gICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcud2F0ZXJtYXJrJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihyZXNvdXJjZUppZCwgdHJ1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgICQoJyNhY3RpdmVTcGVha2VyJykuY3NzKCd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xuICAgICAgICAgICAgJCgnLndhdGVybWFyaycpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihyZXNvdXJjZUppZCwgZmFsc2UpO1xuICAgICAgICAgICAgaWYoZm9jdXNlZFZpZGVvSW5mbykge1xuICAgICAgICAgICAgICAgIHZhciBmb2N1c1Jlc291cmNlSmlkID0gZm9jdXNlZFZpZGVvSW5mby5yZXNvdXJjZUppZDtcbiAgICAgICAgICAgICAgICB2YXIgb2xkQ29udGFpbmVyID0gZ2V0UGFydGljaXBhbnRDb250YWluZXIoZm9jdXNSZXNvdXJjZUppZCk7XG5cbiAgICAgICAgICAgICAgICBpZiAob2xkQ29udGFpbmVyICYmIG9sZENvbnRhaW5lci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIG9sZENvbnRhaW5lci5yZW1vdmVDbGFzcyhcInZpZGVvQ29udGFpbmVyRm9jdXNlZFwiKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IG51bGw7XG4gICAgICAgICAgICAgICAgaWYoZm9jdXNSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAueG1wcC5maW5kSmlkRnJvbVJlc291cmNlKGZvY3VzUmVzb3VyY2VKaWQpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSW5kaWNhdGVzIGlmIHRoZSBsYXJnZSB2aWRlbyBpcyBjdXJyZW50bHkgdmlzaWJsZS5cbiAgICAgKlxuICAgICAqIEByZXR1cm4gPHR0PnRydWU8L3R0PiBpZiB2aXNpYmxlLCA8dHQ+ZmFsc2U8L3R0PiAtIG90aGVyd2lzZVxuICAgICAqL1xuICAgIG15LmlzTGFyZ2VWaWRlb1Zpc2libGUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICQoJyNsYXJnZVZpZGVvJykuaXMoJzp2aXNpYmxlJyk7XG4gICAgfTtcblxuICAgIG15LmlzTGFyZ2VWaWRlb09uVG9wID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgRXRoZXJwYWQgPSByZXF1aXJlKFwiLi4vZXRoZXJwYWQvRXRoZXJwYWRcIik7XG4gICAgICAgIHZhciBQcmV6aSA9IHJlcXVpcmUoXCIuLi9wcmV6aS9QcmV6aVwiKTtcbiAgICAgICAgcmV0dXJuICFQcmV6aS5pc1ByZXNlbnRhdGlvblZpc2libGUoKSAmJiAhRXRoZXJwYWQuaXNWaXNpYmxlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiBjb250YWluZXIgZm9yIHBhcnRpY2lwYW50IGlkZW50aWZpZWQgYnkgZ2l2ZW4gcGVlckppZCBleGlzdHNcbiAgICAgKiBpbiB0aGUgZG9jdW1lbnQgYW5kIGNyZWF0ZXMgaXQgZXZlbnR1YWxseS5cbiAgICAgKiBcbiAgICAgKiBAcGFyYW0gcGVlckppZCBwZWVyIEppZCB0byBjaGVjay5cbiAgICAgKiBAcGFyYW0gdXNlcklkIHVzZXIgZW1haWwgb3IgaWQgZm9yIHNldHRpbmcgdGhlIGF2YXRhclxuICAgICAqIFxuICAgICAqIEByZXR1cm4gUmV0dXJucyA8dHQ+dHJ1ZTwvdHQ+IGlmIHRoZSBwZWVyIGNvbnRhaW5lciBleGlzdHMsXG4gICAgICogPHR0PmZhbHNlPC90dD4gLSBvdGhlcndpc2VcbiAgICAgKi9cbiAgICBteS5lbnN1cmVQZWVyQ29udGFpbmVyRXhpc3RzID0gZnVuY3Rpb24ocGVlckppZCwgdXNlcklkKSB7XG4gICAgICAgIENvbnRhY3RMaXN0LmVuc3VyZUFkZENvbnRhY3QocGVlckppZCwgdXNlcklkKTtcblxuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuXG4gICAgICAgIGlmICghJCgnIycgKyB2aWRlb1NwYW5JZCkubGVuZ3RoKSB7XG4gICAgICAgICAgICB2YXIgY29udGFpbmVyID1cbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5hZGRSZW1vdGVWaWRlb0NvbnRhaW5lcihwZWVySmlkLCB2aWRlb1NwYW5JZCwgdXNlcklkKTtcbiAgICAgICAgICAgIEF2YXRhci5zZXRVc2VyQXZhdGFyKHBlZXJKaWQsIHVzZXJJZCk7XG4gICAgICAgICAgICAvLyBTZXQgZGVmYXVsdCBkaXNwbGF5IG5hbWUuXG4gICAgICAgICAgICBzZXREaXNwbGF5TmFtZSh2aWRlb1NwYW5JZCk7XG5cbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW3ZpZGVvU3BhbklkXSA9XG4gICAgICAgICAgICAgICAgbmV3IENvbm5lY3Rpb25JbmRpY2F0b3IoY29udGFpbmVyLCBwZWVySmlkLCBWaWRlb0xheW91dCk7XG5cbiAgICAgICAgICAgIHZhciBuaWNrZmllbGQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICBuaWNrZmllbGQuY2xhc3NOYW1lID0gXCJuaWNrXCI7XG4gICAgICAgICAgICBuaWNrZmllbGQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUocmVzb3VyY2VKaWQpKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChuaWNrZmllbGQpO1xuXG4gICAgICAgICAgICAvLyBJbiBjYXNlIHRoaXMgaXMgbm90IGN1cnJlbnRseSBpbiB0aGUgbGFzdCBuIHdlIGRvbid0IHNob3cgaXQuXG4gICAgICAgICAgICBpZiAobG9jYWxMYXN0TkNvdW50XG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TkNvdW50ID4gMFxuICAgICAgICAgICAgICAgICYmICQoJyNyZW1vdGVWaWRlb3M+c3BhbicpLmxlbmd0aCA+PSBsb2NhbExhc3ROQ291bnQgKyAyKSB7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdoaWRlJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIG15LmFkZFJlbW90ZVZpZGVvQ29udGFpbmVyID0gZnVuY3Rpb24ocGVlckppZCwgc3BhbklkKSB7XG4gICAgICAgIHZhciBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgIGNvbnRhaW5lci5pZCA9IHNwYW5JZDtcbiAgICAgICAgY29udGFpbmVyLmNsYXNzTmFtZSA9ICd2aWRlb2NvbnRhaW5lcic7XG4gICAgICAgIHZhciByZW1vdGVzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3JlbW90ZVZpZGVvcycpO1xuICAgICAgICByZW1vdGVzLmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIC8vIElmIHRoZSBwZWVySmlkIGlzIG51bGwgdGhlbiB0aGlzIHZpZGVvIHNwYW4gY291bGRuJ3QgYmUgZGlyZWN0bHlcbiAgICAgICAgLy8gYXNzb2NpYXRlZCB3aXRoIGEgcGFydGljaXBhbnQgKHRoaXMgY291bGQgaGFwcGVuIGluIHRoZSBjYXNlIG9mIHByZXppKS5cbiAgICAgICAgaWYgKEFQUC54bXBwLmlzTW9kZXJhdG9yKCkgJiYgcGVlckppZCAhPT0gbnVsbClcbiAgICAgICAgICAgIGFkZFJlbW90ZVZpZGVvTWVudShwZWVySmlkLCBjb250YWluZXIpO1xuICAgICAgICBBdWRpb0xldmVscy51cGRhdGVBdWRpb0xldmVsQ2FudmFzKHBlZXJKaWQsIFZpZGVvTGF5b3V0KTtcblxuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGF1ZGlvIG9yIHZpZGVvIHN0cmVhbSBlbGVtZW50LlxuICAgICAqL1xuICAgIG15LmNyZWF0ZVN0cmVhbUVsZW1lbnQgPSBmdW5jdGlvbiAoc2lkLCBzdHJlYW0pIHtcbiAgICAgICAgdmFyIGlzVmlkZW8gPSBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPiAwO1xuXG4gICAgICAgIHZhciBlbGVtZW50ID0gaXNWaWRlb1xuICAgICAgICAgICAgICAgICAgICAgICAgPyBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd2aWRlbycpXG4gICAgICAgICAgICAgICAgICAgICAgICA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2F1ZGlvJyk7XG4gICAgICAgIHZhciBpZCA9IChpc1ZpZGVvID8gJ3JlbW90ZVZpZGVvXycgOiAncmVtb3RlQXVkaW9fJylcbiAgICAgICAgICAgICAgICAgICAgKyBzaWQgKyAnXycgKyBBUFAuUlRDLmdldFN0cmVhbUlEKHN0cmVhbSk7XG5cbiAgICAgICAgZWxlbWVudC5pZCA9IGlkO1xuICAgICAgICBlbGVtZW50LmF1dG9wbGF5ID0gdHJ1ZTtcbiAgICAgICAgZWxlbWVudC5vbmNvbnRleHRtZW51ID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gZmFsc2U7IH07XG5cbiAgICAgICAgcmV0dXJuIGVsZW1lbnQ7XG4gICAgfTtcblxuICAgIG15LmFkZFJlbW90ZVN0cmVhbUVsZW1lbnRcbiAgICAgICAgPSBmdW5jdGlvbiAoY29udGFpbmVyLCBzaWQsIHN0cmVhbSwgcGVlckppZCwgdGhlc3NyYykge1xuICAgICAgICB2YXIgbmV3RWxlbWVudElkID0gbnVsbDtcblxuICAgICAgICB2YXIgaXNWaWRlbyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG5cbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgdmFyIHN0cmVhbUVsZW1lbnQgPSBWaWRlb0xheW91dC5jcmVhdGVTdHJlYW1FbGVtZW50KHNpZCwgc3RyZWFtKTtcbiAgICAgICAgICAgIG5ld0VsZW1lbnRJZCA9IHN0cmVhbUVsZW1lbnQuaWQ7XG5cbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChzdHJlYW1FbGVtZW50KTtcblxuICAgICAgICAgICAgdmFyIHNlbCA9ICQoJyMnICsgbmV3RWxlbWVudElkKTtcbiAgICAgICAgICAgIHNlbC5oaWRlKCk7XG5cbiAgICAgICAgICAgIC8vIElmIHRoZSBjb250YWluZXIgaXMgY3VycmVudGx5IHZpc2libGUgd2UgYXR0YWNoIHRoZSBzdHJlYW0uXG4gICAgICAgICAgICBpZiAoIWlzVmlkZW9cbiAgICAgICAgICAgICAgICB8fCAoY29udGFpbmVyLm9mZnNldFBhcmVudCAhPT0gbnVsbCAmJiBpc1ZpZGVvKSkge1xuICAgICAgICAgICAgICAgIHZhciB2aWRlb1N0cmVhbSA9IEFQUC5zaW11bGNhc3QuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0oc3RyZWFtKTtcbiAgICAgICAgICAgICAgICBBUFAuUlRDLmF0dGFjaE1lZGlhU3RyZWFtKHNlbCwgdmlkZW9TdHJlYW0pO1xuXG4gICAgICAgICAgICAgICAgaWYgKGlzVmlkZW8pXG4gICAgICAgICAgICAgICAgICAgIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWwsIHRoZXNzcmMsIHN0cmVhbSwgcGVlckppZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHN0cmVhbS5vbmVuZGVkID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzdHJlYW0gZW5kZWQnLCB0aGlzKTtcblxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnJlbW92ZVJlbW90ZVN0cmVhbUVsZW1lbnQoXG4gICAgICAgICAgICAgICAgICAgIHN0cmVhbSwgaXNWaWRlbywgY29udGFpbmVyKTtcblxuICAgICAgICAgICAgICAgIC8vIE5PVEUoZ3ApIGl0IHNlZW1zIHRoYXQgdW5kZXIgY2VydGFpbiBjaXJjdW1zdGFuY2VzLCB0aGVcbiAgICAgICAgICAgICAgICAvLyBvbmVuZGVkIGV2ZW50IGlzIG5vdCBmaXJlZCBhbmQgdGh1cyB0aGUgY29udGFjdCBsaXN0IGlzIG5vdFxuICAgICAgICAgICAgICAgIC8vIHVwZGF0ZWQuXG4gICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAvLyBUaGUgb25lbmRlZCBldmVudCBvZiBhIHN0cmVhbSBzaG91bGQgYmUgZmlyZWQgd2hlbiB0aGUgU1NSQ3NcbiAgICAgICAgICAgICAgICAvLyBjb3JyZXNwb25kaW5nIHRvIHRoYXQgc3RyZWFtIGFyZSByZW1vdmVkIGZyb20gdGhlIFNEUDsgYnV0XG4gICAgICAgICAgICAgICAgLy8gdGhpcyBkb2Vzbid0IHNlZW0gdG8gYWx3YXlzIGJlIHRoZSBjYXNlLCByZXN1bHRpbmcgaW4gZ2hvc3RcbiAgICAgICAgICAgICAgICAvLyBjb250YWN0cy5cbiAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgIC8vIEluIGFuIGF0dGVtcHQgdG8gZml4IHRoZSBnaG9zdCBjb250YWN0cyBwcm9ibGVtLCBJJ20gbW92aW5nXG4gICAgICAgICAgICAgICAgLy8gdGhlIHJlbW92ZUNvbnRhY3QoKSBtZXRob2QgY2FsbCBpbiBhcHAuanMsIGluc2lkZSB0aGVcbiAgICAgICAgICAgICAgICAvLyAnbXVjLmxlZnQnIGV2ZW50IGhhbmRsZXIuXG5cbiAgICAgICAgICAgICAgICAvL2lmIChwZWVySmlkKVxuICAgICAgICAgICAgICAgIC8vICAgIENvbnRhY3RMaXN0LnJlbW92ZUNvbnRhY3QocGVlckppZCk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAvLyBBZGQgY2xpY2sgaGFuZGxlci5cbiAgICAgICAgICAgIGNvbnRhaW5lci5vbmNsaWNrID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICAgICAgLypcbiAgICAgICAgICAgICAgICAgKiBGSVhNRSBJdCB0dXJucyBvdXQgdGhhdCB2aWRlb1RodW1iIG1heSBub3QgZXhpc3QgKGlmIHRoZXJlIGlzXG4gICAgICAgICAgICAgICAgICogbm8gYWN0dWFsIHZpZGVvKS5cbiAgICAgICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgICB2YXIgdmlkZW9UaHVtYiA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpLmdldCgwKTtcbiAgICAgICAgICAgICAgICBpZiAodmlkZW9UaHVtYikge1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5oYW5kbGVWaWRlb1RodW1iQ2xpY2tlZChcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmModmlkZW9UaHVtYiksXG4gICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHBlZXJKaWQpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIEFkZCBob3ZlciBoYW5kbGVyXG4gICAgICAgICAgICAkKGNvbnRhaW5lcikuaG92ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZShjb250YWluZXIuaWQsIHRydWUpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB2aWRlb1NyYyA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICYmICQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYygkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKS5nZXQoMCkpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlIHZpZGVvIGhhcyBiZWVuIFwicGlubmVkXCIgYnkgdGhlIHVzZXIgd2Ugd2FudCB0b1xuICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZSBkaXNwbGF5IG5hbWUgb24gcGxhY2UuXG4gICAgICAgICAgICAgICAgICAgIGlmICghVmlkZW9MYXlvdXQuaXNMYXJnZVZpZGVvVmlzaWJsZSgpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfHwgdmlkZW9TcmMgIT09IEFQUC5SVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpXG4gICAgICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zaG93RGlzcGxheU5hbWUoY29udGFpbmVyLmlkLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBuZXdFbGVtZW50SWQ7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlbW92ZXMgdGhlIHJlbW90ZSBzdHJlYW0gZWxlbWVudCBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiBzdHJlYW0gYW5kXG4gICAgICogcGFyZW50IGNvbnRhaW5lci5cbiAgICAgKiBcbiAgICAgKiBAcGFyYW0gc3RyZWFtIHRoZSBzdHJlYW1cbiAgICAgKiBAcGFyYW0gaXNWaWRlbyA8dHQ+dHJ1ZTwvdHQ+IGlmIGdpdmVuIDx0dD5zdHJlYW08L3R0PiBpcyBhIHZpZGVvIG9uZS5cbiAgICAgKiBAcGFyYW0gY29udGFpbmVyXG4gICAgICovXG4gICAgbXkucmVtb3ZlUmVtb3RlU3RyZWFtRWxlbWVudCA9IGZ1bmN0aW9uIChzdHJlYW0sIGlzVmlkZW8sIGNvbnRhaW5lcikge1xuICAgICAgICBpZiAoIWNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICB2YXIgc2VsZWN0ID0gbnVsbDtcbiAgICAgICAgdmFyIHJlbW92ZWRWaWRlb1NyYyA9IG51bGw7XG4gICAgICAgIGlmIChpc1ZpZGVvKSB7XG4gICAgICAgICAgICBzZWxlY3QgPSAkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKTtcbiAgICAgICAgICAgIHJlbW92ZWRWaWRlb1NyYyA9IEFQUC5SVEMuZ2V0VmlkZW9TcmMoc2VsZWN0LmdldCgwKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgc2VsZWN0ID0gJCgnIycgKyBjb250YWluZXIuaWQgKyAnPmF1ZGlvJyk7XG5cblxuICAgICAgICAvLyBNYXJrIHZpZGVvIGFzIHJlbW92ZWQgdG8gY2FuY2VsIHdhaXRpbmcgbG9vcChpZiB2aWRlbyBpcyByZW1vdmVkXG4gICAgICAgIC8vIGJlZm9yZSBoYXMgc3RhcnRlZClcbiAgICAgICAgc2VsZWN0LnJlbW92ZWQgPSB0cnVlO1xuICAgICAgICBzZWxlY3QucmVtb3ZlKCk7XG5cbiAgICAgICAgdmFyIGF1ZGlvQ291bnQgPSAkKCcjJyArIGNvbnRhaW5lci5pZCArICc+YXVkaW8nKS5sZW5ndGg7XG4gICAgICAgIHZhciB2aWRlb0NvdW50ID0gJCgnIycgKyBjb250YWluZXIuaWQgKyAnPnZpZGVvJykubGVuZ3RoO1xuXG4gICAgICAgIGlmICghYXVkaW9Db3VudCAmJiAhdmlkZW9Db3VudCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXCJSZW1vdmUgd2hvbGUgdXNlclwiLCBjb250YWluZXIuaWQpO1xuICAgICAgICAgICAgaWYoVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbY29udGFpbmVyLmlkXSlcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tjb250YWluZXIuaWRdLnJlbW92ZSgpO1xuICAgICAgICAgICAgLy8gUmVtb3ZlIHdob2xlIGNvbnRhaW5lclxuICAgICAgICAgICAgY29udGFpbmVyLnJlbW92ZSgpO1xuXG4gICAgICAgICAgICBVSVV0aWwucGxheVNvdW5kTm90aWZpY2F0aW9uKCd1c2VyTGVmdCcpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHJlbW92ZWRWaWRlb1NyYylcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZVJlbW92ZWRWaWRlbyhyZW1vdmVkVmlkZW9TcmMpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93L2hpZGUgcGVlciBjb250YWluZXIgZm9yIHRoZSBnaXZlbiByZXNvdXJjZUppZC5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiBzaG93UGVlckNvbnRhaW5lcihyZXNvdXJjZUppZCwgc3RhdGUpIHtcbiAgICAgICAgdmFyIHBlZXJDb250YWluZXIgPSAkKCcjcGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICBpZiAoIXBlZXJDb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdmFyIGlzSGlkZSA9IHN0YXRlID09PSAnaGlkZSc7XG4gICAgICAgIHZhciByZXNpemVUaHVtYm5haWxzID0gZmFsc2U7XG5cbiAgICAgICAgaWYgKCFpc0hpZGUpIHtcbiAgICAgICAgICAgIGlmICghcGVlckNvbnRhaW5lci5pcygnOnZpc2libGUnKSkge1xuICAgICAgICAgICAgICAgIHJlc2l6ZVRodW1ibmFpbHMgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHBlZXJDb250YWluZXIuc2hvdygpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgamlkID0gQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICBpZiAoc3RhdGUgPT0gJ3Nob3cnKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIC8vIHBlZXJDb250YWluZXIuY3NzKCctd2Via2l0LWZpbHRlcicsICcnKTtcblxuICAgICAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihqaWQsIGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgLy8gaWYgKHN0YXRlID09ICdhdmF0YXInKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIC8vIHBlZXJDb250YWluZXIuY3NzKCctd2Via2l0LWZpbHRlcicsICdncmF5c2NhbGUoMTAwJSknKTtcbiAgICAgICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoamlkLCB0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChwZWVyQ29udGFpbmVyLmlzKCc6dmlzaWJsZScpICYmIGlzSGlkZSlcbiAgICAgICAge1xuICAgICAgICAgICAgcmVzaXplVGh1bWJuYWlscyA9IHRydWU7XG4gICAgICAgICAgICBwZWVyQ29udGFpbmVyLmhpZGUoKTtcbiAgICAgICAgICAgIGlmKFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWRdKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzWydwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWRdLmhpZGUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXNpemVUaHVtYm5haWxzKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBXZSB3YW50IHRvIGJlIGFibGUgdG8gcGluIGEgcGFydGljaXBhbnQgZnJvbSB0aGUgY29udGFjdCBsaXN0LCBldmVuXG4gICAgICAgIC8vIGlmIGhlJ3Mgbm90IGluIHRoZSBsYXN0TiBzZXQhXG4gICAgICAgIC8vIENvbnRhY3RMaXN0LnNldENsaWNrYWJsZShyZXNvdXJjZUppZCwgIWlzSGlkZSk7XG5cbiAgICB9O1xuXG4gICAgbXkuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgICAgICBOaWNrbmFtZUhhbmRsZXIuc2V0Tmlja25hbWUobmFtZSk7XG5cbiAgICAgICAgaWYgKCEkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgICAgIGlmIChOaWNrbmFtZUhhbmRsZXIuZ2V0Tmlja25hbWUoKSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB2YXIgbWVIVE1MID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXCJtZVwiKTtcbiAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmh0bWwoTmlja25hbWVIYW5kbGVyLmdldE5pY2tuYW1lKCkgKyBcIiAoXCIgKyBtZUhUTUwgKyBcIilcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdmFyIGRlZmF1bHRIVE1MID0gQVBQLnRyYW5zbGF0aW9uLmdlbmVyYXRlVHJhbnNsYXRvbkhUTUwoXG4gICAgICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5ERUZBVUxUX0xPQ0FMX0RJU1BMQVlfTkFNRSk7XG4gICAgICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKVxuICAgICAgICAgICAgICAgICAgICAuaHRtbChkZWZhdWx0SFRNTCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLnNob3coKTtcbiAgICAgICAgfVxuXG4gICAgICAgICQoJyNlZGl0RGlzcGxheU5hbWUnKS5oaWRlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzL2hpZGVzIHRoZSBkaXNwbGF5IG5hbWUgb24gdGhlIHJlbW90ZSB2aWRlby5cbiAgICAgKiBAcGFyYW0gdmlkZW9TcGFuSWQgdGhlIGlkZW50aWZpZXIgb2YgdGhlIHZpZGVvIHNwYW4gZWxlbWVudFxuICAgICAqIEBwYXJhbSBpc1Nob3cgaW5kaWNhdGVzIGlmIHRoZSBkaXNwbGF5IG5hbWUgc2hvdWxkIGJlIHNob3duIG9yIGhpZGRlblxuICAgICAqL1xuICAgIG15LnNob3dEaXNwbGF5TmFtZSA9IGZ1bmN0aW9uKHZpZGVvU3BhbklkLCBpc1Nob3cpIHtcbiAgICAgICAgdmFyIG5hbWVTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5kaXNwbGF5bmFtZScpLmdldCgwKTtcbiAgICAgICAgaWYgKGlzU2hvdykge1xuICAgICAgICAgICAgaWYgKG5hbWVTcGFuICYmIG5hbWVTcGFuLmlubmVySFRNTCAmJiBuYW1lU3Bhbi5pbm5lckhUTUwubGVuZ3RoKSBcbiAgICAgICAgICAgICAgICBuYW1lU3Bhbi5zZXRBdHRyaWJ1dGUoXCJzdHlsZVwiLCBcImRpc3BsYXk6aW5saW5lLWJsb2NrO1wiKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGlmIChuYW1lU3BhbilcbiAgICAgICAgICAgICAgICBuYW1lU3Bhbi5zZXRBdHRyaWJ1dGUoXCJzdHlsZVwiLCBcImRpc3BsYXk6bm9uZTtcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MgdGhlIHByZXNlbmNlIHN0YXR1cyBtZXNzYWdlIGZvciB0aGUgZ2l2ZW4gdmlkZW8uXG4gICAgICovXG4gICAgbXkuc2V0UHJlc2VuY2VTdGF0dXMgPSBmdW5jdGlvbiAodmlkZW9TcGFuSWQsIHN0YXR1c01zZykge1xuXG4gICAgICAgIGlmICghJCgnIycgKyB2aWRlb1NwYW5JZCkubGVuZ3RoKSB7XG4gICAgICAgICAgICAvLyBObyBjb250YWluZXJcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBzdGF0dXNTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5zdGF0dXMnKTtcbiAgICAgICAgaWYgKCFzdGF0dXNTcGFuLmxlbmd0aCkge1xuICAgICAgICAgICAgLy9BZGQgc3RhdHVzIHNwYW5cbiAgICAgICAgICAgIHN0YXR1c1NwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICBzdGF0dXNTcGFuLmNsYXNzTmFtZSA9ICdzdGF0dXMnO1xuICAgICAgICAgICAgc3RhdHVzU3Bhbi5pZCA9IHZpZGVvU3BhbklkICsgJ19zdGF0dXMnO1xuICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQoc3RhdHVzU3Bhbik7XG5cbiAgICAgICAgICAgIHN0YXR1c1NwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnN0YXR1cycpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRGlzcGxheSBzdGF0dXNcbiAgICAgICAgaWYgKHN0YXR1c01zZyAmJiBzdGF0dXNNc2cubGVuZ3RoKSB7XG4gICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkICsgJ19zdGF0dXMnKS50ZXh0KHN0YXR1c01zZyk7XG4gICAgICAgICAgICBzdGF0dXNTcGFuLmdldCgwKS5zZXRBdHRyaWJ1dGUoXCJzdHlsZVwiLCBcImRpc3BsYXk6aW5saW5lLWJsb2NrO1wiKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIEhpZGVcbiAgICAgICAgICAgIHN0YXR1c1NwYW4uZ2V0KDApLnNldEF0dHJpYnV0ZShcInN0eWxlXCIsIFwiZGlzcGxheTpub25lO1wiKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIHZpc3VhbCBpbmRpY2F0b3IgZm9yIHRoZSBtb2RlcmF0b3Igb2YgdGhlIGNvbmZlcmVuY2UuXG4gICAgICovXG4gICAgbXkuc2hvd01vZGVyYXRvckluZGljYXRvciA9IGZ1bmN0aW9uICgpIHtcblxuICAgICAgICB2YXIgaXNNb2RlcmF0b3IgPSBBUFAueG1wcC5pc01vZGVyYXRvcigpO1xuICAgICAgICBpZiAoaXNNb2RlcmF0b3IpIHtcbiAgICAgICAgICAgIHZhciBpbmRpY2F0b3JTcGFuID0gJCgnI2xvY2FsVmlkZW9Db250YWluZXIgLmZvY3VzaW5kaWNhdG9yJyk7XG5cbiAgICAgICAgICAgIGlmIChpbmRpY2F0b3JTcGFuLmNoaWxkcmVuKCkubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGNyZWF0ZU1vZGVyYXRvckluZGljYXRvckVsZW1lbnQoaW5kaWNhdG9yU3BhblswXSk7XG4gICAgICAgICAgICAgICAgLy90cmFuc2xhdGVzIHRleHQgaW4gZm9jdXMgaW5kaWNhdG9yXG4gICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJCgnI2xvY2FsVmlkZW9Db250YWluZXIgLmZvY3VzaW5kaWNhdG9yJykpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIG1lbWJlcnMgPSBBUFAueG1wcC5nZXRNZW1iZXJzKCk7XG5cbiAgICAgICAgT2JqZWN0LmtleXMobWVtYmVycykuZm9yRWFjaChmdW5jdGlvbiAoamlkKSB7XG5cbiAgICAgICAgICAgIGlmIChTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpID09PSAnZm9jdXMnKSB7XG4gICAgICAgICAgICAgICAgLy8gU2tpcCBzZXJ2ZXIgc2lkZSBmb2N1c1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgICAgIHZhciB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB2YXIgdmlkZW9Db250YWluZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWRlb1NwYW5JZCk7XG5cbiAgICAgICAgICAgIGlmICghdmlkZW9Db250YWluZXIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiTm8gdmlkZW8gY29udGFpbmVyIGZvciBcIiArIGppZCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgbWVtYmVyID0gbWVtYmVyc1tqaWRdO1xuXG4gICAgICAgICAgICBpZiAobWVtYmVyLnJvbGUgPT09ICdtb2RlcmF0b3InKSB7XG4gICAgICAgICAgICAgICAgLy8gUmVtb3ZlIG1lbnUgaWYgcGVlciBpcyBtb2RlcmF0b3JcbiAgICAgICAgICAgICAgICB2YXIgbWVudVNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnJlbW90ZXZpZGVvbWVudScpO1xuICAgICAgICAgICAgICAgIGlmIChtZW51U3Bhbi5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVtb3ZlUmVtb3RlVmlkZW9NZW51KHZpZGVvU3BhbklkKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gU2hvdyBtb2RlcmF0b3IgaW5kaWNhdG9yXG4gICAgICAgICAgICAgICAgdmFyIGluZGljYXRvclNwYW5cbiAgICAgICAgICAgICAgICAgICAgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJyAuZm9jdXNpbmRpY2F0b3InKTtcblxuICAgICAgICAgICAgICAgIGlmICghaW5kaWNhdG9yU3BhbiB8fCBpbmRpY2F0b3JTcGFuLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBpbmRpY2F0b3JTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgICAgICAgICAgICAgICAgICBpbmRpY2F0b3JTcGFuLmNsYXNzTmFtZSA9ICdmb2N1c2luZGljYXRvcic7XG5cbiAgICAgICAgICAgICAgICAgICAgdmlkZW9Db250YWluZXIuYXBwZW5kQ2hpbGQoaW5kaWNhdG9yU3Bhbik7XG5cbiAgICAgICAgICAgICAgICAgICAgY3JlYXRlTW9kZXJhdG9ySW5kaWNhdG9yRWxlbWVudChpbmRpY2F0b3JTcGFuKTtcbiAgICAgICAgICAgICAgICAgICAgLy90cmFuc2xhdGVzIHRleHQgaW4gZm9jdXMgaW5kaWNhdG9yc1xuICAgICAgICAgICAgICAgICAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlRWxlbWVudCgkKCcjJyArIHZpZGVvU3BhbklkICsgJyAuZm9jdXNpbmRpY2F0b3InKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChpc01vZGVyYXRvcikge1xuICAgICAgICAgICAgICAgIC8vIFdlIGFyZSBtb2RlcmF0b3IsIGJ1dCB1c2VyIGlzIG5vdCAtIGFkZCBtZW51XG4gICAgICAgICAgICAgICAgaWYgKCQoJyNyZW1vdGVfcG9wdXBtZW51XycgKyByZXNvdXJjZUppZCkubGVuZ3RoIDw9IDApIHtcbiAgICAgICAgICAgICAgICAgICAgYWRkUmVtb3RlVmlkZW9NZW51KFxuICAgICAgICAgICAgICAgICAgICAgICAgamlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIHZpZGVvIG11dGVkIGluZGljYXRvciBvdmVyIHNtYWxsIHZpZGVvcy5cbiAgICAgKi9cbiAgICBteS5zaG93VmlkZW9JbmRpY2F0b3IgPSBmdW5jdGlvbih2aWRlb1NwYW5JZCwgaXNNdXRlZCkge1xuICAgICAgICB2YXIgdmlkZW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnZpZGVvTXV0ZWQnKTtcblxuICAgICAgICBpZiAoaXNNdXRlZCA9PT0gJ2ZhbHNlJykge1xuICAgICAgICAgICAgaWYgKHZpZGVvTXV0ZWRTcGFuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICB2aWRlb011dGVkU3Bhbi5yZW1vdmUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGlmKHZpZGVvTXV0ZWRTcGFuLmxlbmd0aCA9PSAwKSB7XG4gICAgICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4uY2xhc3NOYW1lID0gJ3ZpZGVvTXV0ZWQnO1xuXG4gICAgICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQodmlkZW9NdXRlZFNwYW4pO1xuXG4gICAgICAgICAgICAgICAgdmFyIG11dGVkSW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgICAgICAgICAgICAgIG11dGVkSW5kaWNhdG9yLmNsYXNzTmFtZSA9ICdpY29uLWNhbWVyYS1kaXNhYmxlZCc7XG4gICAgICAgICAgICAgICAgVUlVdGlsLnNldFRvb2x0aXAobXV0ZWRJbmRpY2F0b3IsXG4gICAgICAgICAgICAgICAgICAgIFwidmlkZW90aHVtYm5haWwudmlkZW9tdXRlXCIsXG4gICAgICAgICAgICAgICAgICAgIFwidG9wXCIpO1xuICAgICAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmFwcGVuZENoaWxkKG11dGVkSW5kaWNhdG9yKTtcbiAgICAgICAgICAgICAgICAvL3RyYW5zbGF0ZSB0ZXh0cyBmb3IgbXV0ZWQgaW5kaWNhdG9yXG4gICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJCgnIycgKyB2aWRlb1NwYW5JZCAgKyBcIiA+IHNwYW4gPiBpXCIpKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTXV0ZVBvc2l0aW9uKHZpZGVvU3BhbklkKTtcblxuICAgICAgICB9XG4gICAgfTtcblxuICAgIG15LnVwZGF0ZU11dGVQb3NpdGlvbiA9IGZ1bmN0aW9uICh2aWRlb1NwYW5JZCkge1xuICAgICAgICB2YXIgYXVkaW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLmF1ZGlvTXV0ZWQnKTtcbiAgICAgICAgdmFyIGNvbm5lY3Rpb25JbmRpY2F0b3IgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5kaXYuY29ubmVjdGlvbmluZGljYXRvcicpO1xuICAgICAgICB2YXIgdmlkZW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnZpZGVvTXV0ZWQnKTtcbiAgICAgICAgaWYoY29ubmVjdGlvbkluZGljYXRvci5sZW5ndGggPiAwXG4gICAgICAgICAgICAmJiBjb25uZWN0aW9uSW5kaWNhdG9yWzBdLnN0eWxlLmRpc3BsYXkgIT0gXCJub25lXCIpIHtcbiAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IFwiMjNweFwifSk7XG4gICAgICAgICAgICB2aWRlb011dGVkU3Bhbi5jc3Moe3JpZ2h0OiAoKGF1ZGlvTXV0ZWRTcGFuLmxlbmd0aCA+IDA/IDIzIDogMCkgKyAzMCkgKyBcInB4XCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IFwiMHB4XCJ9KTtcbiAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IChhdWRpb011dGVkU3Bhbi5sZW5ndGggPiAwPyAzMCA6IDApICsgXCJweFwifSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogU2hvd3MgYXVkaW8gbXV0ZWQgaW5kaWNhdG9yIG92ZXIgc21hbGwgdmlkZW9zLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBpc011dGVkXG4gICAgICovXG4gICAgbXkuc2hvd0F1ZGlvSW5kaWNhdG9yID0gZnVuY3Rpb24odmlkZW9TcGFuSWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIGF1ZGlvTXV0ZWRTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5hdWRpb011dGVkJyk7XG5cbiAgICAgICAgaWYgKGlzTXV0ZWQgPT09ICdmYWxzZScpIHtcbiAgICAgICAgICAgIGlmIChhdWRpb011dGVkU3Bhbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4ucG9wb3ZlcignaGlkZScpO1xuICAgICAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLnJlbW92ZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYoYXVkaW9NdXRlZFNwYW4ubGVuZ3RoID09IDAgKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uY2xhc3NOYW1lID0gJ2F1ZGlvTXV0ZWQnO1xuICAgICAgICAgICAgICAgIFVJVXRpbC5zZXRUb29sdGlwKGF1ZGlvTXV0ZWRTcGFuLFxuICAgICAgICAgICAgICAgICAgICBcInZpZGVvdGh1bWJuYWlsLm11dGVcIixcbiAgICAgICAgICAgICAgICAgICAgXCJ0b3BcIik7XG5cbiAgICAgICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkKVswXS5hcHBlbmRDaGlsZChhdWRpb011dGVkU3Bhbik7XG4gICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJCgnIycgKyB2aWRlb1NwYW5JZCArIFwiID4gc3BhblwiKSk7XG4gICAgICAgICAgICAgICAgdmFyIG11dGVkSW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgICAgICAgICAgICAgIG11dGVkSW5kaWNhdG9yLmNsYXNzTmFtZSA9ICdpY29uLW1pYy1kaXNhYmxlZCc7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uYXBwZW5kQ2hpbGQobXV0ZWRJbmRpY2F0b3IpO1xuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVNdXRlUG9zaXRpb24odmlkZW9TcGFuSWQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qXG4gICAgICogU2hvd3Mgb3IgaGlkZXMgdGhlIGF1ZGlvIG11dGVkIGluZGljYXRvciBvdmVyIHRoZSBsb2NhbCB0aHVtYm5haWwgdmlkZW8uXG4gICAgICogQHBhcmFtIHtib29sZWFufSBpc011dGVkXG4gICAgICovXG4gICAgbXkuc2hvd0xvY2FsQXVkaW9JbmRpY2F0b3IgPSBmdW5jdGlvbihpc011dGVkKSB7XG4gICAgICAgIFZpZGVvTGF5b3V0LnNob3dBdWRpb0luZGljYXRvcignbG9jYWxWaWRlb0NvbnRhaW5lcicsIGlzTXV0ZWQudG9TdHJpbmcoKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIGxhcmdlIHZpZGVvIGNvbnRhaW5lci5cbiAgICAgKi9cbiAgICBteS5yZXNpemVMYXJnZVZpZGVvQ29udGFpbmVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBDaGF0LnJlc2l6ZUNoYXQoKTtcbiAgICAgICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcbiAgICAgICAgdmFyIGF2YWlsYWJsZVdpZHRoID0gVUlVdGlsLmdldEF2YWlsYWJsZVZpZGVvV2lkdGgoKTtcblxuICAgICAgICBpZiAoYXZhaWxhYmxlV2lkdGggPCAwIHx8IGF2YWlsYWJsZUhlaWdodCA8IDApIHJldHVybjtcblxuICAgICAgICAkKCcjdmlkZW9zcGFjZScpLndpZHRoKGF2YWlsYWJsZVdpZHRoKTtcbiAgICAgICAgJCgnI3ZpZGVvc3BhY2UnKS5oZWlnaHQoYXZhaWxhYmxlSGVpZ2h0KTtcbiAgICAgICAgJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS53aWR0aChhdmFpbGFibGVXaWR0aCk7XG4gICAgICAgICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykuaGVpZ2h0KGF2YWlsYWJsZUhlaWdodCk7XG5cbiAgICAgICAgdmFyIGF2YXRhclNpemUgPSBpbnRlcmZhY2VDb25maWcuQUNUSVZFX1NQRUFLRVJfQVZBVEFSX1NJWkU7XG4gICAgICAgIHZhciB0b3AgPSBhdmFpbGFibGVIZWlnaHQgLyAyIC0gYXZhdGFyU2l6ZSAvIDQgKiAzO1xuICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndG9wJywgdG9wKTtcblxuICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGh1bWJuYWlscy5cbiAgICAgKi9cbiAgICBteS5yZXNpemVUaHVtYm5haWxzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB2aWRlb1NwYWNlV2lkdGggPSAkKCcjcmVtb3RlVmlkZW9zJykud2lkdGgoKTtcblxuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9TcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHdpZHRoID0gdGh1bWJuYWlsU2l6ZVswXTtcbiAgICAgICAgdmFyIGhlaWdodCA9IHRodW1ibmFpbFNpemVbMV07XG5cbiAgICAgICAgLy8gc2l6ZSB2aWRlb3Mgc28gdGhhdCB3aGlsZSBrZWVwaW5nIEFSIGFuZCBtYXggaGVpZ2h0LCB3ZSBoYXZlIGFcbiAgICAgICAgLy8gbmljZSBmaXRcbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcycpLmhlaWdodChoZWlnaHQpO1xuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS53aWR0aCh3aWR0aCk7XG4gICAgICAgICQoJyNyZW1vdGVWaWRlb3M+c3BhbicpLmhlaWdodChoZWlnaHQpO1xuXG4gICAgICAgICQoJy51c2VyQXZhdGFyJykuY3NzKCdsZWZ0JywgKHdpZHRoIC0gaGVpZ2h0KSAvIDIpO1xuXG5cblxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwicmVtb3RldmlkZW8ucmVzaXplZFwiLCBbd2lkdGgsIGhlaWdodF0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBkb21pbmFudCBzcGVha2VyIFVJLlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCB0b1xuICAgICAqIGFjdGl2YXRlL2RlYWN0aXZhdGVcbiAgICAgKiBAcGFyYW0gaXNFbmFibGUgaW5kaWNhdGVzIGlmIHRoZSBkb21pbmFudCBzcGVha2VyIHNob3VsZCBiZSBlbmFibGVkIG9yXG4gICAgICogZGlzYWJsZWRcbiAgICAgKi9cbiAgICBteS5lbmFibGVEb21pbmFudFNwZWFrZXIgPSBmdW5jdGlvbihyZXNvdXJjZUppZCwgaXNFbmFibGUpIHtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSBudWxsO1xuICAgICAgICB2YXIgdmlkZW9Db250YWluZXJJZCA9IG51bGw7XG4gICAgICAgIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ2xvY2FsVmlkZW9XcmFwcGVyJztcbiAgICAgICAgICAgIHZpZGVvQ29udGFpbmVySWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB2aWRlb0NvbnRhaW5lcklkID0gdmlkZW9TcGFuSWQ7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSByZXNvdXJjZUppZDtcbiAgICAgICAgdmFyIG5hbWVTcGFuID0gJCgnIycgKyB2aWRlb0NvbnRhaW5lcklkICsgJz5zcGFuLmRpc3BsYXluYW1lJyk7XG4gICAgICAgIGlmIChuYW1lU3Bhbi5sZW5ndGggPiAwKVxuICAgICAgICAgICAgZGlzcGxheU5hbWUgPSBuYW1lU3Bhbi5odG1sKCk7XG5cbiAgICAgICAgY29uc29sZS5sb2coXCJVSSBlbmFibGUgZG9taW5hbnQgc3BlYWtlclwiLFxuICAgICAgICAgICAgZGlzcGxheU5hbWUsXG4gICAgICAgICAgICByZXNvdXJjZUppZCxcbiAgICAgICAgICAgIGlzRW5hYmxlKTtcblxuICAgICAgICB2aWRlb1NwYW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWRlb0NvbnRhaW5lcklkKTtcblxuICAgICAgICBpZiAoIXZpZGVvU3Bhbikge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHZpZGVvID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+dmlkZW8nKTtcblxuICAgICAgICBpZiAodmlkZW8gJiYgdmlkZW8ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgaWYgKGlzRW5hYmxlKSB7XG4gICAgICAgICAgICAgICAgdmFyIGlzTGFyZ2VWaWRlb1Zpc2libGUgPSBWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9PblRvcCgpO1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSh2aWRlb0NvbnRhaW5lcklkLCBpc0xhcmdlVmlkZW9WaXNpYmxlKTtcblxuICAgICAgICAgICAgICAgIGlmICghdmlkZW9TcGFuLmNsYXNzTGlzdC5jb250YWlucyhcImRvbWluYW50c3BlYWtlclwiKSlcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFuLmNsYXNzTGlzdC5hZGQoXCJkb21pbmFudHNwZWFrZXJcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zaG93RGlzcGxheU5hbWUodmlkZW9Db250YWluZXJJZCwgZmFsc2UpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHZpZGVvU3Bhbi5jbGFzc0xpc3QuY29udGFpbnMoXCJkb21pbmFudHNwZWFrZXJcIikpXG4gICAgICAgICAgICAgICAgICAgIHZpZGVvU3Bhbi5jbGFzc0xpc3QucmVtb3ZlKFwiZG9taW5hbnRzcGVha2VyXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCkpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgdGhlIHRodW1ibmFpbCBzaXplLlxuICAgICAqXG4gICAgICogQHBhcmFtIHZpZGVvU3BhY2VXaWR0aCB0aGUgd2lkdGggb2YgdGhlIHZpZGVvIHNwYWNlXG4gICAgICovXG4gICAgbXkuY2FsY3VsYXRlVGh1bWJuYWlsU2l6ZSA9IGZ1bmN0aW9uICh2aWRlb1NwYWNlV2lkdGgpIHtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIHRoZSBhdmFpbGFibGUgaGVpZ2h0LCB3aGljaCBpcyB0aGUgaW5uZXIgd2luZG93IGhlaWdodCBtaW51c1xuICAgICAgIC8vIDM5cHggZm9yIHRoZSBoZWFkZXIgbWludXMgMnB4IGZvciB0aGUgZGVsaW1pdGVyIGxpbmVzIG9uIHRoZSB0b3AgYW5kXG4gICAgICAgLy8gYm90dG9tIG9mIHRoZSBsYXJnZSB2aWRlbywgbWludXMgdGhlIDM2cHggc3BhY2UgaW5zaWRlIHRoZSByZW1vdGVWaWRlb3NcbiAgICAgICAvLyBjb250YWluZXIgdXNlZCBmb3IgaGlnaGxpZ2h0aW5nIHNoYWRvdy5cbiAgICAgICB2YXIgYXZhaWxhYmxlSGVpZ2h0ID0gMTAwO1xuXG4gICAgICAgIHZhciBudW12aWRzID0gJCgnI3JlbW90ZVZpZGVvcz5zcGFuOnZpc2libGUnKS5sZW5ndGg7XG4gICAgICAgIGlmIChsb2NhbExhc3ROQ291bnQgJiYgbG9jYWxMYXN0TkNvdW50ID4gMCkge1xuICAgICAgICAgICAgbnVtdmlkcyA9IE1hdGgubWluKGxvY2FsTGFzdE5Db3VudCArIDEsIG51bXZpZHMpO1xuICAgICAgICB9XG5cbiAgICAgICAvLyBSZW1vdmUgdGhlIDNweCBib3JkZXJzIGFycm91bmQgdmlkZW9zIGFuZCBib3JkZXIgYXJvdW5kIHRoZSByZW1vdGVcbiAgICAgICAvLyB2aWRlb3MgYXJlYSBhbmQgdGhlIDQgcGl4ZWxzIGJldHdlZW4gdGhlIGxvY2FsIHZpZGVvIGFuZCB0aGUgb3RoZXJzXG4gICAgICAgLy9UT0RPOiBGaW5kIG91dCB3aGVyZSB0aGUgNCBwaXhlbHMgY29tZSBmcm9tIGFuZCByZW1vdmUgdGhlbVxuICAgICAgIHZhciBhdmFpbGFibGVXaW5XaWR0aCA9IHZpZGVvU3BhY2VXaWR0aCAtIDIgKiAzICogbnVtdmlkcyAtIDcwIC0gNDtcblxuICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IGF2YWlsYWJsZVdpbldpZHRoIC8gbnVtdmlkcztcbiAgICAgICB2YXIgYXNwZWN0UmF0aW8gPSAxNi4wIC8gOS4wO1xuICAgICAgIHZhciBtYXhIZWlnaHQgPSBNYXRoLm1pbigxNjAsIGF2YWlsYWJsZUhlaWdodCk7XG4gICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gTWF0aC5taW4obWF4SGVpZ2h0LCBhdmFpbGFibGVXaWR0aCAvIGFzcGVjdFJhdGlvKTtcbiAgICAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0IDwgYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbykge1xuICAgICAgICAgICBhdmFpbGFibGVXaWR0aCA9IE1hdGguZmxvb3IoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8pO1xuICAgICAgIH1cblxuICAgICAgIHJldHVybiBbYXZhaWxhYmxlV2lkdGgsIGF2YWlsYWJsZUhlaWdodF07XG4gICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgcmVtb3RlIHZpZGVvIG1lbnUuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gamlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZm9yIHdoaWNoIHdlJ3JlIGFkZGluZyBhIG1lbnUuXG4gICAgICogQHBhcmFtIGlzTXV0ZWQgaW5kaWNhdGVzIHRoZSBjdXJyZW50IG11dGUgc3RhdGVcbiAgICAgKi9cbiAgICBteS51cGRhdGVSZW1vdGVWaWRlb01lbnUgPSBmdW5jdGlvbihqaWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIG11dGVNZW51SXRlbVxuICAgICAgICAgICAgPSAkKCcjcmVtb3RlX3BvcHVwbWVudV8nXG4gICAgICAgICAgICAgICAgICAgICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKVxuICAgICAgICAgICAgICAgICAgICArICc+bGk+YS5tdXRlbGluaycpO1xuXG4gICAgICAgIHZhciBtdXRlZEluZGljYXRvciA9IFwiPGkgY2xhc3M9J2ljb24tbWljLWRpc2FibGVkJz48L2k+XCI7XG5cbiAgICAgICAgaWYgKG11dGVNZW51SXRlbS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBtdXRlTGluayA9IG11dGVNZW51SXRlbS5nZXQoMCk7XG5cbiAgICAgICAgICAgIGlmIChpc011dGVkID09PSAndHJ1ZScpIHtcbiAgICAgICAgICAgICAgICBtdXRlTGluay5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArICcgTXV0ZWQnO1xuICAgICAgICAgICAgICAgIG11dGVMaW5rLmNsYXNzTmFtZSA9ICdtdXRlbGluayBkaXNhYmxlZCc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBtdXRlTGluay5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArICcgTXV0ZSc7XG4gICAgICAgICAgICAgICAgbXV0ZUxpbmsuY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IGRvbWluYW50IHNwZWFrZXIgcmVzb3VyY2UgamlkLlxuICAgICAqL1xuICAgIG15LmdldERvbWluYW50U3BlYWtlclJlc291cmNlSmlkID0gZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY3VycmVudERvbWluYW50U3BlYWtlcjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyByZXNvdXJjZSBqaWQgdG8gdGhlIGdpdmVuIHBlZXIgY29udGFpbmVyXG4gICAgICogRE9NIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHRoZSBjb3JyZXNwb25kaW5nIHJlc291cmNlIGppZCB0byB0aGUgZ2l2ZW4gcGVlciBjb250YWluZXJcbiAgICAgKiBET00gZWxlbWVudFxuICAgICAqL1xuICAgIG15LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZCA9IGZ1bmN0aW9uIChjb250YWluZXJFbGVtZW50KSB7XG4gICAgICAgIHZhciBpID0gY29udGFpbmVyRWxlbWVudC5pZC5pbmRleE9mKCdwYXJ0aWNpcGFudF8nKTtcblxuICAgICAgICBpZiAoaSA+PSAwKVxuICAgICAgICAgICAgcmV0dXJuIGNvbnRhaW5lckVsZW1lbnQuaWQuc3Vic3RyaW5nKGkgKyAxMik7IFxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBjb250YWN0IGxpc3QgaXRlbSBjbGlja2VkLlxuICAgICAqL1xuICAgICQoQ29udGFjdExpc3QpLmJpbmQoJ2NvbnRhY3RjbGlja2VkJywgZnVuY3Rpb24oZXZlbnQsIGppZCkge1xuICAgICAgICBpZiAoIWppZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHJlc291cmNlID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgdmFyIHZpZGVvQ29udGFpbmVyID0gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlKTtcbiAgICAgICAgaWYgKHZpZGVvQ29udGFpbmVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciB2aWRlb1RodW1iID0gJCgndmlkZW8nLCB2aWRlb0NvbnRhaW5lcikuZ2V0KDApO1xuICAgICAgICAgICAgLy8gSXQgaXMgbm90IGFsd2F5cyB0aGUgY2FzZSB0aGF0IGEgdmlkZW9UaHVtYiBleGlzdHMgKGlmIHRoZXJlIGlzXG4gICAgICAgICAgICAvLyBubyBhY3R1YWwgdmlkZW8pLlxuICAgICAgICAgICAgaWYgKHZpZGVvVGh1bWIpIHtcbiAgICAgICAgICAgICAgICBpZiAodmlkZW9UaHVtYi5zcmMgJiYgdmlkZW9UaHVtYi5zcmMgIT0gJycpIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBXZSBoYXZlIGEgdmlkZW8gc3JjLCBncmVhdCEgTGV0J3MgdXBkYXRlIHRoZSBsYXJnZSB2aWRlb1xuICAgICAgICAgICAgICAgICAgICAvLyBub3cuXG5cbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQoXG4gICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1RodW1iLnNyYyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSBkb24ndCBoYXZlIGEgdmlkZW8gc3JjIGZvciBqaWQsIHRoZXJlJ3MgYWJzb2x1dGVseVxuICAgICAgICAgICAgICAgICAgICAvLyBubyBwb2ludCBpbiBjYWxsaW5nIGhhbmRsZVZpZGVvVGh1bWJDbGlja2VkOyBRdWl0ZVxuICAgICAgICAgICAgICAgICAgICAvLyBzaW1wbHksIGl0IHdvbid0IHdvcmsgYmVjYXVzZSBpdCBuZWVkcyBhbiBzcmMgdG8gYXR0YWNoXG4gICAgICAgICAgICAgICAgICAgIC8vIHRvIHRoZSBsYXJnZSB2aWRlby5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gSW5zdGVhZCwgd2UgdHJpZ2dlciB0aGUgcGlubmVkIGVuZHBvaW50IGNoYW5nZWQgZXZlbnQgdG9cbiAgICAgICAgICAgICAgICAgICAgLy8gbGV0IHRoZSBicmlkZ2UgYWRqdXN0IGl0cyBsYXN0TiBzZXQgZm9yIG15amlkIGFuZCBzdG9yZVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgcGlubmVkIHVzZXIgaW4gdGhlIGxhc3ROUGlja3VwSmlkIHZhcmlhYmxlIHRvIGJlXG4gICAgICAgICAgICAgICAgICAgIC8vIHBpY2tlZCB1cCBsYXRlciBieSB0aGUgbGFzdE4gY2hhbmdlZCBldmVudCBoYW5kbGVyLlxuXG4gICAgICAgICAgICAgICAgICAgIGxhc3ROUGlja3VwSmlkID0gamlkO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5QSU5ORURfRU5EUE9JTlQsXG4gICAgICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGppZCA9PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAgICAgJChcIiNsb2NhbFZpZGVvQ29udGFpbmVyXCIpLmNsaWNrKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIE9uIGF1ZGlvIG11dGVkIGV2ZW50LlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ2F1ZGlvbXV0ZWQubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgLypcbiAgICAgICAgIC8vIEZJWE1FOiBidXQgZm9jdXMgY2FuIG5vdCBtdXRlIGluIHRoaXMgY2FzZSA/IC0gY2hlY2tcbiAgICAgICAgaWYgKGppZCA9PT0geG1wcC5teUppZCgpKSB7XG5cbiAgICAgICAgICAgIC8vIFRoZSBsb2NhbCBtdXRlIGluZGljYXRvciBpcyBjb250cm9sbGVkIGxvY2FsbHlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSovXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChqaWQgPT09IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ2xvY2FsVmlkZW9Db250YWluZXInO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQpO1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBtdXRlZEF1ZGlvc1tqaWRdID0gaXNNdXRlZDtcblxuICAgICAgICBpZiAoQVBQLnhtcHAuaXNNb2RlcmF0b3IoKSkge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3RlVmlkZW9NZW51KGppZCwgaXNNdXRlZCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodmlkZW9TcGFuSWQpXG4gICAgICAgICAgICBWaWRlb0xheW91dC5zaG93QXVkaW9JbmRpY2F0b3IodmlkZW9TcGFuSWQsIGlzTXV0ZWQpO1xuICAgIH0pO1xuXG4gICAgLyoqXG4gICAgICogT24gdmlkZW8gbXV0ZWQgZXZlbnQuXG4gICAgICovXG4gICAgJChkb2N1bWVudCkuYmluZCgndmlkZW9tdXRlZC5tdWMnLCBmdW5jdGlvbiAoZXZlbnQsIGppZCwgdmFsdWUpIHtcbiAgICAgICAgdmFyIGlzTXV0ZWQgPSAodmFsdWUgPT09IFwidHJ1ZVwiKTtcbiAgICAgICAgaWYoamlkICE9PSBBUFAueG1wcC5teUppZCgpICYmICFBUFAuUlRDLm11dGVSZW1vdGVWaWRlb1N0cmVhbShqaWQsIGlzTXV0ZWQpKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihqaWQsIGlzTXV0ZWQpO1xuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSBudWxsO1xuICAgICAgICBpZiAoamlkID09PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdsb2NhbFZpZGVvQ29udGFpbmVyJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMoamlkKTtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHZpZGVvU3BhbklkKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd1ZpZGVvSW5kaWNhdG9yKHZpZGVvU3BhbklkLCB2YWx1ZSk7XG4gICAgfSk7XG5cbiAgICAvKipcbiAgICAgKiBEaXNwbGF5IG5hbWUgY2hhbmdlZC5cbiAgICAgKi9cbiAgICBteS5vbkRpc3BsYXlOYW1lQ2hhbmdlZCA9XG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChqaWQsIGRpc3BsYXlOYW1lLCBzdGF0dXMpIHtcbiAgICAgICAgaWYgKGppZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInXG4gICAgICAgICAgICB8fCBqaWQgPT09IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgIHNldERpc3BsYXlOYW1lKCdsb2NhbFZpZGVvQ29udGFpbmVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlOYW1lKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMoamlkKTtcbiAgICAgICAgICAgIHNldERpc3BsYXlOYW1lKFxuICAgICAgICAgICAgICAgICdwYXJ0aWNpcGFudF8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSxcbiAgICAgICAgICAgICAgICBkaXNwbGF5TmFtZSxcbiAgICAgICAgICAgICAgICBzdGF0dXMpO1xuICAgICAgICB9XG5cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT24gZG9taW5hbnQgc3BlYWtlciBjaGFuZ2VkIGV2ZW50LlxuICAgICAqL1xuICAgIG15Lm9uRG9taW5hbnRTcGVha2VyQ2hhbmdlZCA9IGZ1bmN0aW9uIChyZXNvdXJjZUppZCkge1xuICAgICAgICAvLyBXZSBpZ25vcmUgbG9jYWwgdXNlciBldmVudHMuXG4gICAgICAgIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdmFyIG1lbWJlcnMgPSBBUFAueG1wcC5nZXRNZW1iZXJzKCk7XG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgY3VycmVudCBkb21pbmFudCBzcGVha2VyLlxuICAgICAgICBpZiAocmVzb3VyY2VKaWQgIT09IGN1cnJlbnREb21pbmFudFNwZWFrZXIpIHtcbiAgICAgICAgICAgIHZhciBvbGRTcGVha2VyVmlkZW9TcGFuSWQgPSBcInBhcnRpY2lwYW50X1wiICsgY3VycmVudERvbWluYW50U3BlYWtlcixcbiAgICAgICAgICAgICAgICBuZXdTcGVha2VyVmlkZW9TcGFuSWQgPSBcInBhcnRpY2lwYW50X1wiICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB2YXIgY3VycmVudEpJRCA9IEFQUC54bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UoY3VycmVudERvbWluYW50U3BlYWtlcik7XG4gICAgICAgICAgICB2YXIgbmV3SklEID0gQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICBpZihjdXJyZW50RG9taW5hbnRTcGVha2VyICYmICghbWVtYmVycyB8fCAhbWVtYmVyc1tjdXJyZW50SklEXSB8fFxuICAgICAgICAgICAgICAgICFtZW1iZXJzW2N1cnJlbnRKSURdLmRpc3BsYXlOYW1lKSkge1xuICAgICAgICAgICAgICAgIHNldERpc3BsYXlOYW1lKG9sZFNwZWFrZXJWaWRlb1NwYW5JZCwgbnVsbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZihyZXNvdXJjZUppZCAmJiAoIW1lbWJlcnMgfHwgIW1lbWJlcnNbbmV3SklEXSB8fFxuICAgICAgICAgICAgICAgICFtZW1iZXJzW25ld0pJRF0uZGlzcGxheU5hbWUpKSB7XG4gICAgICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUobmV3U3BlYWtlclZpZGVvU3BhbklkLCBudWxsLFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9ET01JTkFOVF9TUEVBS0VSX0RJU1BMQVlfTkFNRSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjdXJyZW50RG9taW5hbnRTcGVha2VyID0gcmVzb3VyY2VKaWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPYnRhaW4gY29udGFpbmVyIGZvciBuZXcgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgdmFyIGNvbnRhaW5lciAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcbiAgICAgICAgICAgICAgICAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAvLyBMb2NhbCB2aWRlbyB3aWxsIG5vdCBoYXZlIGNvbnRhaW5lciBmb3VuZCwgYnV0IHRoYXQncyBva1xuICAgICAgICAvLyBzaW5jZSB3ZSBkb24ndCB3YW50IHRvIHN3aXRjaCB0byBsb2NhbCB2aWRlby5cbiAgICAgICAgaWYgKGNvbnRhaW5lciAmJiAhZm9jdXNlZFZpZGVvSW5mbylcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIHZpZGVvID0gY29udGFpbmVyLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwidmlkZW9cIik7XG5cbiAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgbGFyZ2UgdmlkZW8gaWYgdGhlIHZpZGVvIHNvdXJjZSBpcyBhbHJlYWR5IGF2YWlsYWJsZSxcbiAgICAgICAgICAgIC8vIG90aGVyd2lzZSB3YWl0IGZvciB0aGUgXCJ2aWRlb2FjdGl2ZS5qaW5nbGVcIiBldmVudC5cbiAgICAgICAgICAgIGlmICh2aWRlby5sZW5ndGggJiYgdmlkZW9bMF0uY3VycmVudFRpbWUgPiAwKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oQVBQLlJUQy5nZXRWaWRlb1NyYyh2aWRlb1swXSksIHJlc291cmNlSmlkKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBsYXN0IE4gY2hhbmdlIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGxhc3RORW5kcG9pbnRzIHRoZSBsaXN0IG9mIGxhc3QgTiBlbmRwb2ludHNcbiAgICAgKiBAcGFyYW0gZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB0aGUgbGlzdCBjdXJyZW50bHkgZW50ZXJpbmcgbGFzdCBOXG4gICAgICogZW5kcG9pbnRzXG4gICAgICovXG4gICAgbXkub25MYXN0TkVuZHBvaW50c0NoYW5nZWQgPSBmdW5jdGlvbiAoIGxhc3RORW5kcG9pbnRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kcG9pbnRzRW50ZXJpbmdMYXN0TixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSkge1xuICAgICAgICBpZiAobGFzdE5Db3VudCAhPT0gbGFzdE5FbmRwb2ludHMubGVuZ3RoKVxuICAgICAgICAgICAgbGFzdE5Db3VudCA9IGxhc3RORW5kcG9pbnRzLmxlbmd0aDtcblxuICAgICAgICBsYXN0TkVuZHBvaW50c0NhY2hlID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgLy8gU2F5IEEsIEIsIEMsIEQsIEUsIGFuZCBGIGFyZSBpbiBhIGNvbmZlcmVuY2UgYW5kIExhc3ROID0gMy5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gSWYgTGFzdE4gZHJvcHMgdG8sIHNheSwgMiwgYmVjYXVzZSBvZiBhZGFwdGl2aXR5LCB0aGVuIEUgc2hvdWxkIHNlZVxuICAgICAgICAvLyB0aHVtYm5haWxzIGZvciBBLCBCIGFuZCBDLiBBIGFuZCBCIGFyZSBpbiBFJ3Mgc2VydmVyIHNpZGUgTGFzdE4gc2V0LFxuICAgICAgICAvLyBzbyBFIHNlZXMgdGhlbS4gQyBpcyBvbmx5IGluIEUncyBsb2NhbCBMYXN0TiBzZXQuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIElmIEYgc3RhcnRzIHRhbGtpbmcgYW5kIExhc3ROID0gMywgdGhlbiBFIHNob3VsZCBzZWUgdGh1bWJuYWlscyBmb3JcbiAgICAgICAgLy8gRiwgQSwgQi4gQiBnZXRzIFwiZWplY3RlZFwiIGZyb20gRSdzIHNlcnZlciBzaWRlIExhc3ROIHNldCwgYnV0IGl0XG4gICAgICAgIC8vIGVudGVycyBFJ3MgbG9jYWwgTGFzdE4gZWplY3RpbmcgQy5cblxuICAgICAgICAvLyBJbmNyZWFzZSB0aGUgbG9jYWwgTGFzdE4gc2V0IHNpemUsIGlmIG5lY2Vzc2FyeS5cbiAgICAgICAgaWYgKGxhc3ROQ291bnQgPiBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgIGxvY2FsTGFzdE5Db3VudCA9IGxhc3ROQ291bnQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGxvY2FsIExhc3ROIHNldCBwcmVzZXJ2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGVcbiAgICAgICAgLy8gZW5kcG9pbnRzIGFwcGVhcmVkIGluIHRoZSBMYXN0Ti9sb2NhbCBMYXN0TiBzZXQuXG5cbiAgICAgICAgdmFyIG5leHRMb2NhbExhc3ROU2V0ID0gbGFzdE5FbmRwb2ludHMuc2xpY2UoMCk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbG9jYWxMYXN0TlNldC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKG5leHRMb2NhbExhc3ROU2V0Lmxlbmd0aCA+PSBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbG9jYWxMYXN0TlNldFtpXTtcbiAgICAgICAgICAgIGlmIChuZXh0TG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBuZXh0TG9jYWxMYXN0TlNldC5wdXNoKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxvY2FsTGFzdE5TZXQgPSBuZXh0TG9jYWxMYXN0TlNldDtcblxuICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IGZhbHNlO1xuXG4gICAgICAgIC8vIEhhbmRsZSBMYXN0Ti9sb2NhbCBMYXN0TiBjaGFuZ2VzLlxuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS5lYWNoKGZ1bmN0aW9uKCBpbmRleCwgZWxlbWVudCApIHtcbiAgICAgICAgICAgIHZhciByZXNvdXJjZUppZCA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZChlbGVtZW50KTtcblxuICAgICAgICAgICAgdmFyIGlzUmVjZWl2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA8IDApIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlJlbW92ZSBmcm9tIGxhc3QgTlwiLCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdoaWRlJyk7XG4gICAgICAgICAgICAgICAgaXNSZWNlaXZlZCA9IGZhbHNlO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgICYmICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpLmlzKCc6dmlzaWJsZScpXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA+PSAwKSB7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdhdmF0YXInKTtcbiAgICAgICAgICAgICAgICBpc1JlY2VpdmVkID0gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghaXNSZWNlaXZlZCkge1xuICAgICAgICAgICAgICAgIC8vIHJlc291cmNlSmlkIGhhcyBkcm9wcGVkIG91dCBvZiB0aGUgc2VydmVyIHNpZGUgbGFzdE4gc2V0LCBzb1xuICAgICAgICAgICAgICAgIC8vIGl0IGlzIG5vIGxvbmdlciBiZWluZyByZWNlaXZlZC4gSWYgcmVzb3VyY2VKaWQgd2FzIGJlaW5nXG4gICAgICAgICAgICAgICAgLy8gZGlzcGxheWVkIGluIHRoZSBsYXJnZSB2aWRlbyB3ZSBoYXZlIHRvIHN3aXRjaCB0byBhbm90aGVyXG4gICAgICAgICAgICAgICAgLy8gdXNlci5cbiAgICAgICAgICAgICAgICB2YXIgbGFyZ2VWaWRlb1Jlc291cmNlID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcbiAgICAgICAgICAgICAgICBpZiAoIXVwZGF0ZUxhcmdlVmlkZW8gJiYgcmVzb3VyY2VKaWQgPT09IGxhcmdlVmlkZW9SZXNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICB1cGRhdGVMYXJnZVZpZGVvID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB8fCBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmxlbmd0aCA8IDApXG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgaWYgKGVuZHBvaW50c0VudGVyaW5nTGFzdE4gJiYgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Ti5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmZvckVhY2goZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG5cbiAgICAgICAgICAgICAgICB2YXIgaXNWaXNpYmxlID0gJCgnI3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZCkuaXMoJzp2aXNpYmxlJyk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdzaG93Jyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc1Zpc2libGUpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJBZGQgdG8gbGFzdCBOXCIsIHJlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgICAgICAgICB2YXIgamlkID0gQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBtZWRpYVN0cmVhbSA9IEFQUC5SVEMucmVtb3RlU3RyZWFtc1tqaWRdW01lZGlhU3RyZWFtVHlwZS5WSURFT19UWVBFXTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNlbCA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQgKyAnPnZpZGVvJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhU3RyZWFtLnN0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5SVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsLCB2aWRlb1N0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChsYXN0TlBpY2t1cEppZCA9PSBtZWRpYVN0cmVhbS5wZWVyamlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDbGVhbiB1cCB0aGUgbGFzdE4gcGlja3VwIGppZC5cbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3ROUGlja3VwSmlkID0gbnVsbDtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRG9uJ3QgZmlyZSB0aGUgZXZlbnRzIGFnYWluLCB0aGV5J3ZlIGFscmVhZHlcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGJlZW4gZmlyZWQgaW4gdGhlIGNvbnRhY3QgbGlzdCBjbGljayBoYW5kbGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJChzZWwpLmF0dHIoJ3NyYycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKG1lZGlhU3RyZWFtLnBlZXJqaWQpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlTGFyZ2VWaWRlbyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWwsIG1lZGlhU3RyZWFtLnNzcmMsIG1lZGlhU3RyZWFtLnN0cmVhbSwgcmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBUaGUgZW5kcG9pbnQgdGhhdCB3YXMgYmVpbmcgc2hvd24gaW4gdGhlIGxhcmdlIHZpZGVvIGhhcyBkcm9wcGVkIG91dFxuICAgICAgICAvLyBvZiB0aGUgbGFzdE4gc2V0IGFuZCB0aGVyZSB3YXMgbm8gbGFzdE4gcGlja3VwIGppZC4gV2UgbmVlZCB0byB1cGRhdGVcbiAgICAgICAgLy8gdGhlIGxhcmdlIHZpZGVvIG5vdy5cblxuICAgICAgICBpZiAodXBkYXRlTGFyZ2VWaWRlbykge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UsIGNvbnRhaW5lciwgc3JjO1xuICAgICAgICAgICAgdmFyIG15UmVzb3VyY2VcbiAgICAgICAgICAgICAgICA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcblxuICAgICAgICAgICAgLy8gRmluZCBvdXQgd2hpY2ggZW5kcG9pbnQgdG8gc2hvdyBpbiB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxhc3RORW5kcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgcmVzb3VyY2UgPSBsYXN0TkVuZHBvaW50c1tpXTtcbiAgICAgICAgICAgICAgICBpZiAoIXJlc291cmNlIHx8IHJlc291cmNlID09PSBteVJlc291cmNlKVxuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgICAgIGNvbnRhaW5lciA9ICQoXCIjcGFydGljaXBhbnRfXCIgKyByZXNvdXJjZSk7XG4gICAgICAgICAgICAgICAgaWYgKGNvbnRhaW5lci5sZW5ndGggPT0gMClcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgICAgICBzcmMgPSAkKCd2aWRlbycsIGNvbnRhaW5lcikuYXR0cignc3JjJyk7XG4gICAgICAgICAgICAgICAgaWYgKCFzcmMpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAgICAgLy8gdmlkZW9TcmNUb1NzcmMgbmVlZHMgdG8gYmUgdXBkYXRlIGZvciB0aGlzIGNhbGwgdG8gc3VjY2VlZC5cbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKHNyYyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBteS5vblNpbXVsY2FzdExheWVyc0NoYW5naW5nID0gZnVuY3Rpb24gKGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKSB7XG4gICAgICAgIGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzLmZvckVhY2goZnVuY3Rpb24gKGVzbCkge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSBlc2wuZW5kcG9pbnQ7XG5cbiAgICAgICAgICAgIC8vIGlmIGxhc3ROIGlzIGVuYWJsZWQgKmFuZCogdGhlIGVuZHBvaW50IGlzICpub3QqIGluIHRoZSBsYXN0TiBzZXQsXG4gICAgICAgICAgICAvLyB0aGVuIGlnbm9yZSB0aGUgZXZlbnQgKD0gZG8gbm90IHByZWxvYWQgYW55dGhpbmcpLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIFRoZSBicmlkZ2UgY291bGQgcHJvYmFibHkgc3RvcCBzZW5kaW5nIHRoaXMgbWVzc2FnZSBpZiBpdCdzIGZvclxuICAgICAgICAgICAgLy8gYW4gZW5kcG9pbnQgdGhhdCdzIG5vdCBpbiBsYXN0Ti5cblxuICAgICAgICAgICAgaWYgKGxhc3ROQ291bnQgIT0gLTFcbiAgICAgICAgICAgICAgICAmJiAobGFzdE5Db3VudCA8IDEgfHwgbGFzdE5FbmRwb2ludHNDYWNoZS5pbmRleE9mKHJlc291cmNlKSA9PT0gLTEpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcHJpbWFyeVNTUkMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgIC8vIEdldCBzZXNzaW9uIGFuZCBzdHJlYW0gZnJvbSBwcmltYXJ5IHNzcmMuXG4gICAgICAgICAgICB2YXIgcmVzID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhwcmltYXJ5U1NSQyk7XG4gICAgICAgICAgICB2YXIgc2lkID0gcmVzLnNpZDtcbiAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtID0gcmVzLnN0cmVhbTtcblxuICAgICAgICAgICAgaWYgKHNpZCAmJiBlbGVjdGVkU3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBBUFAuc2ltdWxjYXN0LmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMocHJpbWFyeVNTUkMpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFtlc2wsIHByaW1hcnlTU1JDLCBtc2lkLCBzaWQsIGVsZWN0ZWRTdHJlYW1dKTtcblxuICAgICAgICAgICAgICAgIHZhciBwcmVsb2FkID0gKFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKEFQUC54bXBwLmdldEppZEZyb21TU1JDKHByaW1hcnlTU1JDKSkgPT0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocHJlbG9hZCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnUHJlbG9hZGluZyByZW1vdGUgdmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgPSAkKCc8dmlkZW8gYXV0b3BsYXk+PC92aWRlbz4nKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gc3NyY3MgYXJlIHVuaXF1ZSBpbiBhbiBydHAgc2Vzc2lvblxuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZF9zc3JjID0gcHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShsYXJnZVZpZGVvU3RhdGUucHJlbG9hZCwgZWxlY3RlZFN0cmVhbSlcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignQ291bGQgbm90IGZpbmQgYSBzdHJlYW0gb3IgYSBzZXNzaW9uLicsIHNpZCwgZWxlY3RlZFN0cmVhbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBzaW11bGNhc3QgbGF5ZXJzIGNoYW5nZWQgZXZlbnQuXG4gICAgICovXG4gICAgbXkub25TaW11bGNhc3RMYXllcnNDaGFuZ2VkID0gZnVuY3Rpb24gKGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKSB7XG4gICAgICAgIGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzLmZvckVhY2goZnVuY3Rpb24gKGVzbCkge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSBlc2wuZW5kcG9pbnQ7XG5cbiAgICAgICAgICAgIC8vIGlmIGxhc3ROIGlzIGVuYWJsZWQgKmFuZCogdGhlIGVuZHBvaW50IGlzICpub3QqIGluIHRoZSBsYXN0TiBzZXQsXG4gICAgICAgICAgICAvLyB0aGVuIGlnbm9yZSB0aGUgZXZlbnQgKD0gZG8gbm90IGNoYW5nZSBsYXJnZSB2aWRlby90aHVtYm5haWxcbiAgICAgICAgICAgIC8vIFNSQ3MpLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBldmVuIGlmIHdlIGlnbm9yZSB0aGUgXCJjaGFuZ2VkXCIgZXZlbnQgaW4gdGhpcyBldmVudFxuICAgICAgICAgICAgLy8gaGFuZGxlciwgdGhlIGJyaWRnZSBtdXN0IGNvbnRpbnVlIHNlbmRpbmcgdGhlc2UgZXZlbnRzIGJlY2F1c2VcbiAgICAgICAgICAgIC8vIHRoZSBzaW11bGNhc3QgY29kZSBpbiBzaW11bGNhc3QuanMgdXNlcyBpdCB0byBrbm93IHdoYXQncyBnb2luZ1xuICAgICAgICAgICAgLy8gdG8gYmUgc3RyZWFtZWQgYnkgdGhlIGJyaWRnZSB3aGVuL2lmIHRoZSBlbmRwb2ludCBnZXRzIGJhY2sgaW50b1xuICAgICAgICAgICAgLy8gdGhlIGxhc3ROIHNldC5cblxuICAgICAgICAgICAgaWYgKGxhc3ROQ291bnQgIT0gLTFcbiAgICAgICAgICAgICAgICAmJiAobGFzdE5Db3VudCA8IDEgfHwgbGFzdE5FbmRwb2ludHNDYWNoZS5pbmRleE9mKHJlc291cmNlKSA9PT0gLTEpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcHJpbWFyeVNTUkMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgIC8vIEdldCBzZXNzaW9uIGFuZCBzdHJlYW0gZnJvbSBwcmltYXJ5IHNzcmMuXG4gICAgICAgICAgICB2YXIgcmVzID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhwcmltYXJ5U1NSQyk7XG4gICAgICAgICAgICB2YXIgc2lkID0gcmVzLnNpZDtcbiAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtID0gcmVzLnN0cmVhbTtcblxuICAgICAgICAgICAgaWYgKHNpZCAmJiBlbGVjdGVkU3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBBUFAuc2ltdWxjYXN0LmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMocHJpbWFyeVNTUkMpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdTd2l0Y2hpbmcgc2ltdWxjYXN0IHN1YnN0cmVhbS4nKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oW2VzbCwgcHJpbWFyeVNTUkMsIG1zaWQsIHNpZCwgZWxlY3RlZFN0cmVhbV0pO1xuXG4gICAgICAgICAgICAgICAgdmFyIG1zaWRQYXJ0cyA9IG1zaWQuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsUmVtb3RlVmlkZW8gPSAkKFsnIycsICdyZW1vdGVWaWRlb18nLCBzaWQsICdfJywgbXNpZFBhcnRzWzBdXS5qb2luKCcnKSk7XG5cbiAgICAgICAgICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IChTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChBUFAueG1wcC5nZXRKaWRGcm9tU1NSQyhwcmltYXJ5U1NSQykpXG4gICAgICAgICAgICAgICAgICAgID09IGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIHZhciB1cGRhdGVGb2N1c2VkVmlkZW9TcmMgPSAoZm9jdXNlZFZpZGVvSW5mbyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAhPSAnJyAmJlxuICAgICAgICAgICAgICAgICAgICAoQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSkgPT0gZm9jdXNlZFZpZGVvSW5mby5zcmMpKTtcblxuICAgICAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtVXJsO1xuICAgICAgICAgICAgICAgIGlmIChsYXJnZVZpZGVvU3RhdGUucHJlbG9hZF9zc3JjID09IHByaW1hcnlTU1JDKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5zZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSwgQVBQLlJUQy5nZXRWaWRlb1NyYyhsYXJnZVZpZGVvU3RhdGUucHJlbG9hZFswXSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRcbiAgICAgICAgICAgICAgICAgICAgICAgICYmIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRfc3NyYyA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShzZWxSZW1vdGVWaWRlbywgZWxlY3RlZFN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIGppZCA9IEFQUC54bXBwLmdldEppZEZyb21TU1JDKHByaW1hcnlTU1JDKTtcblxuICAgICAgICAgICAgICAgIGlmICh1cGRhdGVMYXJnZVZpZGVvKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSksIG51bGwsXG4gICAgICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAodXBkYXRlRm9jdXNlZFZpZGVvU3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvY3VzZWRWaWRlb0luZm8uc3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIHZpZGVvSWQ7XG4gICAgICAgICAgICAgICAgaWYocmVzb3VyY2UgPT0gQVBQLnhtcHAubXlSZXNvdXJjZSgpKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9JZCA9IFwibG9jYWxWaWRlb0NvbnRhaW5lclwiO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2aWRlb0lkID0gXCJwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgY29ubmVjdGlvbkluZGljYXRvciA9IFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW3ZpZGVvSWRdO1xuICAgICAgICAgICAgICAgIGlmKGNvbm5lY3Rpb25JbmRpY2F0b3IpXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25JbmRpY2F0b3IudXBkYXRlUG9wb3ZlckRhdGEoKTtcblxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBhIHN0cmVhbSBvciBhIHNpZC4nLCBzaWQsIGVsZWN0ZWRTdHJlYW0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyBsb2NhbCBzdGF0c1xuICAgICAqIEBwYXJhbSBwZXJjZW50XG4gICAgICogQHBhcmFtIG9iamVjdFxuICAgICAqL1xuICAgIG15LnVwZGF0ZUxvY2FsQ29ubmVjdGlvblN0YXRzID0gZnVuY3Rpb24gKHBlcmNlbnQsIG9iamVjdCkge1xuICAgICAgICB2YXIgcmVzb2x1dGlvbiA9IG51bGw7XG4gICAgICAgIGlmKG9iamVjdC5yZXNvbHV0aW9uICE9PSBudWxsKVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNvbHV0aW9uID0gb2JqZWN0LnJlc29sdXRpb247XG4gICAgICAgICAgICBvYmplY3QucmVzb2x1dGlvbiA9IHJlc29sdXRpb25bQVBQLnhtcHAubXlKaWQoKV07XG4gICAgICAgICAgICBkZWxldGUgcmVzb2x1dGlvbltBUFAueG1wcC5teUppZCgpXTtcbiAgICAgICAgfVxuICAgICAgICB1cGRhdGVTdGF0c0luZGljYXRvcihcImxvY2FsVmlkZW9Db250YWluZXJcIiwgcGVyY2VudCwgb2JqZWN0KTtcbiAgICAgICAgZm9yKHZhciBqaWQgaW4gcmVzb2x1dGlvbilcbiAgICAgICAge1xuICAgICAgICAgICAgaWYocmVzb2x1dGlvbltqaWRdID09PSBudWxsKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgdmFyIGlkID0gJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgaWYoVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbaWRdKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2lkXS51cGRhdGVSZXNvbHV0aW9uKHJlc29sdXRpb25bamlkXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHJlbW90ZSBzdGF0cy5cbiAgICAgKiBAcGFyYW0gamlkIHRoZSBqaWQgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdGF0c1xuICAgICAqIEBwYXJhbSBwZXJjZW50IHRoZSBjb25uZWN0aW9uIHF1YWxpdHkgcGVyY2VudFxuICAgICAqIEBwYXJhbSBvYmplY3QgdGhlIHN0YXRzIGRhdGFcbiAgICAgKi9cbiAgICBteS51cGRhdGVDb25uZWN0aW9uU3RhdHMgPSBmdW5jdGlvbiAoamlkLCBwZXJjZW50LCBvYmplY3QpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuICAgICAgICB1cGRhdGVTdGF0c0luZGljYXRvcih2aWRlb1NwYW5JZCwgcGVyY2VudCwgb2JqZWN0KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyB0aGUgY29ubmVjdGlvblxuICAgICAqIEBwYXJhbSBqaWRcbiAgICAgKi9cbiAgICBteS5yZW1vdmVDb25uZWN0aW9uSW5kaWNhdG9yID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpXS5yZW1vdmUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIGNvbm5lY3Rpb24gaW5kaWNhdG9yXG4gICAgICogQHBhcmFtIGppZFxuICAgICAqL1xuICAgIG15LmhpZGVDb25uZWN0aW9uSW5kaWNhdG9yID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpXS5oaWRlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIGFsbCB0aGUgaW5kaWNhdG9yc1xuICAgICAqL1xuICAgIG15Lm9uU3RhdHNTdG9wID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBmb3IodmFyIGluZGljYXRvciBpbiBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9ycylcbiAgICAgICAge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbaW5kaWNhdG9yXS5oaWRlSW5kaWNhdG9yKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkucGFydGljaXBhbnRMZWZ0ID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAvLyBVbmxvY2sgbGFyZ2UgdmlkZW9cbiAgICAgICAgaWYgKGZvY3VzZWRWaWRlb0luZm8gJiYgZm9jdXNlZFZpZGVvSW5mby5qaWQgPT09IGppZClcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiRm9jdXNlZCB2aWRlbyBvd25lciBoYXMgbGVmdCB0aGUgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgICAgIGZvY3VzZWRWaWRlb0luZm8gPSBudWxsO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIG15Lm9uVmlkZW9UeXBlQ2hhbmdlZCA9IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgaWYoamlkICYmXG4gICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpID09PSBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKVxuICAgICAgICB7XG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUuaXNEZXNrdG9wID0gQVBQLlJUQy5pc1ZpZGVvU3JjRGVza3RvcChqaWQpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0VmlkZW9TaXplID0gbGFyZ2VWaWRlb1N0YXRlLmlzRGVza3RvcFxuICAgICAgICAgICAgICAgID8gZ2V0RGVza3RvcFZpZGVvU2l6ZVxuICAgICAgICAgICAgICAgIDogZ2V0Q2FtZXJhVmlkZW9TaXplO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0VmlkZW9Qb3NpdGlvbiA9IGxhcmdlVmlkZW9TdGF0ZS5pc0Rlc2t0b3BcbiAgICAgICAgICAgICAgICA/IGdldERlc2t0b3BWaWRlb1Bvc2l0aW9uXG4gICAgICAgICAgICAgICAgOiBnZXRDYW1lcmFWaWRlb1Bvc2l0aW9uO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQucG9zaXRpb25MYXJnZShudWxsLCBudWxsKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBteTtcbn0oVmlkZW9MYXlvdXQgfHwge30pKTtcblxubW9kdWxlLmV4cG9ydHMgPSBWaWRlb0xheW91dDsiLCIvL3ZhciBub3VucyA9IFtcbi8vXTtcbnZhciBwbHVyYWxOb3VucyA9IFtcbiAgICBcIkFsaWVuc1wiLCBcIkFuaW1hbHNcIiwgXCJBbnRlbG9wZXNcIiwgXCJBbnRzXCIsIFwiQXBlc1wiLCBcIkFwcGxlc1wiLCBcIkJhYm9vbnNcIiwgXCJCYWN0ZXJpYVwiLCBcIkJhZGdlcnNcIiwgXCJCYW5hbmFzXCIsIFwiQmF0c1wiLFxuICAgIFwiQmVhcnNcIiwgXCJCaXJkc1wiLCBcIkJvbm9ib3NcIiwgXCJCcmlkZXNcIiwgXCJCdWdzXCIsIFwiQnVsbHNcIiwgXCJCdXR0ZXJmbGllc1wiLCBcIkNoZWV0YWhzXCIsXG4gICAgXCJDaGVycmllc1wiLCBcIkNoaWNrZW5cIiwgXCJDaGlsZHJlblwiLCBcIkNoaW1wc1wiLCBcIkNsb3duc1wiLCBcIkNvd3NcIiwgXCJDcmVhdHVyZXNcIiwgXCJEaW5vc2F1cnNcIiwgXCJEb2dzXCIsIFwiRG9scGhpbnNcIixcbiAgICBcIkRvbmtleXNcIiwgXCJEcmFnb25zXCIsIFwiRHVja3NcIiwgXCJEd2FyZnNcIiwgXCJFYWdsZXNcIiwgXCJFbGVwaGFudHNcIiwgXCJFbHZlc1wiLCBcIkZBSUxcIiwgXCJGYXRoZXJzXCIsXG4gICAgXCJGaXNoXCIsIFwiRmxvd2Vyc1wiLCBcIkZyb2dzXCIsIFwiRnJ1aXRcIiwgXCJGdW5naVwiLCBcIkdhbGF4aWVzXCIsIFwiR2Vlc2VcIiwgXCJHb2F0c1wiLFxuICAgIFwiR29yaWxsYXNcIiwgXCJIZWRnZWhvZ3NcIiwgXCJIaXBwb3NcIiwgXCJIb3JzZXNcIiwgXCJIdW50ZXJzXCIsIFwiSW5zZWN0c1wiLCBcIktpZHNcIiwgXCJLbmlnaHRzXCIsXG4gICAgXCJMZW1vbnNcIiwgXCJMZW11cnNcIiwgXCJMZW9wYXJkc1wiLCBcIkxpZmVGb3Jtc1wiLCBcIkxpb25zXCIsIFwiTGl6YXJkc1wiLCBcIk1pY2VcIiwgXCJNb25rZXlzXCIsIFwiTW9uc3RlcnNcIixcbiAgICBcIk11c2hyb29tc1wiLCBcIk9jdG9wb2Rlc1wiLCBcIk9yYW5nZXNcIiwgXCJPcmFuZ3V0YW5zXCIsIFwiT3JnYW5pc21zXCIsIFwiUGFudHNcIiwgXCJQYXJyb3RzXCIsIFwiUGVuZ3VpbnNcIixcbiAgICBcIlBlb3BsZVwiLCBcIlBpZ2VvbnNcIiwgXCJQaWdzXCIsIFwiUGluZWFwcGxlc1wiLCBcIlBsYW50c1wiLCBcIlBvdGF0b2VzXCIsIFwiUHJpZXN0c1wiLCBcIlJhdHNcIiwgXCJSZXB0aWxlc1wiLCBcIlJlcHRpbGlhbnNcIixcbiAgICBcIlJoaW5vc1wiLCBcIlNlYWd1bGxzXCIsIFwiU2hlZXBcIiwgXCJTaWJsaW5nc1wiLCBcIlNuYWtlc1wiLCBcIlNwYWdoZXR0aVwiLCBcIlNwaWRlcnNcIiwgXCJTcXVpZFwiLCBcIlNxdWlycmVsc1wiLFxuICAgIFwiU3RhcnNcIiwgXCJTdHVkZW50c1wiLCBcIlRlYWNoZXJzXCIsIFwiVGlnZXJzXCIsIFwiVG9tYXRvZXNcIiwgXCJUcmVlc1wiLCBcIlZhbXBpcmVzXCIsIFwiVmVnZXRhYmxlc1wiLCBcIlZpcnVzZXNcIiwgXCJWdWxjYW5zXCIsXG4gICAgXCJXYXJld29sdmVzXCIsIFwiV2Vhc2Vsc1wiLCBcIldoYWxlc1wiLCBcIldpdGNoZXNcIiwgXCJXaXphcmRzXCIsIFwiV29sdmVzXCIsIFwiV29ya2Vyc1wiLCBcIldvcm1zXCIsIFwiWmVicmFzXCJcbl07XG4vL3ZhciBwbGFjZXMgPSBbXG4vL1wiUHViXCIsIFwiVW5pdmVyc2l0eVwiLCBcIkFpcnBvcnRcIiwgXCJMaWJyYXJ5XCIsIFwiTWFsbFwiLCBcIlRoZWF0ZXJcIiwgXCJTdGFkaXVtXCIsIFwiT2ZmaWNlXCIsIFwiU2hvd1wiLCBcIkdhbGxvd3NcIiwgXCJCZWFjaFwiLFxuLy8gXCJDZW1ldGVyeVwiLCBcIkhvc3BpdGFsXCIsIFwiUmVjZXB0aW9uXCIsIFwiUmVzdGF1cmFudFwiLCBcIkJhclwiLCBcIkNodXJjaFwiLCBcIkhvdXNlXCIsIFwiU2Nob29sXCIsIFwiU3F1YXJlXCIsIFwiVmlsbGFnZVwiLFxuLy8gXCJDaW5lbWFcIiwgXCJNb3ZpZXNcIiwgXCJQYXJ0eVwiLCBcIlJlc3Ryb29tXCIsIFwiRW5kXCIsIFwiSmFpbFwiLCBcIlBvc3RPZmZpY2VcIiwgXCJTdGF0aW9uXCIsIFwiQ2lyY3VzXCIsIFwiR2F0ZXNcIiwgXCJFbnRyYW5jZVwiLFxuLy8gXCJCcmlkZ2VcIlxuLy9dO1xudmFyIHZlcmJzID0gW1xuICAgIFwiQWJhbmRvblwiLCBcIkFkYXB0XCIsIFwiQWR2ZXJ0aXNlXCIsIFwiQW5zd2VyXCIsIFwiQW50aWNpcGF0ZVwiLCBcIkFwcHJlY2lhdGVcIixcbiAgICBcIkFwcHJvYWNoXCIsIFwiQXJndWVcIiwgXCJBc2tcIiwgXCJCaXRlXCIsIFwiQmxvc3NvbVwiLCBcIkJsdXNoXCIsIFwiQnJlYXRoZVwiLCBcIkJyZWVkXCIsIFwiQnJpYmVcIiwgXCJCdXJuXCIsIFwiQ2FsY3VsYXRlXCIsXG4gICAgXCJDbGVhblwiLCBcIkNvZGVcIiwgXCJDb21tdW5pY2F0ZVwiLCBcIkNvbXB1dGVcIiwgXCJDb25mZXNzXCIsIFwiQ29uZmlzY2F0ZVwiLCBcIkNvbmp1Z2F0ZVwiLCBcIkNvbmp1cmVcIiwgXCJDb25zdW1lXCIsXG4gICAgXCJDb250ZW1wbGF0ZVwiLCBcIkNyYXdsXCIsIFwiRGFuY2VcIiwgXCJEZWxlZ2F0ZVwiLCBcIkRldm91clwiLCBcIkRldmVsb3BcIiwgXCJEaWZmZXJcIiwgXCJEaXNjdXNzXCIsXG4gICAgXCJEaXNzb2x2ZVwiLCBcIkRyaW5rXCIsIFwiRWF0XCIsIFwiRWxhYm9yYXRlXCIsIFwiRW1hbmNpcGF0ZVwiLCBcIkVzdGltYXRlXCIsIFwiRXhwaXJlXCIsIFwiRXh0aW5ndWlzaFwiLFxuICAgIFwiRXh0cmFjdFwiLCBcIkZBSUxcIiwgXCJGYWNpbGl0YXRlXCIsIFwiRmFsbFwiLCBcIkZlZWRcIiwgXCJGaW5pc2hcIiwgXCJGbG9zc1wiLCBcIkZseVwiLCBcIkZvbGxvd1wiLCBcIkZyYWdtZW50XCIsIFwiRnJlZXplXCIsXG4gICAgXCJHYXRoZXJcIiwgXCJHbG93XCIsIFwiR3Jvd1wiLCBcIkhleFwiLCBcIkhpZGVcIiwgXCJIdWdcIiwgXCJIdXJyeVwiLCBcIkltcHJvdmVcIiwgXCJJbnRlcnNlY3RcIiwgXCJJbnZlc3RpZ2F0ZVwiLCBcIkppbnhcIixcbiAgICBcIkpva2VcIiwgXCJKdWJpbGF0ZVwiLCBcIktpc3NcIiwgXCJMYXVnaFwiLCBcIk1hbmFnZVwiLCBcIk1lZXRcIiwgXCJNZXJnZVwiLCBcIk1vdmVcIiwgXCJPYmplY3RcIiwgXCJPYnNlcnZlXCIsIFwiT2ZmZXJcIixcbiAgICBcIlBhaW50XCIsIFwiUGFydGljaXBhdGVcIiwgXCJQYXJ0eVwiLCBcIlBlcmZvcm1cIiwgXCJQbGFuXCIsIFwiUHVyc3VlXCIsIFwiUGllcmNlXCIsIFwiUGxheVwiLCBcIlBvc3Rwb25lXCIsIFwiUHJheVwiLCBcIlByb2NsYWltXCIsXG4gICAgXCJRdWVzdGlvblwiLCBcIlJlYWRcIiwgXCJSZWNrb25cIiwgXCJSZWpvaWNlXCIsIFwiUmVwcmVzZW50XCIsIFwiUmVzaXplXCIsIFwiUmh5bWVcIiwgXCJTY3JlYW1cIiwgXCJTZWFyY2hcIiwgXCJTZWxlY3RcIiwgXCJTaGFyZVwiLCBcIlNob290XCIsXG4gICAgXCJTaG91dFwiLCBcIlNpZ25hbFwiLCBcIlNpbmdcIiwgXCJTa2F0ZVwiLCBcIlNsZWVwXCIsIFwiU21pbGVcIiwgXCJTbW9rZVwiLCBcIlNvbHZlXCIsIFwiU3BlbGxcIiwgXCJTdGVlclwiLCBcIlN0aW5rXCIsXG4gICAgXCJTdWJzdGl0dXRlXCIsIFwiU3dpbVwiLCBcIlRhc3RlXCIsIFwiVGVhY2hcIiwgXCJUZXJtaW5hdGVcIiwgXCJUaGlua1wiLCBcIlR5cGVcIiwgXCJVbml0ZVwiLCBcIlZhbmlzaFwiLCBcIldvcnNoaXBcIlxuXTtcbnZhciBhZHZlcmJzID0gW1xuICAgIFwiQWJzZW50bHlcIiwgXCJBY2N1cmF0ZWx5XCIsIFwiQWNjdXNpbmdseVwiLCBcIkFkb3JhYmx5XCIsIFwiQWxsVGhlVGltZVwiLCBcIkFsb25lXCIsIFwiQWx3YXlzXCIsIFwiQW1hemluZ2x5XCIsIFwiQW5ncmlseVwiLFxuICAgIFwiQW54aW91c2x5XCIsIFwiQW55d2hlcmVcIiwgXCJBcHBhbGxpbmdseVwiLCBcIkFwcGFyZW50bHlcIiwgXCJBcnRpY3VsYXRlbHlcIiwgXCJBc3RvbmlzaGluZ2x5XCIsIFwiQmFkbHlcIiwgXCJCYXJlbHlcIixcbiAgICBcIkJlYXV0aWZ1bGx5XCIsIFwiQmxpbmRseVwiLCBcIkJyYXZlbHlcIiwgXCJCcmlnaHRseVwiLCBcIkJyaXNrbHlcIiwgXCJCcnV0YWxseVwiLCBcIkNhbG1seVwiLCBcIkNhcmVmdWxseVwiLCBcIkNhc3VhbGx5XCIsXG4gICAgXCJDYXV0aW91c2x5XCIsIFwiQ2xldmVybHlcIiwgXCJDb25zdGFudGx5XCIsIFwiQ29ycmVjdGx5XCIsIFwiQ3JhemlseVwiLCBcIkN1cmlvdXNseVwiLCBcIkN5bmljYWxseVwiLCBcIkRhaWx5XCIsXG4gICAgXCJEYW5nZXJvdXNseVwiLCBcIkRlbGliZXJhdGVseVwiLCBcIkRlbGljYXRlbHlcIiwgXCJEZXNwZXJhdGVseVwiLCBcIkRpc2NyZWV0bHlcIiwgXCJFYWdlcmx5XCIsIFwiRWFzaWx5XCIsIFwiRXVwaG9yaWNseVwiLFxuICAgIFwiRXZlbmx5XCIsIFwiRXZlcnl3aGVyZVwiLCBcIkV4YWN0bHlcIiwgXCJFeHBlY3RhbnRseVwiLCBcIkV4dGVuc2l2ZWx5XCIsIFwiRkFJTFwiLCBcIkZlcm9jaW91c2x5XCIsIFwiRmllcmNlbHlcIiwgXCJGaW5lbHlcIixcbiAgICBcIkZsYXRseVwiLCBcIkZyZXF1ZW50bHlcIiwgXCJGcmlnaHRlbmluZ2x5XCIsIFwiR2VudGx5XCIsIFwiR2xvcmlvdXNseVwiLCBcIkdyaW1seVwiLCBcIkd1aWx0aWx5XCIsIFwiSGFwcGlseVwiLFxuICAgIFwiSGFyZFwiLCBcIkhhc3RpbHlcIiwgXCJIZXJvaWNhbGx5XCIsIFwiSGlnaFwiLCBcIkhpZ2hseVwiLCBcIkhvdXJseVwiLCBcIkh1bWJseVwiLCBcIkh5c3RlcmljYWxseVwiLCBcIkltbWVuc2VseVwiLFxuICAgIFwiSW1wYXJ0aWFsbHlcIiwgXCJJbXBvbGl0ZWx5XCIsIFwiSW5kaWZmZXJlbnRseVwiLCBcIkludGVuc2VseVwiLCBcIkplYWxvdXNseVwiLCBcIkpvdmlhbGx5XCIsIFwiS2luZGx5XCIsIFwiTGF6aWx5XCIsXG4gICAgXCJMaWdodGx5XCIsIFwiTG91ZGx5XCIsIFwiTG92aW5nbHlcIiwgXCJMb3lhbGx5XCIsIFwiTWFnbmlmaWNlbnRseVwiLCBcIk1hbGV2b2xlbnRseVwiLCBcIk1lcnJpbHlcIiwgXCJNaWdodGlseVwiLCBcIk1pc2VyYWJseVwiLFxuICAgIFwiTXlzdGVyaW91c2x5XCIsIFwiTk9UXCIsIFwiTmVydm91c2x5XCIsIFwiTmljZWx5XCIsIFwiTm93aGVyZVwiLCBcIk9iamVjdGl2ZWx5XCIsIFwiT2Jub3hpb3VzbHlcIiwgXCJPYnNlc3NpdmVseVwiLFxuICAgIFwiT2J2aW91c2x5XCIsIFwiT2Z0ZW5cIiwgXCJQYWluZnVsbHlcIiwgXCJQYXRpZW50bHlcIiwgXCJQbGF5ZnVsbHlcIiwgXCJQb2xpdGVseVwiLCBcIlBvb3JseVwiLCBcIlByZWNpc2VseVwiLCBcIlByb21wdGx5XCIsXG4gICAgXCJRdWlja2x5XCIsIFwiUXVpZXRseVwiLCBcIlJhbmRvbWx5XCIsIFwiUmFwaWRseVwiLCBcIlJhcmVseVwiLCBcIlJlY2tsZXNzbHlcIiwgXCJSZWd1bGFybHlcIiwgXCJSZW1vcnNlZnVsbHlcIiwgXCJSZXNwb25zaWJseVwiLFxuICAgIFwiUnVkZWx5XCIsIFwiUnV0aGxlc3NseVwiLCBcIlNhZGx5XCIsIFwiU2Nvcm5mdWxseVwiLCBcIlNlYW1sZXNzbHlcIiwgXCJTZWxkb21cIiwgXCJTZWxmaXNobHlcIiwgXCJTZXJpb3VzbHlcIiwgXCJTaGFraWx5XCIsXG4gICAgXCJTaGFycGx5XCIsIFwiU2lkZXdheXNcIiwgXCJTaWxlbnRseVwiLCBcIlNsZWVwaWx5XCIsIFwiU2xpZ2h0bHlcIiwgXCJTbG93bHlcIiwgXCJTbHlseVwiLCBcIlNtb290aGx5XCIsIFwiU29mdGx5XCIsIFwiU29sZW1ubHlcIiwgXCJTdGVhZGlseVwiLCBcIlN0ZXJubHlcIiwgXCJTdHJhbmdlbHlcIiwgXCJTdHJvbmdseVwiLCBcIlN0dW5uaW5nbHlcIiwgXCJTdXJlbHlcIiwgXCJUZW5kZXJseVwiLCBcIlRob3VnaHRmdWxseVwiLFxuICAgIFwiVGlnaHRseVwiLCBcIlVuZWFzaWx5XCIsIFwiVmFuaXNoaW5nbHlcIiwgXCJWaW9sZW50bHlcIiwgXCJXYXJtbHlcIiwgXCJXZWFrbHlcIiwgXCJXZWFyaWx5XCIsIFwiV2Vla2x5XCIsIFwiV2VpcmRseVwiLCBcIldlbGxcIixcbiAgICBcIldlbGxcIiwgXCJXaWNrZWRseVwiLCBcIldpbGRseVwiLCBcIldpc2VseVwiLCBcIldvbmRlcmZ1bGx5XCIsIFwiWWVhcmx5XCJcbl07XG52YXIgYWRqZWN0aXZlcyA9IFtcbiAgICBcIkFib21pbmFibGVcIiwgXCJBY2N1cmF0ZVwiLCBcIkFkb3JhYmxlXCIsIFwiQWxsXCIsIFwiQWxsZWdlZFwiLCBcIkFuY2llbnRcIiwgXCJBbmdyeVwiLCBcIkFuZ3J5XCIsIFwiQW54aW91c1wiLCBcIkFwcGFsbGluZ1wiLFxuICAgIFwiQXBwYXJlbnRcIiwgXCJBc3RvbmlzaGluZ1wiLCBcIkF0dHJhY3RpdmVcIiwgXCJBd2Vzb21lXCIsIFwiQmFieVwiLCBcIkJhZFwiLCBcIkJlYXV0aWZ1bFwiLCBcIkJlbmlnblwiLCBcIkJpZ1wiLCBcIkJpdHRlclwiLFxuICAgIFwiQmxpbmRcIiwgXCJCbHVlXCIsIFwiQm9sZFwiLCBcIkJyYXZlXCIsIFwiQnJpZ2h0XCIsIFwiQnJpc2tcIiwgXCJDYWxtXCIsIFwiQ2Ftb3VmbGFnZWRcIiwgXCJDYXN1YWxcIiwgXCJDYXV0aW91c1wiLFxuICAgIFwiQ2hvcHB5XCIsIFwiQ2hvc2VuXCIsIFwiQ2xldmVyXCIsIFwiQ29sZFwiLCBcIkNvb2xcIiwgXCJDcmF3bHlcIiwgXCJDcmF6eVwiLCBcIkNyZWVweVwiLCBcIkNydWVsXCIsIFwiQ3VyaW91c1wiLCBcIkN5bmljYWxcIixcbiAgICBcIkRhbmdlcm91c1wiLCBcIkRhcmtcIiwgXCJEZWxpY2F0ZVwiLCBcIkRlc3BlcmF0ZVwiLCBcIkRpZmZpY3VsdFwiLCBcIkRpc2NyZWV0XCIsIFwiRGlzZ3Vpc2VkXCIsIFwiRGl6enlcIixcbiAgICBcIkR1bWJcIiwgXCJFYWdlclwiLCBcIkVhc3lcIiwgXCJFZGd5XCIsIFwiRWxlY3RyaWNcIiwgXCJFbGVnYW50XCIsIFwiRW1hbmNpcGF0ZWRcIiwgXCJFbm9ybW91c1wiLCBcIkV1cGhvcmljXCIsIFwiRXZpbFwiLFxuICAgIFwiRkFJTFwiLCBcIkZhc3RcIiwgXCJGZXJvY2lvdXNcIiwgXCJGaWVyY2VcIiwgXCJGaW5lXCIsIFwiRmxhd2VkXCIsIFwiRmx5aW5nXCIsIFwiRm9vbGlzaFwiLCBcIkZveHlcIixcbiAgICBcIkZyZWV6aW5nXCIsIFwiRnVubnlcIiwgXCJGdXJpb3VzXCIsIFwiR2VudGxlXCIsIFwiR2xvcmlvdXNcIiwgXCJHb2xkZW5cIiwgXCJHb29kXCIsIFwiR3JlZW5cIiwgXCJHcmVlblwiLCBcIkd1aWx0eVwiLFxuICAgIFwiSGFpcnlcIiwgXCJIYXBweVwiLCBcIkhhcmRcIiwgXCJIYXN0eVwiLCBcIkhhenlcIiwgXCJIZXJvaWNcIiwgXCJIb3N0aWxlXCIsIFwiSG90XCIsIFwiSHVtYmxlXCIsIFwiSHVtb25nb3VzXCIsXG4gICAgXCJIdW1vcm91c1wiLCBcIkh5c3RlcmljYWxcIiwgXCJJZGVhbGlzdGljXCIsIFwiSWdub3JhbnRcIiwgXCJJbW1lbnNlXCIsIFwiSW1wYXJ0aWFsXCIsIFwiSW1wb2xpdGVcIiwgXCJJbmRpZmZlcmVudFwiLFxuICAgIFwiSW5mdXJpYXRlZFwiLCBcIkluc2lnaHRmdWxcIiwgXCJJbnRlbnNlXCIsIFwiSW50ZXJlc3RpbmdcIiwgXCJJbnRpbWlkYXRlZFwiLCBcIkludHJpZ3VpbmdcIiwgXCJKZWFsb3VzXCIsIFwiSm9sbHlcIiwgXCJKb3ZpYWxcIixcbiAgICBcIkp1bXB5XCIsIFwiS2luZFwiLCBcIkxhdWdoaW5nXCIsIFwiTGF6eVwiLCBcIkxpcXVpZFwiLCBcIkxvbmVseVwiLCBcIkxvbmdpbmdcIiwgXCJMb3VkXCIsIFwiTG92aW5nXCIsIFwiTG95YWxcIiwgXCJNYWNhYnJlXCIsIFwiTWFkXCIsXG4gICAgXCJNYWdpY2FsXCIsIFwiTWFnbmlmaWNlbnRcIiwgXCJNYWxldm9sZW50XCIsIFwiTWVkaWV2YWxcIiwgXCJNZW1vcmFibGVcIiwgXCJNZXJlXCIsIFwiTWVycnlcIiwgXCJNaWdodHlcIixcbiAgICBcIk1pc2NoaWV2b3VzXCIsIFwiTWlzZXJhYmxlXCIsIFwiTW9kaWZpZWRcIiwgXCJNb29keVwiLCBcIk1vc3RcIiwgXCJNeXN0ZXJpb3VzXCIsIFwiTXlzdGljYWxcIiwgXCJOZWVkeVwiLFxuICAgIFwiTmVydm91c1wiLCBcIk5pY2VcIiwgXCJPYmplY3RpdmVcIiwgXCJPYm5veGlvdXNcIiwgXCJPYnNlc3NpdmVcIiwgXCJPYnZpb3VzXCIsIFwiT3BpbmlvbmF0ZWRcIiwgXCJPcmFuZ2VcIixcbiAgICBcIlBhaW5mdWxcIiwgXCJQYXNzaW9uYXRlXCIsIFwiUGVyZmVjdFwiLCBcIlBpbmtcIiwgXCJQbGF5ZnVsXCIsIFwiUG9pc29ub3VzXCIsIFwiUG9saXRlXCIsIFwiUG9vclwiLCBcIlBvcHVsYXJcIiwgXCJQb3dlcmZ1bFwiLFxuICAgIFwiUHJlY2lzZVwiLCBcIlByZXNlcnZlZFwiLCBcIlByZXR0eVwiLCBcIlB1cnBsZVwiLCBcIlF1aWNrXCIsIFwiUXVpZXRcIiwgXCJSYW5kb21cIiwgXCJSYXBpZFwiLCBcIlJhcmVcIiwgXCJSZWFsXCIsXG4gICAgXCJSZWFzc3VyaW5nXCIsIFwiUmVja2xlc3NcIiwgXCJSZWRcIiwgXCJSZWd1bGFyXCIsIFwiUmVtb3JzZWZ1bFwiLCBcIlJlc3BvbnNpYmxlXCIsIFwiUmljaFwiLCBcIlJ1ZGVcIiwgXCJSdXRobGVzc1wiLFxuICAgIFwiU2FkXCIsIFwiU2NhcmVkXCIsIFwiU2NhcnlcIiwgXCJTY29ybmZ1bFwiLCBcIlNjcmVhbWluZ1wiLCBcIlNlbGZpc2hcIiwgXCJTZXJpb3VzXCIsIFwiU2hhZHlcIiwgXCJTaGFreVwiLCBcIlNoYXJwXCIsXG4gICAgXCJTaGlueVwiLCBcIlNoeVwiLCBcIlNpbXBsZVwiLCBcIlNsZWVweVwiLCBcIlNsb3dcIiwgXCJTbHlcIiwgXCJTbWFsbFwiLCBcIlNtYXJ0XCIsIFwiU21lbGx5XCIsIFwiU21pbGluZ1wiLCBcIlNtb290aFwiLFxuICAgIFwiU211Z1wiLCBcIlNvYmVyXCIsIFwiU29mdFwiLCBcIlNvbGVtblwiLCBcIlNxdWFyZVwiLCBcIlNxdWFyZVwiLCBcIlN0ZWFkeVwiLCBcIlN0cmFuZ2VcIiwgXCJTdHJvbmdcIixcbiAgICBcIlN0dW5uaW5nXCIsIFwiU3ViamVjdGl2ZVwiLCBcIlN1Y2Nlc3NmdWxcIiwgXCJTdXJseVwiLCBcIlN3ZWV0XCIsIFwiVGFjdGZ1bFwiLCBcIlRlbnNlXCIsXG4gICAgXCJUaG91Z2h0ZnVsXCIsIFwiVGlnaHRcIiwgXCJUaW55XCIsIFwiVG9sZXJhbnRcIiwgXCJVbmVhc3lcIiwgXCJVbmlxdWVcIiwgXCJVbnNlZW5cIiwgXCJXYXJtXCIsIFwiV2Vha1wiLFxuICAgIFwiV2VpcmRcIiwgXCJXZWxsQ29va2VkXCIsIFwiV2lsZFwiLCBcIldpc2VcIiwgXCJXaXR0eVwiLCBcIldvbmRlcmZ1bFwiLCBcIldvcnJpZWRcIiwgXCJZZWxsb3dcIiwgXCJZb3VuZ1wiLFxuICAgIFwiWmVhbG91c1wiXG4gICAgXTtcbi8vdmFyIHByb25vdW5zID0gW1xuLy9dO1xuLy92YXIgY29uanVuY3Rpb25zID0gW1xuLy9cIkFuZFwiLCBcIk9yXCIsIFwiRm9yXCIsIFwiQWJvdmVcIiwgXCJCZWZvcmVcIiwgXCJBZ2FpbnN0XCIsIFwiQmV0d2VlblwiXG4vL107XG5cbi8qXG4gKiBNYXBzIGEgc3RyaW5nIChjYXRlZ29yeSBuYW1lKSB0byB0aGUgYXJyYXkgb2Ygd29yZHMgZnJvbSB0aGF0IGNhdGVnb3J5LlxuICovXG52YXIgQ0FURUdPUklFUyA9XG57XG4gICAgLy9cIl9OT1VOX1wiOiBub3VucyxcbiAgICBcIl9QTFVSQUxOT1VOX1wiOiBwbHVyYWxOb3VucyxcbiAgICAvL1wiX1BMQUNFX1wiOiBwbGFjZXMsXG4gICAgXCJfVkVSQl9cIjogdmVyYnMsXG4gICAgXCJfQURWRVJCX1wiOiBhZHZlcmJzLFxuICAgIFwiX0FESkVDVElWRV9cIjogYWRqZWN0aXZlc1xuICAgIC8vXCJfUFJPTk9VTl9cIjogcHJvbm91bnMsXG4gICAgLy9cIl9DT05KVU5DVElPTl9cIjogY29uanVuY3Rpb25zLFxufTtcblxudmFyIFBBVFRFUk5TID0gW1xuICAgIFwiX0FESkVDVElWRV9fUExVUkFMTk9VTl9fVkVSQl9fQURWRVJCX1wiXG5cbiAgICAvLyBCZWF1dGlmdWxGdW5naU9yU3BhZ2hldHRpXG4gICAgLy9cIl9BREpFQ1RJVkVfX1BMVVJBTE5PVU5fX0NPTkpVTkNUSU9OX19QTFVSQUxOT1VOX1wiLFxuXG4gICAgLy8gQW1hemluZ2x5U2NhcnlUb3lcbiAgICAvL1wiX0FEVkVSQl9fQURKRUNUSVZFX19OT1VOX1wiLFxuXG4gICAgLy8gTmVpdGhlclRyYXNoTm9yUmlmbGVcbiAgICAvL1wiTmVpdGhlcl9OT1VOX05vcl9OT1VOX1wiLFxuICAgIC8vXCJFaXRoZXJfTk9VTl9Pcl9OT1VOX1wiLFxuXG4gICAgLy8gRWl0aGVyQ29wdWxhdGVPckludmVzdGlnYXRlXG4gICAgLy9cIkVpdGhlcl9WRVJCX09yX1ZFUkJfXCIsXG4gICAgLy9cIk5laXRoZXJfVkVSQl9Ob3JfVkVSQl9cIixcblxuICAgIC8vXCJUaGVfQURKRUNUSVZFX19BREpFQ1RJVkVfX05PVU5fXCIsXG4gICAgLy9cIlRoZV9BRFZFUkJfX0FESkVDVElWRV9fTk9VTl9cIixcbiAgICAvL1wiVGhlX0FEVkVSQl9fQURKRUNUSVZFX19OT1VOX3NcIixcbiAgICAvL1wiVGhlX0FEVkVSQl9fQURKRUNUSVZFX19QTFVSQUxOT1VOX19WRVJCX1wiLFxuXG4gICAgLy8gV29sdmVzQ29tcHV0ZUJhZGx5XG4gICAgLy9cIl9QTFVSQUxOT1VOX19WRVJCX19BRFZFUkJfXCIsXG5cbiAgICAvLyBVbml0ZUZhY2lsaXRhdGVBbmRNZXJnZVxuICAgIC8vXCJfVkVSQl9fVkVSQl9BbmRfVkVSQl9cIixcblxuICAgIC8vTmFzdHlXaXRjaGVzQXRUaGVQdWJcbiAgICAvL1wiX0FESkVDVElWRV9fUExVUkFMTk9VTl9BdFRoZV9QTEFDRV9cIixcbl07XG5cblxuLypcbiAqIFJldHVybnMgYSByYW5kb20gZWxlbWVudCBmcm9tIHRoZSBhcnJheSAnYXJyJ1xuICovXG5mdW5jdGlvbiByYW5kb21FbGVtZW50KGFycilcbntcbiAgICByZXR1cm4gYXJyW01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFyci5sZW5ndGgpXTtcbn1cblxuLypcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgc3RyaW5nICdzJyBjb250YWlucyBvbmUgb2YgdGhlXG4gKiB0ZW1wbGF0ZSBzdHJpbmdzLlxuICovXG5mdW5jdGlvbiBoYXNUZW1wbGF0ZShzKVxue1xuICAgIGZvciAodmFyIHRlbXBsYXRlIGluIENBVEVHT1JJRVMpe1xuICAgICAgICBpZiAocy5pbmRleE9mKHRlbXBsYXRlKSA+PSAwKXtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIEdlbmVyYXRlcyBuZXcgcm9vbSBuYW1lLlxuICovXG52YXIgUm9vbU5hbWVHZW5lcmF0b3IgPSB7XG4gICAgZ2VuZXJhdGVSb29tV2l0aG91dFNlcGFyYXRvcjogZnVuY3Rpb24oKVxuICAgIHtcbiAgICAgICAgLy8gTm90ZSB0aGF0IGlmIG1vcmUgdGhhbiBvbmUgcGF0dGVybiBpcyBhdmFpbGFibGUsIHRoZSBjaG9pY2Ugb2YgJ25hbWUnIHdvbid0IGJlIHJhbmRvbSAobmFtZXMgZnJvbSBwYXR0ZXJuc1xuICAgICAgICAvLyB3aXRoIGZld2VyIG9wdGlvbnMgd2lsbCBoYXZlIGhpZ2hlciBwcm9iYWJpbGl0eSBvZiBiZWluZyBjaG9zZW4gdGhhdCBuYW1lcyBmcm9tIHBhdHRlcm5zIHdpdGggbW9yZSBvcHRpb25zKS5cbiAgICAgICAgdmFyIG5hbWUgPSByYW5kb21FbGVtZW50KFBBVFRFUk5TKTtcbiAgICAgICAgdmFyIHdvcmQ7XG4gICAgICAgIHdoaWxlIChoYXNUZW1wbGF0ZShuYW1lKSl7XG4gICAgICAgICAgICBmb3IgKHZhciB0ZW1wbGF0ZSBpbiBDQVRFR09SSUVTKXtcbiAgICAgICAgICAgICAgICB3b3JkID0gcmFuZG9tRWxlbWVudChDQVRFR09SSUVTW3RlbXBsYXRlXSk7XG4gICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUucmVwbGFjZSh0ZW1wbGF0ZSwgd29yZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmFtZTtcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gUm9vbU5hbWVHZW5lcmF0b3I7XG4iLCJ2YXIgYW5pbWF0ZVRpbWVvdXQsIHVwZGF0ZVRpbWVvdXQ7XG5cbnZhciBSb29tTmFtZUdlbmVyYXRvciA9IHJlcXVpcmUoXCIuL1Jvb21uYW1lR2VuZXJhdG9yXCIpO1xuXG5mdW5jdGlvbiBlbnRlcl9yb29tKClcbntcbiAgICB2YXIgdmFsID0gJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLnZhbCgpO1xuICAgIGlmKCF2YWwpIHtcbiAgICAgICAgdmFsID0gJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLmF0dHIoXCJyb29tX25hbWVcIik7XG4gICAgfVxuICAgIGlmICh2YWwpIHtcbiAgICAgICAgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lID0gXCIvXCIgKyB2YWw7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBhbmltYXRlKHdvcmQpIHtcbiAgICB2YXIgY3VycmVudFZhbCA9ICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5hdHRyKFwicGxhY2Vob2xkZXJcIik7XG4gICAgJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLmF0dHIoXCJwbGFjZWhvbGRlclwiLCBjdXJyZW50VmFsICsgd29yZC5zdWJzdHIoMCwgMSkpO1xuICAgIGFuaW1hdGVUaW1lb3V0ID0gc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgYW5pbWF0ZSh3b3JkLnN1YnN0cmluZygxLCB3b3JkLmxlbmd0aCkpXG4gICAgfSwgNzApO1xufVxuXG5mdW5jdGlvbiB1cGRhdGVfcm9vbW5hbWUoKVxue1xuICAgIHZhciB3b3JkID0gUm9vbU5hbWVHZW5lcmF0b3IuZ2VuZXJhdGVSb29tV2l0aG91dFNlcGFyYXRvcigpO1xuICAgICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5hdHRyKFwicm9vbV9uYW1lXCIsIHdvcmQpO1xuICAgICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5hdHRyKFwicGxhY2Vob2xkZXJcIiwgXCJcIik7XG4gICAgY2xlYXJUaW1lb3V0KGFuaW1hdGVUaW1lb3V0KTtcbiAgICBhbmltYXRlKHdvcmQpO1xuICAgIHVwZGF0ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KHVwZGF0ZV9yb29tbmFtZSwgMTAwMDApO1xufVxuXG5cbmZ1bmN0aW9uIHNldHVwV2VsY29tZVBhZ2UoKVxue1xuICAgICQoXCIjdmlkZW9jb25mZXJlbmNlX3BhZ2VcIikuaGlkZSgpO1xuICAgICQoXCIjZG9tYWluX25hbWVcIikudGV4dChcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5wcm90b2NvbCArIFwiLy9cIiArIHdpbmRvdy5sb2NhdGlvbi5ob3N0ICsgXCIvXCIpO1xuICAgIGlmIChpbnRlcmZhY2VDb25maWcuU0hPV19KSVRTSV9XQVRFUk1BUkspIHtcbiAgICAgICAgdmFyIGxlZnRXYXRlcm1hcmtEaXZcbiAgICAgICAgICAgID0gJChcIiN3ZWxjb21lX3BhZ2VfaGVhZGVyIGRpdltjbGFzcz0nd2F0ZXJtYXJrIGxlZnR3YXRlcm1hcmsnXVwiKTtcbiAgICAgICAgaWYobGVmdFdhdGVybWFya0RpdiAmJiBsZWZ0V2F0ZXJtYXJrRGl2Lmxlbmd0aCA+IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIGxlZnRXYXRlcm1hcmtEaXYuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgICAgICAgICBsZWZ0V2F0ZXJtYXJrRGl2LnBhcmVudCgpLmdldCgwKS5ocmVmXG4gICAgICAgICAgICAgICAgPSBpbnRlcmZhY2VDb25maWcuSklUU0lfV0FURVJNQVJLX0xJTks7XG4gICAgICAgIH1cblxuICAgIH1cblxuICAgIGlmIChpbnRlcmZhY2VDb25maWcuU0hPV19CUkFORF9XQVRFUk1BUkspIHtcbiAgICAgICAgdmFyIHJpZ2h0V2F0ZXJtYXJrRGl2XG4gICAgICAgICAgICA9ICQoXCIjd2VsY29tZV9wYWdlX2hlYWRlciBkaXZbY2xhc3M9J3dhdGVybWFyayByaWdodHdhdGVybWFyayddXCIpO1xuICAgICAgICBpZihyaWdodFdhdGVybWFya0RpdiAmJiByaWdodFdhdGVybWFya0Rpdi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICByaWdodFdhdGVybWFya0Rpdi5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICAgICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LnBhcmVudCgpLmdldCgwKS5ocmVmXG4gICAgICAgICAgICAgICAgPSBpbnRlcmZhY2VDb25maWcuQlJBTkRfV0FURVJNQVJLX0xJTks7XG4gICAgICAgICAgICByaWdodFdhdGVybWFya0Rpdi5nZXQoMCkuc3R5bGUuYmFja2dyb3VuZEltYWdlXG4gICAgICAgICAgICAgICAgPSBcInVybChpbWFnZXMvcmlnaHR3YXRlcm1hcmsucG5nKVwiO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX1BPV0VSRURfQlkpIHtcbiAgICAgICAgJChcIiN3ZWxjb21lX3BhZ2VfaGVhZGVyPmFbY2xhc3M9J3Bvd2VyZWRieSddXCIpXG4gICAgICAgICAgICAuY3NzKHtkaXNwbGF5OiAnYmxvY2snfSk7XG4gICAgfVxuXG4gICAgJChcIiNlbnRlcl9yb29tX2J1dHRvblwiKS5jbGljayhmdW5jdGlvbigpXG4gICAge1xuICAgICAgICBlbnRlcl9yb29tKCk7XG4gICAgfSk7XG5cbiAgICAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikua2V5ZG93bihmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgaWYgKGV2ZW50LmtleUNvZGUgPT09IDEzIC8qIGVudGVyICovKSB7XG4gICAgICAgICAgICBlbnRlcl9yb29tKCk7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIGlmICghKGludGVyZmFjZUNvbmZpZy5HRU5FUkFURV9ST09NTkFNRVNfT05fV0VMQ09NRV9QQUdFID09PSBmYWxzZSkpe1xuICAgICAgICB2YXIgdXBkYXRlVGltZW91dDtcbiAgICAgICAgdmFyIGFuaW1hdGVUaW1lb3V0O1xuICAgICAgICAkKFwiI3JlbG9hZF9yb29tbmFtZVwiKS5jbGljayhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodXBkYXRlVGltZW91dCk7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQoYW5pbWF0ZVRpbWVvdXQpO1xuICAgICAgICAgICAgdXBkYXRlX3Jvb21uYW1lKCk7XG4gICAgICAgIH0pO1xuICAgICAgICAkKFwiI3JlbG9hZF9yb29tbmFtZVwiKS5zaG93KCk7XG5cblxuICAgICAgICB1cGRhdGVfcm9vbW5hbWUoKTtcbiAgICB9XG5cbiAgICAkKFwiI2Rpc2FibGVfd2VsY29tZVwiKS5jbGljayhmdW5jdGlvbiAoKSB7XG4gICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZFxuICAgICAgICAgICAgPSAkKFwiI2Rpc2FibGVfd2VsY29tZVwiKS5pcyhcIjpjaGVja2VkXCIpO1xuICAgIH0pO1xuXG59XG5cbm1vZHVsZS5leHBvcnRzID0gc2V0dXBXZWxjb21lUGFnZTsiLCJ2YXIgRXZlbnRFbWl0dGVyID0gcmVxdWlyZShcImV2ZW50c1wiKTtcbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgQ1FFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9jb25uZWN0aW9ucXVhbGl0eS9DUUV2ZW50c1wiKTtcbnZhciBYTVBQRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UveG1wcC9YTVBQRXZlbnRzXCIpO1xuXG4vKipcbiAqIGxvY2FsIHN0YXRzXG4gKiBAdHlwZSB7e319XG4gKi9cbnZhciBzdGF0cyA9IHt9O1xuXG4vKipcbiAqIHJlbW90ZSBzdGF0c1xuICogQHR5cGUge3t9fVxuICovXG52YXIgcmVtb3RlU3RhdHMgPSB7fTtcblxuLyoqXG4gKiBJbnRlcnZhbCBmb3Igc2VuZGluZyBzdGF0aXN0aWNzIHRvIG90aGVyIHBhcnRpY2lwYW50c1xuICogQHR5cGUge251bGx9XG4gKi9cbnZhciBzZW5kSW50ZXJ2YWxJZCA9IG51bGw7XG5cblxuLyoqXG4gKiBTdGFydCBzdGF0aXN0aWNzIHNlbmRpbmcuXG4gKi9cbmZ1bmN0aW9uIHN0YXJ0U2VuZGluZ1N0YXRzKCkge1xuICAgIHNlbmRTdGF0cygpO1xuICAgIHNlbmRJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoc2VuZFN0YXRzLCAxMDAwMCk7XG59XG5cbi8qKlxuICogU2VuZHMgc3RhdGlzdGljcyB0byBvdGhlciBwYXJ0aWNpcGFudHNcbiAqL1xuZnVuY3Rpb24gc2VuZFN0YXRzKCkge1xuICAgIEFQUC54bXBwLmFkZFRvUHJlc2VuY2UoXCJjb25uZWN0aW9uUXVhbGl0eVwiLCBjb252ZXJ0VG9NVUNTdGF0cyhzdGF0cykpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIHN0YXRpc3RpY3MgdG8gZm9ybWF0IGZvciBzZW5kaW5nIHRocm91Z2ggWE1QUFxuICogQHBhcmFtIHN0YXRzIHRoZSBzdGF0aXN0aWNzXG4gKiBAcmV0dXJucyB7e2JpdHJhdGVfZG9ud2xvYWQ6ICosIGJpdHJhdGVfdXBscG9hZDogKiwgcGFja2V0TG9zc190b3RhbDogKiwgcGFja2V0TG9zc19kb3dubG9hZDogKiwgcGFja2V0TG9zc191cGxvYWQ6ICp9fVxuICovXG5mdW5jdGlvbiBjb252ZXJ0VG9NVUNTdGF0cyhzdGF0cykge1xuICAgIHJldHVybiB7XG4gICAgICAgIFwiYml0cmF0ZV9kb3dubG9hZFwiOiBzdGF0cy5iaXRyYXRlLmRvd25sb2FkLFxuICAgICAgICBcImJpdHJhdGVfdXBsb2FkXCI6IHN0YXRzLmJpdHJhdGUudXBsb2FkLFxuICAgICAgICBcInBhY2tldExvc3NfdG90YWxcIjogc3RhdHMucGFja2V0TG9zcy50b3RhbCxcbiAgICAgICAgXCJwYWNrZXRMb3NzX2Rvd25sb2FkXCI6IHN0YXRzLnBhY2tldExvc3MuZG93bmxvYWQsXG4gICAgICAgIFwicGFja2V0TG9zc191cGxvYWRcIjogc3RhdHMucGFja2V0TG9zcy51cGxvYWRcbiAgICB9O1xufVxuXG4vKipcbiAqIENvbnZlcnRzIHN0YXRpdGlzdGljcyB0byBmb3JtYXQgdXNlZCBieSBWaWRlb0xheW91dFxuICogQHBhcmFtIHN0YXRzXG4gKiBAcmV0dXJucyB7e2JpdHJhdGU6IHtkb3dubG9hZDogKiwgdXBsb2FkOiAqfSwgcGFja2V0TG9zczoge3RvdGFsOiAqLCBkb3dubG9hZDogKiwgdXBsb2FkOiAqfX19XG4gKi9cbmZ1bmN0aW9uIHBhcnNlTVVDU3RhdHMoc3RhdHMpIHtcbiAgICByZXR1cm4ge1xuICAgICAgICBiaXRyYXRlOiB7XG4gICAgICAgICAgICBkb3dubG9hZDogc3RhdHMuYml0cmF0ZV9kb3dubG9hZCxcbiAgICAgICAgICAgIHVwbG9hZDogc3RhdHMuYml0cmF0ZV91cGxvYWRcbiAgICAgICAgfSxcbiAgICAgICAgcGFja2V0TG9zczoge1xuICAgICAgICAgICAgdG90YWw6IHN0YXRzLnBhY2tldExvc3NfdG90YWwsXG4gICAgICAgICAgICBkb3dubG9hZDogc3RhdHMucGFja2V0TG9zc19kb3dubG9hZCxcbiAgICAgICAgICAgIHVwbG9hZDogc3RhdHMucGFja2V0TG9zc191cGxvYWRcbiAgICAgICAgfVxuICAgIH07XG59XG5cblxudmFyIENvbm5lY3Rpb25RdWFsaXR5ID0ge1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5SRU1PVEVfU1RBVFMsIHRoaXMudXBkYXRlUmVtb3RlU3RhdHMpO1xuICAgICAgICBBUFAuc3RhdGlzdGljcy5hZGRDb25uZWN0aW9uU3RhdHNMaXN0ZW5lcih0aGlzLnVwZGF0ZUxvY2FsU3RhdHMpO1xuICAgICAgICBBUFAuc3RhdGlzdGljcy5hZGRSZW1vdGVTdGF0c1N0b3BMaXN0ZW5lcih0aGlzLnN0b3BTZW5kaW5nU3RhdHMpO1xuXG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGxvY2FsIHN0YXRpc3RpY3NcbiAgICAgKiBAcGFyYW0gZGF0YSBuZXcgc3RhdGlzdGljc1xuICAgICAqL1xuICAgIHVwZGF0ZUxvY2FsU3RhdHM6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIHN0YXRzID0gZGF0YTtcbiAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoQ1FFdmVudHMuTE9DQUxTVEFUU19VUERBVEVELCAxMDAgLSBzdGF0cy5wYWNrZXRMb3NzLnRvdGFsLCBzdGF0cyk7XG4gICAgICAgIGlmIChzZW5kSW50ZXJ2YWxJZCA9PSBudWxsKSB7XG4gICAgICAgICAgICBzdGFydFNlbmRpbmdTdGF0cygpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgcmVtb3RlIHN0YXRpc3RpY3NcbiAgICAgKiBAcGFyYW0gamlkIHRoZSBqaWQgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdGF0aXN0aWNzXG4gICAgICogQHBhcmFtIGRhdGEgdGhlIHN0YXRpc3RpY3NcbiAgICAgKi9cbiAgICB1cGRhdGVSZW1vdGVTdGF0czogZnVuY3Rpb24gKGppZCwgZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSA9PSBudWxsIHx8IGRhdGEucGFja2V0TG9zc190b3RhbCA9PSBudWxsKSB7XG4gICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChDUUV2ZW50cy5SRU1PVEVTVEFUU19VUERBVEVELCBqaWQsIG51bGwsIG51bGwpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHJlbW90ZVN0YXRzW2ppZF0gPSBwYXJzZU1VQ1N0YXRzKGRhdGEpO1xuXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KENRRXZlbnRzLlJFTU9URVNUQVRTX1VQREFURUQsXG4gICAgICAgICAgICBqaWQsIDEwMCAtIGRhdGEucGFja2V0TG9zc190b3RhbCwgcmVtb3RlU3RhdHNbamlkXSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFN0b3BzIHN0YXRpc3RpY3Mgc2VuZGluZy5cbiAgICAgKi9cbiAgICBzdG9wU2VuZGluZ1N0YXRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwoc2VuZEludGVydmFsSWQpO1xuICAgICAgICBzZW5kSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgICAgIC8vbm90aWZ5IFVJIGFib3V0IHN0b3BwaW5nIHN0YXRpc3RpY3MgZ2F0aGVyaW5nXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KENRRXZlbnRzLlNUT1ApO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBsb2NhbCBzdGF0aXN0aWNzLlxuICAgICAqL1xuICAgIGdldFN0YXRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzdGF0cztcbiAgICB9LFxuICAgIFxuICAgIGFkZExpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9XG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29ubmVjdGlvblF1YWxpdHk7IiwiLyogZ2xvYmFsICQsIGFsZXJ0LCBBUFAsIGNoYW5nZUxvY2FsVmlkZW8sIGNocm9tZSwgY29uZmlnLCBnZXRDb25mZXJlbmNlSGFuZGxlcixcbiBnZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMgKi9cbi8qKlxuICogSW5kaWNhdGVzIHRoYXQgZGVza3RvcCBzdHJlYW0gaXMgY3VycmVudGx5IGluIHVzZShmb3IgdG9nZ2xlIHB1cnBvc2UpLlxuICogQHR5cGUge2Jvb2xlYW59XG4gKi9cbnZhciBpc1VzaW5nU2NyZWVuU3RyZWFtID0gZmFsc2U7XG4vKipcbiAqIEluZGljYXRlcyB0aGF0IHN3aXRjaCBzdHJlYW0gb3BlcmF0aW9uIGlzIGluIHByb2dyZXNzIGFuZCBwcmV2ZW50IGZyb21cbiAqIHRyaWdnZXJpbmcgbmV3IGV2ZW50cy5cbiAqIEB0eXBlIHtib29sZWFufVxuICovXG52YXIgc3dpdGNoSW5Qcm9ncmVzcyA9IGZhbHNlO1xuXG4vKipcbiAqIE1ldGhvZCB1c2VkIHRvIGdldCBzY3JlZW4gc2hhcmluZyBzdHJlYW0uXG4gKlxuICogQHR5cGUge2Z1bmN0aW9uIChzdHJlYW1fY2FsbGJhY2ssIGZhaWx1cmVfY2FsbGJhY2t9XG4gKi9cbnZhciBvYnRhaW5EZXNrdG9wU3RyZWFtID0gbnVsbDtcblxuLyoqXG4gKiBJbmRpY2F0ZXMgd2hldGhlciBkZXNrdG9wIHNoYXJpbmcgZXh0ZW5zaW9uIGlzIGluc3RhbGxlZC5cbiAqIEB0eXBlIHtib29sZWFufVxuICovXG52YXIgZXh0SW5zdGFsbGVkID0gZmFsc2U7XG5cbi8qKlxuICogSW5kaWNhdGVzIHdoZXRoZXIgdXBkYXRlIG9mIGRlc2t0b3Agc2hhcmluZyBleHRlbnNpb24gaXMgcmVxdWlyZWQuXG4gKiBAdHlwZSB7Ym9vbGVhbn1cbiAqL1xudmFyIGV4dFVwZGF0ZVJlcXVpcmVkID0gZmFsc2U7XG5cbi8qKlxuICogRmxhZyB1c2VkIHRvIGNhY2hlIGRlc2t0b3Agc2hhcmluZyBlbmFibGVkIHN0YXRlLiBEbyBub3QgdXNlIGRpcmVjdGx5IGFzXG4gKiBpdCBjYW4gYmUgPHR0Pm51bGw8L3R0Pi5cbiAqXG4gKiBAdHlwZSB7bnVsbHxib29sZWFufVxuICovXG52YXIgX2Rlc2t0b3BTaGFyaW5nRW5hYmxlZCA9IG51bGw7XG5cbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xuXG52YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG52YXIgRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXG4gICAgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9kZXNrdG9wc2hhcmluZy9EZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXNcIik7XG5cbi8qKlxuICogTWV0aG9kIG9idGFpbnMgZGVza3RvcCBzdHJlYW0gZnJvbSBXZWJSVEMgJ3NjcmVlbicgc291cmNlLlxuICogRmxhZyAnY2hyb21lOi8vZmxhZ3MvI2VuYWJsZS11c2VybWVkaWEtc2NyZWVuLWNhcHR1cmUnIG11c3QgYmUgZW5hYmxlZC5cbiAqL1xuZnVuY3Rpb24gb2J0YWluV2ViUlRDU2NyZWVuKHN0cmVhbUNhbGxiYWNrLCBmYWlsQ2FsbGJhY2spIHtcbiAgICBBUFAuUlRDLmdldFVzZXJNZWRpYVdpdGhDb25zdHJhaW50cyhcbiAgICAgICAgWydzY3JlZW4nXSxcbiAgICAgICAgc3RyZWFtQ2FsbGJhY2ssXG4gICAgICAgIGZhaWxDYWxsYmFja1xuICAgICk7XG59XG5cbi8qKlxuICogQ29uc3RydWN0cyBpbmxpbmUgaW5zdGFsbCBVUkwgZm9yIENocm9tZSBkZXNrdG9wIHN0cmVhbWluZyBleHRlbnNpb24uXG4gKiBUaGUgJ2Nocm9tZUV4dGVuc2lvbklkJyBtdXN0IGJlIGRlZmluZWQgaW4gY29uZmlnLmpzLlxuICogQHJldHVybnMge3N0cmluZ31cbiAqL1xuZnVuY3Rpb24gZ2V0V2ViU3RvcmVJbnN0YWxsVXJsKClcbntcbiAgICByZXR1cm4gXCJodHRwczovL2Nocm9tZS5nb29nbGUuY29tL3dlYnN0b3JlL2RldGFpbC9cIiArXG4gICAgICAgIGNvbmZpZy5jaHJvbWVFeHRlbnNpb25JZDtcbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciBleHRlbnNpb24gdXBkYXRlIGlzIHJlcXVpcmVkLlxuICogQHBhcmFtIG1pblZlcnNpb24gbWluaW1hbCByZXF1aXJlZCB2ZXJzaW9uXG4gKiBAcGFyYW0gZXh0VmVyc2lvbiBjdXJyZW50IGV4dGVuc2lvbiB2ZXJzaW9uXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAqL1xuZnVuY3Rpb24gaXNVcGRhdGVSZXF1aXJlZChtaW5WZXJzaW9uLCBleHRWZXJzaW9uKVxue1xuICAgIHRyeVxuICAgIHtcbiAgICAgICAgdmFyIHMxID0gbWluVmVyc2lvbi5zcGxpdCgnLicpO1xuICAgICAgICB2YXIgczIgPSBleHRWZXJzaW9uLnNwbGl0KCcuJyk7XG5cbiAgICAgICAgdmFyIGxlbiA9IE1hdGgubWF4KHMxLmxlbmd0aCwgczIubGVuZ3RoKTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47IGkrKylcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIG4xID0gMCxcbiAgICAgICAgICAgICAgICBuMiA9IDA7XG5cbiAgICAgICAgICAgIGlmIChpIDwgczEubGVuZ3RoKVxuICAgICAgICAgICAgICAgIG4xID0gcGFyc2VJbnQoczFbaV0pO1xuICAgICAgICAgICAgaWYgKGkgPCBzMi5sZW5ndGgpXG4gICAgICAgICAgICAgICAgbjIgPSBwYXJzZUludChzMltpXSk7XG5cbiAgICAgICAgICAgIGlmIChpc05hTihuMSkgfHwgaXNOYU4objIpKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAobjEgIT09IG4yKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHJldHVybiBuMSA+IG4yO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gd2lsbCBoYXBwZW4gaWYgYm90aHMgdmVyc2lvbiBoYXMgaWRlbnRpY2FsIG51bWJlcnMgaW5cbiAgICAgICAgLy8gdGhlaXIgY29tcG9uZW50cyAoZXZlbiBpZiBvbmUgb2YgdGhlbSBpcyBsb25nZXIsIGhhcyBtb3JlIGNvbXBvbmVudHMpXG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY2F0Y2ggKGUpXG4gICAge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRmFpbGVkIHRvIHBhcnNlIGV4dGVuc2lvbiB2ZXJzaW9uXCIsIGUpO1xuICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKFwiZGlhbG9nLmVycm9yXCIsXG4gICAgICAgICAgICBcImRpYWxvZy5kZXRlY3RleHRcIik7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gY2hlY2tFeHRJbnN0YWxsZWQoY2FsbGJhY2spIHtcbiAgICBpZiAoIWNocm9tZS5ydW50aW1lKSB7XG4gICAgICAgIC8vIE5vIEFQSSwgc28gbm8gZXh0ZW5zaW9uIGZvciBzdXJlXG4gICAgICAgIGNhbGxiYWNrKGZhbHNlLCBmYWxzZSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoXG4gICAgICAgIGNvbmZpZy5jaHJvbWVFeHRlbnNpb25JZCxcbiAgICAgICAgeyBnZXRWZXJzaW9uOiB0cnVlIH0sXG4gICAgICAgIGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZSB8fCAhcmVzcG9uc2UudmVyc2lvbikge1xuICAgICAgICAgICAgICAgIC8vIENvbW11bmljYXRpb24gZmFpbHVyZSAtIGFzc3VtZSB0aGF0IG5vIGVuZHBvaW50IGV4aXN0c1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAgICAgICAgICAgXCJFeHRlbnNpb24gbm90IGluc3RhbGxlZD86IFwiLCBjaHJvbWUucnVudGltZS5sYXN0RXJyb3IpO1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGZhbHNlLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ2hlY2sgaW5zdGFsbGVkIGV4dGVuc2lvbiB2ZXJzaW9uXG4gICAgICAgICAgICB2YXIgZXh0VmVyc2lvbiA9IHJlc3BvbnNlLnZlcnNpb247XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRXh0ZW5zaW9uIHZlcnNpb24gaXM6ICcgKyBleHRWZXJzaW9uKTtcbiAgICAgICAgICAgIHZhciB1cGRhdGVSZXF1aXJlZFxuICAgICAgICAgICAgICAgID0gaXNVcGRhdGVSZXF1aXJlZChjb25maWcubWluQ2hyb21lRXh0VmVyc2lvbiwgZXh0VmVyc2lvbik7XG4gICAgICAgICAgICBjYWxsYmFjayghdXBkYXRlUmVxdWlyZWQsIHVwZGF0ZVJlcXVpcmVkKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cbmZ1bmN0aW9uIGRvR2V0U3RyZWFtRnJvbUV4dGVuc2lvbihzdHJlYW1DYWxsYmFjaywgZmFpbENhbGxiYWNrKSB7XG4gICAgLy8gU2VuZHMgJ2dldFN0cmVhbScgbXNnIHRvIHRoZSBleHRlbnNpb24uXG4gICAgLy8gRXh0ZW5zaW9uIGlkIG11c3QgYmUgZGVmaW5lZCBpbiB0aGUgY29uZmlnLlxuICAgIGNocm9tZS5ydW50aW1lLnNlbmRNZXNzYWdlKFxuICAgICAgICBjb25maWcuY2hyb21lRXh0ZW5zaW9uSWQsXG4gICAgICAgIHsgZ2V0U3RyZWFtOiB0cnVlLCBzb3VyY2VzOiBjb25maWcuZGVza3RvcFNoYXJpbmdTb3VyY2VzIH0sXG4gICAgICAgIGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZSkge1xuICAgICAgICAgICAgICAgIGZhaWxDYWxsYmFjayhjaHJvbWUucnVudGltZS5sYXN0RXJyb3IpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiUmVzcG9uc2UgZnJvbSBleHRlbnNpb246IFwiICsgcmVzcG9uc2UpO1xuICAgICAgICAgICAgaWYgKHJlc3BvbnNlLnN0cmVhbUlkKSB7XG4gICAgICAgICAgICAgICAgQVBQLlJUQy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMoXG4gICAgICAgICAgICAgICAgICAgIFsnZGVza3RvcCddLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdHJlYW1DYWxsYmFjayhzdHJlYW0pO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmYWlsQ2FsbGJhY2ssXG4gICAgICAgICAgICAgICAgICAgIG51bGwsIG51bGwsIG51bGwsXG4gICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlLnN0cmVhbUlkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZmFpbENhbGxiYWNrKFwiRXh0ZW5zaW9uIGZhaWxlZCB0byBnZXQgdGhlIHN0cmVhbVwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICk7XG59XG4vKipcbiAqIEFza3MgQ2hyb21lIGV4dGVuc2lvbiB0byBjYWxsIGNob29zZURlc2t0b3BNZWRpYSBhbmQgZ2V0cyBjaHJvbWUgJ2Rlc2t0b3AnXG4gKiBzdHJlYW0gZm9yIHJldHVybmVkIHN0cmVhbSB0b2tlbi5cbiAqL1xuZnVuY3Rpb24gb2J0YWluU2NyZWVuRnJvbUV4dGVuc2lvbihzdHJlYW1DYWxsYmFjaywgZmFpbENhbGxiYWNrKSB7XG4gICAgaWYgKGV4dEluc3RhbGxlZCkge1xuICAgICAgICBkb0dldFN0cmVhbUZyb21FeHRlbnNpb24oc3RyZWFtQ2FsbGJhY2ssIGZhaWxDYWxsYmFjayk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKGV4dFVwZGF0ZVJlcXVpcmVkKSB7XG4gICAgICAgICAgICBhbGVydChcbiAgICAgICAgICAgICAgICAnSml0c2kgRGVza3RvcCBTdHJlYW1lciByZXF1aXJlcyB1cGRhdGUuICcgK1xuICAgICAgICAgICAgICAgICdDaGFuZ2VzIHdpbGwgdGFrZSBlZmZlY3QgYWZ0ZXIgbmV4dCBDaHJvbWUgcmVzdGFydC4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNocm9tZS53ZWJzdG9yZS5pbnN0YWxsKFxuICAgICAgICAgICAgZ2V0V2ViU3RvcmVJbnN0YWxsVXJsKCksXG4gICAgICAgICAgICBmdW5jdGlvbiAoYXJnKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJFeHRlbnNpb24gaW5zdGFsbGVkIHN1Y2Nlc3NmdWxseVwiLCBhcmcpO1xuICAgICAgICAgICAgICAgIC8vIFdlIG5lZWQgdG8gcmVsb2FkIHRoZSBwYWdlIGluIG9yZGVyIHRvIGdldCB0aGUgYWNjZXNzIHRvXG4gICAgICAgICAgICAgICAgLy8gY2hyb21lLnJ1bnRpbWVcbiAgICAgICAgICAgICAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKGZhbHNlKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoYXJnKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJGYWlsZWQgdG8gaW5zdGFsbCB0aGUgZXh0ZW5zaW9uXCIsIGFyZyk7XG4gICAgICAgICAgICAgICAgZmFpbENhbGxiYWNrKGFyZyk7XG4gICAgICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcihcImRpYWxvZy5lcnJvclwiLFxuICAgICAgICAgICAgICAgICAgICBcImRpYWxvZy5mYWlsdG9pbnN0YWxsXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH1cbn1cblxuLyoqXG4gKiBDYWxsIHRoaXMgbWV0aG9kIHRvIHRvZ2dsZSBkZXNrdG9wIHNoYXJpbmcgZmVhdHVyZS5cbiAqIEBwYXJhbSBtZXRob2QgcGFzcyBcImV4dFwiIHRvIHVzZSBjaHJvbWUgZXh0ZW5zaW9uIGZvciBkZXNrdG9wIGNhcHR1cmUoY2hyb21lXG4gKiAgICAgICAgZXh0ZW5zaW9uIHJlcXVpcmVkKSwgcGFzcyBcIndlYnJ0Y1wiIHRvIHVzZSBXZWJSVEMgXCJzY3JlZW5cIiBkZXNrdG9wXG4gKiAgICAgICAgc291cmNlKCdjaHJvbWU6Ly9mbGFncy8jZW5hYmxlLXVzZXJtZWRpYS1zY3JlZW4tY2FwdHVyZScgbXVzdCBiZVxuICogICAgICAgIGVuYWJsZWQpLCBwYXNzIGFueSBvdGhlciBzdHJpbmcgb3Igbm90aGluZyBpbiBvcmRlciB0byBkaXNhYmxlIHRoaXNcbiAqICAgICAgICBmZWF0dXJlIGNvbXBsZXRlbHkuXG4gKi9cbmZ1bmN0aW9uIHNldERlc2t0b3BTaGFyaW5nKG1ldGhvZCkge1xuICAgIC8vIENoZWNrIGlmIHdlIGFyZSBydW5uaW5nIGNocm9tZVxuICAgIGlmICghbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYSkge1xuICAgICAgICBvYnRhaW5EZXNrdG9wU3RyZWFtID0gbnVsbDtcbiAgICAgICAgY29uc29sZS5pbmZvKFwiRGVza3RvcCBzaGFyaW5nIGRpc2FibGVkXCIpO1xuICAgIH0gZWxzZSBpZiAobWV0aG9kID09IFwiZXh0XCIpIHtcbiAgICAgICAgb2J0YWluRGVza3RvcFN0cmVhbSA9IG9idGFpblNjcmVlbkZyb21FeHRlbnNpb247XG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlVzaW5nIENocm9tZSBleHRlbnNpb24gZm9yIGRlc2t0b3Agc2hhcmluZ1wiKTtcbiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSBcIndlYnJ0Y1wiKSB7XG4gICAgICAgIG9idGFpbkRlc2t0b3BTdHJlYW0gPSBvYnRhaW5XZWJSVENTY3JlZW47XG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlVzaW5nIENocm9tZSBXZWJSVEMgZm9yIGRlc2t0b3Agc2hhcmluZ1wiKTtcbiAgICB9XG5cbiAgICAvLyBSZXNldCBlbmFibGVkIGNhY2hlXG4gICAgX2Rlc2t0b3BTaGFyaW5nRW5hYmxlZCA9IG51bGw7XG59XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgPGxpbmsgcmVsPWNocm9tZS13ZWJzdG9yZS1pdGVtIC8+IHdpdGggZXh0ZW5zaW9uIGlkIHNldCBpblxuICogY29uZmlnLmpzIHRvIHN1cHBvcnQgaW5saW5lIGluc3RhbGxzLiBIb3N0IHNpdGUgbXVzdCBiZSBzZWxlY3RlZCBhcyBtYWluXG4gKiB3ZWJzaXRlIG9mIHB1Ymxpc2hlZCBleHRlbnNpb24uXG4gKi9cbmZ1bmN0aW9uIGluaXRJbmxpbmVJbnN0YWxscygpXG57XG4gICAgJChcImxpbmtbcmVsPWNocm9tZS13ZWJzdG9yZS1pdGVtXVwiKS5hdHRyKFwiaHJlZlwiLCBnZXRXZWJTdG9yZUluc3RhbGxVcmwoKSk7XG59XG5cbmZ1bmN0aW9uIGdldFZpZGVvU3RyZWFtRmFpbGVkKGVycm9yKSB7XG4gICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBvYnRhaW4gdGhlIHN0cmVhbSB0byBzd2l0Y2ggdG9cIiwgZXJyb3IpO1xuICAgIHN3aXRjaEluUHJvZ3Jlc3MgPSBmYWxzZTtcbiAgICBpc1VzaW5nU2NyZWVuU3RyZWFtID0gZmFsc2U7XG4gICAgbmV3U3RyZWFtQ3JlYXRlZChudWxsKTtcbn1cblxuZnVuY3Rpb24gZ2V0RGVza3RvcFN0cmVhbUZhaWxlZChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gb2J0YWluIHRoZSBzdHJlYW0gdG8gc3dpdGNoIHRvXCIsIGVycm9yKTtcbiAgICBzd2l0Y2hJblByb2dyZXNzID0gZmFsc2U7XG59XG5cbmZ1bmN0aW9uIHN0cmVhbVN3aXRjaERvbmUoKSB7XG4gICAgc3dpdGNoSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgIGV2ZW50RW1pdHRlci5lbWl0KFxuICAgICAgICBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuU1dJVENISU5HX0RPTkUsXG4gICAgICAgIGlzVXNpbmdTY3JlZW5TdHJlYW0pO1xufVxuXG5mdW5jdGlvbiBuZXdTdHJlYW1DcmVhdGVkKHN0cmVhbSlcbntcbiAgICBldmVudEVtaXR0ZXIuZW1pdChEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuTkVXX1NUUkVBTV9DUkVBVEVELFxuICAgICAgICBzdHJlYW0sIGlzVXNpbmdTY3JlZW5TdHJlYW0sIHN0cmVhbVN3aXRjaERvbmUpO1xufVxuXG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGlzVXNpbmdTY3JlZW5TdHJlYW06IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGlzVXNpbmdTY3JlZW5TdHJlYW07XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSA8dHQ+dHJ1ZTwvdHQ+IGlmIGRlc2t0b3Agc2hhcmluZyBmZWF0dXJlIGlzIGF2YWlsYWJsZVxuICAgICAqICAgICAgICAgIGFuZCBlbmFibGVkLlxuICAgICAqL1xuICAgIGlzRGVza3RvcFNoYXJpbmdFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChfZGVza3RvcFNoYXJpbmdFbmFibGVkID09PSBudWxsKSB7XG4gICAgICAgICAgICBpZiAob2J0YWluRGVza3RvcFN0cmVhbSA9PT0gb2J0YWluU2NyZWVuRnJvbUV4dGVuc2lvbikge1xuICAgICAgICAgICAgICAgIC8vIFBhcnNlIGNocm9tZSB2ZXJzaW9uXG4gICAgICAgICAgICAgICAgdmFyIHVzZXJBZ2VudCA9IG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICAvLyBXZSBjYW4gYXNzdW1lIHRoYXQgdXNlciBhZ2VudCBpcyBjaHJvbWUsIGJlY2F1c2UgaXQnc1xuICAgICAgICAgICAgICAgIC8vIGVuZm9yY2VkIHdoZW4gJ2V4dCcgc3RyZWFtaW5nIG1ldGhvZCBpcyBzZXRcbiAgICAgICAgICAgICAgICB2YXIgdmVyID0gcGFyc2VJbnQodXNlckFnZW50Lm1hdGNoKC9jaHJvbWVcXC8oXFxkKylcXC4vKVsxXSwgMTApO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiQ2hyb21lIHZlcnNpb25cIiArIHVzZXJBZ2VudCwgdmVyKTtcbiAgICAgICAgICAgICAgICBfZGVza3RvcFNoYXJpbmdFbmFibGVkID0gdmVyID49IDM0O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBfZGVza3RvcFNoYXJpbmdFbmFibGVkID1cbiAgICAgICAgICAgICAgICAgICAgb2J0YWluRGVza3RvcFN0cmVhbSA9PT0gb2J0YWluV2ViUlRDU2NyZWVuO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBfZGVza3RvcFNoYXJpbmdFbmFibGVkO1xuICAgIH0sXG4gICAgXG4gICAgaW5pdDogZnVuY3Rpb24gKCkge1xuICAgICAgICBzZXREZXNrdG9wU2hhcmluZyhjb25maWcuZGVza3RvcFNoYXJpbmcpO1xuXG4gICAgICAgIC8vIEluaXRpYWxpemUgQ2hyb21lIGV4dGVuc2lvbiBpbmxpbmUgaW5zdGFsbHNcbiAgICAgICAgaWYgKGNvbmZpZy5jaHJvbWVFeHRlbnNpb25JZCkge1xuXG4gICAgICAgICAgICBpbml0SW5saW5lSW5zdGFsbHMoKTtcblxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgZXh0ZW5zaW9uIGlzIGluc3RhbGxlZFxuICAgICAgICAgICAgY2hlY2tFeHRJbnN0YWxsZWQoZnVuY3Rpb24gKGluc3RhbGxlZCwgdXBkYXRlUmVxdWlyZWQpIHtcbiAgICAgICAgICAgICAgICBleHRJbnN0YWxsZWQgPSBpbnN0YWxsZWQ7XG4gICAgICAgICAgICAgICAgZXh0VXBkYXRlUmVxdWlyZWQgPSB1cGRhdGVSZXF1aXJlZDtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXG4gICAgICAgICAgICAgICAgICAgIFwiQ2hyb21lIGV4dGVuc2lvbiBpbnN0YWxsZWQ6IFwiICsgZXh0SW5zdGFsbGVkICtcbiAgICAgICAgICAgICAgICAgICAgXCIgdXBkYXRlUmVxdWlyZWQ6IFwiICsgZXh0VXBkYXRlUmVxdWlyZWQpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuSU5JVCk7XG4gICAgfSxcblxuICAgIGFkZExpc3RlbmVyOiBmdW5jdGlvbiAobGlzdGVuZXIsIHR5cGUpXG4gICAge1xuICAgICAgICBldmVudEVtaXR0ZXIub24odHlwZSwgbGlzdGVuZXIpO1xuICAgIH0sXG5cbiAgICByZW1vdmVMaXN0ZW5lcjogZnVuY3Rpb24gKGxpc3RlbmVyLCB0eXBlKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICogVG9nZ2xlcyBzY3JlZW4gc2hhcmluZy5cbiAgICAgKi9cbiAgICB0b2dnbGVTY3JlZW5TaGFyaW5nOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChzd2l0Y2hJblByb2dyZXNzIHx8ICFvYnRhaW5EZXNrdG9wU3RyZWFtKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJTd2l0Y2ggaW4gcHJvZ3Jlc3Mgb3Igbm8gbWV0aG9kIGRlZmluZWRcIik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgc3dpdGNoSW5Qcm9ncmVzcyA9IHRydWU7XG5cbiAgICAgICAgaWYgKCFpc1VzaW5nU2NyZWVuU3RyZWFtKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBTd2l0Y2ggdG8gZGVza3RvcCBzdHJlYW1cbiAgICAgICAgICAgIG9idGFpbkRlc2t0b3BTdHJlYW0oXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBXZSBub3cgdXNlIHNjcmVlbiBzdHJlYW1cbiAgICAgICAgICAgICAgICAgICAgaXNVc2luZ1NjcmVlblN0cmVhbSA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIC8vIEhvb2sgJ2VuZGVkJyBldmVudCB0byByZXN0b3JlIGNhbWVyYVxuICAgICAgICAgICAgICAgICAgICAvLyB3aGVuIHNjcmVlbiBzdHJlYW0gc3RvcHNcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLmFkZEV2ZW50TGlzdGVuZXIoJ2VuZGVkJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFzd2l0Y2hJblByb2dyZXNzICYmIGlzVXNpbmdTY3JlZW5TdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLmRlc2t0b3BzaGFyaW5nLnRvZ2dsZVNjcmVlblNoYXJpbmcoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIG5ld1N0cmVhbUNyZWF0ZWQoc3RyZWFtKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGdldERlc2t0b3BTdHJlYW1GYWlsZWQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRGlzYWJsZSBzY3JlZW4gc3RyZWFtXG4gICAgICAgICAgICBBUFAuUlRDLmdldFVzZXJNZWRpYVdpdGhDb25zdHJhaW50cyhcbiAgICAgICAgICAgICAgICBbJ3ZpZGVvJ10sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBXZSBhcmUgbm93IHVzaW5nIGNhbWVyYSBzdHJlYW1cbiAgICAgICAgICAgICAgICAgICAgaXNVc2luZ1NjcmVlblN0cmVhbSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICBuZXdTdHJlYW1DcmVhdGVkKHN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBnZXRWaWRlb1N0cmVhbUZhaWxlZCwgY29uZmlnLnJlc29sdXRpb24gfHwgJzM2MCdcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG4iLCIvL21hcHMga2V5Y29kZSB0byBjaGFyYWN0ZXIsIGlkIG9mIHBvcG92ZXIgZm9yIGdpdmVuIGZ1bmN0aW9uIGFuZCBmdW5jdGlvblxudmFyIHNob3J0Y3V0cyA9IHtcbiAgICA2Nzoge1xuICAgICAgICBjaGFyYWN0ZXI6IFwiQ1wiLFxuICAgICAgICBpZDogXCJ0b2dnbGVDaGF0UG9wb3ZlclwiLFxuICAgICAgICBmdW5jdGlvbjogQVBQLlVJLnRvZ2dsZUNoYXRcbiAgICB9LFxuICAgIDcwOiB7XG4gICAgICAgIGNoYXJhY3RlcjogXCJGXCIsXG4gICAgICAgIGlkOiBcImZpbG1zdHJpcFBvcG92ZXJcIixcbiAgICAgICAgZnVuY3Rpb246IEFQUC5VSS50b2dnbGVGaWxtU3RyaXBcbiAgICB9LFxuICAgIDc3OiB7XG4gICAgICAgIGNoYXJhY3RlcjogXCJNXCIsXG4gICAgICAgIGlkOiBcIm11dGVQb3BvdmVyXCIsXG4gICAgICAgIGZ1bmN0aW9uOiBBUFAuVUkudG9nZ2xlQXVkaW9cbiAgICB9LFxuICAgIDg0OiB7XG4gICAgICAgIGNoYXJhY3RlcjogXCJUXCIsXG4gICAgICAgIGZ1bmN0aW9uOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmKCFBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAgICAgQVBQLlVJLnRvZ2dsZUF1ZGlvKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9LFxuICAgIDg2OiB7XG4gICAgICAgIGNoYXJhY3RlcjogXCJWXCIsXG4gICAgICAgIGlkOiBcInRvZ2dsZVZpZGVvUG9wb3ZlclwiLFxuICAgICAgICBmdW5jdGlvbjogQVBQLlVJLnRvZ2dsZVZpZGVvXG4gICAgfVxufTtcblxuXG52YXIgS2V5Ym9hcmRTaG9ydGN1dCA9IHtcbiAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHdpbmRvdy5vbmtleXVwID0gZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgdmFyIGtleWNvZGUgPSBlLndoaWNoO1xuICAgICAgICAgICAgaWYoISgkKFwiOmZvY3VzXCIpLmlzKFwiaW5wdXRbdHlwZT10ZXh0XVwiKSB8fFxuICAgICAgICAgICAgICAgICQoXCI6Zm9jdXNcIikuaXMoXCJpbnB1dFt0eXBlPXBhc3N3b3JkXVwiKSB8fFxuICAgICAgICAgICAgICAgICQoXCI6Zm9jdXNcIikuaXMoXCJ0ZXh0YXJlYVwiKSkpIHtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHNob3J0Y3V0c1trZXljb2RlXSA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgICAgICAgICBzaG9ydGN1dHNba2V5Y29kZV0uZnVuY3Rpb24oKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoa2V5Y29kZSA+PSBcIjBcIi5jaGFyQ29kZUF0KDApICYmXG4gICAgICAgICAgICAgICAgICAgIGtleWNvZGUgPD0gXCI5XCIuY2hhckNvZGVBdCgwKSkge1xuICAgICAgICAgICAgICAgICAgICBBUFAuVUkuY2xpY2tPblZpZGVvKGtleWNvZGUgLSBcIjBcIi5jaGFyQ29kZUF0KDApICsgMSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vZXNjIHdoaWxlIHRoZSBzbWlsZXlzIGFyZSB2aXNpYmxlIGhpZGVzIHRoZW1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoa2V5Y29kZSA9PT0gMjcgJiYgJCgnI3NtaWxleXNDb250YWluZXInKS5pcygnOnZpc2libGUnKSkge1xuICAgICAgICAgICAgICAgIEFQUC5VSS50b2dnbGVTbWlsZXlzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgd2luZG93Lm9ua2V5ZG93biA9IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIGlmKCEoJChcIjpmb2N1c1wiKS5pcyhcImlucHV0W3R5cGU9dGV4dF1cIikgfHxcbiAgICAgICAgICAgICAgICAkKFwiOmZvY3VzXCIpLmlzKFwiaW5wdXRbdHlwZT1wYXNzd29yZF1cIikgfHxcbiAgICAgICAgICAgICAgICAkKFwiOmZvY3VzXCIpLmlzKFwidGV4dGFyZWFcIikpKSB7XG4gICAgICAgICAgICAgICAgaWYoZS53aGljaCA9PT0gXCJUXCIuY2hhckNvZGVBdCgwKSkge1xuICAgICAgICAgICAgICAgICAgICBpZihBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAuVUkudG9nZ2xlQXVkaW8oKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAkKCdib2R5JykucG9wb3Zlcih7IHNlbGVjdG9yOiAnW2RhdGEtdG9nZ2xlPXBvcG92ZXJdJyxcbiAgICAgICAgICAgIHRyaWdnZXI6ICdjbGljayBob3ZlcicsXG4gICAgICAgICAgICBjb250ZW50OiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoXCJjb250ZW50XCIpICtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5nZXRTaG9ydGN1dCh0aGlzLmdldEF0dHJpYnV0ZShcInNob3J0Y3V0XCIpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBpbmRpY2F0ZXMgdGhlIHBvcG92ZXIgYXNzb2NpYXRlZCB3aXRoIHRoZSBzaG9ydGN1dFxuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9IHRoZSBrZXlib2FyZCBzaG9ydGN1dCB1c2VkIGZvciB0aGUgaWQgZ2l2ZW5cbiAgICAgKi9cbiAgICBnZXRTaG9ydGN1dDogZnVuY3Rpb24gKGlkKSB7XG4gICAgICAgIGZvciAodmFyIGtleWNvZGUgaW4gc2hvcnRjdXRzKSB7XG4gICAgICAgICAgICBpZiAoc2hvcnRjdXRzLmhhc093blByb3BlcnR5KGtleWNvZGUpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNob3J0Y3V0c1trZXljb2RlXS5pZCA9PT0gaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFwiIChcIiArIHNob3J0Y3V0c1trZXljb2RlXS5jaGFyYWN0ZXIgKyBcIilcIjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBLZXlib2FyZFNob3J0Y3V0O1xuIiwidmFyIGVtYWlsID0gJyc7XG52YXIgZGlzcGxheU5hbWUgPSAnJztcbnZhciB1c2VySWQ7XG52YXIgbGFuZ3VhZ2UgPSBudWxsO1xuXG5cbmZ1bmN0aW9uIHN1cHBvcnRzTG9jYWxTdG9yYWdlKCkge1xuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiAnbG9jYWxTdG9yYWdlJyBpbiB3aW5kb3cgJiYgd2luZG93LmxvY2FsU3RvcmFnZSAhPT0gbnVsbDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwibG9jYWxzdG9yYWdlIGlzIG5vdCBzdXBwb3J0ZWRcIik7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59XG5cblxuZnVuY3Rpb24gZ2VuZXJhdGVVbmlxdWVJZCgpIHtcbiAgICBmdW5jdGlvbiBfcDgoKSB7XG4gICAgICAgIHJldHVybiAoTWF0aC5yYW5kb20oKS50b1N0cmluZygxNikgKyBcIjAwMDAwMDAwMFwiKS5zdWJzdHIoMiwgOCk7XG4gICAgfVxuICAgIHJldHVybiBfcDgoKSArIF9wOCgpICsgX3A4KCkgKyBfcDgoKTtcbn1cblxuaWYgKHN1cHBvcnRzTG9jYWxTdG9yYWdlKCkpIHtcbiAgICBpZiAoIXdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQpIHtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCA9IGdlbmVyYXRlVW5pcXVlSWQoKTtcbiAgICAgICAgY29uc29sZS5sb2coXCJnZW5lcmF0ZWQgaWRcIiwgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCk7XG4gICAgfVxuICAgIHVzZXJJZCA9IHdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQgfHwgJyc7XG4gICAgZW1haWwgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmVtYWlsIHx8ICcnO1xuICAgIGRpc3BsYXlOYW1lID0gd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSB8fCAnJztcbiAgICBsYW5ndWFnZSA9IHdpbmRvdy5sb2NhbFN0b3JhZ2UubGFuZ3VhZ2U7XG59IGVsc2Uge1xuICAgIGNvbnNvbGUubG9nKFwibG9jYWwgc3RvcmFnZSBpcyBub3Qgc3VwcG9ydGVkXCIpO1xuICAgIHVzZXJJZCA9IGdlbmVyYXRlVW5pcXVlSWQoKTtcbn1cblxudmFyIFNldHRpbmdzID1cbntcbiAgICBzZXREaXNwbGF5TmFtZTogZnVuY3Rpb24gKG5ld0Rpc3BsYXlOYW1lKSB7XG4gICAgICAgIGRpc3BsYXlOYW1lID0gbmV3RGlzcGxheU5hbWU7XG4gICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2UuZGlzcGxheW5hbWUgPSBkaXNwbGF5TmFtZTtcbiAgICAgICAgcmV0dXJuIGRpc3BsYXlOYW1lO1xuICAgIH0sXG4gICAgc2V0RW1haWw6IGZ1bmN0aW9uIChuZXdFbWFpbClcbiAgICB7XG4gICAgICAgIGVtYWlsID0gbmV3RW1haWw7XG4gICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2UuZW1haWwgPSBuZXdFbWFpbDtcbiAgICAgICAgcmV0dXJuIGVtYWlsO1xuICAgIH0sXG4gICAgZ2V0U2V0dGluZ3M6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGVtYWlsOiBlbWFpbCxcbiAgICAgICAgICAgIGRpc3BsYXlOYW1lOiBkaXNwbGF5TmFtZSxcbiAgICAgICAgICAgIHVpZDogdXNlcklkLFxuICAgICAgICAgICAgbGFuZ3VhZ2U6IGxhbmd1YWdlXG4gICAgICAgIH07XG4gICAgfSxcbiAgICBzZXRMYW5ndWFnZTogZnVuY3Rpb24gKGxhbmcpIHtcbiAgICAgICAgbGFuZ3VhZ2UgPSBsYW5nO1xuICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLmxhbmd1YWdlID0gbGFuZztcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNldHRpbmdzO1xuIiwiLyoqXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNpbXVsY2FzdExvZ2dlcihuYW1lLCBsdmwpIHtcbiAgICB0aGlzLm5hbWUgPSBuYW1lO1xuICAgIHRoaXMubHZsID0gbHZsO1xufVxuXG5TaW11bGNhc3RMb2dnZXIucHJvdG90eXBlLmxvZyA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgaWYgKHRoaXMubHZsKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKHRleHQpO1xuICAgIH1cbn07XG5cblNpbXVsY2FzdExvZ2dlci5wcm90b3R5cGUuaW5mbyA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgaWYgKHRoaXMubHZsID4gMSkge1xuICAgICAgICBjb25zb2xlLmluZm8odGV4dCk7XG4gICAgfVxufTtcblxuU2ltdWxjYXN0TG9nZ2VyLnByb3RvdHlwZS5maW5lID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICBpZiAodGhpcy5sdmwgPiAyKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKHRleHQpO1xuICAgIH1cbn07XG5cblNpbXVsY2FzdExvZ2dlci5wcm90b3R5cGUuZXJyb3IgPSBmdW5jdGlvbiAodGV4dCkge1xuICAgIGNvbnNvbGUuZXJyb3IodGV4dCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNpbXVsY2FzdExvZ2dlcjsiLCJ2YXIgU2ltdWxjYXN0TG9nZ2VyID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0TG9nZ2VyXCIpO1xudmFyIFNpbXVsY2FzdFV0aWxzID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0VXRpbHNcIik7XG52YXIgTWVkaWFTdHJlYW1UeXBlID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL01lZGlhU3RyZWFtVHlwZXNcIik7XG5cbmZ1bmN0aW9uIFNpbXVsY2FzdFJlY2VpdmVyKCkge1xuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMgPSBuZXcgU2ltdWxjYXN0VXRpbHMoKTtcbiAgICB0aGlzLmxvZ2dlciA9IG5ldyBTaW11bGNhc3RMb2dnZXIoJ1NpbXVsY2FzdFJlY2VpdmVyJywgMSk7XG59XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fcmVtb3RlVmlkZW9Tb3VyY2VDYWNoZSA9ICcnO1xuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9yZW1vdGVNYXBzID0ge1xuICAgIG1zaWQyUXVhbGl0eToge30sXG4gICAgc3NyYzJNc2lkOiB7fSxcbiAgICBtc2lkMnNzcmM6IHt9LFxuICAgIHJlY2VpdmluZ1ZpZGVvU3RyZWFtczoge31cbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fY2FjaGVSZW1vdGVWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB0aGlzLl9yZW1vdGVWaWRlb1NvdXJjZUNhY2hlID0gdGhpcy5zaW11bGNhc3RVdGlscy5fZ2V0VmlkZW9Tb3VyY2VzKGxpbmVzKTtcbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fcmVzdG9yZVJlbW90ZVZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHRoaXMuX3JlbW90ZVZpZGVvU291cmNlQ2FjaGUpO1xufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9lbnN1cmVHb29nQ29uZmVyZW5jZSA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciBzYjtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0Vuc3VyaW5nIHgtZ29vZ2xlLWNvbmZlcmVuY2UgZmxhZy4uLicpXG5cbiAgICBpZiAodGhpcy5zaW11bGNhc3RVdGlscy5faW5kZXhPZkFycmF5KCdhPXgtZ29vZ2xlLWZsYWc6Y29uZmVyZW5jZScsIGxpbmVzKSA9PT0gdGhpcy5zaW11bGNhc3RVdGlscy5fZW1wdHlDb21wb3VuZEluZGV4KSB7XG4gICAgICAgIC8vIFRPRE8oZ3ApIGRvIHRoYXQgZm9yIHRoZSBhdWRpbyBhcyB3ZWxsIGFzIHN1Z2dlc3RlZCBieSBmaXBwby5cbiAgICAgICAgLy8gQWRkIHRoZSBnb29nbGUgY29uZmVyZW5jZSBmbGFnXG4gICAgICAgIHNiID0gdGhpcy5zaW11bGNhc3RVdGlscy5fZ2V0VmlkZW9Tb3VyY2VzKGxpbmVzKTtcbiAgICAgICAgc2IgPSBbJ2E9eC1nb29nbGUtZmxhZzpjb25mZXJlbmNlJ10uY29uY2F0KHNiKTtcbiAgICAgICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgc2IpO1xuICAgIH1cbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fcmVzdG9yZVNpbXVsY2FzdEdyb3VwcyA9IGZ1bmN0aW9uIChzYikge1xuICAgIHRoaXMuX3Jlc3RvcmVSZW1vdGVWaWRlb1NvdXJjZXMoc2IpO1xufTtcblxuLyoqXG4gKiBSZXN0b3JlcyB0aGUgc2ltdWxjYXN0IGdyb3VwcyBvZiB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uLiBJblxuICogdHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gd2UgcmVtb3ZlIHRob3NlIGluIG9yZGVyIGZvciB0aGUgc2V0IHJlbW90ZVxuICogZGVzY3JpcHRpb24gdG8gc3VjY2VlZC4gVGhlIGZvY3VzIG5lZWRzIHRoZSBzaWduYWwgdGhlIGdyb3VwcyB0byBuZXdcbiAqIHBhcnRpY2lwYW50cy5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHZhciBzYjtcblxuICAgIGlmICghdGhpcy5zaW11bGNhc3RVdGlscy5pc1ZhbGlkRGVzY3JpcHRpb24oZGVzYykpIHtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpZy5lbmFibGVTaW11bGNhc3QpIHtcbiAgICAgICAgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICAgICAgdGhpcy5fcmVzdG9yZVNpbXVsY2FzdEdyb3VwcyhzYik7XG5cbiAgICAgICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fZW5zdXJlT3JkZXIgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgdmlkZW9Tb3VyY2VzLCBzYjtcblxuICAgIHZpZGVvU291cmNlcyA9IHRoaXMucGFyc2VNZWRpYShsaW5lcywgWyd2aWRlbyddKVswXTtcbiAgICBzYiA9IHRoaXMuX2NvbXBpbGVWaWRlb1NvdXJjZXModmlkZW9Tb3VyY2VzKTtcblxuICAgIHRoaXMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHNiKTtcbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5fdXBkYXRlUmVtb3RlTWFwcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciByZW1vdGVWaWRlb1NvdXJjZXMgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLnBhcnNlTWVkaWEobGluZXMsIFsndmlkZW8nXSlbMF0sXG4gICAgICAgIHZpZGVvU291cmNlLCBxdWFsaXR5O1xuXG4gICAgLy8gKHJlKSBpbml0aWFsaXplIHRoZSByZW1vdGUgbWFwcy5cbiAgICB0aGlzLl9yZW1vdGVNYXBzLm1zaWQyUXVhbGl0eSA9IHt9O1xuICAgIHRoaXMuX3JlbW90ZU1hcHMuc3NyYzJNc2lkID0ge307XG4gICAgdGhpcy5fcmVtb3RlTWFwcy5tc2lkMnNzcmMgPSB7fTtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAocmVtb3RlVmlkZW9Tb3VyY2VzLmdyb3VwcyAmJiByZW1vdGVWaWRlb1NvdXJjZXMuZ3JvdXBzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICByZW1vdGVWaWRlb1NvdXJjZXMuZ3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKGdyb3VwKSB7XG4gICAgICAgICAgICBpZiAoZ3JvdXAuc2VtYW50aWNzID09PSAnU0lNJyAmJiBncm91cC5zc3JjcyAmJiBncm91cC5zc3Jjcy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgICAgICAgICBxdWFsaXR5ID0gMDtcbiAgICAgICAgICAgICAgICBncm91cC5zc3Jjcy5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIHZpZGVvU291cmNlID0gcmVtb3RlVmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY107XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuX3JlbW90ZU1hcHMubXNpZDJRdWFsaXR5W3ZpZGVvU291cmNlLm1zaWRdID0gcXVhbGl0eSsrO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLl9yZW1vdGVNYXBzLnNzcmMyTXNpZFt2aWRlb1NvdXJjZS5zc3JjXSA9IHZpZGVvU291cmNlLm1zaWQ7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuX3JlbW90ZU1hcHMubXNpZDJzc3JjW3ZpZGVvU291cmNlLm1zaWRdID0gdmlkZW9Tb3VyY2Uuc3NyYztcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9zZXRSZWNlaXZpbmdWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uIChyZXNvdXJjZSwgc3NyYykge1xuICAgIHRoaXMuX3JlbW90ZU1hcHMucmVjZWl2aW5nVmlkZW9TdHJlYW1zW3Jlc291cmNlXSA9IHNzcmM7XG59O1xuXG4vKipcbiAqIFJldHVybnMgYSBzdHJlYW0gd2l0aCBzaW5nbGUgdmlkZW8gdHJhY2ssIHRoZSBvbmUgY3VycmVudGx5IGJlaW5nXG4gKiByZWNlaXZlZCBieSB0aGlzIGVuZHBvaW50LlxuICpcbiAqIEBwYXJhbSBzdHJlYW0gdGhlIHJlbW90ZSBzaW11bGNhc3Qgc3RyZWFtLlxuICogQHJldHVybnMge3dlYmtpdE1lZGlhU3RyZWFtfVxuICovXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0gPSBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgdmFyIHRyYWNrcywgaSwgZWxlY3RlZFRyYWNrLCBtc2lkLCBxdWFsaXR5ID0gMCwgcmVjZWl2aW5nVHJhY2tJZDtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAoY29uZmlnLmVuYWJsZVNpbXVsY2FzdCkge1xuXG4gICAgICAgIHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLnNvbWUoZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICAgICAgICByZXR1cm4gT2JqZWN0LmtleXMoc2VsZi5fcmVtb3RlTWFwcy5yZWNlaXZpbmdWaWRlb1N0cmVhbXMpLnNvbWUoZnVuY3Rpb24gKHJlc291cmNlKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNzcmMgPSBzZWxmLl9yZW1vdGVNYXBzLnJlY2VpdmluZ1ZpZGVvU3RyZWFtc1tyZXNvdXJjZV07XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBzZWxmLl9yZW1vdGVNYXBzLnNzcmMyTXNpZFtzc3JjXTtcbiAgICAgICAgICAgICAgICBpZiAobXNpZCA9PSBbc3RyZWFtLmlkLCB0cmFjay5pZF0uam9pbignICcpKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZWN0ZWRUcmFjayA9IHRyYWNrO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKCFlbGVjdGVkVHJhY2spIHtcbiAgICAgICAgICAgIC8vIHdlIGRvbid0IGhhdmUgYW4gZWxlY3RlZCB0cmFjaywgY2hvb3NlIGJ5IGluaXRpYWwgcXVhbGl0eS5cbiAgICAgICAgICAgIHRyYWNrcyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIG1zaWQgPSBbc3RyZWFtLmlkLCB0cmFja3NbaV0uaWRdLmpvaW4oJyAnKTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5fcmVtb3RlTWFwcy5tc2lkMlF1YWxpdHlbbXNpZF0gPT09IHF1YWxpdHkpIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlY3RlZFRyYWNrID0gdHJhY2tzW2ldO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFRPRE8oZ3ApIGlmIHRoZSBpbml0aWFsUXVhbGl0eSBjb3VsZCBub3QgYmUgc2F0aXNmaWVkLCBsb3dlclxuICAgICAgICAgICAgLy8gdGhlIHJlcXVpcmVtZW50IGFuZCB0cnkgYWdhaW4uXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gKGVsZWN0ZWRUcmFjaylcbiAgICAgICAgPyBuZXcgd2Via2l0TWVkaWFTdHJlYW0oW2VsZWN0ZWRUcmFja10pXG4gICAgICAgIDogc3RyZWFtO1xufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLmdldFJlY2VpdmluZ1NTUkMgPSBmdW5jdGlvbiAoamlkKSB7XG4gICAgdmFyIHJlc291cmNlID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICB2YXIgc3NyYyA9IHRoaXMuX3JlbW90ZU1hcHMucmVjZWl2aW5nVmlkZW9TdHJlYW1zW3Jlc291cmNlXTtcblxuICAgIC8vIElmIHdlIGhhdmVuJ3QgcmVjZWl2aW5nIGEgXCJjaGFuZ2VkXCIgZXZlbnQgeWV0LCB0aGVuIHdlIG11c3QgYmUgcmVjZWl2aW5nXG4gICAgLy8gbG93IHF1YWxpdHkgKHRoYXQgdGhlIHNlbmRlciBhbHdheXMgc3RyZWFtcykuXG4gICAgaWYoIXNzcmMpXG4gICAge1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtT2JqZWN0ID0gQVBQLlJUQy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtID0gcmVtb3RlU3RyZWFtT2JqZWN0LmdldE9yaWdpbmFsU3RyZWFtKCk7XG4gICAgICAgIHZhciB0cmFja3MgPSByZW1vdGVTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICAgICAgaWYgKHRyYWNrcykge1xuICAgICAgICAgICAgZm9yICh2YXIgayA9IDA7IGsgPCB0cmFja3MubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2sgPSB0cmFja3Nba107XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBbcmVtb3RlU3RyZWFtLmlkLCB0cmFjay5pZF0uam9pbignICcpO1xuICAgICAgICAgICAgICAgIHZhciBfc3NyYyA9IHRoaXMuX3JlbW90ZU1hcHMubXNpZDJzc3JjW21zaWRdO1xuICAgICAgICAgICAgICAgIHZhciBxdWFsaXR5ID0gdGhpcy5fcmVtb3RlTWFwcy5tc2lkMlF1YWxpdHlbbXNpZF07XG4gICAgICAgICAgICAgICAgaWYgKHF1YWxpdHkgPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBzc3JjID0gX3NzcmM7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNzcmM7XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW1CeVNTUkMgPSBmdW5jdGlvbiAoc3NyYylcbntcbiAgICB2YXIgc2lkLCBlbGVjdGVkU3RyZWFtO1xuICAgIHZhciBpLCBqLCBrO1xuICAgIHZhciBqaWQgPSBBUFAueG1wcC5nZXRKaWRGcm9tU1NSQyhzc3JjKTtcbiAgICBpZihqaWQgJiYgQVBQLlJUQy5yZW1vdGVTdHJlYW1zW2ppZF0pXG4gICAge1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtT2JqZWN0ID0gQVBQLlJUQy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtID0gcmVtb3RlU3RyZWFtT2JqZWN0LmdldE9yaWdpbmFsU3RyZWFtKCk7XG4gICAgICAgIHZhciB0cmFja3MgPSByZW1vdGVTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICAgICAgaWYgKHRyYWNrcykge1xuICAgICAgICAgICAgZm9yIChrID0gMDsgayA8IHRyYWNrcy5sZW5ndGg7IGsrKykge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IHRyYWNrc1trXTtcbiAgICAgICAgICAgICAgICB2YXIgbXNpZCA9IFtyZW1vdGVTdHJlYW0uaWQsIHRyYWNrLmlkXS5qb2luKCcgJyk7XG4gICAgICAgICAgICAgICAgdmFyIHRtcCA9IHRoaXMuX3JlbW90ZU1hcHMubXNpZDJzc3JjW21zaWRdO1xuICAgICAgICAgICAgICAgIGlmICh0bXAgPT0gc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBlbGVjdGVkU3RyZWFtID0gbmV3IHdlYmtpdE1lZGlhU3RyZWFtKFt0cmFja10pO1xuICAgICAgICAgICAgICAgICAgICBzaWQgPSByZW1vdGVTdHJlYW1PYmplY3Quc2lkO1xuICAgICAgICAgICAgICAgICAgICAvLyBzdHJlYW0gZm91bmQsIHN0b3AuXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIGNvbnNvbGUuZGVidWcoQVBQLlJUQy5yZW1vdGVTdHJlYW1zLCBqaWQsIHNzcmMpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIHNpZDogc2lkLFxuICAgICAgICBzdHJlYW06IGVsZWN0ZWRTdHJlYW1cbiAgICB9O1xufTtcblxuLyoqXG4gKiBHZXRzIHRoZSBmdWxseSBxdWFsaWZpZWQgbXNpZCAoc3RyZWFtLmlkICsgdHJhY2suaWQpIGFzc29jaWF0ZWQgdG8gdGhlXG4gKiBTU1JDLlxuICpcbiAqIEBwYXJhbSBzc3JjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMgPSBmdW5jdGlvbiAoc3NyYykge1xuICAgIHJldHVybiB0aGlzLl9yZW1vdGVNYXBzLnNzcmMyTXNpZFtzc3JjXTtcbn07XG5cbi8qKlxuICogUmVtb3ZlcyB0aGUgc3NyYy1ncm91cDpTSU0gZnJvbSB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uIGJhY2F1c2UgQ2hyb21lXG4gKiBlaXRoZXIgZ2V0cyBjb25mdXNlZCBhbmQgdGhpbmtzIHRoaXMgaXMgYW4gRklEIGdyb3VwIG9yLCBpZiBhbiBGSUQgZ3JvdXBcbiAqIGlzIGFscmVhZHkgcHJlc2VudCwgaXQgZmFpbHMgdG8gc2V0IHRoZSByZW1vdGUgZGVzY3JpcHRpb24uXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuXG4gICAgaWYgKGRlc2MgJiYgZGVzYy5zZHApIHtcbiAgICAgICAgdmFyIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgICAgIHRoaXMuX3VwZGF0ZVJlbW90ZU1hcHMoc2IpO1xuICAgICAgICB0aGlzLl9jYWNoZVJlbW90ZVZpZGVvU291cmNlcyhzYik7XG5cbiAgICAgICAgLy8gTk9URShncCkgdGhpcyBuZWVkcyB0byBiZSBjYWxsZWQgYWZ0ZXIgdXBkYXRlUmVtb3RlTWFwcyBiZWNhdXNlIHdlXG4gICAgICAgIC8vIG5lZWQgdGhlIHNpbXVsY2FzdCBncm91cCBpbiB0aGUgX3VwZGF0ZVJlbW90ZU1hcHMoKSBtZXRob2QuXG4gICAgICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlbW92ZVNpbXVsY2FzdEdyb3VwKHNiKTtcblxuICAgICAgICBpZiAoZGVzYy5zZHAuaW5kZXhPZignYT1zc3JjLWdyb3VwOlNJTScpICE9PSAtMSkge1xuICAgICAgICAgICAgLy8gV2UgZG9uJ3QgbmVlZCB0aGUgZ29vZyBjb25mZXJlbmNlIGZsYWcgaWYgd2UncmUgbm90IGRvaW5nXG4gICAgICAgICAgICAvLyBzaW11bGNhc3QuXG4gICAgICAgICAgICB0aGlzLl9lbnN1cmVHb29nQ29uZmVyZW5jZShzYik7XG4gICAgICAgIH1cblxuICAgICAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLmZpbmUoWydUcmFuc2Zvcm1lZCByZW1vdGUgZGVzY3JpcHRpb24nLCBkZXNjLnNkcF0uam9pbignICcpKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2ltdWxjYXN0UmVjZWl2ZXI7IiwidmFyIFNpbXVsY2FzdExvZ2dlciA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdExvZ2dlclwiKTtcbnZhciBTaW11bGNhc3RVdGlscyA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdFV0aWxzXCIpO1xuXG5mdW5jdGlvbiBTaW11bGNhc3RTZW5kZXIoKSB7XG4gICAgdGhpcy5zaW11bGNhc3RVdGlscyA9IG5ldyBTaW11bGNhc3RVdGlscygpO1xuICAgIHRoaXMubG9nZ2VyID0gbmV3IFNpbXVsY2FzdExvZ2dlcignU2ltdWxjYXN0U2VuZGVyJywgMSk7XG59XG5cblNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuZGlzcGxheWVkTG9jYWxWaWRlb1N0cmVhbSA9IG51bGw7XG5cblNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2dlbmVyYXRlR3VpZCA9IChmdW5jdGlvbiAoKSB7XG4gICAgZnVuY3Rpb24gczQoKSB7XG4gICAgICAgIHJldHVybiBNYXRoLmZsb29yKCgxICsgTWF0aC5yYW5kb20oKSkgKiAweDEwMDAwKVxuICAgICAgICAgICAgLnRvU3RyaW5nKDE2KVxuICAgICAgICAgICAgLnN1YnN0cmluZygxKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gczQoKSArIHM0KCkgKyAnLScgKyBzNCgpICsgJy0nICsgczQoKSArICctJyArXG4gICAgICAgICAgICBzNCgpICsgJy0nICsgczQoKSArIHM0KCkgKyBzNCgpO1xuICAgIH07XG59KCkpO1xuXG4vLyBSZXR1cm5zIGEgcmFuZG9tIGludGVnZXIgYmV0d2VlbiBtaW4gKGluY2x1ZGVkKSBhbmQgbWF4IChleGNsdWRlZClcbi8vIFVzaW5nIE1hdGgucm91bmQoKSBnaXZlcyBhIG5vbi11bmlmb3JtIGRpc3RyaWJ1dGlvbiFcblNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2dlbmVyYXRlUmFuZG9tU1NSQyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgbWluID0gMCwgbWF4ID0gMHhmZmZmZmZmZjtcbiAgICByZXR1cm4gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogKG1heCAtIG1pbikpICsgbWluO1xufTtcblxuU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5nZXRMb2NhbFZpZGVvU3RyZWFtID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiAodGhpcy5kaXNwbGF5ZWRMb2NhbFZpZGVvU3RyZWFtICE9IG51bGwpXG4gICAgICAgID8gdGhpcy5kaXNwbGF5ZWRMb2NhbFZpZGVvU3RyZWFtXG4gICAgICAgIC8vIGluIGNhc2Ugd2UgaGF2ZSBubyBzaW11bGNhc3QgYXQgYWxsLCBpLmUuIHdlIGRpZG4ndCBwZXJmb3JtIHRoZSBHVU1cbiAgICAgICAgOiBBUFAuUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKTtcbn07XG5cbmZ1bmN0aW9uIE5hdGl2ZVNpbXVsY2FzdFNlbmRlcigpIHtcbiAgICBTaW11bGNhc3RTZW5kZXIuY2FsbCh0aGlzKTsgLy8gY2FsbCB0aGUgc3VwZXIgY29uc3RydWN0b3IuXG59XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKFNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUpO1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9sb2NhbEV4cGxvc2lvbk1hcCA9IHt9O1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5faXNVc2luZ1NjcmVlblN0cmVhbSA9IGZhbHNlO1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fbG9jYWxWaWRlb1NvdXJjZUNhY2hlID0gJyc7XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5fbG9jYWxFeHBsb3Npb25NYXAgPSB7fTtcbiAgICB0aGlzLl9pc1VzaW5nU2NyZWVuU3RyZWFtID0gQVBQLmRlc2t0b3BzaGFyaW5nLmlzVXNpbmdTY3JlZW5TdHJlYW0oKTtcbn07XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2NhY2hlTG9jYWxWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB0aGlzLl9sb2NhbFZpZGVvU291cmNlQ2FjaGUgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9nZXRWaWRlb1NvdXJjZXMobGluZXMpO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fcmVzdG9yZUxvY2FsVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgdGhpcy5fbG9jYWxWaWRlb1NvdXJjZUNhY2hlKTtcbn07XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2FwcGVuZFNpbXVsY2FzdEdyb3VwID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHZpZGVvU291cmNlcywgc3NyY0dyb3VwLCBzaW1TU1JDLCBudW1PZlN1YnMgPSAyLCBpLCBzYiwgbXNpZDtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0FwcGVuZGluZyBzaW11bGNhc3QgZ3JvdXAuLi4nKTtcblxuICAgIC8vIEdldCB0aGUgcHJpbWFyeSBTU1JDIGluZm9ybWF0aW9uLlxuICAgIHZpZGVvU291cmNlcyA9IHRoaXMuc2ltdWxjYXN0VXRpbHMucGFyc2VNZWRpYShsaW5lcywgWyd2aWRlbyddKVswXTtcblxuICAgIC8vIFN0YXJ0IGJ1aWxkaW5nIHRoZSBTSU0gU1NSQyBncm91cC5cbiAgICBzc3JjR3JvdXAgPSBbJ2E9c3NyYy1ncm91cDpTSU0nXTtcblxuICAgIC8vIFRoZSB2aWRlbyBzb3VyY2UgYnVmZmVyLlxuICAgIHNiID0gW107XG5cbiAgICAvLyBDcmVhdGUgdGhlIHNpbXVsY2FzdCBzdWItc3RyZWFtcy5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbnVtT2ZTdWJzOyBpKyspIHtcbiAgICAgICAgLy8gVE9ETyhncCkgcHJldmVudCBTU1JDIGNvbGxpc2lvbi5cbiAgICAgICAgc2ltU1NSQyA9IHRoaXMuX2dlbmVyYXRlUmFuZG9tU1NSQygpO1xuICAgICAgICBzc3JjR3JvdXAucHVzaChzaW1TU1JDKTtcblxuICAgICAgICBpZiAodmlkZW9Tb3VyY2VzLmJhc2UpIHtcbiAgICAgICAgICAgIHNiLnNwbGljZS5hcHBseShzYiwgW3NiLmxlbmd0aCwgMF0uY29uY2F0KFxuICAgICAgICAgICAgICAgIFtbXCJhPXNzcmM6XCIsIHNpbVNTUkMsIFwiIGNuYW1lOlwiLCB2aWRlb1NvdXJjZXMuYmFzZS5jbmFtZV0uam9pbignJyksXG4gICAgICAgICAgICAgICAgICAgIFtcImE9c3NyYzpcIiwgc2ltU1NSQywgXCIgbXNpZDpcIiwgdmlkZW9Tb3VyY2VzLmJhc2UubXNpZF0uam9pbignJyldXG4gICAgICAgICAgICApKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubG9nZ2VyLmluZm8oWydHZW5lcmF0ZWQgc3Vic3RyZWFtICcsIGksICcgd2l0aCBTU1JDICcsIHNpbVNTUkMsICcuJ10uam9pbignJykpO1xuXG4gICAgfVxuXG4gICAgLy8gQWRkIHRoZSBncm91cCBzaW0gbGF5ZXJzLlxuICAgIHNiLnNwbGljZSgwLCAwLCBzc3JjR3JvdXAuam9pbignICcpKVxuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgc2IpO1xufTtcblxuLy8gRG9lcyB0aGUgYWN0dWFsIHBhdGNoaW5nLlxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fZW5zdXJlU2ltdWxjYXN0R3JvdXAgPSBmdW5jdGlvbiAobGluZXMpIHtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0Vuc3VyaW5nIHNpbXVsY2FzdCBncm91cC4uLicpO1xuXG4gICAgaWYgKHRoaXMuc2ltdWxjYXN0VXRpbHMuX2luZGV4T2ZBcnJheSgnYT1zc3JjLWdyb3VwOlNJTScsIGxpbmVzKSA9PT0gdGhpcy5zaW11bGNhc3RVdGlscy5fZW1wdHlDb21wb3VuZEluZGV4KSB7XG4gICAgICAgIHRoaXMuX2FwcGVuZFNpbXVsY2FzdEdyb3VwKGxpbmVzKTtcbiAgICAgICAgdGhpcy5fY2FjaGVMb2NhbFZpZGVvU291cmNlcyhsaW5lcyk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy8gdmVyaWZ5IHRoYXQgdGhlIHNzcmNzIHBhcnRpY2lwYXRpbmcgaW4gdGhlIFNJTSBncm91cCBhcmUgcHJlc2VudFxuICAgICAgICAvLyBpbiB0aGUgU0RQIChuZWVkZWQgZm9yIHByZXNlbmNlKS5cbiAgICAgICAgdGhpcy5fcmVzdG9yZUxvY2FsVmlkZW9Tb3VyY2VzKGxpbmVzKTtcbiAgICB9XG59O1xuXG4vKipcbiAqIFByb2R1Y2VzIGEgc2luZ2xlIHN0cmVhbSB3aXRoIG11bHRpcGxlIHRyYWNrcyBmb3IgbG9jYWwgdmlkZW8gc291cmNlcy5cbiAqXG4gKiBAcGFyYW0gbGluZXNcbiAqIEBwcml2YXRlXG4gKi9cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2V4cGxvZGVTaW11bGNhc3RTZW5kZXJTb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHNiLCBtc2lkLCBzaWQsIHRpZCwgdmlkZW9Tb3VyY2VzLCBzZWxmO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnRXhwbG9kaW5nIGxvY2FsIHZpZGVvIHNvdXJjZXMuLi4nKTtcblxuICAgIHZpZGVvU291cmNlcyA9IHRoaXMuc2ltdWxjYXN0VXRpbHMucGFyc2VNZWRpYShsaW5lcywgWyd2aWRlbyddKVswXTtcblxuICAgIHNlbGYgPSB0aGlzO1xuICAgIGlmICh2aWRlb1NvdXJjZXMuZ3JvdXBzICYmIHZpZGVvU291cmNlcy5ncm91cHMubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgIHZpZGVvU291cmNlcy5ncm91cHMuZm9yRWFjaChmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgIGlmIChncm91cC5zZW1hbnRpY3MgPT09ICdTSU0nKSB7XG4gICAgICAgICAgICAgICAgZ3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEdldCB0aGUgbXNpZCBmb3IgdGhpcyBzc3JjLi5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuX2xvY2FsRXhwbG9zaW9uTWFwW3NzcmNdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAuLiBlaXRoZXIgZnJvbSB0aGUgZXhwbG9zaW9uIG1hcC4uXG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gc2VsZi5fbG9jYWxFeHBsb3Npb25NYXBbc3NyY107XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAuLiBvciBnZW5lcmF0ZSBhIG5ldyBvbmUgKG1zaWQpLlxuICAgICAgICAgICAgICAgICAgICAgICAgc2lkID0gdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5zdWJzdHJpbmcoMCwgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZC5pbmRleE9mKCcgJykpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICB0aWQgPSBzZWxmLl9nZW5lcmF0ZUd1aWQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1zaWQgPSBbc2lkLCB0aWRdLmpvaW4oJyAnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuX2xvY2FsRXhwbG9zaW9uTWFwW3NzcmNdID0gbXNpZDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEFzc2lnbiBpdCB0byB0aGUgc291cmNlIG9iamVjdC5cbiAgICAgICAgICAgICAgICAgICAgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZCA9IG1zaWQ7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gVE9ETyhncCkgQ2hhbmdlIHRoZSBtc2lkIG9mIGFzc29jaWF0ZWQgc291cmNlcy5cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgc2IgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9jb21waWxlVmlkZW9Tb3VyY2VzKHZpZGVvU291cmNlcyk7XG5cbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCBzYik7XG59O1xuXG4vKipcbiAqIEdVTSBmb3Igc2ltdWxjYXN0LlxuICpcbiAqIEBwYXJhbSBjb25zdHJhaW50c1xuICogQHBhcmFtIHN1Y2Nlc3NcbiAqIEBwYXJhbSBlcnJcbiAqL1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5nZXRVc2VyTWVkaWEgPSBmdW5jdGlvbiAoY29uc3RyYWludHMsIHN1Y2Nlc3MsIGVycikge1xuXG4gICAgLy8gVGhlcmUncyBub3RoaW5nIHNwZWNpYWwgdG8gZG8gZm9yIG5hdGl2ZSBzaW11bGNhc3QsIHNvIGp1c3QgZG8gYSBub3JtYWwgR1VNLlxuICAgIG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIGZ1bmN0aW9uIChocVN0cmVhbSkge1xuICAgICAgICBzdWNjZXNzKGhxU3RyZWFtKTtcbiAgICB9LCBlcnIpO1xufTtcblxuLyoqXG4gKiBQcmVwYXJlcyB0aGUgbG9jYWwgZGVzY3JpcHRpb24gZm9yIHB1YmxpYyB1c2FnZSAoaS5lLiB0byBiZSBzaWduYWxlZFxuICogdGhyb3VnaCBKaW5nbGUgdG8gdGhlIGZvY3VzKS5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMge1JUQ1Nlc3Npb25EZXNjcmlwdGlvbn1cbiAqL1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgdmFyIHNiO1xuXG4gICAgaWYgKCF0aGlzLnNpbXVsY2FzdFV0aWxzLmlzVmFsaWREZXNjcmlwdGlvbihkZXNjKSB8fCB0aGlzLl9pc1VzaW5nU2NyZWVuU3RyZWFtKSB7XG4gICAgICAgIHJldHVybiBkZXNjO1xuICAgIH1cblxuXG4gICAgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICB0aGlzLl9leHBsb2RlU2ltdWxjYXN0U2VuZGVyU291cmNlcyhzYik7XG5cbiAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgIH0pO1xuXG4gICAgdGhpcy5sb2dnZXIuZmluZShbJ0V4cGxvZGVkIGxvY2FsIHZpZGVvIHNvdXJjZXMnLCBkZXNjLnNkcF0uam9pbignICcpKTtcblxuICAgIHJldHVybiBkZXNjO1xufTtcblxuLyoqXG4gKiBFbnN1cmVzIHRoYXQgdGhlIHNpbXVsY2FzdCBncm91cCBpcyBwcmVzZW50IGluIHRoZSBhbnN3ZXIsIF9pZl8gbmF0aXZlXG4gKiBzaW11bGNhc3QgaXMgZW5hYmxlZCxcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtQW5zd2VyID0gZnVuY3Rpb24gKGRlc2MpIHtcblxuICAgIGlmICghdGhpcy5zaW11bGNhc3RVdGlscy5pc1ZhbGlkRGVzY3JpcHRpb24oZGVzYykgfHwgdGhpcy5faXNVc2luZ1NjcmVlblN0cmVhbSkge1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICB2YXIgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICAvLyBFdmVuIGlmIHdlIGhhdmUgZW5hYmxlZCBuYXRpdmUgc2ltdWxjYXN0aW5nIHByZXZpb3VzbHlcbiAgICAvLyAod2l0aCBhIGNhbGwgdG8gU0xEIHdpdGggYW4gYXBwcm9wcmlhdGUgU0RQLCBmb3IgZXhhbXBsZSksXG4gICAgLy8gY3JlYXRlQW5zd2VyIHNlZW1zIHRvIGNvbnNpc3RlbnRseSBnZW5lcmF0ZSBpbmNvbXBsZXRlIFNEUFxuICAgIC8vIHdpdGggbWlzc2luZyBTU1JDUy5cbiAgICAvL1xuICAgIC8vIFNvLCBzdWJzZXF1ZW50IGNhbGxzIHRvIFNMRCB3aWxsIGhhdmUgbWlzc2luZyBTU1JDUyBhbmQgcHJlc2VuY2VcbiAgICAvLyB3b24ndCBoYXZlIHRoZSBjb21wbGV0ZSBsaXN0IG9mIFNSQ3MuXG4gICAgdGhpcy5fZW5zdXJlU2ltdWxjYXN0R3JvdXAoc2IpO1xuXG4gICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICB9KTtcblxuICAgIHRoaXMubG9nZ2VyLmZpbmUoWydUcmFuc2Zvcm1lZCBhbnN3ZXInLCBkZXNjLnNkcF0uam9pbignICcpKTtcblxuICAgIHJldHVybiBkZXNjO1xufTtcblxuXG4vKipcbiAqXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiBkZXNjO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQgPSBmdW5jdGlvbiAoc3NyYywgZW5hYmxlZCkge1xuICAgIC8vIE5vdGhpbmcgdG8gZG8gaGVyZSwgbmF0aXZlIHNpbXVsY2FzdCBkb2VzIHRoYXQgYXV0by1tYWdpY2FsbHkuXG59O1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gTmF0aXZlU2ltdWxjYXN0U2VuZGVyO1xuXG5mdW5jdGlvbiBTaW1wbGVTaW11bGNhc3RTZW5kZXIoKSB7XG4gICAgU2ltdWxjYXN0U2VuZGVyLmNhbGwodGhpcyk7XG59XG5cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKFNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUpO1xuXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmxvY2FsU3RyZWFtID0gbnVsbDtcblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2xvY2FsTWFwcyA9IHtcbiAgICBtc2lkczogW10sXG4gICAgbXNpZDJzc3JjOiB7fVxufTtcblxuLyoqXG4gKiBHcm91cHMgbG9jYWwgdmlkZW8gc291cmNlcyB0b2dldGhlciBpbiB0aGUgc3NyYy1ncm91cDpTSU0gZ3JvdXAuXG4gKlxuICogQHBhcmFtIGxpbmVzXG4gKiBAcHJpdmF0ZVxuICovXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9ncm91cExvY2FsVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHNiLCB2aWRlb1NvdXJjZXMsIHNzcmNzID0gW10sIHNzcmM7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdHcm91cGluZyBsb2NhbCB2aWRlbyBzb3VyY2VzLi4uJyk7XG5cbiAgICB2aWRlb1NvdXJjZXMgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLnBhcnNlTWVkaWEobGluZXMsIFsndmlkZW8nXSlbMF07XG5cbiAgICBmb3IgKHNzcmMgaW4gdmlkZW9Tb3VyY2VzLnNvdXJjZXMpIHtcbiAgICAgICAgLy8gaml0c2ktbWVldCBkZXN0cm95cy9jcmVhdGVzIHN0cmVhbXMgYXQgdmFyaW91cyBwbGFjZXMgY2F1c2luZ1xuICAgICAgICAvLyB0aGUgb3JpZ2luYWwgbG9jYWwgc3RyZWFtIGlkcyB0byBjaGFuZ2UuIFRoZSBvbmx5IHRoaW5nIHRoYXRcbiAgICAgICAgLy8gcmVtYWlucyB1bmNoYW5nZWQgaXMgdGhlIHRyYWNraWQuXG4gICAgICAgIHRoaXMuX2xvY2FsTWFwcy5tc2lkMnNzcmNbdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZC5zcGxpdCgnICcpWzFdXSA9IHNzcmM7XG4gICAgfVxuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIFRPRE8oZ3ApIGFkZCBvbmx5IFwiZnJlZVwiIHNvdXJjZXMuXG4gICAgdGhpcy5fbG9jYWxNYXBzLm1zaWRzLmZvckVhY2goZnVuY3Rpb24gKG1zaWQpIHtcbiAgICAgICAgc3NyY3MucHVzaChzZWxmLl9sb2NhbE1hcHMubXNpZDJzc3JjW21zaWRdKTtcbiAgICB9KTtcblxuICAgIGlmICghdmlkZW9Tb3VyY2VzLmdyb3Vwcykge1xuICAgICAgICB2aWRlb1NvdXJjZXMuZ3JvdXBzID0gW107XG4gICAgfVxuXG4gICAgdmlkZW9Tb3VyY2VzLmdyb3Vwcy5wdXNoKHtcbiAgICAgICAgJ3NlbWFudGljcyc6ICdTSU0nLFxuICAgICAgICAnc3NyY3MnOiBzc3Jjc1xuICAgIH0pO1xuXG4gICAgc2IgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9jb21waWxlVmlkZW9Tb3VyY2VzKHZpZGVvU291cmNlcyk7XG5cbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCBzYik7XG59O1xuXG4vKipcbiAqIEdVTSBmb3Igc2ltdWxjYXN0LlxuICpcbiAqIEBwYXJhbSBjb25zdHJhaW50c1xuICogQHBhcmFtIHN1Y2Nlc3NcbiAqIEBwYXJhbSBlcnJcbiAqL1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5nZXRVc2VyTWVkaWEgPSBmdW5jdGlvbiAoY29uc3RyYWludHMsIHN1Y2Nlc3MsIGVycikge1xuXG4gICAgLy8gVE9ETyhncCkgd2hhdCBpZiB3ZSByZXF1ZXN0IGEgcmVzb2x1dGlvbiBub3Qgc3VwcG9ydGVkIGJ5IHRoZSBoYXJkd2FyZT9cbiAgICAvLyBUT0RPKGdwKSBtYWtlIHRoZSBscSBzdHJlYW0gY29uZmlndXJhYmxlOyBhbHRob3VnaCB0aGlzIHdvdWxkbid0IHdvcmsgd2l0aCBuYXRpdmUgc2ltdWxjYXN0XG4gICAgdmFyIGxxQ29uc3RyYWludHMgPSB7XG4gICAgICAgIGF1ZGlvOiBmYWxzZSxcbiAgICAgICAgdmlkZW86IHtcbiAgICAgICAgICAgIG1hbmRhdG9yeToge1xuICAgICAgICAgICAgICAgIG1heFdpZHRoOiAzMjAsXG4gICAgICAgICAgICAgICAgbWF4SGVpZ2h0OiAxODAsXG4gICAgICAgICAgICAgICAgbWF4RnJhbWVSYXRlOiAxNVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0hRIGNvbnN0cmFpbnRzOiAnLCBjb25zdHJhaW50cyk7XG4gICAgdGhpcy5sb2dnZXIuaW5mbygnTFEgY29uc3RyYWludHM6ICcsIGxxQ29uc3RyYWludHMpO1xuXG5cbiAgICAvLyBOT1RFKGdwKSBpZiB3ZSByZXF1ZXN0IHRoZSBscSBzdHJlYW0gZmlyc3Qgd2Via2l0R2V0VXNlck1lZGlhXG4gICAgLy8gZmFpbHMgcmFuZG9tbHkuIFRlc3RlZCB3aXRoIENocm9tZSAzNy4gQXMgZmlwcG8gc3VnZ2VzdGVkLCB0aGVcbiAgICAvLyByZWFzb24gYXBwZWFycyB0byBiZSB0aGF0IENocm9tZSBvbmx5IGFjcXVpcmVzIHRoZSBjYW0gb25jZSBhbmRcbiAgICAvLyB0aGVuIGRvd25zY2FsZXMgdGhlIHBpY3R1cmUgKGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0zNDY2MTYjYzExKVxuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIGZ1bmN0aW9uIChocVN0cmVhbSkge1xuXG4gICAgICAgIHNlbGYubG9jYWxTdHJlYW0gPSBocVN0cmVhbTtcblxuICAgICAgICAvLyByZXNldCBsb2NhbCBtYXBzLlxuICAgICAgICBzZWxmLl9sb2NhbE1hcHMubXNpZHMgPSBbXTtcbiAgICAgICAgc2VsZi5fbG9jYWxNYXBzLm1zaWQyc3NyYyA9IHt9O1xuXG4gICAgICAgIC8vIGFkZCBocSB0cmFja2lkIHRvIGxvY2FsIG1hcFxuICAgICAgICBzZWxmLl9sb2NhbE1hcHMubXNpZHMucHVzaChocVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpWzBdLmlkKTtcblxuICAgICAgICBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKGxxQ29uc3RyYWludHMsIGZ1bmN0aW9uIChscVN0cmVhbSkge1xuXG4gICAgICAgICAgICBzZWxmLmRpc3BsYXllZExvY2FsVmlkZW9TdHJlYW0gPSBscVN0cmVhbTtcblxuICAgICAgICAgICAgLy8gTk9URShncCkgVGhlIHNwZWNpZmljYXRpb24gc2F5cyBBcnJheS5mb3JFYWNoKCkgd2lsbCB2aXNpdFxuICAgICAgICAgICAgLy8gdGhlIGFycmF5IGVsZW1lbnRzIGluIG51bWVyaWMgb3JkZXIsIGFuZCB0aGF0IGl0IGRvZXNuJ3RcbiAgICAgICAgICAgIC8vIHZpc2l0IGVsZW1lbnRzIHRoYXQgZG9uJ3QgZXhpc3QuXG5cbiAgICAgICAgICAgIC8vIGFkZCBscSB0cmFja2lkIHRvIGxvY2FsIG1hcFxuICAgICAgICAgICAgc2VsZi5fbG9jYWxNYXBzLm1zaWRzLnNwbGljZSgwLCAwLCBscVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpWzBdLmlkKTtcblxuICAgICAgICAgICAgc2VsZi5sb2NhbFN0cmVhbS5hZGRUcmFjayhscVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpWzBdKTtcbiAgICAgICAgICAgIHN1Y2Nlc3Moc2VsZi5sb2NhbFN0cmVhbSk7XG4gICAgICAgIH0sIGVycik7XG4gICAgfSwgZXJyKTtcbn07XG5cbi8qKlxuICogUHJlcGFyZXMgdGhlIGxvY2FsIGRlc2NyaXB0aW9uIGZvciBwdWJsaWMgdXNhZ2UgKGkuZS4gdG8gYmUgc2lnbmFsZWRcbiAqIHRocm91Z2ggSmluZ2xlIHRvIHRoZSBmb2N1cykuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHtSVENTZXNzaW9uRGVzY3JpcHRpb259XG4gKi9cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHZhciBzYjtcblxuICAgIGlmICghdGhpcy5zaW11bGNhc3RVdGlscy5pc1ZhbGlkRGVzY3JpcHRpb24oZGVzYykpIHtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG4gICAgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICB0aGlzLl9ncm91cExvY2FsVmlkZW9Tb3VyY2VzKHNiKTtcblxuICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgfSk7XG5cbiAgICB0aGlzLmxvZ2dlci5maW5lKCdHcm91cGVkIGxvY2FsIHZpZGVvIHNvdXJjZXMnKTtcbiAgICB0aGlzLmxvZ2dlci5maW5lKGRlc2Muc2RwKTtcblxuICAgIHJldHVybiBkZXNjO1xufTtcblxuLyoqXG4gKiBFbnN1cmVzIHRoYXQgdGhlIHNpbXVsY2FzdCBncm91cCBpcyBwcmVzZW50IGluIHRoZSBhbnN3ZXIsIF9pZl8gbmF0aXZlXG4gKiBzaW11bGNhc3QgaXMgZW5hYmxlZCxcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtQW5zd2VyID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gZGVzYztcbn07XG5cblxuLyoqXG4gKlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcblxuICAgIHZhciBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlbW92ZVNpbXVsY2FzdEdyb3VwKHNiKTtcblxuICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgfSk7XG5cbiAgICB0aGlzLmxvZ2dlci5maW5lKCdUcmFuc2Zvcm1lZCBsb2NhbCBkZXNjcmlwdGlvbicpO1xuICAgIHRoaXMubG9nZ2VyLmZpbmUoZGVzYy5zZHApO1xuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZCA9IGZ1bmN0aW9uIChzc3JjLCBlbmFibGVkKSB7XG4gICAgdmFyIHRyYWNraWQ7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2dnZXIubG9nKFsnUmVxdWVzdGVkIHRvJywgZW5hYmxlZCA/ICdlbmFibGUnIDogJ2Rpc2FibGUnLCBzc3JjXS5qb2luKCcgJykpO1xuICAgIGlmIChPYmplY3Qua2V5cyh0aGlzLl9sb2NhbE1hcHMubXNpZDJzc3JjKS5zb21lKGZ1bmN0aW9uICh0aWQpIHtcbiAgICAgICAgLy8gU2VhcmNoIGZvciB0aGUgdHJhY2sgaWQgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgc3NyY1xuICAgICAgICBpZiAoc2VsZi5fbG9jYWxNYXBzLm1zaWQyc3NyY1t0aWRdID09IHNzcmMpIHtcbiAgICAgICAgICAgIHRyYWNraWQgPSB0aWQ7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH0pICYmIHNlbGYubG9jYWxTdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5zb21lKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAvLyBTdGFydC9zdG9wIHRoZSB0cmFjayB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSB0cmFjayBpZFxuICAgICAgICBpZiAodHJhY2suaWQgPT09IHRyYWNraWQpIHtcbiAgICAgICAgICAgIHRyYWNrLmVuYWJsZWQgPSBlbmFibGVkO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9KSkge1xuICAgICAgICB0aGlzLmxvZ2dlci5sb2coW3RyYWNraWQsIGVuYWJsZWQgPyAnZW5hYmxlZCcgOiAnZGlzYWJsZWQnXS5qb2luKCcgJykpO1xuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKGVuYWJsZWRcbiAgICAgICAgICAgID8gJ3NpbXVsY2FzdGxheWVyc3RhcnRlZCdcbiAgICAgICAgICAgIDogJ3NpbXVsY2FzdGxheWVyc3RvcHBlZCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKFwiSSBkb24ndCBoYXZlIGEgbG9jYWwgc3RyZWFtIHdpdGggU1NSQyBcIiArIHNzcmMpO1xuICAgIH1cbn07XG5cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBTaW1wbGVTaW11bGNhc3RTZW5kZXI7XG5cbmZ1bmN0aW9uIE5vU2ltdWxjYXN0U2VuZGVyKCkge1xuICAgIFNpbXVsY2FzdFNlbmRlci5jYWxsKHRoaXMpO1xufVxuXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKFNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUpO1xuXG4vKipcbiAqIEdVTSBmb3Igc2ltdWxjYXN0LlxuICpcbiAqIEBwYXJhbSBjb25zdHJhaW50c1xuICogQHBhcmFtIHN1Y2Nlc3NcbiAqIEBwYXJhbSBlcnJcbiAqL1xuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgc3VjY2VzcywgZXJyKSB7XG4gICAgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYShjb25zdHJhaW50cywgZnVuY3Rpb24gKGhxU3RyZWFtKSB7XG4gICAgICAgIHN1Y2Nlc3MoaHFTdHJlYW0pO1xuICAgIH0sIGVycik7XG59O1xuXG4vKipcbiAqIFByZXBhcmVzIHRoZSBsb2NhbCBkZXNjcmlwdGlvbiBmb3IgcHVibGljIHVzYWdlIChpLmUuIHRvIGJlIHNpZ25hbGVkXG4gKiB0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7UlRDU2Vzc2lvbkRlc2NyaXB0aW9ufVxuICovXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiBkZXNjO1xufTtcblxuLyoqXG4gKiBFbnN1cmVzIHRoYXQgdGhlIHNpbXVsY2FzdCBncm91cCBpcyBwcmVzZW50IGluIHRoZSBhbnN3ZXIsIF9pZl8gbmF0aXZlXG4gKiBzaW11bGNhc3QgaXMgZW5hYmxlZCxcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1BbnN3ZXIgPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiBkZXNjO1xufTtcblxuXG4vKipcbiAqXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkID0gZnVuY3Rpb24gKHNzcmMsIGVuYWJsZWQpIHtcblxufTtcblxuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gTm9TaW11bGNhc3RTZW5kZXI7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIFwibmF0aXZlXCI6IE5hdGl2ZVNpbXVsY2FzdFNlbmRlcixcbiAgICBcIm5vXCI6IE5vU2ltdWxjYXN0U2VuZGVyXG59XG4iLCJ2YXIgU2ltdWxjYXN0TG9nZ2VyID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0TG9nZ2VyXCIpO1xuXG4vKipcbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gU2ltdWxjYXN0VXRpbHMoKSB7XG4gICAgdGhpcy5sb2dnZXIgPSBuZXcgU2ltdWxjYXN0TG9nZ2VyKFwiU2ltdWxjYXN0VXRpbHNcIiwgMSk7XG59XG5cbi8qKlxuICpcbiAqIEB0eXBlIHt7fX1cbiAqIEBwcml2YXRlXG4gKi9cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fZW1wdHlDb21wb3VuZEluZGV4ID0ge307XG5cbi8qKlxuICpcbiAqIEBwYXJhbSBsaW5lc1xuICogQHBhcmFtIHZpZGVvU291cmNlc1xuICogQHByaXZhdGVcbiAqL1xuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9yZXBsYWNlVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzLCB2aWRlb1NvdXJjZXMpIHtcbiAgICB2YXIgaSwgaW5WaWRlbyA9IGZhbHNlLCBpbmRleCA9IC0xLCBob3dNYW55ID0gMDtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ1JlcGxhY2luZyB2aWRlbyBzb3VyY2VzLi4uJyk7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdtPScubGVuZ3RoKSA9PT0gJ209Jykge1xuICAgICAgICAgICAgLy8gT3V0IG9mIHZpZGVvLlxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdtPXZpZGVvICcubGVuZ3RoKSA9PT0gJ209dmlkZW8gJykge1xuICAgICAgICAgICAgLy8gSW4gdmlkZW8uXG4gICAgICAgICAgICBpblZpZGVvID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpblZpZGVvICYmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYzonLmxlbmd0aCkgPT09ICdhPXNzcmM6J1xuICAgICAgICAgICAgfHwgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmMtZ3JvdXA6Jy5sZW5ndGgpID09PSAnYT1zc3JjLWdyb3VwOicpKSB7XG5cbiAgICAgICAgICAgIGlmIChpbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBpbmRleCA9IGk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGhvd01hbnkrKztcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vICBlZmZpY2llbmN5IGJhYnkgOylcbiAgICBsaW5lcy5zcGxpY2UuYXBwbHkobGluZXMsXG4gICAgICAgIFtpbmRleCwgaG93TWFueV0uY29uY2F0KHZpZGVvU291cmNlcykpO1xuXG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuaXNWYWxpZERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpXG57XG4gICAgcmV0dXJuIGRlc2MgJiYgZGVzYyAhPSBudWxsXG4gICAgICAgICYmIGRlc2MudHlwZSAmJiBkZXNjLnR5cGUgIT0gJydcbiAgICAgICAgJiYgZGVzYy5zZHAgJiYgZGVzYy5zZHAgIT0gJyc7XG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX2dldFZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciBpLCBpblZpZGVvID0gZmFsc2UsIHNiID0gW107XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdHZXR0aW5nIHZpZGVvIHNvdXJjZXMuLi4nKTtcblxuICAgIGZvciAoaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAoaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ209Jy5sZW5ndGgpID09PSAnbT0nKSB7XG4gICAgICAgICAgICAvLyBPdXQgb2YgdmlkZW8uXG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ209dmlkZW8gJy5sZW5ndGgpID09PSAnbT12aWRlbyAnKSB7XG4gICAgICAgICAgICAvLyBJbiB2aWRlby5cbiAgICAgICAgICAgIGluVmlkZW8gPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmM6Jy5sZW5ndGgpID09PSAnYT1zc3JjOicpIHtcbiAgICAgICAgICAgIC8vIEluIFNTUkMuXG4gICAgICAgICAgICBzYi5wdXNoKGxpbmVzW2ldKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjLWdyb3VwOicubGVuZ3RoKSA9PT0gJ2E9c3NyYy1ncm91cDonKSB7XG4gICAgICAgICAgICBzYi5wdXNoKGxpbmVzW2ldKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzYjtcbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5wYXJzZU1lZGlhID0gZnVuY3Rpb24gKGxpbmVzLCBtZWRpYXR5cGVzKSB7XG4gICAgdmFyIGksIHJlcyA9IFtdLCB0eXBlLCBjdXJfbWVkaWEsIGlkeCwgc3NyY3MsIGN1cl9zc3JjLCBzc3JjLFxuICAgICAgICBzc3JjX2F0dHJpYnV0ZSwgZ3JvdXAsIHNlbWFudGljcywgc2tpcCA9IHRydWU7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdQYXJzaW5nIG1lZGlhIHNvdXJjZXMuLi4nKTtcblxuICAgIGZvciAoaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsICdtPScubGVuZ3RoKSA9PT0gJ209Jykge1xuXG4gICAgICAgICAgICB0eXBlID0gbGluZXNbaV1cbiAgICAgICAgICAgICAgICAuc3Vic3RyKCdtPScubGVuZ3RoLCBsaW5lc1tpXS5pbmRleE9mKCcgJykgLSAnbT0nLmxlbmd0aCk7XG4gICAgICAgICAgICBza2lwID0gbWVkaWF0eXBlcyAhPT0gdW5kZWZpbmVkICYmIG1lZGlhdHlwZXMuaW5kZXhPZih0eXBlKSA9PT0gLTE7XG5cbiAgICAgICAgICAgIGlmICghc2tpcCkge1xuICAgICAgICAgICAgICAgIGN1cl9tZWRpYSA9IHtcbiAgICAgICAgICAgICAgICAgICAgJ3R5cGUnOiB0eXBlLFxuICAgICAgICAgICAgICAgICAgICAnc291cmNlcyc6IHt9LFxuICAgICAgICAgICAgICAgICAgICAnZ3JvdXBzJzogW11cbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgcmVzLnB1c2goY3VyX21lZGlhKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICB9IGVsc2UgaWYgKCFza2lwICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjOicubGVuZ3RoKSA9PT0gJ2E9c3NyYzonKSB7XG5cbiAgICAgICAgICAgIGlkeCA9IGxpbmVzW2ldLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgIHNzcmMgPSBsaW5lc1tpXS5zdWJzdHJpbmcoJ2E9c3NyYzonLmxlbmd0aCwgaWR4KTtcbiAgICAgICAgICAgIGlmIChjdXJfbWVkaWEuc291cmNlc1tzc3JjXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgY3VyX3NzcmMgPSB7J3NzcmMnOiBzc3JjfTtcbiAgICAgICAgICAgICAgICBjdXJfbWVkaWEuc291cmNlc1tzc3JjXSA9IGN1cl9zc3JjO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzc3JjX2F0dHJpYnV0ZSA9IGxpbmVzW2ldLnN1YnN0cihpZHggKyAxKS5zcGxpdCgnOicsIDIpWzBdO1xuICAgICAgICAgICAgY3VyX3NzcmNbc3NyY19hdHRyaWJ1dGVdID0gbGluZXNbaV0uc3Vic3RyKGlkeCArIDEpLnNwbGl0KCc6JywgMilbMV07XG5cbiAgICAgICAgICAgIGlmIChjdXJfbWVkaWEuYmFzZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgY3VyX21lZGlhLmJhc2UgPSBjdXJfc3NyYztcbiAgICAgICAgICAgIH1cblxuICAgICAgICB9IGVsc2UgaWYgKCFza2lwICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjLWdyb3VwOicubGVuZ3RoKSA9PT0gJ2E9c3NyYy1ncm91cDonKSB7XG4gICAgICAgICAgICBpZHggPSBsaW5lc1tpXS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICBzZW1hbnRpY3MgPSBsaW5lc1tpXS5zdWJzdHIoMCwgaWR4KS5zdWJzdHIoJ2E9c3NyYy1ncm91cDonLmxlbmd0aCk7XG4gICAgICAgICAgICBzc3JjcyA9IGxpbmVzW2ldLnN1YnN0cihpZHgpLnRyaW0oKS5zcGxpdCgnICcpO1xuICAgICAgICAgICAgZ3JvdXAgPSB7XG4gICAgICAgICAgICAgICAgJ3NlbWFudGljcyc6IHNlbWFudGljcyxcbiAgICAgICAgICAgICAgICAnc3NyY3MnOiBzc3Jjc1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGN1cl9tZWRpYS5ncm91cHMucHVzaChncm91cCk7XG4gICAgICAgIH0gZWxzZSBpZiAoIXNraXAgJiYgKGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zZW5kcmVjdicubGVuZ3RoKSA9PT0gJ2E9c2VuZHJlY3YnIHx8XG4gICAgICAgICAgICBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9cmVjdm9ubHknLmxlbmd0aCkgPT09ICdhPXJlY3Zvbmx5JyB8fFxuICAgICAgICAgICAgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNlbmRvbmx5Jy5sZW5ndGgpID09PSAnYT1zZW5kb25seScgfHxcbiAgICAgICAgICAgIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1pbmFjdGl2ZScubGVuZ3RoKSA9PT0gJ2E9aW5hY3RpdmUnKSkge1xuXG4gICAgICAgICAgICBjdXJfbWVkaWEuZGlyZWN0aW9uID0gbGluZXNbaV0uc3Vic3RyaW5nKCdhPScubGVuZ3RoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiByZXM7XG59O1xuXG4vKipcbiAqIFRoZSBfaW5kZXhPZkFycmF5KCkgbWV0aG9kIHJldHVybnMgdGhlIGZpcnN0IGEgQ29tcG91bmRJbmRleCBhdCB3aGljaCBhXG4gKiBnaXZlbiBlbGVtZW50IGNhbiBiZSBmb3VuZCBpbiB0aGUgYXJyYXksIG9yIF9lbXB0eUNvbXBvdW5kSW5kZXggaWYgaXQgaXNcbiAqIG5vdCBwcmVzZW50LlxuICpcbiAqIEV4YW1wbGU6XG4gKlxuICogX2luZGV4T2ZBcnJheSgnMycsIFsgJ3RoaXMgaXMgbGluZSAxJywgJ3RoaXMgaXMgbGluZSAyJywgJ3RoaXMgaXMgbGluZSAzJyBdKVxuICpcbiAqIHJldHVybnMge3JvdzogMiwgY29sdW1uOiAxNH1cbiAqXG4gKiBAcGFyYW0gbmVlZGxlXG4gKiBAcGFyYW0gaGF5c3RhY2tcbiAqIEBwYXJhbSBzdGFydFxuICogQHJldHVybnMge31cbiAqIEBwcml2YXRlXG4gKi9cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5faW5kZXhPZkFycmF5ID0gZnVuY3Rpb24gKG5lZWRsZSwgaGF5c3RhY2ssIHN0YXJ0KSB7XG4gICAgdmFyIGxlbmd0aCA9IGhheXN0YWNrLmxlbmd0aCwgaWR4LCBpO1xuXG4gICAgaWYgKCFzdGFydCkge1xuICAgICAgICBzdGFydCA9IDA7XG4gICAgfVxuXG4gICAgZm9yIChpID0gc3RhcnQ7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICBpZHggPSBoYXlzdGFja1tpXS5pbmRleE9mKG5lZWRsZSk7XG4gICAgICAgIGlmIChpZHggIT09IC0xKSB7XG4gICAgICAgICAgICByZXR1cm4ge3JvdzogaSwgY29sdW1uOiBpZHh9O1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9lbXB0eUNvbXBvdW5kSW5kZXg7XG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX3JlbW92ZVNpbXVsY2FzdEdyb3VwID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIGk7XG5cbiAgICBmb3IgKGkgPSBsaW5lcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgICAgICBpZiAobGluZXNbaV0uaW5kZXhPZignYT1zc3JjLWdyb3VwOlNJTScpICE9PSAtMSkge1xuICAgICAgICAgICAgbGluZXMuc3BsaWNlKGksIDEpO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9jb21waWxlVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKHZpZGVvU291cmNlcykge1xuICAgIHZhciBzYiA9IFtdLCBzc3JjLCBhZGRlZFNTUkNzID0gW107XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdDb21waWxpbmcgdmlkZW8gc291cmNlcy4uLicpO1xuXG4gICAgLy8gQWRkIHRoZSBncm91cHNcbiAgICBpZiAodmlkZW9Tb3VyY2VzLmdyb3VwcyAmJiB2aWRlb1NvdXJjZXMuZ3JvdXBzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICB2aWRlb1NvdXJjZXMuZ3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKGdyb3VwKSB7XG4gICAgICAgICAgICBpZiAoZ3JvdXAuc3NyY3MgJiYgZ3JvdXAuc3NyY3MubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgc2IucHVzaChbWydhPXNzcmMtZ3JvdXA6JywgZ3JvdXAuc2VtYW50aWNzXS5qb2luKCcnKSwgZ3JvdXAuc3NyY3Muam9pbignICcpXS5qb2luKCcgJykpO1xuXG4gICAgICAgICAgICAgICAgLy8gaWYgKGdyb3VwLnNlbWFudGljcyAhPT0gJ1NJTScpIHtcbiAgICAgICAgICAgICAgICBncm91cC5zc3Jjcy5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIGFkZGVkU1NSQ3MucHVzaChzc3JjKTtcbiAgICAgICAgICAgICAgICAgICAgc2Iuc3BsaWNlLmFwcGx5KHNiLCBbc2IubGVuZ3RoLCAwXS5jb25jYXQoW1xuICAgICAgICAgICAgICAgICAgICAgICAgW1wiYT1zc3JjOlwiLCBzc3JjLCBcIiBjbmFtZTpcIiwgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10uY25hbWVdLmpvaW4oJycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgW1wiYT1zc3JjOlwiLCBzc3JjLCBcIiBtc2lkOlwiLCB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkXS5qb2luKCcnKV0pKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAvL31cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gVGhlbiBhZGQgYW55IGZyZWUgc291cmNlcy5cbiAgICBpZiAodmlkZW9Tb3VyY2VzLnNvdXJjZXMpIHtcbiAgICAgICAgZm9yIChzc3JjIGluIHZpZGVvU291cmNlcy5zb3VyY2VzKSB7XG4gICAgICAgICAgICBpZiAoYWRkZWRTU1JDcy5pbmRleE9mKHNzcmMpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIHNiLnNwbGljZS5hcHBseShzYiwgW3NiLmxlbmd0aCwgMF0uY29uY2F0KFtcbiAgICAgICAgICAgICAgICAgICAgW1wiYT1zc3JjOlwiLCBzc3JjLCBcIiBjbmFtZTpcIiwgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10uY25hbWVdLmpvaW4oJycpLFxuICAgICAgICAgICAgICAgICAgICBbXCJhPXNzcmM6XCIsIHNzcmMsIFwiIG1zaWQ6XCIsIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWRdLmpvaW4oJycpXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNiO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTaW11bGNhc3RVdGlsczsiLCIvKmpzbGludCBwbHVzcGx1czogdHJ1ZSAqL1xuLypqc2xpbnQgbm9tZW46IHRydWUqL1xuXG52YXIgU2ltdWxjYXN0U2VuZGVyID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0U2VuZGVyXCIpO1xudmFyIE5vU2ltdWxjYXN0U2VuZGVyID0gU2ltdWxjYXN0U2VuZGVyW1wibm9cIl07XG52YXIgTmF0aXZlU2ltdWxjYXN0U2VuZGVyID0gU2ltdWxjYXN0U2VuZGVyW1wibmF0aXZlXCJdO1xudmFyIFNpbXVsY2FzdFJlY2VpdmVyID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0UmVjZWl2ZXJcIik7XG52YXIgU2ltdWxjYXN0VXRpbHMgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RVdGlsc1wiKTtcbnZhciBSVENFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvUlRDRXZlbnRzXCIpO1xuXG5cbi8qKlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBTaW11bGNhc3RNYW5hZ2VyKCkge1xuXG4gICAgLy8gQ3JlYXRlIHRoZSBzaW11bGNhc3QgdXRpbGl0aWVzLlxuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMgPSBuZXcgU2ltdWxjYXN0VXRpbHMoKTtcblxuICAgIC8vIENyZWF0ZSByZW1vdGUgc2ltdWxjYXN0LlxuICAgIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIgPSBuZXcgU2ltdWxjYXN0UmVjZWl2ZXIoKTtcblxuICAgIC8vIEluaXRpYWxpemUgbG9jYWwgc2ltdWxjYXN0LlxuXG4gICAgLy8gVE9ETyhncCkgbW92ZSBpbnRvIFNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFVzZXJNZWRpYSBhbmQgdGFrZSBpbnRvXG4gICAgLy8gYWNjb3VudCBjb25zdHJhaW50cy5cbiAgICBpZiAoIWNvbmZpZy5lbmFibGVTaW11bGNhc3QpIHtcbiAgICAgICAgdGhpcy5zaW11bGNhc3RTZW5kZXIgPSBuZXcgTm9TaW11bGNhc3RTZW5kZXIoKTtcbiAgICB9IGVsc2Uge1xuXG4gICAgICAgIHZhciBpc0Nocm9taXVtID0gd2luZG93LmNocm9tZSxcbiAgICAgICAgICAgIHZlbmRvck5hbWUgPSB3aW5kb3cubmF2aWdhdG9yLnZlbmRvcjtcbiAgICAgICAgaWYoaXNDaHJvbWl1bSAhPT0gbnVsbCAmJiBpc0Nocm9taXVtICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgIC8qIHNraXAgb3BlcmEgKi9cbiAgICAgICAgICAgICYmIHZlbmRvck5hbWUgPT09IFwiR29vZ2xlIEluYy5cIlxuICAgICAgICAgICAgLyogc2tpcCBDaHJvbWl1bSBhcyBzdWdnZXN0ZWQgYnkgZmlwcG8gKi9cbiAgICAgICAgICAgICYmICF3aW5kb3cubmF2aWdhdG9yLmFwcFZlcnNpb24ubWF0Y2goL0Nocm9taXVtXFwvLykgKSB7XG4gICAgICAgICAgICB2YXIgdmVyID0gcGFyc2VJbnQod2luZG93Lm5hdmlnYXRvci5hcHBWZXJzaW9uLm1hdGNoKC9DaHJvbWVcXC8oXFxkKylcXC4vKVsxXSwgMTApO1xuICAgICAgICAgICAgaWYgKHZlciA+IDM3KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaW11bGNhc3RTZW5kZXIgPSBuZXcgTmF0aXZlU2ltdWxjYXN0U2VuZGVyKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyID0gbmV3IE5vU2ltdWxjYXN0U2VuZGVyKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlciA9IG5ldyBOb1NpbXVsY2FzdFNlbmRlcigpO1xuICAgICAgICB9XG5cbiAgICB9XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuU0lNVUxDQVNUX0xBWUVSX0NIQU5HRUQsXG4gICAgICAgIGZ1bmN0aW9uIChlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICAgICAgZW5kcG9pbnRTaW11bGNhc3RMYXllcnMuZm9yRWFjaChmdW5jdGlvbiAoZXNsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNzcmMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG4gICAgICAgICAgICAgICAgc2ltdWxjYXN0Ll9zZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShlc2wuZW5kcG9pbnQsIHNzcmMpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIEFQUC5SVEMuYWRkTGlzdGVuZXIoUlRDRXZlbnRzLlNJTVVMQ0FTVF9TVEFSVCwgZnVuY3Rpb24gKHNpbXVsY2FzdExheWVyKSB7XG4gICAgICAgIHZhciBzc3JjID0gc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG4gICAgICAgIHNpbXVsY2FzdC5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQoc3NyYywgdHJ1ZSk7XG4gICAgfSk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuU0lNVUxDQVNUX1NUT1AsIGZ1bmN0aW9uIChzaW11bGNhc3RMYXllcikge1xuICAgICAgICB2YXIgc3NyYyA9IHNpbXVsY2FzdExheWVyLnByaW1hcnlTU1JDO1xuICAgICAgICBzaW11bGNhc3QuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkKHNzcmMsIGZhbHNlKTtcbiAgICB9KTtcblxufVxuXG4vKipcbiAqIFJlc3RvcmVzIHRoZSBzaW11bGNhc3QgZ3JvdXBzIG9mIHRoZSByZW1vdGUgZGVzY3JpcHRpb24uIEluXG4gKiB0cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiB3ZSByZW1vdmUgdGhvc2UgaW4gb3JkZXIgZm9yIHRoZSBzZXQgcmVtb3RlXG4gKiBkZXNjcmlwdGlvbiB0byBzdWNjZWVkLiBUaGUgZm9jdXMgbmVlZHMgdGhlIHNpZ25hbCB0aGUgZ3JvdXBzIHRvIG5ld1xuICogcGFydGljaXBhbnRzLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci5yZXZlcnNlVHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24oZGVzYyk7XG59O1xuXG4vKipcbiAqIFJlbW92ZXMgdGhlIHNzcmMtZ3JvdXA6U0lNIGZyb20gdGhlIHJlbW90ZSBkZXNjcmlwdGlvbiBiYWNhdXNlIENocm9tZVxuICogZWl0aGVyIGdldHMgY29uZnVzZWQgYW5kIHRoaW5rcyB0aGlzIGlzIGFuIEZJRCBncm91cCBvciwgaWYgYW4gRklEIGdyb3VwXG4gKiBpcyBhbHJlYWR5IHByZXNlbnQsIGl0IGZhaWxzIHRvIHNldCB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLnRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uKGRlc2MpO1xufTtcblxuLyoqXG4gKiBHZXRzIHRoZSBmdWxseSBxdWFsaWZpZWQgbXNpZCAoc3RyZWFtLmlkICsgdHJhY2suaWQpIGFzc29jaWF0ZWQgdG8gdGhlXG4gKiBTU1JDLlxuICpcbiAqIEBwYXJhbSBzc3JjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0UmVtb3RlVmlkZW9TdHJlYW1JZEJ5U1NSQyA9IGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIuZ2V0UmVtb3RlVmlkZW9TdHJlYW1JZEJ5U1NSQyhzc3JjKTtcbn07XG5cbi8qKlxuICogUmV0dXJucyBhIHN0cmVhbSB3aXRoIHNpbmdsZSB2aWRlbyB0cmFjaywgdGhlIG9uZSBjdXJyZW50bHkgYmVpbmdcbiAqIHJlY2VpdmVkIGJ5IHRoaXMgZW5kcG9pbnQuXG4gKlxuICogQHBhcmFtIHN0cmVhbSB0aGUgcmVtb3RlIHNpbXVsY2FzdCBzdHJlYW0uXG4gKiBAcmV0dXJucyB7d2Via2l0TWVkaWFTdHJlYW19XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKHN0cmVhbSk7XG59O1xuXG4vKipcbiAqXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RTZW5kZXIudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihkZXNjKTtcbn07XG5cbi8qKlxuICpcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRMb2NhbFZpZGVvU3RyZWFtID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0U2VuZGVyLmdldExvY2FsVmlkZW9TdHJlYW0oKTtcbn07XG5cbi8qKlxuICogR1VNIGZvciBzaW11bGNhc3QuXG4gKlxuICogQHBhcmFtIGNvbnN0cmFpbnRzXG4gKiBAcGFyYW0gc3VjY2Vzc1xuICogQHBhcmFtIGVyclxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRVc2VyTWVkaWEgPSBmdW5jdGlvbiAoY29uc3RyYWludHMsIHN1Y2Nlc3MsIGVycikge1xuXG4gICAgdGhpcy5zaW11bGNhc3RTZW5kZXIuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLCBzdWNjZXNzLCBlcnIpO1xufTtcblxuLyoqXG4gKiBQcmVwYXJlcyB0aGUgbG9jYWwgZGVzY3JpcHRpb24gZm9yIHB1YmxpYyB1c2FnZSAoaS5lLiB0byBiZSBzaWduYWxlZFxuICogdGhyb3VnaCBKaW5nbGUgdG8gdGhlIGZvY3VzKS5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMge1JUQ1Nlc3Npb25EZXNjcmlwdGlvbn1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFNlbmRlci5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihkZXNjKTtcbn07XG5cbi8qKlxuICogRW5zdXJlcyB0aGF0IHRoZSBzaW11bGNhc3QgZ3JvdXAgaXMgcHJlc2VudCBpbiB0aGUgYW5zd2VyLCBfaWZfIG5hdGl2ZVxuICogc2ltdWxjYXN0IGlzIGVuYWJsZWQsXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS50cmFuc2Zvcm1BbnN3ZXIgPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFNlbmRlci50cmFuc2Zvcm1BbnN3ZXIoZGVzYyk7XG59O1xuXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdTU1JDID0gZnVuY3Rpb24gKGppZCkge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLmdldFJlY2VpdmluZ1NTUkMoamlkKTtcbn07XG5cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtQnlTU1JDID0gZnVuY3Rpb24gKG1zaWQpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhtc2lkKTtcbn07XG5cbi8qKlxuICpcbiAqIEBwYXJhbSBsaW5lc1xuICogQHBhcmFtIG1lZGlhdHlwZXNcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5wYXJzZU1lZGlhID0gZnVuY3Rpb24obGluZXMsIG1lZGlhdHlwZXMpIHtcbiAgICB2YXIgc2IgPSBsaW5lcy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFV0aWxzLnBhcnNlTWVkaWEoc2IsIG1lZGlhdHlwZXMpO1xufTtcblxuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuX3NldFJlY2VpdmluZ1ZpZGVvU3RyZWFtID0gZnVuY3Rpb24ocmVzb3VyY2UsIHNzcmMpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLl9zZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShyZXNvdXJjZSwgc3NyYyk7XG59O1xuXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQgPSBmdW5jdGlvbihzc3JjLCBlbmFibGVkKSB7XG4gICAgdGhpcy5zaW11bGNhc3RTZW5kZXIuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkKHNzcmMsIGVuYWJsZWQpO1xufTtcblxuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUucmVzZXRTZW5kZXIgPSBmdW5jdGlvbigpIHtcbiAgICBpZiAodHlwZW9mIHRoaXMuc2ltdWxjYXN0U2VuZGVyLnJlc2V0ID09PSAnZnVuY3Rpb24nKXtcbiAgICAgICAgdGhpcy5zaW11bGNhc3RTZW5kZXIucmVzZXQoKTtcbiAgICB9XG59O1xuXG52YXIgc2ltdWxjYXN0ID0gbmV3IFNpbXVsY2FzdE1hbmFnZXIoKTtcblxubW9kdWxlLmV4cG9ydHMgPSBzaW11bGNhc3Q7IiwiLyoqXG4gKiBQcm92aWRlcyBzdGF0aXN0aWNzIGZvciB0aGUgbG9jYWwgc3RyZWFtLlxuICovXG5cblxuLyoqXG4gKiBTaXplIG9mIHRoZSB3ZWJhdWRpbyBhbmFsaXplciBidWZmZXIuXG4gKiBAdHlwZSB7bnVtYmVyfVxuICovXG52YXIgV0VCQVVESU9fQU5BTElaRVJfRkZUX1NJWkUgPSAyMDQ4O1xuXG4vKipcbiAqIFZhbHVlIG9mIHRoZSB3ZWJhdWRpbyBhbmFsaXplciBzbW9vdGhpbmcgdGltZSBwYXJhbWV0ZXIuXG4gKiBAdHlwZSB7bnVtYmVyfVxuICovXG52YXIgV0VCQVVESU9fQU5BTElaRVJfU01PT1RJTkdfVElNRSA9IDAuODtcblxuLyoqXG4gKiBDb252ZXJ0cyB0aW1lIGRvbWFpbiBkYXRhIGFycmF5IHRvIGF1ZGlvIGxldmVsLlxuICogQHBhcmFtIGFycmF5IHRoZSB0aW1lIGRvbWFpbiBkYXRhIGFycmF5LlxuICogQHJldHVybnMge251bWJlcn0gdGhlIGF1ZGlvIGxldmVsXG4gKi9cbmZ1bmN0aW9uIHRpbWVEb21haW5EYXRhVG9BdWRpb0xldmVsKHNhbXBsZXMpIHtcblxuICAgIHZhciBtYXhWb2x1bWUgPSAwO1xuXG4gICAgdmFyIGxlbmd0aCA9IHNhbXBsZXMubGVuZ3RoO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAobWF4Vm9sdW1lIDwgc2FtcGxlc1tpXSlcbiAgICAgICAgICAgIG1heFZvbHVtZSA9IHNhbXBsZXNbaV07XG4gICAgfVxuXG4gICAgcmV0dXJuIHBhcnNlRmxvYXQoKChtYXhWb2x1bWUgLSAxMjcpIC8gMTI4KS50b0ZpeGVkKDMpKTtcbn07XG5cbi8qKlxuICogQW5pbWF0ZXMgYXVkaW8gbGV2ZWwgY2hhbmdlXG4gKiBAcGFyYW0gbmV3TGV2ZWwgdGhlIG5ldyBhdWRpbyBsZXZlbFxuICogQHBhcmFtIGxhc3RMZXZlbCB0aGUgbGFzdCBhdWRpbyBsZXZlbFxuICogQHJldHVybnMge051bWJlcn0gdGhlIGF1ZGlvIGxldmVsIHRvIGJlIHNldFxuICovXG5mdW5jdGlvbiBhbmltYXRlTGV2ZWwobmV3TGV2ZWwsIGxhc3RMZXZlbClcbntcbiAgICB2YXIgdmFsdWUgPSAwO1xuICAgIHZhciBkaWZmID0gbGFzdExldmVsIC0gbmV3TGV2ZWw7XG4gICAgaWYoZGlmZiA+IDAuMilcbiAgICB7XG4gICAgICAgIHZhbHVlID0gbGFzdExldmVsIC0gMC4yO1xuICAgIH1cbiAgICBlbHNlIGlmKGRpZmYgPCAtMC40KVxuICAgIHtcbiAgICAgICAgdmFsdWUgPSBsYXN0TGV2ZWwgKyAwLjQ7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHZhbHVlID0gbmV3TGV2ZWw7XG4gICAgfVxuXG4gICAgcmV0dXJuIHBhcnNlRmxvYXQodmFsdWUudG9GaXhlZCgzKSk7XG59XG5cblxuLyoqXG4gKiA8dHQ+TG9jYWxTdGF0c0NvbGxlY3RvcjwvdHQ+IGNhbGN1bGF0ZXMgc3RhdGlzdGljcyBmb3IgdGhlIGxvY2FsIHN0cmVhbS5cbiAqXG4gKiBAcGFyYW0gc3RyZWFtIHRoZSBsb2NhbCBzdHJlYW1cbiAqIEBwYXJhbSBpbnRlcnZhbCBzdGF0cyByZWZyZXNoIGludGVydmFsIGdpdmVuIGluIG1zLlxuICogQHBhcmFtIHtmdW5jdGlvbihMb2NhbFN0YXRzQ29sbGVjdG9yKX0gdXBkYXRlQ2FsbGJhY2sgdGhlIGNhbGxiYWNrIGNhbGxlZCBvbiBzdGF0c1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVwZGF0ZS5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBMb2NhbFN0YXRzQ29sbGVjdG9yKHN0cmVhbSwgaW50ZXJ2YWwsIHN0YXRpc3RpY3NTZXJ2aWNlLCBldmVudEVtaXR0ZXIpIHtcbiAgICB3aW5kb3cuQXVkaW9Db250ZXh0ID0gd2luZG93LkF1ZGlvQ29udGV4dCB8fCB3aW5kb3cud2Via2l0QXVkaW9Db250ZXh0O1xuICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtO1xuICAgIHRoaXMuaW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgdGhpcy5pbnRlcnZhbE1pbGlzID0gaW50ZXJ2YWw7XG4gICAgdGhpcy5ldmVudEVtaXR0ZXIgPSBldmVudEVtaXR0ZXI7XG4gICAgdGhpcy5hdWRpb0xldmVsID0gMDtcbiAgICB0aGlzLnN0YXRpc3RpY3NTZXJ2aWNlID0gc3RhdGlzdGljc1NlcnZpY2U7XG59XG5cbi8qKlxuICogU3RhcnRzIHRoZSBjb2xsZWN0aW5nIHRoZSBzdGF0aXN0aWNzLlxuICovXG5Mb2NhbFN0YXRzQ29sbGVjdG9yLnByb3RvdHlwZS5zdGFydCA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAoY29uZmlnLmRpc2FibGVBdWRpb0xldmVscyB8fCAhd2luZG93LkF1ZGlvQ29udGV4dClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgdmFyIGNvbnRleHQgPSBuZXcgQXVkaW9Db250ZXh0KCk7XG4gICAgdmFyIGFuYWx5c2VyID0gY29udGV4dC5jcmVhdGVBbmFseXNlcigpO1xuICAgIGFuYWx5c2VyLnNtb290aGluZ1RpbWVDb25zdGFudCA9IFdFQkFVRElPX0FOQUxJWkVSX1NNT09USU5HX1RJTUU7XG4gICAgYW5hbHlzZXIuZmZ0U2l6ZSA9IFdFQkFVRElPX0FOQUxJWkVSX0ZGVF9TSVpFO1xuXG5cbiAgICB2YXIgc291cmNlID0gY29udGV4dC5jcmVhdGVNZWRpYVN0cmVhbVNvdXJjZSh0aGlzLnN0cmVhbSk7XG4gICAgc291cmNlLmNvbm5lY3QoYW5hbHlzZXIpO1xuXG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICB0aGlzLmludGVydmFsSWQgPSBzZXRJbnRlcnZhbChcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYW5hbHlzZXIuZnJlcXVlbmN5QmluQ291bnQpO1xuICAgICAgICAgICAgYW5hbHlzZXIuZ2V0Qnl0ZVRpbWVEb21haW5EYXRhKGFycmF5KTtcbiAgICAgICAgICAgIHZhciBhdWRpb0xldmVsID0gdGltZURvbWFpbkRhdGFUb0F1ZGlvTGV2ZWwoYXJyYXkpO1xuICAgICAgICAgICAgaWYoYXVkaW9MZXZlbCAhPSBzZWxmLmF1ZGlvTGV2ZWwpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmF1ZGlvTGV2ZWwgPSBhbmltYXRlTGV2ZWwoYXVkaW9MZXZlbCwgc2VsZi5hdWRpb0xldmVsKTtcbiAgICAgICAgICAgICAgICBzZWxmLmV2ZW50RW1pdHRlci5lbWl0KFxuICAgICAgICAgICAgICAgICAgICBcInN0YXRpc3RpY3MuYXVkaW9MZXZlbFwiLFxuICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRpc3RpY3NTZXJ2aWNlLkxPQ0FMX0pJRCxcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5hdWRpb0xldmVsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5pbnRlcnZhbE1pbGlzXG4gICAgKTtcblxufTtcblxuLyoqXG4gKiBTdG9wcyBjb2xsZWN0aW5nIHRoZSBzdGF0aXN0aWNzLlxuICovXG5Mb2NhbFN0YXRzQ29sbGVjdG9yLnByb3RvdHlwZS5zdG9wID0gZnVuY3Rpb24gKCkge1xuICAgIGlmICh0aGlzLmludGVydmFsSWQpIHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmludGVydmFsSWQpO1xuICAgICAgICB0aGlzLmludGVydmFsSWQgPSBudWxsO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gTG9jYWxTdGF0c0NvbGxlY3RvcjsiLCIvKiBnbG9iYWwgc3NyYzJqaWQgKi9cbi8qIGpzaGludCAtVzExNyAqL1xudmFyIFJUQ0Jyb3dzZXJUeXBlID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1JUQ0Jyb3dzZXJUeXBlXCIpO1xuXG5cbi8qKlxuICogQ2FsY3VsYXRlcyBwYWNrZXQgbG9zdCBwZXJjZW50IHVzaW5nIHRoZSBudW1iZXIgb2YgbG9zdCBwYWNrZXRzIGFuZCB0aGVcbiAqIG51bWJlciBvZiBhbGwgcGFja2V0LlxuICogQHBhcmFtIGxvc3RQYWNrZXRzIHRoZSBudW1iZXIgb2YgbG9zdCBwYWNrZXRzXG4gKiBAcGFyYW0gdG90YWxQYWNrZXRzIHRoZSBudW1iZXIgb2YgYWxsIHBhY2tldHMuXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBwYWNrZXQgbG9zcyBwZXJjZW50XG4gKi9cbmZ1bmN0aW9uIGNhbGN1bGF0ZVBhY2tldExvc3MobG9zdFBhY2tldHMsIHRvdGFsUGFja2V0cykge1xuICAgIGlmKCF0b3RhbFBhY2tldHMgfHwgdG90YWxQYWNrZXRzIDw9IDAgfHwgIWxvc3RQYWNrZXRzIHx8IGxvc3RQYWNrZXRzIDw9IDApXG4gICAgICAgIHJldHVybiAwO1xuICAgIHJldHVybiBNYXRoLnJvdW5kKChsb3N0UGFja2V0cy90b3RhbFBhY2tldHMpKjEwMCk7XG59XG5cbmZ1bmN0aW9uIGdldFN0YXRWYWx1ZShpdGVtLCBuYW1lKSB7XG4gICAgaWYoIWtleU1hcFtBUFAuUlRDLmdldEJyb3dzZXJUeXBlKCldW25hbWVdKVxuICAgICAgICB0aHJvdyBcIlRoZSBwcm9wZXJ0eSBpc24ndCBzdXBwb3J0ZWQhXCI7XG4gICAgdmFyIGtleSA9IGtleU1hcFtBUFAuUlRDLmdldEJyb3dzZXJUeXBlKCldW25hbWVdO1xuICAgIHJldHVybiBBUFAuUlRDLmdldEJyb3dzZXJUeXBlKCkgPT0gUlRDQnJvd3NlclR5cGUuUlRDX0JST1dTRVJfQ0hST01FPyBpdGVtLnN0YXQoa2V5KSA6IGl0ZW1ba2V5XTtcbn1cblxuLyoqXG4gKiBQZWVyIHN0YXRpc3RpY3MgZGF0YSBob2xkZXIuXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gUGVlclN0YXRzKClcbntcbiAgICB0aGlzLnNzcmMyTG9zcyA9IHt9O1xuICAgIHRoaXMuc3NyYzJBdWRpb0xldmVsID0ge307XG4gICAgdGhpcy5zc3JjMmJpdHJhdGUgPSB7fTtcbiAgICB0aGlzLnNzcmMycmVzb2x1dGlvbiA9IHt9O1xufVxuXG4vKipcbiAqIFRoZSBiYW5kd2lkdGhcbiAqIEB0eXBlIHt7fX1cbiAqL1xuUGVlclN0YXRzLmJhbmR3aWR0aCA9IHt9O1xuXG4vKipcbiAqIFRoZSBiaXQgcmF0ZVxuICogQHR5cGUge3t9fVxuICovXG5QZWVyU3RhdHMuYml0cmF0ZSA9IHt9O1xuXG5cblxuLyoqXG4gKiBUaGUgcGFja2V0IGxvc3MgcmF0ZVxuICogQHR5cGUge3t9fVxuICovXG5QZWVyU3RhdHMucGFja2V0TG9zcyA9IG51bGw7XG5cbi8qKlxuICogU2V0cyBwYWNrZXRzIGxvc3MgcmF0ZSBmb3IgZ2l2ZW4gPHR0PnNzcmM8L3R0PiB0aGF0IGJsb25nIHRvIHRoZSBwZWVyXG4gKiByZXByZXNlbnRlZCBieSB0aGlzIGluc3RhbmNlLlxuICogQHBhcmFtIHNzcmMgYXVkaW8gb3IgdmlkZW8gUlRQIHN0cmVhbSBTU1JDLlxuICogQHBhcmFtIGxvc3NSYXRlIG5ldyBwYWNrZXQgbG9zcyByYXRlIHZhbHVlIHRvIGJlIHNldC5cbiAqL1xuUGVlclN0YXRzLnByb3RvdHlwZS5zZXRTc3JjTG9zcyA9IGZ1bmN0aW9uIChzc3JjLCBsb3NzUmF0ZSlcbntcbiAgICB0aGlzLnNzcmMyTG9zc1tzc3JjXSA9IGxvc3NSYXRlO1xufTtcblxuLyoqXG4gKiBTZXRzIHJlc29sdXRpb24gZm9yIGdpdmVuIDx0dD5zc3JjPC90dD4gdGhhdCBiZWxvbmcgdG8gdGhlIHBlZXJcbiAqIHJlcHJlc2VudGVkIGJ5IHRoaXMgaW5zdGFuY2UuXG4gKiBAcGFyYW0gc3NyYyBhdWRpbyBvciB2aWRlbyBSVFAgc3RyZWFtIFNTUkMuXG4gKiBAcGFyYW0gcmVzb2x1dGlvbiBuZXcgcmVzb2x1dGlvbiB2YWx1ZSB0byBiZSBzZXQuXG4gKi9cblBlZXJTdGF0cy5wcm90b3R5cGUuc2V0U3NyY1Jlc29sdXRpb24gPSBmdW5jdGlvbiAoc3NyYywgcmVzb2x1dGlvbilcbntcbiAgICBpZihyZXNvbHV0aW9uID09PSBudWxsICYmIHRoaXMuc3NyYzJyZXNvbHV0aW9uW3NzcmNdKVxuICAgIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuc3NyYzJyZXNvbHV0aW9uW3NzcmNdO1xuICAgIH1cbiAgICBlbHNlIGlmKHJlc29sdXRpb24gIT09IG51bGwpXG4gICAgICAgIHRoaXMuc3NyYzJyZXNvbHV0aW9uW3NzcmNdID0gcmVzb2x1dGlvbjtcbn07XG5cbi8qKlxuICogU2V0cyB0aGUgYml0IHJhdGUgZm9yIGdpdmVuIDx0dD5zc3JjPC90dD4gdGhhdCBibG9uZyB0byB0aGUgcGVlclxuICogcmVwcmVzZW50ZWQgYnkgdGhpcyBpbnN0YW5jZS5cbiAqIEBwYXJhbSBzc3JjIGF1ZGlvIG9yIHZpZGVvIFJUUCBzdHJlYW0gU1NSQy5cbiAqIEBwYXJhbSBiaXRyYXRlIG5ldyBiaXRyYXRlIHZhbHVlIHRvIGJlIHNldC5cbiAqL1xuUGVlclN0YXRzLnByb3RvdHlwZS5zZXRTc3JjQml0cmF0ZSA9IGZ1bmN0aW9uIChzc3JjLCBiaXRyYXRlKVxue1xuICAgIGlmKHRoaXMuc3NyYzJiaXRyYXRlW3NzcmNdKVxuICAgIHtcbiAgICAgICAgdGhpcy5zc3JjMmJpdHJhdGVbc3NyY10uZG93bmxvYWQgKz0gYml0cmF0ZS5kb3dubG9hZDtcbiAgICAgICAgdGhpcy5zc3JjMmJpdHJhdGVbc3NyY10udXBsb2FkICs9IGJpdHJhdGUudXBsb2FkO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgdGhpcy5zc3JjMmJpdHJhdGVbc3NyY10gPSBiaXRyYXRlO1xuICAgIH1cbn07XG5cbi8qKlxuICogU2V0cyBuZXcgYXVkaW8gbGV2ZWwoaW5wdXQgb3Igb3V0cHV0KSBmb3IgZ2l2ZW4gPHR0PnNzcmM8L3R0PiB0aGF0IGlkZW50aWZpZXNcbiAqIHRoZSBzdHJlYW0gd2hpY2ggYmVsb25ncyB0byB0aGUgcGVlciByZXByZXNlbnRlZCBieSB0aGlzIGluc3RhbmNlLlxuICogQHBhcmFtIHNzcmMgUlRQIHN0cmVhbSBTU1JDIGZvciB3aGljaCBjdXJyZW50IGF1ZGlvIGxldmVsIHZhbHVlIHdpbGwgYmVcbiAqICAgICAgICB1cGRhdGVkLlxuICogQHBhcmFtIGF1ZGlvTGV2ZWwgdGhlIG5ldyBhdWRpbyBsZXZlbCB2YWx1ZSB0byBiZSBzZXQuIFZhbHVlIGlzIHRydW5jYXRlZCB0b1xuICogICAgICAgIGZpdCB0aGUgcmFuZ2UgZnJvbSAwIHRvIDEuXG4gKi9cblBlZXJTdGF0cy5wcm90b3R5cGUuc2V0U3NyY0F1ZGlvTGV2ZWwgPSBmdW5jdGlvbiAoc3NyYywgYXVkaW9MZXZlbClcbntcbiAgICAvLyBSYW5nZSBsaW1pdCAwIC0gMVxuICAgIHRoaXMuc3NyYzJBdWRpb0xldmVsW3NzcmNdID0gZm9ybWF0QXVkaW9MZXZlbChhdWRpb0xldmVsKTtcbn07XG5cbmZ1bmN0aW9uIGZvcm1hdEF1ZGlvTGV2ZWwoYXVkaW9MZXZlbCkge1xuICAgIHJldHVybiBNYXRoLm1pbihNYXRoLm1heChhdWRpb0xldmVsLCAwKSwgMSk7XG59XG5cbi8qKlxuICogQXJyYXkgd2l0aCB0aGUgdHJhbnNwb3J0IGluZm9ybWF0aW9uLlxuICogQHR5cGUge0FycmF5fVxuICovXG5QZWVyU3RhdHMudHJhbnNwb3J0ID0gW107XG5cblxuLyoqXG4gKiA8dHQ+U3RhdHNDb2xsZWN0b3I8L3R0PiByZWdpc3RlcnMgZm9yIHN0YXRzIHVwZGF0ZXMgb2YgZ2l2ZW5cbiAqIDx0dD5wZWVyY29ubmVjdGlvbjwvdHQ+IGluIGdpdmVuIDx0dD5pbnRlcnZhbDwvdHQ+LiBPbiBlYWNoIHVwZGF0ZSBwYXJ0aWN1bGFyXG4gKiBzdGF0cyBhcmUgZXh0cmFjdGVkIGFuZCBwdXQgaW4ge0BsaW5rIFBlZXJTdGF0c30gb2JqZWN0cy4gT25jZSB0aGUgcHJvY2Vzc2luZ1xuICogaXMgZG9uZSA8dHQ+YXVkaW9MZXZlbHNVcGRhdGVDYWxsYmFjazwvdHQ+IGlzIGNhbGxlZCB3aXRoIDx0dD50aGlzPC90dD5cbiAqIGluc3RhbmNlIGFzIGFuIGV2ZW50IHNvdXJjZS5cbiAqXG4gKiBAcGFyYW0gcGVlcmNvbm5lY3Rpb24gd2ViUlRDIHBlZXIgY29ubmVjdGlvbiBvYmplY3QuXG4gKiBAcGFyYW0gaW50ZXJ2YWwgc3RhdHMgcmVmcmVzaCBpbnRlcnZhbCBnaXZlbiBpbiBtcy5cbiAqIEBwYXJhbSB7ZnVuY3Rpb24oU3RhdHNDb2xsZWN0b3IpfSBhdWRpb0xldmVsc1VwZGF0ZUNhbGxiYWNrIHRoZSBjYWxsYmFja1xuICogY2FsbGVkIG9uIHN0YXRzIHVwZGF0ZS5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBTdGF0c0NvbGxlY3RvcihwZWVyY29ubmVjdGlvbiwgYXVkaW9MZXZlbHNJbnRlcnZhbCwgc3RhdHNJbnRlcnZhbCwgZXZlbnRFbWl0dGVyKVxue1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSBwZWVyY29ubmVjdGlvbjtcbiAgICB0aGlzLmJhc2VsaW5lQXVkaW9MZXZlbHNSZXBvcnQgPSBudWxsO1xuICAgIHRoaXMuY3VycmVudEF1ZGlvTGV2ZWxzUmVwb3J0ID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRTdGF0c1JlcG9ydCA9IG51bGw7XG4gICAgdGhpcy5iYXNlbGluZVN0YXRzUmVwb3J0ID0gbnVsbDtcbiAgICB0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgdGhpcy5ldmVudEVtaXR0ZXIgPSBldmVudEVtaXR0ZXI7XG5cbiAgICAvKipcbiAgICAgKiBHYXRoZXIgUGVlckNvbm5lY3Rpb24gc3RhdHMgb25jZSBldmVyeSB0aGlzIG1hbnkgbWlsbGlzZWNvbmRzLlxuICAgICAqL1xuICAgIHRoaXMuR0FUSEVSX0lOVEVSVkFMID0gMTUwMDA7XG5cbiAgICAvKipcbiAgICAgKiBMb2cgc3RhdHMgdmlhIHRoZSBmb2N1cyBvbmNlIGV2ZXJ5IHRoaXMgbWFueSBtaWxsaXNlY29uZHMuXG4gICAgICovXG4gICAgdGhpcy5MT0dfSU5URVJWQUwgPSA2MDAwMDtcblxuICAgIC8qKlxuICAgICAqIEdhdGhlciBzdGF0cyBhbmQgc3RvcmUgdGhlbSBpbiB0aGlzLnN0YXRzVG9CZUxvZ2dlZC5cbiAgICAgKi9cbiAgICB0aGlzLmdhdGhlclN0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBTZW5kIHRoZSBzdGF0cyBhbHJlYWR5IHNhdmVkIGluIHRoaXMuc3RhdHNUb0JlTG9nZ2VkIHRvIGJlIGxvZ2dlZCB2aWFcbiAgICAgKiB0aGUgZm9jdXMuXG4gICAgICovXG4gICAgdGhpcy5sb2dTdGF0c0ludGVydmFsSWQgPSBudWxsO1xuXG4gICAgLyoqXG4gICAgICogU3RvcmVzIHRoZSBzdGF0aXN0aWNzIHdoaWNoIHdpbGwgYmUgc2VuZCB0byB0aGUgZm9jdXMgdG8gYmUgbG9nZ2VkLlxuICAgICAqL1xuICAgIHRoaXMuc3RhdHNUb0JlTG9nZ2VkID1cbiAgICB7XG4gICAgICAgIHRpbWVzdGFtcHM6IFtdLFxuICAgICAgICBzdGF0czoge31cbiAgICB9O1xuXG4gICAgLy8gVXBkYXRlcyBzdGF0cyBpbnRlcnZhbFxuICAgIHRoaXMuYXVkaW9MZXZlbHNJbnRlcnZhbE1pbGlzID0gYXVkaW9MZXZlbHNJbnRlcnZhbDtcblxuICAgIHRoaXMuc3RhdHNJbnRlcnZhbElkID0gbnVsbDtcbiAgICB0aGlzLnN0YXRzSW50ZXJ2YWxNaWxpcyA9IHN0YXRzSW50ZXJ2YWw7XG4gICAgLy8gTWFwIG9mIGppZHMgdG8gUGVlclN0YXRzXG4gICAgdGhpcy5qaWQyc3RhdHMgPSB7fTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBTdGF0c0NvbGxlY3RvcjtcblxuLyoqXG4gKiBTdG9wcyBzdGF0cyB1cGRhdGVzLlxuICovXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUuc3RvcCA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAodGhpcy5hdWRpb0xldmVsc0ludGVydmFsSWQpIHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCk7XG4gICAgICAgIHRoaXMuYXVkaW9MZXZlbHNJbnRlcnZhbElkID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5zdGF0c0ludGVydmFsSWQpXG4gICAge1xuICAgICAgICBjbGVhckludGVydmFsKHRoaXMuc3RhdHNJbnRlcnZhbElkKTtcbiAgICAgICAgdGhpcy5zdGF0c0ludGVydmFsSWQgPSBudWxsO1xuICAgIH1cblxuICAgIGlmKHRoaXMubG9nU3RhdHNJbnRlcnZhbElkKVxuICAgIHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmxvZ1N0YXRzSW50ZXJ2YWxJZCk7XG4gICAgICAgIHRoaXMubG9nU3RhdHNJbnRlcnZhbElkID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZih0aGlzLmdhdGhlclN0YXRzSW50ZXJ2YWxJZClcbiAgICB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5nYXRoZXJTdGF0c0ludGVydmFsSWQpO1xuICAgICAgICB0aGlzLmdhdGhlclN0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgfVxufTtcblxuLyoqXG4gKiBDYWxsYmFjayBwYXNzZWQgdG8gPHR0PmdldFN0YXRzPC90dD4gbWV0aG9kLlxuICogQHBhcmFtIGVycm9yIGFuIGVycm9yIHRoYXQgb2NjdXJyZWQgb24gPHR0PmdldFN0YXRzPC90dD4gY2FsbC5cbiAqL1xuU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLmVycm9yQ2FsbGJhY2sgPSBmdW5jdGlvbiAoZXJyb3IpXG57XG4gICAgY29uc29sZS5lcnJvcihcIkdldCBzdGF0cyBlcnJvclwiLCBlcnJvcik7XG4gICAgdGhpcy5zdG9wKCk7XG59O1xuXG4vKipcbiAqIFN0YXJ0cyBzdGF0cyB1cGRhdGVzLlxuICovXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUuc3RhcnQgPSBmdW5jdGlvbiAoKVxue1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZighY29uZmlnLmRpc2FibGVBdWRpb0xldmVscykge1xuICAgICAgICB0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCA9IHNldEludGVydmFsKFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIC8vIEludGVydmFsIHVwZGF0ZXNcbiAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVwb3J0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzdWx0cyA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXJlcG9ydCB8fCAhcmVwb3J0LnJlc3VsdCB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGVvZiByZXBvcnQucmVzdWx0ICE9ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gcmVwb3J0O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydC5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5lcnJvcihcIkdvdCBpbnRlcnZhbCByZXBvcnRcIiwgcmVzdWx0cyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmN1cnJlbnRBdWRpb0xldmVsc1JlcG9ydCA9IHJlc3VsdHM7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnByb2Nlc3NBdWRpb0xldmVsUmVwb3J0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmJhc2VsaW5lQXVkaW9MZXZlbHNSZXBvcnQgPVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY3VycmVudEF1ZGlvTGV2ZWxzUmVwb3J0O1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBzZWxmLmVycm9yQ2FsbGJhY2tcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHNlbGYuYXVkaW9MZXZlbHNJbnRlcnZhbE1pbGlzXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgaWYoIWNvbmZpZy5kaXNhYmxlU3RhdHMpIHtcbiAgICAgICAgdGhpcy5zdGF0c0ludGVydmFsSWQgPSBzZXRJbnRlcnZhbChcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAvLyBJbnRlcnZhbCB1cGRhdGVzXG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFyZXBvcnQgfHwgIXJlcG9ydC5yZXN1bHQgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgcmVwb3J0LnJlc3VsdCAhPSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9maXJlZm94XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vY2hyb21lXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydC5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5lcnJvcihcIkdvdCBpbnRlcnZhbCByZXBvcnRcIiwgcmVzdWx0cyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmN1cnJlbnRTdGF0c1JlcG9ydCA9IHJlc3VsdHM7XG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucHJvY2Vzc1N0YXRzUmVwb3J0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJVbnN1cHBvcnRlZCBrZXk6XCIgKyBlLCBlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5iYXNlbGluZVN0YXRzUmVwb3J0ID0gc2VsZi5jdXJyZW50U3RhdHNSZXBvcnQ7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHNlbGYuZXJyb3JDYWxsYmFja1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc2VsZi5zdGF0c0ludGVydmFsTWlsaXNcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZmlnLmxvZ1N0YXRzKSB7XG4gICAgICAgIHRoaXMuZ2F0aGVyU3RhdHNJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5hZGRTdGF0c1RvQmVMb2dnZWQocmVwb3J0LnJlc3VsdCgpKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB0aGlzLkdBVEhFUl9JTlRFUlZBTFxuICAgICAgICApO1xuXG4gICAgICAgIHRoaXMubG9nU3RhdHNJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoXG4gICAgICAgICAgICBmdW5jdGlvbigpIHsgc2VsZi5sb2dTdGF0cygpOyB9LFxuICAgICAgICAgICAgdGhpcy5MT0dfSU5URVJWQUwpO1xuICAgIH1cbn07XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgYSBjZXJ0YWluIHJlY29yZCBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIGxvZ2dlZCBzdGF0aXN0aWNzLlxuICovXG5mdW5jdGlvbiBhY2NlcHRTdGF0KHJlcG9ydElkLCByZXBvcnRUeXBlLCBzdGF0TmFtZSkge1xuICAgIGlmIChyZXBvcnRUeXBlID09IFwiZ29vZ0NhbmRpZGF0ZVBhaXJcIiAmJiBzdGF0TmFtZSA9PSBcImdvb2dDaGFubmVsSWRcIilcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuXG4gICAgaWYgKHJlcG9ydFR5cGUgPT0gXCJzc3JjXCIpIHtcbiAgICAgICAgaWYgKHN0YXROYW1lID09IFwiZ29vZ1RyYWNrSWRcIiB8fFxuICAgICAgICAgICAgc3RhdE5hbWUgPT0gXCJ0cmFuc3BvcnRJZFwiIHx8XG4gICAgICAgICAgICBzdGF0TmFtZSA9PSBcInNzcmNcIilcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciBhIGNlcnRhaW4gcmVjb3JkIHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgbG9nZ2VkIHN0YXRpc3RpY3MuXG4gKi9cbmZ1bmN0aW9uIGFjY2VwdFJlcG9ydChpZCwgdHlwZSkge1xuICAgIGlmIChpZC5zdWJzdHJpbmcoMCwgMTUpID09IFwiZ29vZ0NlcnRpZmljYXRlXCIgfHxcbiAgICAgICAgaWQuc3Vic3RyaW5nKDAsIDkpID09IFwiZ29vZ1RyYWNrXCIgfHxcbiAgICAgICAgaWQuc3Vic3RyaW5nKDAsIDIwKSA9PSBcImdvb2dMaWJqaW5nbGVTZXNzaW9uXCIpXG4gICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgIGlmICh0eXBlID09IFwiZ29vZ0NvbXBvbmVudFwiKVxuICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyB0aGUgc3RhdHMgdG8gdGhlIGZvcm1hdCB1c2VkIGZvciBsb2dnaW5nLCBhbmQgc2F2ZXMgdGhlIGRhdGEgaW5cbiAqIHRoaXMuc3RhdHNUb0JlTG9nZ2VkLlxuICogQHBhcmFtIHJlcG9ydHMgUmVwb3J0cyBhcyBnaXZlbiBieSB3ZWJraXRSVENQZXJDb25uZWN0aW9uLmdldFN0YXRzLlxuICovXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUuYWRkU3RhdHNUb0JlTG9nZ2VkID0gZnVuY3Rpb24gKHJlcG9ydHMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIG51bV9yZWNvcmRzID0gdGhpcy5zdGF0c1RvQmVMb2dnZWQudGltZXN0YW1wcy5sZW5ndGg7XG4gICAgdGhpcy5zdGF0c1RvQmVMb2dnZWQudGltZXN0YW1wcy5wdXNoKG5ldyBEYXRlKCkuZ2V0VGltZSgpKTtcbiAgICByZXBvcnRzLm1hcChmdW5jdGlvbiAocmVwb3J0KSB7XG4gICAgICAgIGlmICghYWNjZXB0UmVwb3J0KHJlcG9ydC5pZCwgcmVwb3J0LnR5cGUpKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB2YXIgc3RhdCA9IHNlbGYuc3RhdHNUb0JlTG9nZ2VkLnN0YXRzW3JlcG9ydC5pZF07XG4gICAgICAgIGlmICghc3RhdCkge1xuICAgICAgICAgICAgc3RhdCA9IHNlbGYuc3RhdHNUb0JlTG9nZ2VkLnN0YXRzW3JlcG9ydC5pZF0gPSB7fTtcbiAgICAgICAgfVxuICAgICAgICBzdGF0LnR5cGUgPSByZXBvcnQudHlwZTtcbiAgICAgICAgcmVwb3J0Lm5hbWVzKCkubWFwKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICBpZiAoIWFjY2VwdFN0YXQocmVwb3J0LmlkLCByZXBvcnQudHlwZSwgbmFtZSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgdmFyIHZhbHVlcyA9IHN0YXRbbmFtZV07XG4gICAgICAgICAgICBpZiAoIXZhbHVlcykge1xuICAgICAgICAgICAgICAgIHZhbHVlcyA9IHN0YXRbbmFtZV0gPSBbXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHdoaWxlICh2YWx1ZXMubGVuZ3RoIDwgbnVtX3JlY29yZHMpIHtcbiAgICAgICAgICAgICAgICB2YWx1ZXMucHVzaChudWxsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhbHVlcy5wdXNoKHJlcG9ydC5zdGF0KG5hbWUpKTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG59O1xuXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUubG9nU3RhdHMgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICBpZighQVBQLnhtcHAuc2VuZExvZ3ModGhpcy5zdGF0c1RvQmVMb2dnZWQpKVxuICAgICAgICByZXR1cm47XG4gICAgLy8gUmVzZXQgdGhlIHN0YXRzXG4gICAgdGhpcy5zdGF0c1RvQmVMb2dnZWQuc3RhdHMgPSB7fTtcbiAgICB0aGlzLnN0YXRzVG9CZUxvZ2dlZC50aW1lc3RhbXBzID0gW107XG59O1xudmFyIGtleU1hcCA9IHt9O1xua2V5TWFwW1JUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0ZJUkVGT1hdID0ge1xuICAgIFwic3NyY1wiOiBcInNzcmNcIixcbiAgICBcInBhY2tldHNSZWNlaXZlZFwiOiBcInBhY2tldHNSZWNlaXZlZFwiLFxuICAgIFwicGFja2V0c0xvc3RcIjogXCJwYWNrZXRzTG9zdFwiLFxuICAgIFwicGFja2V0c1NlbnRcIjogXCJwYWNrZXRzU2VudFwiLFxuICAgIFwiYnl0ZXNSZWNlaXZlZFwiOiBcImJ5dGVzUmVjZWl2ZWRcIixcbiAgICBcImJ5dGVzU2VudFwiOiBcImJ5dGVzU2VudFwiXG59O1xua2V5TWFwW1JUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0NIUk9NRV0gPSB7XG4gICAgXCJyZWNlaXZlQmFuZHdpZHRoXCI6IFwiZ29vZ0F2YWlsYWJsZVJlY2VpdmVCYW5kd2lkdGhcIixcbiAgICBcInNlbmRCYW5kd2lkdGhcIjogXCJnb29nQXZhaWxhYmxlU2VuZEJhbmR3aWR0aFwiLFxuICAgIFwicmVtb3RlQWRkcmVzc1wiOiBcImdvb2dSZW1vdGVBZGRyZXNzXCIsXG4gICAgXCJ0cmFuc3BvcnRUeXBlXCI6IFwiZ29vZ1RyYW5zcG9ydFR5cGVcIixcbiAgICBcImxvY2FsQWRkcmVzc1wiOiBcImdvb2dMb2NhbEFkZHJlc3NcIixcbiAgICBcImFjdGl2ZUNvbm5lY3Rpb25cIjogXCJnb29nQWN0aXZlQ29ubmVjdGlvblwiLFxuICAgIFwic3NyY1wiOiBcInNzcmNcIixcbiAgICBcInBhY2tldHNSZWNlaXZlZFwiOiBcInBhY2tldHNSZWNlaXZlZFwiLFxuICAgIFwicGFja2V0c1NlbnRcIjogXCJwYWNrZXRzU2VudFwiLFxuICAgIFwicGFja2V0c0xvc3RcIjogXCJwYWNrZXRzTG9zdFwiLFxuICAgIFwiYnl0ZXNSZWNlaXZlZFwiOiBcImJ5dGVzUmVjZWl2ZWRcIixcbiAgICBcImJ5dGVzU2VudFwiOiBcImJ5dGVzU2VudFwiLFxuICAgIFwiZ29vZ0ZyYW1lSGVpZ2h0UmVjZWl2ZWRcIjogXCJnb29nRnJhbWVIZWlnaHRSZWNlaXZlZFwiLFxuICAgIFwiZ29vZ0ZyYW1lV2lkdGhSZWNlaXZlZFwiOiBcImdvb2dGcmFtZVdpZHRoUmVjZWl2ZWRcIixcbiAgICBcImdvb2dGcmFtZUhlaWdodFNlbnRcIjogXCJnb29nRnJhbWVIZWlnaHRTZW50XCIsXG4gICAgXCJnb29nRnJhbWVXaWR0aFNlbnRcIjogXCJnb29nRnJhbWVXaWR0aFNlbnRcIixcbiAgICBcImF1ZGlvSW5wdXRMZXZlbFwiOiBcImF1ZGlvSW5wdXRMZXZlbFwiLFxuICAgIFwiYXVkaW9PdXRwdXRMZXZlbFwiOiBcImF1ZGlvT3V0cHV0TGV2ZWxcIlxufTtcblxuXG4vKipcbiAqIFN0YXRzIHByb2Nlc3NpbmcgbG9naWMuXG4gKi9cblN0YXRzQ29sbGVjdG9yLnByb3RvdHlwZS5wcm9jZXNzU3RhdHNSZXBvcnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgaWYgKCF0aGlzLmJhc2VsaW5lU3RhdHNSZXBvcnQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGZvciAodmFyIGlkeCBpbiB0aGlzLmN1cnJlbnRTdGF0c1JlcG9ydCkge1xuICAgICAgICB2YXIgbm93ID0gdGhpcy5jdXJyZW50U3RhdHNSZXBvcnRbaWR4XTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmIChnZXRTdGF0VmFsdWUobm93LCAncmVjZWl2ZUJhbmR3aWR0aCcpIHx8XG4gICAgICAgICAgICAgICAgZ2V0U3RhdFZhbHVlKG5vdywgJ3NlbmRCYW5kd2lkdGgnKSkge1xuICAgICAgICAgICAgICAgIFBlZXJTdGF0cy5iYW5kd2lkdGggPSB7XG4gICAgICAgICAgICAgICAgICAgIFwiZG93bmxvYWRcIjogTWF0aC5yb3VuZChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZ2V0U3RhdFZhbHVlKG5vdywgJ3JlY2VpdmVCYW5kd2lkdGgnKSkgLyAxMDAwKSxcbiAgICAgICAgICAgICAgICAgICAgXCJ1cGxvYWRcIjogTWF0aC5yb3VuZChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZ2V0U3RhdFZhbHVlKG5vdywgJ3NlbmRCYW5kd2lkdGgnKSkgLyAxMDAwKVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2goZSl7Lypub3Qgc3VwcG9ydGVkKi99XG5cbiAgICAgICAgaWYobm93LnR5cGUgPT0gJ2dvb2dDYW5kaWRhdGVQYWlyJylcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIGlwLCB0eXBlLCBsb2NhbElQLCBhY3RpdmU7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGlwID0gZ2V0U3RhdFZhbHVlKG5vdywgJ3JlbW90ZUFkZHJlc3MnKTtcbiAgICAgICAgICAgICAgICB0eXBlID0gZ2V0U3RhdFZhbHVlKG5vdywgXCJ0cmFuc3BvcnRUeXBlXCIpO1xuICAgICAgICAgICAgICAgIGxvY2FsSVAgPSBnZXRTdGF0VmFsdWUobm93LCBcImxvY2FsQWRkcmVzc1wiKTtcbiAgICAgICAgICAgICAgICBhY3RpdmUgPSBnZXRTdGF0VmFsdWUobm93LCBcImFjdGl2ZUNvbm5lY3Rpb25cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXRjaChlKXsvKm5vdCBzdXBwb3J0ZWQqL31cbiAgICAgICAgICAgIGlmKCFpcCB8fCAhdHlwZSB8fCAhbG9jYWxJUCB8fCBhY3RpdmUgIT0gXCJ0cnVlXCIpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB2YXIgYWRkcmVzc1NhdmVkID0gZmFsc2U7XG4gICAgICAgICAgICBmb3IodmFyIGkgPSAwOyBpIDwgUGVlclN0YXRzLnRyYW5zcG9ydC5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZihQZWVyU3RhdHMudHJhbnNwb3J0W2ldLmlwID09IGlwICYmXG4gICAgICAgICAgICAgICAgICAgIFBlZXJTdGF0cy50cmFuc3BvcnRbaV0udHlwZSA9PSB0eXBlICYmXG4gICAgICAgICAgICAgICAgICAgIFBlZXJTdGF0cy50cmFuc3BvcnRbaV0ubG9jYWxpcCA9PSBsb2NhbElQKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgYWRkcmVzc1NhdmVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZihhZGRyZXNzU2F2ZWQpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICBQZWVyU3RhdHMudHJhbnNwb3J0LnB1c2goe2xvY2FsaXA6IGxvY2FsSVAsIGlwOiBpcCwgdHlwZTogdHlwZX0pO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZihub3cudHlwZSA9PSBcImNhbmRpZGF0ZXBhaXJcIilcbiAgICAgICAge1xuICAgICAgICAgICAgaWYobm93LnN0YXRlID09IFwic3VjY2VlZGVkXCIpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgIHZhciBsb2NhbCA9IHRoaXMuY3VycmVudFN0YXRzUmVwb3J0W25vdy5sb2NhbENhbmRpZGF0ZUlkXTtcbiAgICAgICAgICAgIHZhciByZW1vdGUgPSB0aGlzLmN1cnJlbnRTdGF0c1JlcG9ydFtub3cucmVtb3RlQ2FuZGlkYXRlSWRdO1xuICAgICAgICAgICAgUGVlclN0YXRzLnRyYW5zcG9ydC5wdXNoKHtsb2NhbGlwOiBsb2NhbC5pcEFkZHJlc3MgKyBcIjpcIiArIGxvY2FsLnBvcnROdW1iZXIsXG4gICAgICAgICAgICAgICAgaXA6IHJlbW90ZS5pcEFkZHJlc3MgKyBcIjpcIiArIHJlbW90ZS5wb3J0TnVtYmVyLCB0eXBlOiBsb2NhbC50cmFuc3BvcnR9KTtcblxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG5vdy50eXBlICE9ICdzc3JjJyAmJiBub3cudHlwZSAhPSBcIm91dGJvdW5kcnRwXCIgJiZcbiAgICAgICAgICAgIG5vdy50eXBlICE9IFwiaW5ib3VuZHJ0cFwiKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBiZWZvcmUgPSB0aGlzLmJhc2VsaW5lU3RhdHNSZXBvcnRbaWR4XTtcbiAgICAgICAgaWYgKCFiZWZvcmUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihnZXRTdGF0VmFsdWUobm93LCAnc3NyYycpICsgJyBub3QgZW5vdWdoIGRhdGEnKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHNzcmMgPSBnZXRTdGF0VmFsdWUobm93LCAnc3NyYycpO1xuICAgICAgICBpZighc3NyYylcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB2YXIgamlkID0gQVBQLnhtcHAuZ2V0SmlkRnJvbVNTUkMoc3NyYyk7XG4gICAgICAgIGlmICghamlkICYmIChEYXRlLm5vdygpIC0gbm93LnRpbWVzdGFtcCkgPCAzMDAwKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJObyBqaWQgZm9yIHNzcmM6IFwiICsgc3NyYyk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBqaWRTdGF0cyA9IHRoaXMuamlkMnN0YXRzW2ppZF07XG4gICAgICAgIGlmICghamlkU3RhdHMpIHtcbiAgICAgICAgICAgIGppZFN0YXRzID0gbmV3IFBlZXJTdGF0cygpO1xuICAgICAgICAgICAgdGhpcy5qaWQyc3RhdHNbamlkXSA9IGppZFN0YXRzO1xuICAgICAgICB9XG5cblxuICAgICAgICB2YXIgaXNEb3dubG9hZFN0cmVhbSA9IHRydWU7XG4gICAgICAgIHZhciBrZXkgPSAncGFja2V0c1JlY2VpdmVkJztcbiAgICAgICAgaWYgKCFnZXRTdGF0VmFsdWUobm93LCBrZXkpKVxuICAgICAgICB7XG4gICAgICAgICAgICBpc0Rvd25sb2FkU3RyZWFtID0gZmFsc2U7XG4gICAgICAgICAgICBrZXkgPSAncGFja2V0c1NlbnQnO1xuICAgICAgICAgICAgaWYgKCFnZXRTdGF0VmFsdWUobm93LCBrZXkpKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIk5vIHBhY2tldHNSZWNlaXZlZCBub3IgcGFja2V0U2VudCBzdGF0IGZvdW5kXCIpO1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHZhciBwYWNrZXRzTm93ID0gZ2V0U3RhdFZhbHVlKG5vdywga2V5KTtcbiAgICAgICAgaWYoIXBhY2tldHNOb3cgfHwgcGFja2V0c05vdyA8IDApXG4gICAgICAgICAgICBwYWNrZXRzTm93ID0gMDtcblxuICAgICAgICB2YXIgcGFja2V0c0JlZm9yZSA9IGdldFN0YXRWYWx1ZShiZWZvcmUsIGtleSk7XG4gICAgICAgIGlmKCFwYWNrZXRzQmVmb3JlIHx8IHBhY2tldHNCZWZvcmUgPCAwKVxuICAgICAgICAgICAgcGFja2V0c0JlZm9yZSA9IDA7XG4gICAgICAgIHZhciBwYWNrZXRSYXRlID0gcGFja2V0c05vdyAtIHBhY2tldHNCZWZvcmU7XG4gICAgICAgIGlmKCFwYWNrZXRSYXRlIHx8IHBhY2tldFJhdGUgPCAwKVxuICAgICAgICAgICAgcGFja2V0UmF0ZSA9IDA7XG4gICAgICAgIHZhciBjdXJyZW50TG9zcyA9IGdldFN0YXRWYWx1ZShub3csICdwYWNrZXRzTG9zdCcpO1xuICAgICAgICBpZighY3VycmVudExvc3MgfHwgY3VycmVudExvc3MgPCAwKVxuICAgICAgICAgICAgY3VycmVudExvc3MgPSAwO1xuICAgICAgICB2YXIgcHJldmlvdXNMb3NzID0gZ2V0U3RhdFZhbHVlKGJlZm9yZSwgJ3BhY2tldHNMb3N0Jyk7XG4gICAgICAgIGlmKCFwcmV2aW91c0xvc3MgfHwgcHJldmlvdXNMb3NzIDwgMClcbiAgICAgICAgICAgIHByZXZpb3VzTG9zcyA9IDA7XG4gICAgICAgIHZhciBsb3NzUmF0ZSA9IGN1cnJlbnRMb3NzIC0gcHJldmlvdXNMb3NzO1xuICAgICAgICBpZighbG9zc1JhdGUgfHwgbG9zc1JhdGUgPCAwKVxuICAgICAgICAgICAgbG9zc1JhdGUgPSAwO1xuICAgICAgICB2YXIgcGFja2V0c1RvdGFsID0gKHBhY2tldFJhdGUgKyBsb3NzUmF0ZSk7XG5cbiAgICAgICAgamlkU3RhdHMuc2V0U3NyY0xvc3Moc3NyYyxcbiAgICAgICAgICAgIHtcInBhY2tldHNUb3RhbFwiOiBwYWNrZXRzVG90YWwsXG4gICAgICAgICAgICAgICAgXCJwYWNrZXRzTG9zdFwiOiBsb3NzUmF0ZSxcbiAgICAgICAgICAgICAgICBcImlzRG93bmxvYWRTdHJlYW1cIjogaXNEb3dubG9hZFN0cmVhbX0pO1xuXG5cbiAgICAgICAgdmFyIGJ5dGVzUmVjZWl2ZWQgPSAwLCBieXRlc1NlbnQgPSAwO1xuICAgICAgICBpZihnZXRTdGF0VmFsdWUobm93LCBcImJ5dGVzUmVjZWl2ZWRcIikpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ5dGVzUmVjZWl2ZWQgPSBnZXRTdGF0VmFsdWUobm93LCBcImJ5dGVzUmVjZWl2ZWRcIikgLVxuICAgICAgICAgICAgICAgIGdldFN0YXRWYWx1ZShiZWZvcmUsIFwiYnl0ZXNSZWNlaXZlZFwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKGdldFN0YXRWYWx1ZShub3csIFwiYnl0ZXNTZW50XCIpKVxuICAgICAgICB7XG4gICAgICAgICAgICBieXRlc1NlbnQgPSBnZXRTdGF0VmFsdWUobm93LCBcImJ5dGVzU2VudFwiKSAtXG4gICAgICAgICAgICAgICAgZ2V0U3RhdFZhbHVlKGJlZm9yZSwgXCJieXRlc1NlbnRcIik7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgdGltZSA9IE1hdGgucm91bmQoKG5vdy50aW1lc3RhbXAgLSBiZWZvcmUudGltZXN0YW1wKSAvIDEwMDApO1xuICAgICAgICBpZihieXRlc1JlY2VpdmVkIDw9IDAgfHwgdGltZSA8PSAwKVxuICAgICAgICB7XG4gICAgICAgICAgICBieXRlc1JlY2VpdmVkID0gMDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ5dGVzUmVjZWl2ZWQgPSBNYXRoLnJvdW5kKCgoYnl0ZXNSZWNlaXZlZCAqIDgpIC8gdGltZSkgLyAxMDAwKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKGJ5dGVzU2VudCA8PSAwIHx8IHRpbWUgPD0gMClcbiAgICAgICAge1xuICAgICAgICAgICAgYnl0ZXNTZW50ID0gMDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ5dGVzU2VudCA9IE1hdGgucm91bmQoKChieXRlc1NlbnQgKiA4KSAvIHRpbWUpIC8gMTAwMCk7XG4gICAgICAgIH1cblxuICAgICAgICBqaWRTdGF0cy5zZXRTc3JjQml0cmF0ZShzc3JjLCB7XG4gICAgICAgICAgICBcImRvd25sb2FkXCI6IGJ5dGVzUmVjZWl2ZWQsXG4gICAgICAgICAgICBcInVwbG9hZFwiOiBieXRlc1NlbnR9KTtcblxuICAgICAgICB2YXIgcmVzb2x1dGlvbiA9IHtoZWlnaHQ6IG51bGwsIHdpZHRoOiBudWxsfTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmIChnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZUhlaWdodFJlY2VpdmVkXCIpICYmXG4gICAgICAgICAgICAgICAgZ2V0U3RhdFZhbHVlKG5vdywgXCJnb29nRnJhbWVXaWR0aFJlY2VpdmVkXCIpKSB7XG4gICAgICAgICAgICAgICAgcmVzb2x1dGlvbi5oZWlnaHQgPSBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZUhlaWdodFJlY2VpdmVkXCIpO1xuICAgICAgICAgICAgICAgIHJlc29sdXRpb24ud2lkdGggPSBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZVdpZHRoUmVjZWl2ZWRcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIGlmIChnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZUhlaWdodFNlbnRcIikgJiZcbiAgICAgICAgICAgICAgICBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZVdpZHRoU2VudFwiKSkge1xuICAgICAgICAgICAgICAgIHJlc29sdXRpb24uaGVpZ2h0ID0gZ2V0U3RhdFZhbHVlKG5vdywgXCJnb29nRnJhbWVIZWlnaHRTZW50XCIpO1xuICAgICAgICAgICAgICAgIHJlc29sdXRpb24ud2lkdGggPSBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZVdpZHRoU2VudFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjYXRjaChlKXsvKm5vdCBzdXBwb3J0ZWQqL31cblxuICAgICAgICBpZihyZXNvbHV0aW9uLmhlaWdodCAmJiByZXNvbHV0aW9uLndpZHRoKVxuICAgICAgICB7XG4gICAgICAgICAgICBqaWRTdGF0cy5zZXRTc3JjUmVzb2x1dGlvbihzc3JjLCByZXNvbHV0aW9uKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGppZFN0YXRzLnNldFNzcmNSZXNvbHV0aW9uKHNzcmMsIG51bGwpO1xuICAgICAgICB9XG5cblxuICAgIH1cblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBKaWQgc3RhdHNcbiAgICB2YXIgdG90YWxQYWNrZXRzID0ge2Rvd25sb2FkOiAwLCB1cGxvYWQ6IDB9O1xuICAgIHZhciBsb3N0UGFja2V0cyA9IHtkb3dubG9hZDogMCwgdXBsb2FkOiAwfTtcbiAgICB2YXIgYml0cmF0ZURvd25sb2FkID0gMDtcbiAgICB2YXIgYml0cmF0ZVVwbG9hZCA9IDA7XG4gICAgdmFyIHJlc29sdXRpb25zID0ge307XG4gICAgT2JqZWN0LmtleXModGhpcy5qaWQyc3RhdHMpLmZvckVhY2goXG4gICAgICAgIGZ1bmN0aW9uIChqaWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJMb3NzKS5mb3JFYWNoKFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzc3JjKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHR5cGUgPSBcInVwbG9hZFwiO1xuICAgICAgICAgICAgICAgICAgICBpZihzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyTG9zc1tzc3JjXS5pc0Rvd25sb2FkU3RyZWFtKVxuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9IFwiZG93bmxvYWRcIjtcbiAgICAgICAgICAgICAgICAgICAgdG90YWxQYWNrZXRzW3R5cGVdICs9XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyTG9zc1tzc3JjXS5wYWNrZXRzVG90YWw7XG4gICAgICAgICAgICAgICAgICAgIGxvc3RQYWNrZXRzW3R5cGVdICs9XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyTG9zc1tzc3JjXS5wYWNrZXRzTG9zdDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgT2JqZWN0LmtleXMoc2VsZi5qaWQyc3RhdHNbamlkXS5zc3JjMmJpdHJhdGUpLmZvckVhY2goXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgYml0cmF0ZURvd25sb2FkICs9XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyYml0cmF0ZVtzc3JjXS5kb3dubG9hZDtcbiAgICAgICAgICAgICAgICAgICAgYml0cmF0ZVVwbG9hZCArPVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5qaWQyc3RhdHNbamlkXS5zc3JjMmJpdHJhdGVbc3NyY10udXBsb2FkO1xuXG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyYml0cmF0ZVtzc3JjXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmVzb2x1dGlvbnNbamlkXSA9IHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJyZXNvbHV0aW9uO1xuICAgICAgICB9XG4gICAgKTtcblxuICAgIFBlZXJTdGF0cy5iaXRyYXRlID0ge1widXBsb2FkXCI6IGJpdHJhdGVVcGxvYWQsIFwiZG93bmxvYWRcIjogYml0cmF0ZURvd25sb2FkfTtcblxuICAgIFBlZXJTdGF0cy5wYWNrZXRMb3NzID0ge1xuICAgICAgICB0b3RhbDpcbiAgICAgICAgICAgIGNhbGN1bGF0ZVBhY2tldExvc3MobG9zdFBhY2tldHMuZG93bmxvYWQgKyBsb3N0UGFja2V0cy51cGxvYWQsXG4gICAgICAgICAgICAgICAgICAgIHRvdGFsUGFja2V0cy5kb3dubG9hZCArIHRvdGFsUGFja2V0cy51cGxvYWQpLFxuICAgICAgICBkb3dubG9hZDpcbiAgICAgICAgICAgIGNhbGN1bGF0ZVBhY2tldExvc3MobG9zdFBhY2tldHMuZG93bmxvYWQsIHRvdGFsUGFja2V0cy5kb3dubG9hZCksXG4gICAgICAgIHVwbG9hZDpcbiAgICAgICAgICAgIGNhbGN1bGF0ZVBhY2tldExvc3MobG9zdFBhY2tldHMudXBsb2FkLCB0b3RhbFBhY2tldHMudXBsb2FkKVxuICAgIH07XG4gICAgdGhpcy5ldmVudEVtaXR0ZXIuZW1pdChcInN0YXRpc3RpY3MuY29ubmVjdGlvbnN0YXRzXCIsXG4gICAgICAgIHtcbiAgICAgICAgICAgIFwiYml0cmF0ZVwiOiBQZWVyU3RhdHMuYml0cmF0ZSxcbiAgICAgICAgICAgIFwicGFja2V0TG9zc1wiOiBQZWVyU3RhdHMucGFja2V0TG9zcyxcbiAgICAgICAgICAgIFwiYmFuZHdpZHRoXCI6IFBlZXJTdGF0cy5iYW5kd2lkdGgsXG4gICAgICAgICAgICBcInJlc29sdXRpb25cIjogcmVzb2x1dGlvbnMsXG4gICAgICAgICAgICBcInRyYW5zcG9ydFwiOiBQZWVyU3RhdHMudHJhbnNwb3J0XG4gICAgICAgIH0pO1xuICAgIFBlZXJTdGF0cy50cmFuc3BvcnQgPSBbXTtcblxufTtcblxuLyoqXG4gKiBTdGF0cyBwcm9jZXNzaW5nIGxvZ2ljLlxuICovXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUucHJvY2Vzc0F1ZGlvTGV2ZWxSZXBvcnQgPSBmdW5jdGlvbiAoKVxue1xuICAgIGlmICghdGhpcy5iYXNlbGluZUF1ZGlvTGV2ZWxzUmVwb3J0KVxuICAgIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGZvciAodmFyIGlkeCBpbiB0aGlzLmN1cnJlbnRBdWRpb0xldmVsc1JlcG9ydClcbiAgICB7XG4gICAgICAgIHZhciBub3cgPSB0aGlzLmN1cnJlbnRBdWRpb0xldmVsc1JlcG9ydFtpZHhdO1xuXG4gICAgICAgIGlmIChub3cudHlwZSAhPSAnc3NyYycpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGJlZm9yZSA9IHRoaXMuYmFzZWxpbmVBdWRpb0xldmVsc1JlcG9ydFtpZHhdO1xuICAgICAgICBpZiAoIWJlZm9yZSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGdldFN0YXRWYWx1ZShub3csICdzc3JjJykgKyAnIG5vdCBlbm91Z2ggZGF0YScpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc3NyYyA9IGdldFN0YXRWYWx1ZShub3csICdzc3JjJyk7XG4gICAgICAgIHZhciBqaWQgPSBBUFAueG1wcC5nZXRKaWRGcm9tU1NSQyhzc3JjKTtcbiAgICAgICAgaWYgKCFqaWQgJiYgKERhdGUubm93KCkgLSBub3cudGltZXN0YW1wKSA8IDMwMDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIk5vIGppZCBmb3Igc3NyYzogXCIgKyBzc3JjKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGppZFN0YXRzID0gdGhpcy5qaWQyc3RhdHNbamlkXTtcbiAgICAgICAgaWYgKCFqaWRTdGF0cylcbiAgICAgICAge1xuICAgICAgICAgICAgamlkU3RhdHMgPSBuZXcgUGVlclN0YXRzKCk7XG4gICAgICAgICAgICB0aGlzLmppZDJzdGF0c1tqaWRdID0gamlkU3RhdHM7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBdWRpbyBsZXZlbFxuICAgICAgICB2YXIgYXVkaW9MZXZlbCA9IG51bGw7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF1ZGlvTGV2ZWwgPSBnZXRTdGF0VmFsdWUobm93LCAnYXVkaW9JbnB1dExldmVsJyk7XG4gICAgICAgICAgICBpZiAoIWF1ZGlvTGV2ZWwpXG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbCA9IGdldFN0YXRWYWx1ZShub3csICdhdWRpb091dHB1dExldmVsJyk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2goZSkgey8qbm90IHN1cHBvcnRlZCovXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJBdWRpbyBMZXZlbHMgYXJlIG5vdCBhdmFpbGFibGUgaW4gdGhlIHN0YXRpc3RpY3MuXCIpO1xuICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYXVkaW9MZXZlbClcbiAgICAgICAge1xuICAgICAgICAgICAgLy8gVE9ETzogY2FuJ3QgZmluZCBzcGVjcyBhYm91dCB3aGF0IHRoaXMgdmFsdWUgcmVhbGx5IGlzLFxuICAgICAgICAgICAgLy8gYnV0IGl0IHNlZW1zIHRvIHZhcnkgYmV0d2VlbiAwIGFuZCBhcm91bmQgMzJrLlxuICAgICAgICAgICAgYXVkaW9MZXZlbCA9IGF1ZGlvTGV2ZWwgLyAzMjc2NztcbiAgICAgICAgICAgIGppZFN0YXRzLnNldFNzcmNBdWRpb0xldmVsKHNzcmMsIGF1ZGlvTGV2ZWwpO1xuICAgICAgICAgICAgaWYoamlkICE9IEFQUC54bXBwLm15SmlkKCkpXG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEVtaXR0ZXIuZW1pdChcInN0YXRpc3RpY3MuYXVkaW9MZXZlbFwiLCBqaWQsIGF1ZGlvTGV2ZWwpO1xuICAgICAgICB9XG5cbiAgICB9XG5cblxufTtcbiIsIi8qKlxuICogQ3JlYXRlZCBieSBocmlzdG8gb24gOC80LzE0LlxuICovXG52YXIgTG9jYWxTdGF0cyA9IHJlcXVpcmUoXCIuL0xvY2FsU3RhdHNDb2xsZWN0b3IuanNcIik7XG52YXIgUlRQU3RhdHMgPSByZXF1aXJlKFwiLi9SVFBTdGF0c0NvbGxlY3Rvci5qc1wiKTtcbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFN0cmVhbUV2ZW50VHlwZXMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvU3RyZWFtRXZlbnRUeXBlcy5qc1wiKTtcbnZhciBYTVBQRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UveG1wcC9YTVBQRXZlbnRzXCIpO1xuXG52YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG52YXIgbG9jYWxTdGF0cyA9IG51bGw7XG5cbnZhciBydHBTdGF0cyA9IG51bGw7XG5cbmZ1bmN0aW9uIHN0b3BMb2NhbCgpXG57XG4gICAgaWYobG9jYWxTdGF0cylcbiAgICB7XG4gICAgICAgIGxvY2FsU3RhdHMuc3RvcCgpO1xuICAgICAgICBsb2NhbFN0YXRzID0gbnVsbDtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHN0b3BSZW1vdGUoKVxue1xuICAgIGlmKHJ0cFN0YXRzKVxuICAgIHtcbiAgICAgICAgcnRwU3RhdHMuc3RvcCgpO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChcInN0YXRpc3RpY3Muc3RvcFwiKTtcbiAgICAgICAgcnRwU3RhdHMgPSBudWxsO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc3RhcnRSZW1vdGVTdGF0cyAocGVlcmNvbm5lY3Rpb24pIHtcbiAgICBpZihydHBTdGF0cylcbiAgICB7XG4gICAgICAgIHJ0cFN0YXRzLnN0b3AoKTtcbiAgICAgICAgcnRwU3RhdHMgPSBudWxsO1xuICAgIH1cblxuICAgIHJ0cFN0YXRzID0gbmV3IFJUUFN0YXRzKHBlZXJjb25uZWN0aW9uLCAyMDAsIDIwMDAsIGV2ZW50RW1pdHRlcik7XG4gICAgcnRwU3RhdHMuc3RhcnQoKTtcbn1cblxuZnVuY3Rpb24gb25TdHJlYW1DcmVhdGVkKHN0cmVhbSlcbntcbiAgICBpZihzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKS5nZXRBdWRpb1RyYWNrcygpLmxlbmd0aCA9PT0gMClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgbG9jYWxTdGF0cyA9IG5ldyBMb2NhbFN0YXRzKHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLCAyMDAsIHN0YXRpc3RpY3MsXG4gICAgICAgIGV2ZW50RW1pdHRlcik7XG4gICAgbG9jYWxTdGF0cy5zdGFydCgpO1xufVxuXG5mdW5jdGlvbiBvbkRpc3Bvc2VDb25mZXJlbmNlKG9uVW5sb2FkKSB7XG4gICAgc3RvcFJlbW90ZSgpO1xuICAgIGlmKG9uVW5sb2FkKSB7XG4gICAgICAgIHN0b3BMb2NhbCgpO1xuICAgICAgICBldmVudEVtaXR0ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgfVxufVxuXG5cbnZhciBzdGF0aXN0aWNzID1cbntcbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgdGhhdCB0aGlzIGF1ZGlvIGxldmVsIGlzIGZvciBsb2NhbCBqaWQuXG4gICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgKi9cbiAgICBMT0NBTF9KSUQ6ICdsb2NhbCcsXG5cbiAgICBhZGRBdWRpb0xldmVsTGlzdGVuZXI6IGZ1bmN0aW9uKGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKFwic3RhdGlzdGljcy5hdWRpb0xldmVsXCIsIGxpc3RlbmVyKTtcbiAgICB9LFxuXG4gICAgcmVtb3ZlQXVkaW9MZXZlbExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcihcInN0YXRpc3RpY3MuYXVkaW9MZXZlbFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIGFkZENvbm5lY3Rpb25TdGF0c0xpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbihcInN0YXRpc3RpY3MuY29ubmVjdGlvbnN0YXRzXCIsIGxpc3RlbmVyKTtcbiAgICB9LFxuXG4gICAgcmVtb3ZlQ29ubmVjdGlvblN0YXRzTGlzdGVuZXI6IGZ1bmN0aW9uKGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKFwic3RhdGlzdGljcy5jb25uZWN0aW9uc3RhdHNcIiwgbGlzdGVuZXIpO1xuICAgIH0sXG5cblxuICAgIGFkZFJlbW90ZVN0YXRzU3RvcExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbihcInN0YXRpc3RpY3Muc3RvcFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIHJlbW92ZVJlbW90ZVN0YXRzU3RvcExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcihcInN0YXRpc3RpY3Muc3RvcFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIHN0b3A6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgc3RvcExvY2FsKCk7XG4gICAgICAgIHN0b3BSZW1vdGUoKTtcbiAgICAgICAgaWYoZXZlbnRFbWl0dGVyKVxuICAgICAgICB7XG4gICAgICAgICAgICBldmVudEVtaXR0ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc3RvcFJlbW90ZVN0YXRpc3RpY3M6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIHN0b3BSZW1vdGUoKTtcbiAgICB9LFxuXG4gICAgc3RhcnQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihvblN0cmVhbUNyZWF0ZWQsXG4gICAgICAgICAgICBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ1JFQVRFRCk7XG4gICAgICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuRElTUE9TRV9DT05GRVJFTkNFLCBvbkRpc3Bvc2VDb25mZXJlbmNlKTtcbiAgICAgICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5DQUxMX0lOQ09NSU5HLCBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIHN0YXJ0UmVtb3RlU3RhdHMoZXZlbnQucGVlcmNvbm5lY3Rpb24pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbn07XG5cblxuXG5cbm1vZHVsZS5leHBvcnRzID0gc3RhdGlzdGljczsiLCJ2YXIgaTE4biA9IHJlcXVpcmUoXCJpMThuZXh0LWNsaWVudFwiKTtcbnZhciBsYW5ndWFnZXMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS90cmFuc2xhdGlvbi9sYW5ndWFnZXNcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi4vc2V0dGluZ3MvU2V0dGluZ3NcIik7XG52YXIgREVGQVVMVF9MQU5HID0gbGFuZ3VhZ2VzLkVOO1xuXG5pMThuLmFkZFBvc3RQcm9jZXNzb3IoXCJyZXNvbHZlQXBwTmFtZVwiLCBmdW5jdGlvbih2YWx1ZSwga2V5LCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIHZhbHVlLnJlcGxhY2UoXCJfX2FwcF9fXCIsIGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRSk7XG59KTtcblxuXG5cbnZhciBkZWZhdWx0T3B0aW9ucyA9IHtcbiAgICBkZXRlY3RMbmdRUzogXCJsYW5nXCIsXG4gICAgdXNlQ29va2llOiBmYWxzZSxcbiAgICBmYWxsYmFja0xuZzogREVGQVVMVF9MQU5HLFxuICAgIGxvYWQ6IFwidW5zcGVjaWZpY1wiLFxuICAgIHJlc0dldFBhdGg6ICdsYW5nL19fbnNfXy1fX2xuZ19fLmpzb24nLFxuICAgIG5zOiB7XG4gICAgICAgIG5hbWVzcGFjZXM6IFsnbWFpbicsICdsYW5ndWFnZXMnXSxcbiAgICAgICAgZGVmYXVsdE5zOiAnbWFpbidcbiAgICB9LFxuICAgIGxuZ1doaXRlbGlzdCA6IGxhbmd1YWdlcy5nZXRMYW5ndWFnZXMoKSxcbiAgICBmYWxsYmFja09uTnVsbDogdHJ1ZSxcbiAgICBmYWxsYmFja09uRW1wdHk6IHRydWUsXG4gICAgdXNlRGF0YUF0dHJPcHRpb25zOiB0cnVlLFxuICAgIGRlZmF1bHRWYWx1ZUZyb21Db250ZW50OiBmYWxzZSxcbiAgICBhcHA6IGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRSxcbiAgICBnZXRBc3luYzogZmFsc2UsXG4gICAgZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQ6IGZhbHNlLFxuICAgIGN1c3RvbUxvYWQ6IGZ1bmN0aW9uKGxuZywgbnMsIG9wdGlvbnMsIGRvbmUpIHtcbiAgICAgICAgdmFyIHJlc1BhdGggPSBcImxhbmcvX19uc19fLV9fbG5nX18uanNvblwiO1xuICAgICAgICBpZihsbmcgPT09IGxhbmd1YWdlcy5FTilcbiAgICAgICAgICAgIHJlc1BhdGggPSBcImxhbmcvX19uc19fLmpzb25cIjtcbiAgICAgICAgdmFyIHVybCA9IGkxOG4uZnVuY3Rpb25zLmFwcGx5UmVwbGFjZW1lbnQocmVzUGF0aCwgeyBsbmc6IGxuZywgbnM6IG5zIH0pO1xuICAgICAgICBpMThuLmZ1bmN0aW9ucy5hamF4KHtcbiAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCB4aHIpIHtcbiAgICAgICAgICAgICAgICBpMThuLmZ1bmN0aW9ucy5sb2coJ2xvYWRlZDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgZG9uZShudWxsLCBkYXRhKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBlcnJvciA6IGZ1bmN0aW9uKHhociwgc3RhdHVzLCBlcnJvcikge1xuICAgICAgICAgICAgICAgIGlmICgoc3RhdHVzICYmIHN0YXR1cyA9PSAyMDApIHx8XG4gICAgICAgICAgICAgICAgICAgICh4aHIgJiYgeGhyLnN0YXR1cyAmJiB4aHIuc3RhdHVzID09IDIwMCkpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gZmlsZSBsb2FkZWQgYnV0IGludmFsaWQganNvbiwgc3RvcCB3YXN0ZSB0aW1lICFcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMuZXJyb3IoJ1RoZXJlIGlzIGEgdHlwbyBpbjogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICgoc3RhdHVzICYmIHN0YXR1cyA9PSA0MDQpIHx8XG4gICAgICAgICAgICAgICAgICAgICh4aHIgJiYgeGhyLnN0YXR1cyAmJiB4aHIuc3RhdHVzID09IDQwNCkpIHtcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMubG9nKCdEb2VzIG5vdCBleGlzdDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHRoZVN0YXR1cyA9IHN0YXR1cyA/IHN0YXR1cyA6XG4gICAgICAgICAgICAgICAgICAgICAgICAoKHhociAmJiB4aHIuc3RhdHVzKSA/IHhoci5zdGF0dXMgOiBudWxsKTtcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMubG9nKHRoZVN0YXR1cyArICcgd2hlbiBsb2FkaW5nICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHt9KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkYXRhVHlwZTogXCJqc29uXCIsXG4gICAgICAgICAgICBhc3luYyA6IG9wdGlvbnMuZ2V0QXN5bmNcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8vICAgICAgICAgICAgICBvcHRpb25zIGZvciBjYWNoaW5nXG4vLyAgICAgICAgICAgICAgICB1c2VMb2NhbFN0b3JhZ2U6IHRydWUsXG4vLyAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2VFeHBpcmF0aW9uVGltZTogODY0MDAwMDAgLy8gaW4gbXMsIGRlZmF1bHQgMSB3ZWVrXG59O1xuXG5mdW5jdGlvbiBpbml0Q29tcGxldGVkKHQpXG57XG4gICAgJChcIltkYXRhLWkxOG5dXCIpLmkxOG4oKTtcbn1cblxuZnVuY3Rpb24gY2hlY2tGb3JQYXJhbWV0ZXIoKSB7XG4gICAgdmFyIHF1ZXJ5ID0gd2luZG93LmxvY2F0aW9uLnNlYXJjaC5zdWJzdHJpbmcoMSk7XG4gICAgdmFyIHZhcnMgPSBxdWVyeS5zcGxpdChcIiZcIik7XG4gICAgZm9yICh2YXIgaT0wO2k8dmFycy5sZW5ndGg7aSsrKSB7XG4gICAgICAgIHZhciBwYWlyID0gdmFyc1tpXS5zcGxpdChcIj1cIik7XG4gICAgICAgIGlmKHBhaXJbMF0gPT0gXCJsYW5nXCIpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJldHVybiBwYWlyWzFdO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBpbml0OiBmdW5jdGlvbiAobGFuZykge1xuICAgICAgICB2YXIgb3B0aW9ucyA9IGRlZmF1bHRPcHRpb25zO1xuXG5cbiAgICAgICAgaWYoIWxhbmcpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGxhbmcgPSBjaGVja0ZvclBhcmFtZXRlcigpO1xuICAgICAgICAgICAgaWYoIWxhbmcpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICAgICAgICAgICAgICBpZihzZXR0aW5ncylcbiAgICAgICAgICAgICAgICAgICAgbGFuZyA9IHNldHRpbmdzLmxhbmd1YWdlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYobGFuZykge1xuICAgICAgICAgICAgb3B0aW9ucy5sbmcgPSBsYW5nO1xuICAgICAgICB9XG5cbiAgICAgICAgaTE4bi5pbml0KG9wdGlvbnMsIGluaXRDb21wbGV0ZWQpO1xuICAgIH0sXG4gICAgdHJhbnNsYXRlU3RyaW5nOiBmdW5jdGlvbiAoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiBpMThuLnQoa2V5LCBvcHRpb25zKTtcbiAgICB9LFxuICAgIHNldExhbmd1YWdlOiBmdW5jdGlvbiAobGFuZykge1xuICAgICAgICBpZighbGFuZylcbiAgICAgICAgICAgIGxhbmcgPSBERUZBVUxUX0xBTkc7XG4gICAgICAgIGkxOG4uc2V0TG5nKGxhbmcsIGRlZmF1bHRPcHRpb25zLCBpbml0Q29tcGxldGVkKTtcbiAgICB9LFxuICAgIGdldEN1cnJlbnRMYW5ndWFnZTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gaTE4bi5sbmcoKTtcbiAgICB9LFxuICAgIHRyYW5zbGF0ZUVsZW1lbnQ6IGZ1bmN0aW9uIChzZWxlY3Rvcikge1xuICAgICAgICBzZWxlY3Rvci5pMThuKCk7XG4gICAgfSxcbiAgICBnZW5lcmF0ZVRyYW5zbGF0b25IVE1MOiBmdW5jdGlvbiAoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIHZhciBzdHIgPSBcIjxzcGFuIGRhdGEtaTE4bj1cXFwiXCIgKyBrZXkgKyBcIlxcXCJcIjtcbiAgICAgICAgaWYob3B0aW9ucylcbiAgICAgICAge1xuICAgICAgICAgICAgc3RyICs9IFwiIGRhdGEtaTE4bi1vcHRpb25zPVxcXCJcIiArIEpTT04uc3RyaW5naWZ5KG9wdGlvbnMpICsgXCJcXFwiXCI7XG4gICAgICAgIH1cbiAgICAgICAgc3RyICs9IFwiPlwiO1xuICAgICAgICBzdHIgKz0gdGhpcy50cmFuc2xhdGVTdHJpbmcoa2V5LCBvcHRpb25zKTtcbiAgICAgICAgc3RyICs9IFwiPC9zcGFuPlwiO1xuICAgICAgICByZXR1cm4gc3RyO1xuXG4gICAgfVxufTtcbiIsIi8qIGpzaGludCAtVzExNyAqL1xudmFyIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uID0gcmVxdWlyZShcIi4vVHJhY2VhYmxlUGVlckNvbm5lY3Rpb25cIik7XG52YXIgU0RQRGlmZmVyID0gcmVxdWlyZShcIi4vU0RQRGlmZmVyXCIpO1xudmFyIFNEUFV0aWwgPSByZXF1aXJlKFwiLi9TRFBVdGlsXCIpO1xudmFyIFNEUCA9IHJlcXVpcmUoXCIuL1NEUFwiKTtcbnZhciBSVENCcm93c2VyVHlwZSA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENCcm93c2VyVHlwZVwiKTtcblxuLy8gSmluZ2xlIHN0dWZmXG5mdW5jdGlvbiBKaW5nbGVTZXNzaW9uKG1lLCBzaWQsIGNvbm5lY3Rpb24sIHNlcnZpY2UpIHtcbiAgICB0aGlzLm1lID0gbWU7XG4gICAgdGhpcy5zaWQgPSBzaWQ7XG4gICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcbiAgICB0aGlzLmluaXRpYXRvciA9IG51bGw7XG4gICAgdGhpcy5yZXNwb25kZXIgPSBudWxsO1xuICAgIHRoaXMuaXNJbml0aWF0b3IgPSBudWxsO1xuICAgIHRoaXMucGVlcmppZCA9IG51bGw7XG4gICAgdGhpcy5zdGF0ZSA9IG51bGw7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG51bGw7XG4gICAgdGhpcy5yZW1vdGVTRFAgPSBudWxsO1xuICAgIHRoaXMucmVsYXllZFN0cmVhbXMgPSBbXTtcbiAgICB0aGlzLnN0YXJ0VGltZSA9IG51bGw7XG4gICAgdGhpcy5zdG9wVGltZSA9IG51bGw7XG4gICAgdGhpcy5tZWRpYV9jb25zdHJhaW50cyA9IG51bGw7XG4gICAgdGhpcy5wY19jb25zdHJhaW50cyA9IG51bGw7XG4gICAgdGhpcy5pY2VfY29uZmlnID0ge307XG4gICAgdGhpcy5kcmlwX2NvbnRhaW5lciA9IFtdO1xuICAgIHRoaXMuc2VydmljZSA9IHNlcnZpY2U7XG5cbiAgICB0aGlzLnVzZXRyaWNrbGUgPSB0cnVlO1xuICAgIHRoaXMudXNlcHJhbnN3ZXIgPSBmYWxzZTsgLy8gZWFybHkgdHJhbnNwb3J0IHdhcm11cCAtLSBtaW5kIHlvdSwgdGhpcyBtaWdodCBmYWlsLiBkZXBlbmRzIG9uIHdlYnJ0YyBpc3N1ZSAxNzE4XG4gICAgdGhpcy51c2VkcmlwID0gZmFsc2U7IC8vIGRyaXBwaW5nIGlzIHNlbmRpbmcgdHJpY2tsZSBjYW5kaWRhdGVzIG5vdCBvbmUtYnktb25lXG5cbiAgICB0aGlzLmhhZHN0dW5jYW5kaWRhdGUgPSBmYWxzZTtcbiAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSBmYWxzZTtcbiAgICB0aGlzLmxhc3RpY2VjYW5kaWRhdGUgPSBmYWxzZTtcblxuICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG5cbiAgICB0aGlzLnJlYXNvbiA9IG51bGw7XG5cbiAgICB0aGlzLmFkZHNzcmMgPSBbXTtcbiAgICB0aGlzLnJlbW92ZXNzcmMgPSBbXTtcbiAgICB0aGlzLnBlbmRpbmdvcCA9IG51bGw7XG4gICAgdGhpcy5zd2l0Y2hzdHJlYW1zID0gZmFsc2U7XG5cbiAgICB0aGlzLndhaXQgPSB0cnVlO1xuICAgIHRoaXMubG9jYWxTdHJlYW1zU1NSQyA9IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgaW5kaWNhdG9yIHdoaWNoIGRldGVybWluZXMgd2hldGhlciB0aGUgKGxvY2FsKSB2aWRlbyBoYXMgYmVlbiBtdXRlZFxuICAgICAqIGluIHJlc3BvbnNlIHRvIGEgdXNlciBjb21tYW5kIGluIGNvbnRyYXN0IHRvIGFuIGF1dG9tYXRpYyBkZWNpc2lvbiBtYWRlXG4gICAgICogYnkgdGhlIGFwcGxpY2F0aW9uIGxvZ2ljLlxuICAgICAqL1xuICAgIHRoaXMudmlkZW9NdXRlQnlVc2VyID0gZmFsc2U7XG59XG5cbi8vVE9ETzogdGhpcyBhcnJheSBtdXN0IGJlIHJlbW92ZWQgd2hlbiBmaXJlZm94IGltcGxlbWVudCBtdWx0aXN0cmVhbSBzdXBwb3J0XG5KaW5nbGVTZXNzaW9uLm5vdFJlY2VpdmVkU1NSQ3MgPSBbXTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuaW5pdGlhdGUgPSBmdW5jdGlvbiAocGVlcmppZCwgaXNJbml0aWF0b3IpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09IG51bGwpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignYXR0ZW1wdCB0byBpbml0aWF0ZSBvbiBzZXNzaW9uICcgKyB0aGlzLnNpZCArXG4gICAgICAgICAgICAnaW4gc3RhdGUgJyArIHRoaXMuc3RhdGUpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuaXNJbml0aWF0b3IgPSBpc0luaXRpYXRvcjtcbiAgICB0aGlzLnN0YXRlID0gJ3BlbmRpbmcnO1xuICAgIHRoaXMuaW5pdGlhdG9yID0gaXNJbml0aWF0b3IgPyB0aGlzLm1lIDogcGVlcmppZDtcbiAgICB0aGlzLnJlc3BvbmRlciA9ICFpc0luaXRpYXRvciA/IHRoaXMubWUgOiBwZWVyamlkO1xuICAgIHRoaXMucGVlcmppZCA9IHBlZXJqaWQ7XG4gICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0aWNlY2FuZGlkYXRlID0gZmFsc2U7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uXG4gICAgICAgID0gbmV3IFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5pY2VfY29uZmlnLFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cyApO1xuXG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmljZWNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoZXZlbnQuY2FuZGlkYXRlKTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25hZGRzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJSRU1PVEUgU1RSRUFNIEFEREVEOiBcIiArIGV2ZW50LnN0cmVhbSArIFwiIC0gXCIgKyBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGV2ZW50KTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25yZW1vdmVzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgLy8gUmVtb3ZlIHRoZSBzdHJlYW0gZnJvbSByZW1vdGVTdHJlYW1zXG4gICAgICAgIC8vIEZJWE1FOiByZW1vdGVzdHJlYW1yZW1vdmVkLmppbmdsZSBub3QgZGVmaW5lZCBhbnl3aGVyZSh1bnVzZWQpXG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3JlbW90ZXN0cmVhbXJlbW92ZWQuamluZ2xlJywgW2V2ZW50LCBzZWxmLnNpZF0pO1xuICAgIH07XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGlmICghKHNlbGYgJiYgc2VsZi5wZWVyY29ubmVjdGlvbikpIHJldHVybjtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgaWYgKCEoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uKSkgcmV0dXJuO1xuICAgICAgICBzd2l0Y2ggKHNlbGYucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgICAgICBjYXNlICdjb25uZWN0ZWQnOlxuICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRUaW1lID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Rpc2Nvbm5lY3RlZCc6XG4gICAgICAgICAgICAgICAgdGhpcy5zdG9wVGltZSA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgb25JY2VDb25uZWN0aW9uU3RhdGVDaGFuZ2Uoc2VsZi5zaWQsIHNlbGYpO1xuICAgIH07XG4gICAgLy8gYWRkIGFueSBsb2NhbCBhbmQgcmVsYXllZCBzdHJlYW1cbiAgICBBUFAuUlRDLmxvY2FsU3RyZWFtcy5mb3JFYWNoKGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmFkZFN0cmVhbShzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKSk7XG4gICAgfSk7XG4gICAgdGhpcy5yZWxheWVkU3RyZWFtcy5mb3JFYWNoKGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmFkZFN0cmVhbShzdHJlYW0pO1xuICAgIH0pO1xufTtcblxuZnVuY3Rpb24gb25JY2VDb25uZWN0aW9uU3RhdGVDaGFuZ2Uoc2lkLCBzZXNzaW9uKSB7XG4gICAgc3dpdGNoIChzZXNzaW9uLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSkge1xuICAgICAgICBjYXNlICdjaGVja2luZyc6XG4gICAgICAgICAgICBzZXNzaW9uLnRpbWVDaGVja2luZyA9IChuZXcgRGF0ZSgpKS5nZXRUaW1lKCk7XG4gICAgICAgICAgICBzZXNzaW9uLmZpcnN0Y29ubmVjdCA9IHRydWU7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnY29tcGxldGVkJzogLy8gb24gY2FsbGVyIHNpZGVcbiAgICAgICAgY2FzZSAnY29ubmVjdGVkJzpcbiAgICAgICAgICAgIGlmIChzZXNzaW9uLmZpcnN0Y29ubmVjdCkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uZmlyc3Rjb25uZWN0ID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgdmFyIG1ldGFkYXRhID0ge307XG4gICAgICAgICAgICAgICAgbWV0YWRhdGEuc2V0dXBUaW1lXG4gICAgICAgICAgICAgICAgICAgID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKSAtIHNlc3Npb24udGltZUNoZWNraW5nO1xuICAgICAgICAgICAgICAgIHNlc3Npb24ucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgICAgICBpZihyZXMgJiYgcmVzLnJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzLnJlc3VsdCgpLmZvckVhY2goZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXBvcnQudHlwZSA9PSAnZ29vZ0NhbmRpZGF0ZVBhaXInICYmXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9ydC5zdGF0KCdnb29nQWN0aXZlQ29ubmVjdGlvbicpID09ICd0cnVlJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YS5sb2NhbENhbmRpZGF0ZVR5cGVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gcmVwb3J0LnN0YXQoJ2dvb2dMb2NhbENhbmRpZGF0ZVR5cGUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEucmVtb3RlQ2FuZGlkYXRlVHlwZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSByZXBvcnQuc3RhdCgnZ29vZ1JlbW90ZUNhbmRpZGF0ZVR5cGUnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBsb2cgcGFpciBhcyB3ZWxsIHNvIHdlIGNhbiBnZXQgbmljZSBwaWVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY2hhcnRzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLmNhbmRpZGF0ZVBhaXJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gcmVwb3J0LnN0YXQoJ2dvb2dMb2NhbENhbmRpZGF0ZVR5cGUnKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzsnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBvcnQuc3RhdCgnZ29vZ1JlbW90ZUNhbmRpZGF0ZVR5cGUnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVwb3J0LnN0YXQoJ2dvb2dSZW1vdGVBZGRyZXNzJykuaW5kZXhPZignWycpID09PSAwKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YS5pcHY2ID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmFjY2VwdCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5zdGF0ZSA9ICdhY3RpdmUnO1xuXG4gICAgdmFyIHByYW5zd2VyID0gdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uO1xuICAgIGlmICghcHJhbnN3ZXIgfHwgcHJhbnN3ZXIudHlwZSAhPSAncHJhbnN3ZXInKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc29sZS5sb2coJ2dvaW5nIGZyb20gcHJhbnN3ZXIgdG8gYW5zd2VyJyk7XG4gICAgaWYgKHRoaXMudXNldHJpY2tsZSkge1xuICAgICAgICAvLyByZW1vdmUgY2FuZGlkYXRlcyBhbHJlYWR5IHNlbnQgZnJvbSBzZXNzaW9uLWFjY2VwdFxuICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXMocHJhbnN3ZXIuc2RwLCAnYT1jYW5kaWRhdGU6Jyk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHByYW5zd2VyLnNkcCA9IHByYW5zd2VyLnNkcC5yZXBsYWNlKGxpbmVzW2ldICsgJ1xcclxcbicsICcnKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB3aGlsZSAoU0RQVXRpbC5maW5kX2xpbmUocHJhbnN3ZXIuc2RwLCAnYT1pbmFjdGl2ZScpKSB7XG4gICAgICAgIC8vIEZJWE1FOiBjaGFuZ2UgYW55IGluYWN0aXZlIHRvIHNlbmRyZWN2IG9yIHdoYXRldmVyIHRoZXkgd2VyZSBvcmlnaW5hbGx5XG4gICAgICAgIHByYW5zd2VyLnNkcCA9IHByYW5zd2VyLnNkcC5yZXBsYWNlKCdhPWluYWN0aXZlJywgJ2E9c2VuZHJlY3YnKTtcbiAgICB9XG4gICAgcHJhbnN3ZXIgPSBBUFAuc2ltdWxjYXN0LnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKHByYW5zd2VyKTtcbiAgICB2YXIgcHJzZHAgPSBuZXcgU0RQKHByYW5zd2VyLnNkcCk7XG4gICAgdmFyIGFjY2VwdCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1hY2NlcHQnLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHJlc3BvbmRlcjogdGhpcy5yZXNwb25kZXIsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkIH0pO1xuICAgIHByc2RwLnRvSmluZ2xlKGFjY2VwdCwgdGhpcy5pbml0aWF0b3IgPT0gdGhpcy5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHRoaXMubG9jYWxTdHJlYW1zU1NSQyk7XG4gICAgdmFyIHNkcCA9IHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHA7XG4gICAgd2hpbGUgKFNEUFV0aWwuZmluZF9saW5lKHNkcCwgJ2E9aW5hY3RpdmUnKSkge1xuICAgICAgICAvLyBGSVhNRTogY2hhbmdlIGFueSBpbmFjdGl2ZSB0byBzZW5kcmVjdiBvciB3aGF0ZXZlciB0aGV5IHdlcmUgb3JpZ2luYWxseVxuICAgICAgICBzZHAgPSBzZHAucmVwbGFjZSgnYT1pbmFjdGl2ZScsICdhPXNlbmRyZWN2Jyk7XG4gICAgfVxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24obmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogJ2Fuc3dlcicsIHNkcDogc2RwfSksXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldExvY2FsRGVzY3JpcHRpb24gc3VjY2VzcycpO1xuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG5cbiAgICAgICAgICAgIHNlbGYuY29ubmVjdGlvbi5zZW5kSVEoYWNjZXB0LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICBhY2suc291cmNlID0gJ2Fuc3dlcic7XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGFja10pO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlYXNvbjogJChzdGFuemEpLmZpbmQoJ2Vycm9yIDpmaXJzdCcpWzBdLnRhZ05hbWVcbiAgICAgICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ2Fuc3dlcic7XG4gICAgICAgICAgICAgICAgICAgIEppbmdsZVNlc3Npb24ub25KaW5nbGVFcnJvcihzZWxmLnNpZCwgZXJyb3IpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgMTAwMDApO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignc2V0TG9jYWxEZXNjcmlwdGlvbiBmYWlsZWQnLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS50ZXJtaW5hdGUgPSBmdW5jdGlvbiAocmVhc29uKSB7XG4gICAgdGhpcy5zdGF0ZSA9ICdlbmRlZCc7XG4gICAgdGhpcy5yZWFzb24gPSByZWFzb247XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgIT09IG51bGwpIHtcbiAgICAgICAgd2luZG93LmNsZWFySW50ZXJ2YWwodGhpcy5zdGF0c2ludGVydmFsKTtcbiAgICAgICAgdGhpcy5zdGF0c2ludGVydmFsID0gbnVsbDtcbiAgICB9XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5hY3RpdmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGUgPT0gJ2FjdGl2ZSc7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24gKGNhbmRpZGF0ZSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAoY2FuZGlkYXRlICYmICF0aGlzLmxhc3RpY2VjYW5kaWRhdGUpIHtcbiAgICAgICAgdmFyIGljZSA9IFNEUFV0aWwuaWNlcGFyYW1zKHRoaXMubG9jYWxTRFAubWVkaWFbY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXhdLCB0aGlzLmxvY2FsU0RQLnNlc3Npb24pO1xuICAgICAgICB2YXIgamNhbmQgPSBTRFBVdGlsLmNhbmRpZGF0ZVRvSmluZ2xlKGNhbmRpZGF0ZS5jYW5kaWRhdGUpO1xuICAgICAgICBpZiAoIShpY2UgJiYgamNhbmQpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdmYWlsZWQgdG8gZ2V0IGljZSAmJiBqY2FuZCcpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGljZS54bWxucyA9ICd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjEnO1xuXG4gICAgICAgIGlmIChqY2FuZC50eXBlID09PSAnc3JmbHgnKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHN0dW5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9IGVsc2UgaWYgKGpjYW5kLnR5cGUgPT09ICdyZWxheScpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgICAgICBpZiAodGhpcy51c2VkcmlwKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZHJpcF9jb250YWluZXIubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHN0YXJ0IDIwbXMgY2FsbG91dFxuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5kcmlwX2NvbnRhaW5lci5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc2VuZEljZUNhbmRpZGF0ZXMoc2VsZi5kcmlwX2NvbnRhaW5lcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmRyaXBfY29udGFpbmVyID0gW107XG4gICAgICAgICAgICAgICAgICAgIH0sIDIwKTtcblxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLmRyaXBfY29udGFpbmVyLnB1c2goY2FuZGlkYXRlKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHNlbGYuc2VuZEljZUNhbmRpZGF0ZShbY2FuZGlkYXRlXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICAvL2NvbnNvbGUubG9nKCdzZW5kSWNlQ2FuZGlkYXRlOiBsYXN0IGNhbmRpZGF0ZS4nKTtcbiAgICAgICAgaWYgKCF0aGlzLnVzZXRyaWNrbGUpIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3Nob3VsZCBzZW5kIGZ1bGwgb2ZmZXIgbm93Li4uJyk7XG4gICAgICAgICAgICB2YXIgaW5pdCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgICAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24udHlwZSA9PSAnb2ZmZXInID8gJ3Nlc3Npb24taW5pdGlhdGUnIDogJ3Nlc3Npb24tYWNjZXB0JyxcbiAgICAgICAgICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pO1xuICAgICAgICAgICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICB2YXIgc2VuZEppbmdsZSA9IGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgaWYoIXNzcmMpXG4gICAgICAgICAgICAgICAgICAgIHNzcmMgPSB7fTtcbiAgICAgICAgICAgICAgICBzZWxmLmxvY2FsU0RQLnRvSmluZ2xlKGluaXQsIHNlbGYuaW5pdGlhdG9yID09IHNlbGYubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLCBzc3JjKTtcbiAgICAgICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGluaXQsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3Nlc3Npb24gaW5pdGlhdGUgYWNrJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgYWNrID0ge307XG4gICAgICAgICAgICAgICAgICAgICAgICBhY2suc291cmNlID0gJ29mZmVyJztcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGFja10pO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRlID0gJ2Vycm9yJztcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgMTAwMDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMubGFzdGljZWNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIGNvbnNvbGUubG9nKCdIYXZlIHdlIGVuY291bnRlcmVkIGFueSBzcmZseCBjYW5kaWRhdGVzPyAnICsgdGhpcy5oYWRzdHVuY2FuZGlkYXRlKTtcbiAgICAgICAgY29uc29sZS5sb2coJ0hhdmUgd2UgZW5jb3VudGVyZWQgYW55IHJlbGF5IGNhbmRpZGF0ZXM/ICcgKyB0aGlzLmhhZHR1cm5jYW5kaWRhdGUpO1xuXG4gICAgICAgIGlmICghKHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSB8fCB0aGlzLmhhZHR1cm5jYW5kaWRhdGUpICYmIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgIT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ25vc3R1bmNhbmRpZGF0ZXMuamluZ2xlJywgW3RoaXMuc2lkXSk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kSWNlQ2FuZGlkYXRlcyA9IGZ1bmN0aW9uIChjYW5kaWRhdGVzKSB7XG4gICAgY29uc29sZS5sb2coJ3NlbmRJY2VDYW5kaWRhdGVzJywgY2FuZGlkYXRlcyk7XG4gICAgdmFyIGNhbmQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3RyYW5zcG9ydC1pbmZvJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkfSk7XG4gICAgZm9yICh2YXIgbWlkID0gMDsgbWlkIDwgdGhpcy5sb2NhbFNEUC5tZWRpYS5sZW5ndGg7IG1pZCsrKSB7XG4gICAgICAgIHZhciBjYW5kcyA9IGNhbmRpZGF0ZXMuZmlsdGVyKGZ1bmN0aW9uIChlbCkgeyByZXR1cm4gZWwuc2RwTUxpbmVJbmRleCA9PSBtaWQ7IH0pO1xuICAgICAgICB2YXIgbWxpbmUgPSBTRFBVdGlsLnBhcnNlX21saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbbWlkXS5zcGxpdCgnXFxyXFxuJylbMF0pO1xuICAgICAgICBpZiAoY2FuZHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgdmFyIGljZSA9IFNEUFV0aWwuaWNlcGFyYW1zKHRoaXMubG9jYWxTRFAubWVkaWFbbWlkXSwgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKTtcbiAgICAgICAgICAgIGljZS54bWxucyA9ICd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjEnO1xuICAgICAgICAgICAgY2FuZC5jKCdjb250ZW50Jywge2NyZWF0b3I6IHRoaXMuaW5pdGlhdG9yID09IHRoaXMubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLFxuICAgICAgICAgICAgICAgIG5hbWU6IChjYW5kc1swXS5zZHBNaWQ/IGNhbmRzWzBdLnNkcE1pZCA6IG1saW5lLm1lZGlhKVxuICAgICAgICAgICAgfSkuYygndHJhbnNwb3J0JywgaWNlKTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FuZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBjYW5kLmMoJ2NhbmRpZGF0ZScsIFNEUFV0aWwuY2FuZGlkYXRlVG9KaW5nbGUoY2FuZHNbaV0uY2FuZGlkYXRlKSkudXAoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIGFkZCBmaW5nZXJwcmludFxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbbWlkXSwgJ2E9ZmluZ2VycHJpbnQ6JywgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKSkge1xuICAgICAgICAgICAgICAgIHZhciB0bXAgPSBTRFBVdGlsLnBhcnNlX2ZpbmdlcnByaW50KFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbbWlkXSwgJ2E9ZmluZ2VycHJpbnQ6JywgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKSk7XG4gICAgICAgICAgICAgICAgdG1wLnJlcXVpcmVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBjYW5kLmMoXG4gICAgICAgICAgICAgICAgICAgICdmaW5nZXJwcmludCcsXG4gICAgICAgICAgICAgICAgICAgIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOmR0bHM6MCd9KVxuICAgICAgICAgICAgICAgICAgICAudCh0bXAuZmluZ2VycHJpbnQpO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0bXAuZmluZ2VycHJpbnQ7XG4gICAgICAgICAgICAgICAgY2FuZC5hdHRycyh0bXApO1xuICAgICAgICAgICAgICAgIGNhbmQudXAoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhbmQudXAoKTsgLy8gdHJhbnNwb3J0XG4gICAgICAgICAgICBjYW5kLnVwKCk7IC8vIGNvbnRlbnRcbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBtaWdodCBtZXJnZSBsYXN0LWNhbmRpZGF0ZSBub3RpZmljYXRpb24gaW50byB0aGlzLCBidXQgaXQgaXMgY2FsbGVkIGFsb3QgbGF0ZXIuIFNlZSB3ZWJydGMgaXNzdWUgIzIzNDBcbiAgICAvL2NvbnNvbGUubG9nKCd3YXMgdGhpcyB0aGUgbGFzdCBjYW5kaWRhdGUnLCB0aGlzLmxhc3RpY2VjYW5kaWRhdGUpO1xuICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoY2FuZCxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICd0cmFuc3BvcnRpbmZvJztcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbdGhpcy5zaWQsIGFja10pO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAndHJhbnNwb3J0aW5mbyc7XG4gICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3IodGhpcy5zaWQsIGVycm9yKTtcbiAgICAgICAgfSxcbiAgICAgICAgMTAwMDApO1xufTtcblxuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kT2ZmZXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgLy9jb25zb2xlLmxvZygnc2VuZE9mZmVyLi4uJyk7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY3JlYXRlT2ZmZXIoZnVuY3Rpb24gKHNkcCkge1xuICAgICAgICAgICAgc2VsZi5jcmVhdGVkT2ZmZXIoc2RwKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2NyZWF0ZU9mZmVyIGZhaWxlZCcsIGUpO1xuICAgICAgICB9LFxuICAgICAgICB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmNyZWF0ZWRPZmZlciA9IGZ1bmN0aW9uIChzZHApIHtcbiAgICAvL2NvbnNvbGUubG9nKCdjcmVhdGVkT2ZmZXInLCBzZHApO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLmxvY2FsU0RQID0gbmV3IFNEUChzZHAuc2RwKTtcbiAgICAvL3RoaXMubG9jYWxTRFAubWFuZ2xlKCk7XG4gICAgdmFyIHNlbmRKaW5nbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBpbml0ID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLFxuICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWluaXRpYXRlJyxcbiAgICAgICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgICAgIHNpZDogdGhpcy5zaWR9KTtcbiAgICAgICAgc2VsZi5sb2NhbFNEUC50b0ppbmdsZShpbml0LCB0aGlzLmluaXRpYXRvciA9PSB0aGlzLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJywgdGhpcy5sb2NhbFN0cmVhbXNTU1JDKTtcbiAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShpbml0LFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgICAgICBhY2suc291cmNlID0gJ29mZmVyJztcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgIHJlYXNvbjogJChzdGFuemEpLmZpbmQoJ2Vycm9yIDpmaXJzdCcpWzBdLnRhZ05hbWUsXG4gICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAnb2ZmZXInO1xuICAgICAgICAgICAgICAgIEppbmdsZVNlc3Npb24ub25KaW5nbGVFcnJvcihzZWxmLnNpZCwgZXJyb3IpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIDEwMDAwKTtcbiAgICB9XG4gICAgc2RwLnNkcCA9IHRoaXMubG9jYWxTRFAucmF3O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihzZHAsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGlmKHNlbGYudXNldHJpY2tsZSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzZW5kSmluZ2xlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZWxmLnNldExvY2FsRGVzY3JpcHRpb24oKTtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldExvY2FsRGVzY3JpcHRpb24gc3VjY2VzcycpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignc2V0TG9jYWxEZXNjcmlwdGlvbiBmYWlsZWQnLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIGNhbmRzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubG9jYWxTRFAucmF3LCAnYT1jYW5kaWRhdGU6Jyk7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjYW5kcy5sZW5ndGg7IGkrKykge1xuICAgICAgICB2YXIgY2FuZCA9IFNEUFV0aWwucGFyc2VfaWNlY2FuZGlkYXRlKGNhbmRzW2ldKTtcbiAgICAgICAgaWYgKGNhbmQudHlwZSA9PSAnc3JmbHgnKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHN0dW5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9IGVsc2UgaWYgKGNhbmQudHlwZSA9PSAncmVsYXknKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2V0UmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZWxlbSwgZGVzY3R5cGUpIHtcbiAgICAvL2NvbnNvbGUubG9nKCdzZXR0aW5nIHJlbW90ZSBkZXNjcmlwdGlvbi4uLiAnLCBkZXNjdHlwZSk7XG4gICAgdGhpcy5yZW1vdGVTRFAgPSBuZXcgU0RQKCcnKTtcbiAgICB0aGlzLnJlbW90ZVNEUC5mcm9tSmluZ2xlKGVsZW0pO1xuICAgIGlmICh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uICE9PSBudWxsKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdzZXRSZW1vdGVEZXNjcmlwdGlvbiB3aGVuIHJlbW90ZSBkZXNjcmlwdGlvbiBpcyBub3QgbnVsbCwgc2hvdWxkIGJlIHByYW5zd2VyJywgdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbik7XG4gICAgICAgIGlmICh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnR5cGUgPT0gJ3ByYW5zd2VyJykge1xuICAgICAgICAgICAgdmFyIHByYW5zd2VyID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHByYW5zd2VyLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgLy8gbWFrZSBzdXJlIHdlIGhhdmUgaWNlIHVmcmFnIGFuZCBwd2RcbiAgICAgICAgICAgICAgICBpZiAoIVNEUFV0aWwuZmluZF9saW5lKHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1pY2UtdWZyYWc6JywgdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLm1lZGlhW2ldLCAnYT1pY2UtdWZyYWc6JywgcHJhbnN3ZXIuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLm1lZGlhW2ldLCAnYT1pY2UtdWZyYWc6JywgcHJhbnN3ZXIuc2Vzc2lvbikgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignbm8gaWNlIHVmcmFnPycpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXB3ZDonLCBwcmFuc3dlci5zZXNzaW9uKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAubWVkaWFbaV0gKz0gU0RQVXRpbC5maW5kX2xpbmUocHJhbnN3ZXIubWVkaWFbaV0sICdhPWljZS1wd2Q6JywgcHJhbnN3ZXIuc2Vzc2lvbikgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignbm8gaWNlIHB3ZD8nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBjb3B5IG92ZXIgY2FuZGlkYXRlc1xuICAgICAgICAgICAgICAgIHZhciBsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyhwcmFuc3dlci5tZWRpYVtpXSwgJ2E9Y2FuZGlkYXRlOicpO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAubWVkaWFbaV0gKz0gbGluZXNbal0gKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnJlbW90ZVNEUC5yYXcgPSB0aGlzLnJlbW90ZVNEUC5zZXNzaW9uICsgdGhpcy5yZW1vdGVTRFAubWVkaWEuam9pbignJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdmFyIHJlbW90ZWRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHt0eXBlOiBkZXNjdHlwZSwgc2RwOiB0aGlzLnJlbW90ZVNEUC5yYXd9KTtcblxuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0UmVtb3RlRGVzY3JpcHRpb24ocmVtb3RlZGVzYyxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnc2V0UmVtb3RlRGVzY3JpcHRpb24gc3VjY2VzcycpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignc2V0UmVtb3RlRGVzY3JpcHRpb24gZXJyb3InLCBlKTtcbiAgICAgICAgICAgIEppbmdsZVNlc3Npb24ub25KaW5nbGVGYXRhbEVycm9yKHNlbGYsIGUpO1xuICAgICAgICB9XG4gICAgKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmFkZEljZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChlbGVtKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmICh0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdjbG9zZWQnKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uICYmIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgPT0gJ2hhdmUtbG9jYWwtb2ZmZXInKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCd0cmlja2xlIGljZSBjYW5kaWRhdGUgYXJyaXZpbmcgYmVmb3JlIHNlc3Npb24gYWNjZXB0Li4uJyk7XG4gICAgICAgIC8vIGNyZWF0ZSBhIFBSQU5TV0VSIGZvciBzZXRSZW1vdGVEZXNjcmlwdGlvblxuICAgICAgICBpZiAoIXRoaXMucmVtb3RlU0RQKSB7XG4gICAgICAgICAgICB2YXIgY29iYmxlZCA9ICd2PTBcXHJcXG4nICtcbiAgICAgICAgICAgICAgICAnbz0tICcgKyAnMTkyMzUxODUxNicgKyAnIDIgSU4gSVA0IDAuMC4wLjBcXHJcXG4nICsvLyBGSVhNRVxuICAgICAgICAgICAgICAgICdzPS1cXHJcXG4nICtcbiAgICAgICAgICAgICAgICAndD0wIDBcXHJcXG4nO1xuICAgICAgICAgICAgLy8gZmlyc3QsIHRha2Ugc29tZSB0aGluZ3MgZnJvbSB0aGUgbG9jYWwgZGVzY3JpcHRpb25cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5sb2NhbFNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGNvYmJsZWQgKz0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ209JykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLmxvY2FsU0RQLm1lZGlhW2ldLCAnYT1ydHBtYXA6Jykuam9pbignXFxyXFxuJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9bWlkOicpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvYmJsZWQgKz0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9bWlkOicpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvYmJsZWQgKz0gJ2E9aW5hY3RpdmVcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAgPSBuZXcgU0RQKGNvYmJsZWQpO1xuICAgICAgICB9XG4gICAgICAgIC8vIHRoZW4gYWRkIHRoaW5ncyBsaWtlIGljZSBhbmQgZHRscyBmcm9tIHJlbW90ZSBjYW5kaWRhdGVcbiAgICAgICAgZWxlbS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2VsZi5yZW1vdGVTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUoc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0sICdhPW1pZDonICsgJCh0aGlzKS5hdHRyKCduYW1lJykpIHx8XG4gICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLmluZGV4T2YoJ209JyArICQodGhpcykuYXR0cignbmFtZScpKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIVNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1pY2UtdWZyYWc6JykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0bXAgPSAkKHRoaXMpLmZpbmQoJ3RyYW5zcG9ydCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9aWNlLXVmcmFnOicgKyB0bXAuYXR0cigndWZyYWcnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9aWNlLXB3ZDonICsgdG1wLmF0dHIoJ3B3ZCcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgICAgICB0bXAgPSAkKHRoaXMpLmZpbmQoJ3RyYW5zcG9ydD5maW5nZXJwcmludCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRtcC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXSArPSAnYT1maW5nZXJwcmludDonICsgdG1wLmF0dHIoJ2hhc2gnKSArICcgJyArIHRtcC50ZXh0KCkgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ25vIGR0bHMgZmluZ2VycHJpbnQgKHdlYnJ0YyBpc3N1ZSAjMTcxOD8pJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9Y3J5cHRvOjEgQUVTX0NNXzEyOF9ITUFDX1NIQTFfODAgaW5saW5lOkJBQURCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEQkFBREJBQURcXHJcXG4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLnJlbW90ZVNEUC5yYXcgPSB0aGlzLnJlbW90ZVNEUC5zZXNzaW9uICsgdGhpcy5yZW1vdGVTRFAubWVkaWEuam9pbignJyk7XG5cbiAgICAgICAgLy8gd2UgbmVlZCBhIGNvbXBsZXRlIFNEUCB3aXRoIGljZS11ZnJhZy9pY2UtcHdkIGluIGFsbCBwYXJ0c1xuICAgICAgICAvLyB0aGlzIG1ha2VzIHRoZSBhc3N1bXB0aW9uIHRoYXQgdGhlIFBSQU5TV0VSIGlzIGNvbnN0cnVjdGVkIHN1Y2ggdGhhdCB0aGUgaWNlLXVmcmFnIGlzIGluIGFsbCBtZWRpYXBhcnRzXG4gICAgICAgIC8vIGJ1dCBpdCBjb3VsZCBiZSBpbiB0aGUgc2Vzc2lvbiBwYXJ0IGFzIHdlbGwuIHNpbmNlIHRoZSBjb2RlIGFib3ZlIGNvbnN0cnVjdHMgdGhpcyBzZHAgdGhpcyBjYW4ndCBoYXBwZW4gaG93ZXZlclxuICAgICAgICB2YXIgaXNjb21wbGV0ZSA9IHRoaXMucmVtb3RlU0RQLm1lZGlhLmZpbHRlcihmdW5jdGlvbiAobWVkaWFwYXJ0KSB7XG4gICAgICAgICAgICByZXR1cm4gU0RQVXRpbC5maW5kX2xpbmUobWVkaWFwYXJ0LCAnYT1pY2UtdWZyYWc6Jyk7XG4gICAgICAgIH0pLmxlbmd0aCA9PSB0aGlzLnJlbW90ZVNEUC5tZWRpYS5sZW5ndGg7XG5cbiAgICAgICAgaWYgKGlzY29tcGxldGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzZXR0aW5nIHByYW5zd2VyJyk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0UmVtb3RlRGVzY3JpcHRpb24obmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogJ3ByYW5zd2VyJywgc2RwOiB0aGlzLnJlbW90ZVNEUC5yYXcgfSksXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0UmVtb3RlRGVzY3JpcHRpb24gcHJhbnN3ZXIgZmFpbGVkJywgZS50b1N0cmluZygpKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignc2V0dGluZyBwcmFuc3dlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ25vdCB5ZXQgc2V0dGluZyBwcmFuc3dlcicpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8vIG9wZXJhdGUgb24gZWFjaCBjb250ZW50IGVsZW1lbnRcbiAgICBlbGVtLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyB3b3VsZCBsb3ZlIHRvIGRlYWN0aXZhdGUgdGhpcywgYnV0IGZpcmVmb3ggc3RpbGwgcmVxdWlyZXMgaXRcbiAgICAgICAgdmFyIGlkeCA9IC0xO1xuICAgICAgICB2YXIgaTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHNlbGYucmVtb3RlU0RQLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUoc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0sICdhPW1pZDonICsgJCh0aGlzKS5hdHRyKCduYW1lJykpIHx8XG4gICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0uaW5kZXhPZignbT0nICsgJCh0aGlzKS5hdHRyKCduYW1lJykpID09PSAwKSB7XG4gICAgICAgICAgICAgICAgaWR4ID0gaTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoaWR4ID09IC0xKSB7IC8vIGZhbGwgYmFjayB0byBsb2NhbGRlc2NyaXB0aW9uXG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgc2VsZi5sb2NhbFNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZShzZWxmLmxvY2FsU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgICAgICBzZWxmLmxvY2FsU0RQLm1lZGlhW2ldLmluZGV4T2YoJ209JyArICQodGhpcykuYXR0cignbmFtZScpKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBpZHggPSBpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdmFyIG5hbWUgPSAkKHRoaXMpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgLy8gVE9ETzogY2hlY2sgaWNlLXB3ZCBhbmQgaWNlLXVmcmFnP1xuICAgICAgICAkKHRoaXMpLmZpbmQoJ3RyYW5zcG9ydD5jYW5kaWRhdGUnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBsaW5lLCBjYW5kaWRhdGU7XG4gICAgICAgICAgICBsaW5lID0gU0RQVXRpbC5jYW5kaWRhdGVGcm9tSmluZ2xlKHRoaXMpO1xuICAgICAgICAgICAgY2FuZGlkYXRlID0gbmV3IFJUQ0ljZUNhbmRpZGF0ZSh7c2RwTUxpbmVJbmRleDogaWR4LFxuICAgICAgICAgICAgICAgIHNkcE1pZDogbmFtZSxcbiAgICAgICAgICAgICAgICBjYW5kaWRhdGU6IGxpbmV9KTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRJY2VDYW5kaWRhdGUoY2FuZGlkYXRlKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdhZGRJY2VDYW5kaWRhdGUgZmFpbGVkJywgZS50b1N0cmluZygpLCBsaW5lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kQW5zd2VyID0gZnVuY3Rpb24gKHByb3Zpc2lvbmFsKSB7XG4gICAgLy9jb25zb2xlLmxvZygnY3JlYXRlQW5zd2VyJywgcHJvdmlzaW9uYWwpO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgZnVuY3Rpb24gKHNkcCkge1xuICAgICAgICAgICAgc2VsZi5jcmVhdGVkQW5zd2VyKHNkcCwgcHJvdmlzaW9uYWwpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignY3JlYXRlQW5zd2VyIGZhaWxlZCcsIGUpO1xuICAgICAgICB9LFxuICAgICAgICB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmNyZWF0ZWRBbnN3ZXIgPSBmdW5jdGlvbiAoc2RwLCBwcm92aXNpb25hbCkge1xuICAgIC8vY29uc29sZS5sb2coJ2NyZWF0ZUFuc3dlciBjYWxsYmFjaycpO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLmxvY2FsU0RQID0gbmV3IFNEUChzZHAuc2RwKTtcbiAgICAvL3RoaXMubG9jYWxTRFAubWFuZ2xlKCk7XG4gICAgdGhpcy51c2VwcmFuc3dlciA9IHByb3Zpc2lvbmFsID09PSB0cnVlO1xuICAgIGlmICh0aGlzLnVzZXRyaWNrbGUpIHtcbiAgICAgICAgaWYgKHRoaXMudXNlcHJhbnN3ZXIpIHtcbiAgICAgICAgICAgIHNkcC50eXBlID0gJ3ByYW5zd2VyJztcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5sb2NhbFNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHRoaXMubG9jYWxTRFAubWVkaWFbaV0gPSB0aGlzLmxvY2FsU0RQLm1lZGlhW2ldLnJlcGxhY2UoJ2E9c2VuZHJlY3ZcXHJcXG4nLCAnYT1pbmFjdGl2ZVxcclxcbicpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5sb2NhbFNEUC5yYXcgPSB0aGlzLmxvY2FsU0RQLnNlc3Npb24gKyAnXFxyXFxuJyArIHRoaXMubG9jYWxTRFAubWVkaWEuam9pbignJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBzZW5kSmluZ2xlID0gZnVuY3Rpb24gKHNzcmNzKSB7XG5cbiAgICAgICAgICAgICAgICB2YXIgYWNjZXB0ID0gJGlxKHt0bzogc2VsZi5wZWVyamlkLFxuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWFjY2VwdCcsXG4gICAgICAgICAgICAgICAgICAgICAgICBpbml0aWF0b3I6IHNlbGYuaW5pdGlhdG9yLFxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzcG9uZGVyOiBzZWxmLnJlc3BvbmRlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIHNpZDogc2VsZi5zaWQgfSk7XG4gICAgICAgICAgICAgICAgdmFyIHB1YmxpY0xvY2FsRGVzYyA9IEFQUC5zaW11bGNhc3QucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24oc2RwKTtcbiAgICAgICAgICAgICAgICB2YXIgcHVibGljTG9jYWxTRFAgPSBuZXcgU0RQKHB1YmxpY0xvY2FsRGVzYy5zZHApO1xuICAgICAgICAgICAgICAgIHB1YmxpY0xvY2FsU0RQLnRvSmluZ2xlKGFjY2VwdCwgc2VsZi5pbml0aWF0b3IgPT0gc2VsZi5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHNzcmNzKTtcbiAgICAgICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICB9XG4gICAgc2RwLnNkcCA9IHRoaXMubG9jYWxTRFAucmF3O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihzZHAsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcblxuICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnc2V0TG9jYWxEZXNjcmlwdGlvbiBzdWNjZXNzJyk7XG4gICAgICAgICAgICBpZiAoc2VsZi51c2V0cmlja2xlICYmICFzZWxmLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGUpO1xuICAgICAgICB9XG4gICAgKTtcbiAgICB2YXIgY2FuZHMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5yYXcsICdhPWNhbmRpZGF0ZTonKTtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IGNhbmRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHZhciBjYW5kID0gU0RQVXRpbC5wYXJzZV9pY2VjYW5kaWRhdGUoY2FuZHNbal0pO1xuICAgICAgICBpZiAoY2FuZC50eXBlID09ICdzcmZseCcpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAoY2FuZC50eXBlID09ICdyZWxheScpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kVGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbiwgdGV4dCkge1xuICAgIHZhciBzZWxmID0gdGhpcyxcbiAgICAgICAgdGVybSA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi10ZXJtaW5hdGUnLFxuICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pXG4gICAgICAgICAgICAuYygncmVhc29uJylcbiAgICAgICAgICAgIC5jKHJlYXNvbiB8fCAnc3VjY2VzcycpO1xuXG4gICAgaWYgKHRleHQpIHtcbiAgICAgICAgdGVybS51cCgpLmMoJ3RleHQnKS50KHRleHQpO1xuICAgIH1cblxuICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEodGVybSxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbiA9IG51bGw7XG4gICAgICAgICAgICBzZWxmLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICd0ZXJtaW5hdGUnO1xuICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGVycm9yXSk7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWRkU291cmNlID0gZnVuY3Rpb24gKGVsZW0sIGZyb21KaWQpIHtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBGSVhNRTogZGlydHkgd2FpdGluZ1xuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uKVxuICAgIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiYWRkU291cmNlIC0gbG9jYWxEZXNjcmlwdGlvbiBub3QgcmVhZHkgeWV0XCIpXG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNlbGYuYWRkU291cmNlKGVsZW0sIGZyb21KaWQpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIDIwMFxuICAgICAgICApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS5sb2coJ2FkZHNzcmMnLCBuZXcgRGF0ZSgpLmdldFRpbWUoKSk7XG4gICAgY29uc29sZS5sb2coJ2ljZScsIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICB2YXIgc2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG4gICAgdmFyIG15U2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcblxuICAgICQoZWxlbSkuZWFjaChmdW5jdGlvbiAoaWR4LCBjb250ZW50KSB7XG4gICAgICAgIHZhciBuYW1lID0gJChjb250ZW50KS5hdHRyKCduYW1lJyk7XG4gICAgICAgIHZhciBsaW5lcyA9ICcnO1xuICAgICAgICAkKGNvbnRlbnQpLmZpbmQoJ3NzcmMtZ3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJykuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgICAgICB2YXIgc3NyY3MgPSAkKHRoaXMpLmZpbmQoJz5zb3VyY2UnKS5tYXAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG5cbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdmFyIHRtcCA9ICQoY29udGVudCkuZmluZCgnc291cmNlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpOyAvLyBjYW4gaGFuZGxlIGJvdGggPnNvdXJjZSBhbmQgPmRlc2NyaXB0aW9uPnNvdXJjZVxuICAgICAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc3NyYyA9ICQodGhpcykuYXR0cignc3NyYycpO1xuICAgICAgICAgICAgaWYobXlTZHAuY29udGFpbnNTU1JDKHNzcmMpKXtcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGlzIGhhcHBlbnMgd2hlbiBtdWx0aXBsZSBwYXJ0aWNpcGFudHMgY2hhbmdlIHRoZWlyIHN0cmVhbXMgYXQgdGhlIHNhbWUgdGltZSBhbmRcbiAgICAgICAgICAgICAgICAgKiBDb2xpYnJpRm9jdXMubW9kaWZ5U291cmNlcyBoYXZlIHRvIHdhaXQgZm9yIHN0YWJsZSBzdGF0ZS4gSW4gdGhlIG1lYW50aW1lIG11bHRpcGxlXG4gICAgICAgICAgICAgICAgICogYWRkc3NyYyBhcmUgc2NoZWR1bGVkIGZvciB1cGRhdGUgSVEuIFNlZVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkdvdCBhZGQgc3RyZWFtIHJlcXVlc3QgZm9yIG15IG93biBzc3JjOiBcIitzc3JjKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCQodGhpcykuYXR0cigndmFsdWUnKSAmJiAkKHRoaXMpLmF0dHIoJ3ZhbHVlJykubGVuZ3RoKVxuICAgICAgICAgICAgICAgICAgICBsaW5lcyArPSAnOicgKyAkKHRoaXMpLmF0dHIoJ3ZhbHVlJyk7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ1xcclxcbic7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhLCBpZHgpIHtcbiAgICAgICAgICAgIGlmICghU0RQVXRpbC5maW5kX2xpbmUobWVkaWEsICdhPW1pZDonICsgbmFtZSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgc2RwLm1lZGlhW2lkeF0gKz0gbGluZXM7XG4gICAgICAgICAgICBpZiAoIXNlbGYuYWRkc3NyY1tpZHhdKSBzZWxmLmFkZHNzcmNbaWR4XSA9ICcnO1xuICAgICAgICAgICAgc2VsZi5hZGRzc3JjW2lkeF0gKz0gbGluZXM7XG4gICAgICAgIH0pO1xuICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgfSk7XG4gICAgdGhpcy5tb2RpZnlTb3VyY2VzKCk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5yZW1vdmVTb3VyY2UgPSBmdW5jdGlvbiAoZWxlbSwgZnJvbUppZCkge1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIEZJWE1FOiBkaXJ0eSB3YWl0aW5nXG4gICAgaWYgKCF0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pXG4gICAge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJyZW1vdmVTb3VyY2UgLSBsb2NhbERlc2NyaXB0aW9uIG5vdCByZWFkeSB5ZXRcIilcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc2VsZi5yZW1vdmVTb3VyY2UoZWxlbSwgZnJvbUppZCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgMjAwXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zb2xlLmxvZygncmVtb3Zlc3NyYycsIG5ldyBEYXRlKCkuZ2V0VGltZSgpKTtcbiAgICBjb25zb2xlLmxvZygnaWNlJywgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpO1xuICAgIHZhciBzZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICB2YXIgbXlTZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuXG4gICAgJChlbGVtKS5lYWNoKGZ1bmN0aW9uIChpZHgsIGNvbnRlbnQpIHtcbiAgICAgICAgdmFyIG5hbWUgPSAkKGNvbnRlbnQpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgdmFyIGxpbmVzID0gJyc7XG4gICAgICAgICQoY29udGVudCkuZmluZCgnc3NyYy1ncm91cFt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IHRoaXMuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKTtcbiAgICAgICAgICAgIHZhciBzc3JjcyA9ICQodGhpcykuZmluZCgnPnNvdXJjZScpLm1hcChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdzc3JjJyk7XG4gICAgICAgICAgICB9KS5nZXQoKTtcblxuICAgICAgICAgICAgaWYgKHNzcmNzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ2E9c3NyYy1ncm91cDonICsgc2VtYW50aWNzICsgJyAnICsgc3NyY3Muam9pbignICcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB2YXIgdG1wID0gJChjb250ZW50KS5maW5kKCdzb3VyY2VbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJyk7IC8vIGNhbiBoYW5kbGUgYm90aCA+c291cmNlIGFuZCA+ZGVzY3JpcHRpb24+c291cmNlXG4gICAgICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBzc3JjID0gJCh0aGlzKS5hdHRyKCdzc3JjJyk7XG4gICAgICAgICAgICAvLyBUaGlzIHNob3VsZCBuZXZlciBoYXBwZW4sIGJ1dCBjYW4gYmUgdXNlZnVsIGZvciBidWcgZGV0ZWN0aW9uXG4gICAgICAgICAgICBpZihteVNkcC5jb250YWluc1NTUkMoc3NyYykpe1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJHb3QgcmVtb3ZlIHN0cmVhbSByZXF1ZXN0IGZvciBteSBvd24gc3NyYzogXCIrc3NyYyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgJCh0aGlzKS5maW5kKCc+cGFyYW1ldGVyJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ2E9c3NyYzonICsgc3NyYyArICcgJyArICQodGhpcykuYXR0cignbmFtZScpO1xuICAgICAgICAgICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ3ZhbHVlJykgJiYgJCh0aGlzKS5hdHRyKCd2YWx1ZScpLmxlbmd0aClcbiAgICAgICAgICAgICAgICAgICAgbGluZXMgKz0gJzonICsgJCh0aGlzKS5hdHRyKCd2YWx1ZScpO1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdcXHJcXG4nO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBzZHAubWVkaWEuZm9yRWFjaChmdW5jdGlvbihtZWRpYSwgaWR4KSB7XG4gICAgICAgICAgICBpZiAoIVNEUFV0aWwuZmluZF9saW5lKG1lZGlhLCAnYT1taWQ6JyArIG5hbWUpKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIHNkcC5tZWRpYVtpZHhdICs9IGxpbmVzO1xuICAgICAgICAgICAgaWYgKCFzZWxmLnJlbW92ZXNzcmNbaWR4XSkgc2VsZi5yZW1vdmVzc3JjW2lkeF0gPSAnJztcbiAgICAgICAgICAgIHNlbGYucmVtb3Zlc3NyY1tpZHhdICs9IGxpbmVzO1xuICAgICAgICB9KTtcbiAgICAgICAgc2RwLnJhdyA9IHNkcC5zZXNzaW9uICsgc2RwLm1lZGlhLmpvaW4oJycpO1xuICAgIH0pO1xuICAgIHRoaXMubW9kaWZ5U291cmNlcygpO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUubW9kaWZ5U291cmNlcyA9IGZ1bmN0aW9uIChzdWNjZXNzQ2FsbGJhY2spIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgPT0gJ2Nsb3NlZCcpIHJldHVybjtcbiAgICBpZiAoISh0aGlzLmFkZHNzcmMubGVuZ3RoIHx8IHRoaXMucmVtb3Zlc3NyYy5sZW5ndGggfHwgdGhpcy5wZW5kaW5nb3AgIT09IG51bGwgfHwgdGhpcy5zd2l0Y2hzdHJlYW1zKSl7XG4gICAgICAgIC8vIFRoZXJlIGlzIG5vdGhpbmcgdG8gZG8gc2luY2Ugc2NoZWR1bGVkIGpvYiBtaWdodCBoYXZlIGJlZW4gZXhlY3V0ZWQgYnkgYW5vdGhlciBzdWNjZWVkaW5nIGNhbGxcbiAgICAgICAgdGhpcy5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgIGlmKHN1Y2Nlc3NDYWxsYmFjayl7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gRklYTUU6IHRoaXMgaXMgYSBiaWcgaGFja1xuICAgIC8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9MjY4OFxuICAgIC8vIF4gaGFzIGJlZW4gZml4ZWQuXG4gICAgaWYgKCEodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnc3RhYmxlJyAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSA9PSAnY29ubmVjdGVkJykpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdtb2RpZnlTb3VyY2VzIG5vdCB5ZXQnLCB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlLCB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgICAgIHRoaXMud2FpdCA9IHRydWU7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBzZWxmLm1vZGlmeVNvdXJjZXMoc3VjY2Vzc0NhbGxiYWNrKTsgfSwgMjUwKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy53YWl0KSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBzZWxmLm1vZGlmeVNvdXJjZXMoc3VjY2Vzc0NhbGxiYWNrKTsgfSwgMjUwMCk7XG4gICAgICAgIHRoaXMud2FpdCA9IGZhbHNlO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gUmVzZXQgc3dpdGNoIHN0cmVhbXMgZmxhZ1xuICAgIHRoaXMuc3dpdGNoc3RyZWFtcyA9IGZhbHNlO1xuXG4gICAgdmFyIHNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuXG4gICAgLy8gYWRkIHNvdXJjZXNcbiAgICB0aGlzLmFkZHNzcmMuZm9yRWFjaChmdW5jdGlvbihsaW5lcywgaWR4KSB7XG4gICAgICAgIHNkcC5tZWRpYVtpZHhdICs9IGxpbmVzO1xuICAgIH0pO1xuICAgIHRoaXMuYWRkc3NyYyA9IFtdO1xuXG4gICAgLy8gcmVtb3ZlIHNvdXJjZXNcbiAgICB0aGlzLnJlbW92ZXNzcmMuZm9yRWFjaChmdW5jdGlvbihsaW5lcywgaWR4KSB7XG4gICAgICAgIGxpbmVzID0gbGluZXMuc3BsaXQoJ1xcclxcbicpO1xuICAgICAgICBsaW5lcy5wb3AoKTsgLy8gcmVtb3ZlIGVtcHR5IGxhc3QgZWxlbWVudDtcbiAgICAgICAgbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICBzZHAubWVkaWFbaWR4XSA9IHNkcC5tZWRpYVtpZHhdLnJlcGxhY2UobGluZSArICdcXHJcXG4nLCAnJyk7XG4gICAgICAgIH0pO1xuICAgIH0pO1xuICAgIHRoaXMucmVtb3Zlc3NyYyA9IFtdO1xuXG4gICAgLy8gRklYTUU6XG4gICAgLy8gdGhpcyB3YXMgYSBoYWNrIGZvciB0aGUgc2l0dWF0aW9uIHdoZW4gb25seSBvbmUgcGVlciBleGlzdHNcbiAgICAvLyBpbiB0aGUgY29uZmVyZW5jZS5cbiAgICAvLyBjaGVjayBpZiBzdGlsbCByZXF1aXJlZCBhbmQgcmVtb3ZlXG4gICAgaWYgKHNkcC5tZWRpYVswXSlcbiAgICAgICAgc2RwLm1lZGlhWzBdID0gc2RwLm1lZGlhWzBdLnJlcGxhY2UoJ2E9cmVjdm9ubHknLCAnYT1zZW5kcmVjdicpO1xuICAgIGlmIChzZHAubWVkaWFbMV0pXG4gICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcblxuICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdvZmZlcicsIHNkcDogc2RwLnJhd30pLFxuICAgICAgICBmdW5jdGlvbigpIHtcblxuICAgICAgICAgICAgaWYoc2VsZi5zaWduYWxpbmdTdGF0ZSA9PSAnY2xvc2VkJykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJjcmVhdGVBbnN3ZXIgYXR0ZW1wdCBvbiBjbG9zZWQgc3RhdGVcIik7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgICAgICAgICBmdW5jdGlvbihtb2RpZmllZEFuc3dlcikge1xuICAgICAgICAgICAgICAgICAgICAvLyBjaGFuZ2UgdmlkZW8gZGlyZWN0aW9uLCBzZWUgaHR0cHM6Ly9naXRodWIuY29tL2ppdHNpL2ppdG1lZXQvaXNzdWVzLzQxXG4gICAgICAgICAgICAgICAgICAgIGlmIChzZWxmLnBlbmRpbmdvcCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHNkcCA9IG5ldyBTRFAobW9kaWZpZWRBbnN3ZXIuc2RwKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzZHAubWVkaWEubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaChzZWxmLnBlbmRpbmdvcCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdtdXRlJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXNlbmRyZWN2JywgJ2E9cmVjdm9ubHknKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICd1bm11dGUnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2RwLm1lZGlhWzFdID0gc2RwLm1lZGlhWzFdLnJlcGxhY2UoJ2E9cmVjdm9ubHknLCAnYT1zZW5kcmVjdicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RpZmllZEFuc3dlci5zZHAgPSBzZHAucmF3O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5wZW5kaW5nb3AgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IHB1c2hpbmcgZG93biBhbiBhbnN3ZXIgd2hpbGUgaWNlIGNvbm5lY3Rpb24gc3RhdGVcbiAgICAgICAgICAgICAgICAgICAgLy8gaXMgc3RpbGwgY2hlY2tpbmcgaXMgYmFkLi4uXG4gICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coc2VsZi5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIHRyeWluZyB0byB3b3JrIGFyb3VuZCBhbm90aGVyIGNocm9tZSBidWdcbiAgICAgICAgICAgICAgICAgICAgLy9tb2RpZmllZEFuc3dlci5zZHAgPSBtb2RpZmllZEFuc3dlci5zZHAucmVwbGFjZSgvYT1zZXR1cDphY3RpdmUvZywgJ2E9c2V0dXA6YWN0cGFzcycpO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24obW9kaWZpZWRBbnN3ZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdtb2RpZmllZCBzZXRMb2NhbERlc2NyaXB0aW9uIG9rJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoc3VjY2Vzc0NhbGxiYWNrKXtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbW9kaWZpZWQgc2V0TG9jYWxEZXNjcmlwdGlvbiBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdtb2RpZmllZCBhbnN3ZXIgZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdtb2RpZnkgZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICB9XG4gICAgKTtcbn07XG5cbi8qKlxuICogU3dpdGNoZXMgdmlkZW8gc3RyZWFtcy5cbiAqIEBwYXJhbSBuZXdfc3RyZWFtIG5ldyBzdHJlYW0gdGhhdCB3aWxsIGJlIHVzZWQgYXMgdmlkZW8gb2YgdGhpcyBzZXNzaW9uLlxuICogQHBhcmFtIG9sZFN0cmVhbSBvbGQgdmlkZW8gc3RyZWFtIG9mIHRoaXMgc2Vzc2lvbi5cbiAqIEBwYXJhbSBzdWNjZXNzX2NhbGxiYWNrIGNhbGxiYWNrIGV4ZWN1dGVkIGFmdGVyIHN1Y2Nlc3NmdWwgc3RyZWFtIHN3aXRjaC5cbiAqL1xuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc3dpdGNoU3RyZWFtcyA9IGZ1bmN0aW9uIChuZXdfc3RyZWFtLCBvbGRTdHJlYW0sIHN1Y2Nlc3NfY2FsbGJhY2spIHtcblxuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIC8vIFJlbWVtYmVyIFNEUCB0byBmaWd1cmUgb3V0IGFkZGVkL3JlbW92ZWQgU1NSQ3NcbiAgICB2YXIgb2xkU2RwID0gbnVsbDtcbiAgICBpZihzZWxmLnBlZXJjb25uZWN0aW9uKSB7XG4gICAgICAgIGlmKHNlbGYucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbikge1xuICAgICAgICAgICAgb2xkU2RwID0gbmV3IFNEUChzZWxmLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgfVxuICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLnJlbW92ZVN0cmVhbShvbGRTdHJlYW0sIHRydWUpO1xuICAgICAgICBpZihuZXdfc3RyZWFtKVxuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRTdHJlYW0obmV3X3N0cmVhbSk7XG4gICAgfVxuXG4gICAgQVBQLlJUQy5zd2l0Y2hWaWRlb1N0cmVhbXMobmV3X3N0cmVhbSwgb2xkU3RyZWFtKTtcblxuICAgIC8vIENvbmZlcmVuY2UgaXMgbm90IGFjdGl2ZVxuICAgIGlmKCFvbGRTZHAgfHwgIXNlbGYucGVlcmNvbm5lY3Rpb24pIHtcbiAgICAgICAgc3VjY2Vzc19jYWxsYmFjaygpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgc2VsZi5zd2l0Y2hzdHJlYW1zID0gdHJ1ZTtcbiAgICBzZWxmLm1vZGlmeVNvdXJjZXMoZnVuY3Rpb24oKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdtb2RpZnkgc291cmNlcyBkb25lJyk7XG5cbiAgICAgICAgc3VjY2Vzc19jYWxsYmFjaygpO1xuXG4gICAgICAgIHZhciBuZXdTZHAgPSBuZXcgU0RQKHNlbGYucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICBjb25zb2xlLmxvZyhcIlNEUHNcIiwgb2xkU2RwLCBuZXdTZHApO1xuICAgICAgICBzZWxmLm5vdGlmeU15U1NSQ1VwZGF0ZShvbGRTZHAsIG5ld1NkcCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIEZpZ3VyZXMgb3V0IGFkZGVkL3JlbW92ZWQgc3NyY3MgYW5kIHNlbmQgdXBkYXRlIElRcy5cbiAqIEBwYXJhbSBvbGRfc2RwIFNEUCBvYmplY3QgZm9yIG9sZCBkZXNjcmlwdGlvbi5cbiAqIEBwYXJhbSBuZXdfc2RwIFNEUCBvYmplY3QgZm9yIG5ldyBkZXNjcmlwdGlvbi5cbiAqL1xuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUubm90aWZ5TXlTU1JDVXBkYXRlID0gZnVuY3Rpb24gKG9sZF9zZHAsIG5ld19zZHApIHtcblxuICAgIGlmICghKHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgPT0gJ3N0YWJsZScgJiZcbiAgICAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUgPT0gJ2Nvbm5lY3RlZCcpKXtcbiAgICAgICAgY29uc29sZS5sb2coXCJUb28gZWFybHkgdG8gc2VuZCB1cGRhdGVzXCIpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gc2VuZCBzb3VyY2UtcmVtb3ZlIElRLlxuICAgIHNkcERpZmZlciA9IG5ldyBTRFBEaWZmZXIobmV3X3NkcCwgb2xkX3NkcCk7XG4gICAgdmFyIHJlbW92ZSA9ICRpcSh7dG86IHRoaXMucGVlcmppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge1xuICAgICAgICAgICAgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzb3VyY2UtcmVtb3ZlJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkXG4gICAgICAgIH1cbiAgICApO1xuICAgIHZhciByZW1vdmVkID0gc2RwRGlmZmVyLnRvSmluZ2xlKHJlbW92ZSk7XG4gICAgaWYgKHJlbW92ZWQpIHtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShyZW1vdmUsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdnb3QgcmVtb3ZlIHJlc3VsdCcsIHJlcyk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2dvdCByZW1vdmUgZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdyZW1vdmFsIG5vdCBuZWNlc3NhcnknKTtcbiAgICB9XG5cbiAgICAvLyBzZW5kIHNvdXJjZS1hZGQgSVEuXG4gICAgdmFyIHNkcERpZmZlciA9IG5ldyBTRFBEaWZmZXIob2xkX3NkcCwgbmV3X3NkcCk7XG4gICAgdmFyIGFkZCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge1xuICAgICAgICAgICAgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzb3VyY2UtYWRkJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkXG4gICAgICAgIH1cbiAgICApO1xuICAgIHZhciBhZGRlZCA9IHNkcERpZmZlci50b0ppbmdsZShhZGQpO1xuICAgIGlmIChhZGRlZCkge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKGFkZCxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ2dvdCBhZGQgcmVzdWx0JywgcmVzKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignZ290IGFkZCBlcnJvcicsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coJ2FkZGl0aW9uIG5vdCBuZWNlc3NhcnknKTtcbiAgICB9XG59O1xuXG4vKipcbiAqIE11dGVzL3VubXV0ZXMgdGhlIChsb2NhbCkgdmlkZW8gaS5lLiBlbmFibGVzL2Rpc2FibGVzIGFsbCB2aWRlbyB0cmFja3MuXG4gKlxuICogQHBhcmFtIG11dGUgPHR0PnRydWU8L3R0PiB0byBtdXRlIHRoZSAobG9jYWwpIHZpZGVvIGkuZS4gdG8gZGlzYWJsZSBhbGwgdmlkZW9cbiAqIHRyYWNrczsgb3RoZXJ3aXNlLCA8dHQ+ZmFsc2U8L3R0PlxuICogQHBhcmFtIGNhbGxiYWNrIGEgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCB3aXRoIDx0dD5tdXRlPC90dD4gYWZ0ZXIgYWxsIHZpZGVvXG4gKiB0cmFja3MgaGF2ZSBiZWVuIGVuYWJsZWQvZGlzYWJsZWQuIFRoZSBmdW5jdGlvbiBtYXksIG9wdGlvbmFsbHksIHJldHVyblxuICogYW5vdGhlciBmdW5jdGlvbiB3aGljaCBpcyB0byBiZSBpbnZva2VkIGFmdGVyIHRoZSB3aG9sZSBtdXRlL3VubXV0ZSBvcGVyYXRpb25cbiAqIGhhcyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5LlxuICogQHBhcmFtIG9wdGlvbnMgYW4gb2JqZWN0IHdoaWNoIHNwZWNpZmllcyBvcHRpb25hbCBhcmd1bWVudHMgc3VjaCBhcyB0aGVcbiAqIDx0dD5ib29sZWFuPC90dD4ga2V5IDx0dD5ieVVzZXI8L3R0PiB3aXRoIGRlZmF1bHQgdmFsdWUgPHR0PnRydWU8L3R0PiB3aGljaFxuICogc3BlY2lmaWVzIHdoZXRoZXIgdGhlIG1ldGhvZCB3YXMgaW5pdGlhdGVkIGluIHJlc3BvbnNlIHRvIGEgdXNlciBjb21tYW5kIChpblxuICogY29udHJhc3QgdG8gYW4gYXV0b21hdGljIGRlY2lzaW9uIG1hZGUgYnkgdGhlIGFwcGxpY2F0aW9uIGxvZ2ljKVxuICovXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZXRWaWRlb011dGUgPSBmdW5jdGlvbiAobXV0ZSwgY2FsbGJhY2ssIG9wdGlvbnMpIHtcbiAgICB2YXIgYnlVc2VyO1xuXG4gICAgaWYgKG9wdGlvbnMpIHtcbiAgICAgICAgYnlVc2VyID0gb3B0aW9ucy5ieVVzZXI7XG4gICAgICAgIGlmICh0eXBlb2YgYnlVc2VyID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgYnlVc2VyID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIGJ5VXNlciA9IHRydWU7XG4gICAgfVxuICAgIC8vIFRoZSB1c2VyJ3MgY29tbWFuZCB0byBtdXRlIHRoZSAobG9jYWwpIHZpZGVvIHRha2VzIHByZWNlZGVuY2Ugb3ZlciBhbnlcbiAgICAvLyBhdXRvbWF0aWMgZGVjaXNpb24gbWFkZSBieSB0aGUgYXBwbGljYXRpb24gbG9naWMuXG4gICAgaWYgKGJ5VXNlcikge1xuICAgICAgICB0aGlzLnZpZGVvTXV0ZUJ5VXNlciA9IG11dGU7XG4gICAgfSBlbHNlIGlmICh0aGlzLnZpZGVvTXV0ZUJ5VXNlcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5oYXJkTXV0ZVZpZGVvKG11dGUpO1xuXG4gICAgdGhpcy5tb2RpZnlTb3VyY2VzKGNhbGxiYWNrKG11dGUpKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmhhcmRNdXRlVmlkZW8gPSBmdW5jdGlvbiAobXV0ZWQpIHtcbiAgICB0aGlzLnBlbmRpbmdvcCA9IG11dGVkID8gJ211dGUnIDogJ3VubXV0ZSc7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kTXV0ZSA9IGZ1bmN0aW9uIChtdXRlZCwgY29udGVudCkge1xuICAgIHZhciBpbmZvID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLFxuICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWluZm8nLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWQgfSk7XG4gICAgaW5mby5jKG11dGVkID8gJ211dGUnIDogJ3VubXV0ZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgaW5mby5hdHRycyh7J2NyZWF0b3InOiB0aGlzLm1lID09IHRoaXMuaW5pdGlhdG9yID8gJ2NyZWF0b3InIDogJ3Jlc3BvbmRlcid9KTtcbiAgICBpZiAoY29udGVudCkge1xuICAgICAgICBpbmZvLmF0dHJzKHsnbmFtZSc6IGNvbnRlbnR9KTtcbiAgICB9XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kUmluZ2luZyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5mbyA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbmZvJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkIH0pO1xuICAgIGluZm8uYygncmluZ2luZycsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uIChpbnRlcnZhbCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgcmVjdiA9IHthdWRpbzogMCwgdmlkZW86IDB9O1xuICAgIHZhciBsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3RyZWN2ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3Rsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxvc3MgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB2YXIgZGVsdGEgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uICYmIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24gKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAvLyBUT0RPOiB0aGVyZSBhcmUgc28gbXVjaCBzdGF0aXN0aWNzIHlvdSBjYW4gZ2V0IGZyb20gdGhpcy4uXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnR5cGUgPT0gJ3NzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFja2V0c3JlY3YgPSByZXN1bHRzW2ldLnN0YXQoJ3BhY2tldHNSZWNlaXZlZCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHBhY2tldHNsb3N0ID0gcmVzdWx0c1tpXS5zdGF0KCdwYWNrZXRzTG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhY2tldHNyZWN2ICYmIHBhY2tldHNsb3N0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0c3JlY3YgPSBwYXJzZUludChwYWNrZXRzcmVjdiwgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2tldHNsb3N0ID0gcGFyc2VJbnQocGFja2V0c2xvc3QsIDEwKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnN0YXQoJ2dvb2dGcmFtZVJhdGVSZWNlaXZlZCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3Rsb3N0LnZpZGVvID0gbG9zdC52aWRlbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdHJlY3YudmlkZW8gPSByZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWN2LnZpZGVvID0gcGFja2V0c3JlY3Y7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvc3QudmlkZW8gPSBwYWNrZXRzbG9zdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bG9zdC5hdWRpbyA9IGxvc3QuYXVkaW87XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RyZWN2LmF1ZGlvID0gcmVjdi5hdWRpbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdi5hdWRpbyA9IHBhY2tldHNyZWN2O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3N0LmF1ZGlvID0gcGFja2V0c2xvc3Q7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGRlbHRhLmF1ZGlvID0gcmVjdi5hdWRpbyAtIGxhc3RyZWN2LmF1ZGlvO1xuICAgICAgICAgICAgICAgIGRlbHRhLnZpZGVvID0gcmVjdi52aWRlbyAtIGxhc3RyZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgIGxvc3MuYXVkaW8gPSAoZGVsdGEuYXVkaW8gPiAwKSA/IE1hdGguY2VpbCgxMDAgKiAobG9zdC5hdWRpbyAtIGxhc3Rsb3N0LmF1ZGlvKSAvIGRlbHRhLmF1ZGlvKSA6IDA7XG4gICAgICAgICAgICAgICAgbG9zcy52aWRlbyA9IChkZWx0YS52aWRlbyA+IDApID8gTWF0aC5jZWlsKDEwMCAqIChsb3N0LnZpZGVvIC0gbGFzdGxvc3QudmlkZW8pIC8gZGVsdGEudmlkZW8pIDogMDtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwYWNrZXRsb3NzLmppbmdsZScsIFtzZWxmLnNpZCwgbG9zc10pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LCBpbnRlcnZhbCB8fCAzMDAwKTtcbiAgICByZXR1cm4gdGhpcy5zdGF0c2ludGVydmFsO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yID0gZnVuY3Rpb24gKHNlc3Npb24sIGVycm9yKVxue1xuICAgIGNvbnNvbGUuZXJyb3IoXCJKaW5nbGUgZXJyb3JcIiwgZXJyb3IpO1xufVxuXG5KaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvciA9IGZ1bmN0aW9uIChzZXNzaW9uLCBlcnJvcilcbntcbiAgICB0aGlzLnNlcnZpY2Uuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKFwiZGlhbG9nLnNvcnJ5XCIsXG4gICAgICAgIFwiZGlhbG9nLmludGVybmFsRXJyb3JcIik7XG59XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldExvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoKSB7XG4gICAgLy8gcHV0IG91ciBzc3JjcyBpbnRvIHByZXNlbmNlIHNvIG90aGVyIGNsaWVudHMgY2FuIGlkZW50aWZ5IG91ciBzdHJlYW1cbiAgICB2YXIgbmV3c3NyY3MgPSBbXTtcbiAgICB2YXIgbWVkaWEgPSBBUFAuc2ltdWxjYXN0LnBhcnNlTWVkaWEodGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uKTtcbiAgICBtZWRpYS5mb3JFYWNoKGZ1bmN0aW9uIChtZWRpYSkge1xuXG4gICAgICAgIGlmKE9iamVjdC5rZXlzKG1lZGlhLnNvdXJjZXMpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIC8vIFRPRE8oZ3ApIG1heWJlIGV4Y2x1ZGUgRklEIHN0cmVhbXM/XG4gICAgICAgICAgICBPYmplY3Qua2V5cyhtZWRpYS5zb3VyY2VzKS5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgbmV3c3NyY3MucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgICdzc3JjJzogc3NyYyxcbiAgICAgICAgICAgICAgICAgICAgJ3R5cGUnOiBtZWRpYS50eXBlLFxuICAgICAgICAgICAgICAgICAgICAnZGlyZWN0aW9uJzogbWVkaWEuZGlyZWN0aW9uXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmKHRoaXMubG9jYWxTdHJlYW1zU1NSQyAmJiB0aGlzLmxvY2FsU3RyZWFtc1NTUkNbbWVkaWEudHlwZV0pXG4gICAgICAgIHtcbiAgICAgICAgICAgIG5ld3NzcmNzLnB1c2goe1xuICAgICAgICAgICAgICAgICdzc3JjJzogdGhpcy5sb2NhbFN0cmVhbXNTU1JDW21lZGlhLnR5cGVdLFxuICAgICAgICAgICAgICAgICd0eXBlJzogbWVkaWEudHlwZSxcbiAgICAgICAgICAgICAgICAnZGlyZWN0aW9uJzogbWVkaWEuZGlyZWN0aW9uXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZygnbmV3IHNzcmNzJywgbmV3c3NyY3MpO1xuXG4gICAgLy8gSGF2ZSB0byBjbGVhciBwcmVzZW5jZSBtYXAgdG8gZ2V0IHJpZCBvZiByZW1vdmVkIHN0cmVhbXNcbiAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5jbGVhclByZXNlbmNlTWVkaWEoKTtcblxuICAgIGlmIChuZXdzc3Jjcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGZvciAodmFyIGkgPSAxOyBpIDw9IG5ld3NzcmNzLmxlbmd0aDsgaSArKykge1xuICAgICAgICAgICAgLy8gQ2hhbmdlIHZpZGVvIHR5cGUgdG8gc2NyZWVuXG4gICAgICAgICAgICBpZiAobmV3c3NyY3NbaS0xXS50eXBlID09PSAndmlkZW8nICYmIEFQUC5kZXNrdG9wc2hhcmluZy5pc1VzaW5nU2NyZWVuU3RyZWFtKCkpIHtcbiAgICAgICAgICAgICAgICBuZXdzc3Jjc1tpLTFdLnR5cGUgPSAnc2NyZWVuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmFkZE1lZGlhVG9QcmVzZW5jZShpLFxuICAgICAgICAgICAgICAgIG5ld3NzcmNzW2ktMV0udHlwZSwgbmV3c3NyY3NbaS0xXS5zc3JjLCBuZXdzc3Jjc1tpLTFdLmRpcmVjdGlvbik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICB9XG59XG5cbi8vIGFuIGF0dGVtcHQgdG8gd29yayBhcm91bmQgaHR0cHM6Ly9naXRodWIuY29tL2ppdHNpL2ppdG1lZXQvaXNzdWVzLzMyXG5mdW5jdGlvbiBzZW5kS2V5ZnJhbWUocGMpIHtcbiAgICBjb25zb2xlLmxvZygnc2VuZGtleWZyYW1lJywgcGMuaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICBpZiAocGMuaWNlQ29ubmVjdGlvblN0YXRlICE9PSAnY29ubmVjdGVkJykgcmV0dXJuOyAvLyBzYWZlLi4uXG4gICAgcGMuc2V0UmVtb3RlRGVzY3JpcHRpb24oXG4gICAgICAgIHBjLnJlbW90ZURlc2NyaXB0aW9uLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBwYy5jcmVhdGVBbnN3ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKG1vZGlmaWVkQW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgIHBjLnNldExvY2FsRGVzY3JpcHRpb24oXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RpZmllZEFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBub29wXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBjcmVhdGVBbnN3ZXIgZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygndHJpZ2dlcktleWZyYW1lIHNldFJlbW90ZURlc2NyaXB0aW9uIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUucmVtb3RlU3RyZWFtQWRkZWQgPSBmdW5jdGlvbiAoZGF0YSwgdGltZXMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIHRoZXNzcmM7XG4gICAgdmFyIHNzcmMyamlkID0gdGhpcy5jb25uZWN0aW9uLmVtdWMuc3NyYzJqaWQ7XG5cbiAgICAvLyBsb29rIHVwIGFuIGFzc29jaWF0ZWQgSklEIGZvciBhIHN0cmVhbSBpZFxuICAgIGlmIChkYXRhLnN0cmVhbS5pZCAmJiBkYXRhLnN0cmVhbS5pZC5pbmRleE9mKCdtaXhlZG1zbGFiZWwnKSA9PT0gLTEpIHtcbiAgICAgICAgLy8gbG9vayBvbmx5IGF0IGE9c3NyYzogYW5kIF9ub3RfIGF0IGE9c3NyYy1ncm91cDogbGluZXNcblxuICAgICAgICB2YXIgc3NyY2xpbmVzXG4gICAgICAgICAgICA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCwgJ2E9c3NyYzonKTtcbiAgICAgICAgc3NyY2xpbmVzID0gc3NyY2xpbmVzLmZpbHRlcihmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgLy8gTk9URShncCkgcHJldmlvdXNseSB3ZSBmaWx0ZXJlZCBvbiB0aGUgbXNsYWJlbCwgYnV0IHRoYXQgcHJvcGVydHlcbiAgICAgICAgICAgIC8vIGlzIG5vdCBhbHdheXMgcHJlc2VudC5cbiAgICAgICAgICAgIC8vIHJldHVybiBsaW5lLmluZGV4T2YoJ21zbGFiZWw6JyArIGRhdGEuc3RyZWFtLmxhYmVsKSAhPT0gLTE7XG5cbiAgICAgICAgICAgIHJldHVybiAoKGxpbmUuaW5kZXhPZignbXNpZDonICsgZGF0YS5zdHJlYW0uaWQpICE9PSAtMSkpO1xuICAgICAgICB9KTtcbiAgICAgICAgaWYgKHNzcmNsaW5lcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoZXNzcmMgPSBzc3JjbGluZXNbMF0uc3Vic3RyaW5nKDcpLnNwbGl0KCcgJylbMF07XG5cbiAgICAgICAgICAgIC8vIFdlIHNpZ25hbCBvdXIgc3RyZWFtcyAodGhyb3VnaCBKaW5nbGUgdG8gdGhlIGZvY3VzKSBiZWZvcmUgd2Ugc2V0XG4gICAgICAgICAgICAvLyBvdXIgcHJlc2VuY2UgKHRocm91Z2ggd2hpY2ggcGVlcnMgYXNzb2NpYXRlIHJlbW90ZSBzdHJlYW1zIHRvXG4gICAgICAgICAgICAvLyBqaWRzKS4gU28sIGl0IG1pZ2h0IGFycml2ZSB0aGF0IGEgcmVtb3RlIHN0cmVhbSBpcyBhZGRlZCBidXRcbiAgICAgICAgICAgIC8vIHNzcmMyamlkIGlzIG5vdCB5ZXQgdXBkYXRlZCBhbmQgdGh1cyBkYXRhLnBlZXJqaWQgY2Fubm90IGJlXG4gICAgICAgICAgICAvLyBzdWNjZXNzZnVsbHkgc2V0LiBIZXJlIHdlIHdhaXQgZm9yIHVwIHRvIGEgc2Vjb25kIGZvciB0aGVcbiAgICAgICAgICAgIC8vIHByZXNlbmNlIHRvIGFycml2ZS5cblxuICAgICAgICAgICAgaWYgKCFzc3JjMmppZFt0aGVzc3JjXSkge1xuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB0aW1lcyA9PT0gJ3VuZGVmaW5lZCcpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDA7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHRpbWVzID4gMTApXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm5pbmcoJ1dhaXRpbmcgZm9yIGppZCB0aW1lZCBvdXQnLCB0aGVzc3JjKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbihkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1BZGRlZChkLCB0aW1lcysrKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfShkYXRhKSwgMjUwKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBvayB0byBvdmVyd3JpdGUgdGhlIG9uZSBmcm9tIGZvY3VzPyBtaWdodCBzYXZlIHdvcmsgaW4gY29saWJyaS5qc1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ2Fzc29jaWF0ZWQgamlkJywgc3NyYzJqaWRbdGhlc3NyY10sIGRhdGEucGVlcmppZCk7XG4gICAgICAgICAgICBpZiAoc3NyYzJqaWRbdGhlc3NyY10pIHtcbiAgICAgICAgICAgICAgICBkYXRhLnBlZXJqaWQgPSBzc3JjMmppZFt0aGVzc3JjXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIEFQUC5SVEMuY3JlYXRlUmVtb3RlU3RyZWFtKGRhdGEsIHRoaXMuc2lkLCB0aGVzc3JjKTtcblxuICAgIHZhciBpc1ZpZGVvID0gZGF0YS5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPiAwO1xuICAgIC8vIGFuIGF0dGVtcHQgdG8gd29yayBhcm91bmQgaHR0cHM6Ly9naXRodWIuY29tL2ppdHNpL2ppdG1lZXQvaXNzdWVzLzMyXG4gICAgaWYgKGlzVmlkZW8gJiZcbiAgICAgICAgZGF0YS5wZWVyamlkICYmIHRoaXMucGVlcmppZCA9PT0gZGF0YS5wZWVyamlkICYmXG4gICAgICAgIGRhdGEuc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkubGVuZ3RoID09PSAwICYmXG4gICAgICAgIEFQUC5SVEMubG9jYWxWaWRlby5nZXRUcmFja3MoKS5sZW5ndGggPiAwKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbmRLZXlmcmFtZShzZWxmLnBlZXJjb25uZWN0aW9uKTtcbiAgICAgICAgfSwgMzAwMCk7XG4gICAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IEppbmdsZVNlc3Npb247XG4iLCIvKiBqc2hpbnQgLVcxMTcgKi9cbnZhciBTRFBVdGlsID0gcmVxdWlyZShcIi4vU0RQVXRpbFwiKTtcblxuLy8gU0RQIFNUVUZGXG5mdW5jdGlvbiBTRFAoc2RwKSB7XG4gICAgdGhpcy5tZWRpYSA9IHNkcC5zcGxpdCgnXFxyXFxubT0nKTtcbiAgICBmb3IgKHZhciBpID0gMTsgaSA8IHRoaXMubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdGhpcy5tZWRpYVtpXSA9ICdtPScgKyB0aGlzLm1lZGlhW2ldO1xuICAgICAgICBpZiAoaSAhPSB0aGlzLm1lZGlhLmxlbmd0aCAtIDEpIHtcbiAgICAgICAgICAgIHRoaXMubWVkaWFbaV0gKz0gJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zZXNzaW9uID0gdGhpcy5tZWRpYS5zaGlmdCgpICsgJ1xcclxcbic7XG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xufVxuLyoqXG4gKiBSZXR1cm5zIG1hcCBvZiBNZWRpYUNoYW5uZWwgbWFwcGVkIHBlciBjaGFubmVsIGlkeC5cbiAqL1xuU0RQLnByb3RvdHlwZS5nZXRNZWRpYVNzcmNNYXAgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIG1lZGlhX3NzcmNzID0ge307XG4gICAgdmFyIHRtcDtcbiAgICBmb3IgKHZhciBtZWRpYWluZGV4ID0gMDsgbWVkaWFpbmRleCA8IHNlbGYubWVkaWEubGVuZ3RoOyBtZWRpYWluZGV4KyspIHtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5maW5kX2xpbmVzKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPXNzcmM6Jyk7XG4gICAgICAgIHZhciBtaWQgPSBTRFBVdGlsLnBhcnNlX21pZChTRFBVdGlsLmZpbmRfbGluZShzZWxmLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1taWQ6JykpO1xuICAgICAgICB2YXIgbWVkaWEgPSB7XG4gICAgICAgICAgICBtZWRpYWluZGV4OiBtZWRpYWluZGV4LFxuICAgICAgICAgICAgbWlkOiBtaWQsXG4gICAgICAgICAgICBzc3Jjczoge30sXG4gICAgICAgICAgICBzc3JjR3JvdXBzOiBbXVxuICAgICAgICB9O1xuICAgICAgICBtZWRpYV9zc3Jjc1ttZWRpYWluZGV4XSA9IG1lZGlhO1xuICAgICAgICB0bXAuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgdmFyIGxpbmVzc3JjID0gbGluZS5zdWJzdHJpbmcoNykuc3BsaXQoJyAnKVswXTtcbiAgICAgICAgICAgIC8vIGFsbG9jYXRlIG5ldyBDaGFubmVsU3NyY1xuICAgICAgICAgICAgaWYoIW1lZGlhLnNzcmNzW2xpbmVzc3JjXSkge1xuICAgICAgICAgICAgICAgIG1lZGlhLnNzcmNzW2xpbmVzc3JjXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgc3NyYzogbGluZXNzcmMsXG4gICAgICAgICAgICAgICAgICAgIGxpbmVzOiBbXVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBtZWRpYS5zc3Jjc1tsaW5lc3NyY10ubGluZXMucHVzaChsaW5lKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRtcCA9IFNEUFV0aWwuZmluZF9saW5lcyhzZWxmLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zc3JjLWdyb3VwOicpO1xuICAgICAgICB0bXAuZm9yRWFjaChmdW5jdGlvbihsaW5lKXtcbiAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSBsaW5lLnN1YnN0cigwLCBpZHgpLnN1YnN0cigxMyk7XG4gICAgICAgICAgICB2YXIgc3NyY3MgPSBsaW5lLnN1YnN0cigxNCArIHNlbWFudGljcy5sZW5ndGgpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgICAgICBtZWRpYS5zc3JjR3JvdXBzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICBzZW1hbnRpY3M6IHNlbWFudGljcyxcbiAgICAgICAgICAgICAgICAgICAgc3NyY3M6IHNzcmNzXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gbWVkaWFfc3NyY3M7XG59O1xuLyoqXG4gKiBSZXR1cm5zIDx0dD50cnVlPC90dD4gaWYgdGhpcyBTRFAgY29udGFpbnMgZ2l2ZW4gU1NSQy5cbiAqIEBwYXJhbSBzc3JjIHRoZSBzc3JjIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IDx0dD50cnVlPC90dD4gaWYgdGhpcyBTRFAgY29udGFpbnMgZ2l2ZW4gU1NSQy5cbiAqL1xuU0RQLnByb3RvdHlwZS5jb250YWluc1NTUkMgPSBmdW5jdGlvbihzc3JjKSB7XG4gICAgdmFyIG1lZGlhcyA9IHRoaXMuZ2V0TWVkaWFTc3JjTWFwKCk7XG4gICAgdmFyIGNvbnRhaW5zID0gZmFsc2U7XG4gICAgT2JqZWN0LmtleXMobWVkaWFzKS5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhaW5kZXgpe1xuICAgICAgICB2YXIgbWVkaWEgPSBtZWRpYXNbbWVkaWFpbmRleF07XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJDaGVja1wiLCBjaGFubmVsLCBzc3JjKTtcbiAgICAgICAgaWYoT2JqZWN0LmtleXMobWVkaWEuc3NyY3MpLmluZGV4T2Yoc3NyYykgIT0gLTEpe1xuICAgICAgICAgICAgY29udGFpbnMgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIGNvbnRhaW5zO1xufTtcblxuXG4vLyByZW1vdmUgaVNBQyBhbmQgQ04gZnJvbSBTRFBcblNEUC5wcm90b3R5cGUubWFuZ2xlID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBpLCBqLCBtbGluZSwgbGluZXMsIHJ0cG1hcCwgbmV3ZGVzYztcbiAgICBmb3IgKGkgPSAwOyBpIDwgdGhpcy5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICBsaW5lcyA9IHRoaXMubWVkaWFbaV0uc3BsaXQoJ1xcclxcbicpO1xuICAgICAgICBsaW5lcy5wb3AoKTsgLy8gcmVtb3ZlIGVtcHR5IGxhc3QgZWxlbWVudFxuICAgICAgICBtbGluZSA9IFNEUFV0aWwucGFyc2VfbWxpbmUobGluZXMuc2hpZnQoKSk7XG4gICAgICAgIGlmIChtbGluZS5tZWRpYSAhPSAnYXVkaW8nKVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIG5ld2Rlc2MgPSAnJztcbiAgICAgICAgbWxpbmUuZm10Lmxlbmd0aCA9IDA7XG4gICAgICAgIGZvciAoaiA9IDA7IGogPCBsaW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgaWYgKGxpbmVzW2pdLnN1YnN0cigwLCA5KSA9PSAnYT1ydHBtYXA6Jykge1xuICAgICAgICAgICAgICAgIHJ0cG1hcCA9IFNEUFV0aWwucGFyc2VfcnRwbWFwKGxpbmVzW2pdKTtcbiAgICAgICAgICAgICAgICBpZiAocnRwbWFwLm5hbWUgPT0gJ0NOJyB8fCBydHBtYXAubmFtZSA9PSAnSVNBQycpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIG1saW5lLmZtdC5wdXNoKHJ0cG1hcC5pZCk7XG4gICAgICAgICAgICAgICAgbmV3ZGVzYyArPSBsaW5lc1tqXSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBuZXdkZXNjICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5tZWRpYVtpXSA9IFNEUFV0aWwuYnVpbGRfbWxpbmUobWxpbmUpICsgJ1xcclxcbic7XG4gICAgICAgIHRoaXMubWVkaWFbaV0gKz0gbmV3ZGVzYztcbiAgICB9XG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xufTtcblxuLy8gcmVtb3ZlIGxpbmVzIG1hdGNoaW5nIHByZWZpeCBmcm9tIHNlc3Npb24gc2VjdGlvblxuU0RQLnByb3RvdHlwZS5yZW1vdmVTZXNzaW9uTGluZXMgPSBmdW5jdGlvbihwcmVmaXgpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMuc2Vzc2lvbiwgcHJlZml4KTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgc2VsZi5zZXNzaW9uID0gc2VsZi5zZXNzaW9uLnJlcGxhY2UobGluZSArICdcXHJcXG4nLCAnJyk7XG4gICAgfSk7XG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xuICAgIHJldHVybiBsaW5lcztcbn1cbi8vIHJlbW92ZSBsaW5lcyBtYXRjaGluZyBwcmVmaXggZnJvbSBhIG1lZGlhIHNlY3Rpb24gc3BlY2lmaWVkIGJ5IG1lZGlhaW5kZXhcbi8vIFRPRE86IG5vbi1udW1lcmljIG1lZGlhaW5kZXggY291bGQgbWF0Y2ggbWlkXG5TRFAucHJvdG90eXBlLnJlbW92ZU1lZGlhTGluZXMgPSBmdW5jdGlvbihtZWRpYWluZGV4LCBwcmVmaXgpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sIHByZWZpeCk7XG4gICAgbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgIHNlbGYubWVkaWFbbWVkaWFpbmRleF0gPSBzZWxmLm1lZGlhW21lZGlhaW5kZXhdLnJlcGxhY2UobGluZSArICdcXHJcXG4nLCAnJyk7XG4gICAgfSk7XG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xuICAgIHJldHVybiBsaW5lcztcbn1cblxuLy8gYWRkIGNvbnRlbnQncyB0byBhIGppbmdsZSBlbGVtZW50XG5TRFAucHJvdG90eXBlLnRvSmluZ2xlID0gZnVuY3Rpb24gKGVsZW0sIHRoZWNyZWF0b3IsIHNzcmNzKSB7XG4vLyAgICBjb25zb2xlLmxvZyhcIlNTUkNcIiArIHNzcmNzW1wiYXVkaW9cIl0gKyBcIiAtIFwiICsgc3NyY3NbXCJ2aWRlb1wiXSk7XG4gICAgdmFyIGksIGosIGssIG1saW5lLCBzc3JjLCBydHBtYXAsIHRtcCwgbGluZSwgbGluZXM7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIG5ldyBidW5kbGUgcGxhblxuICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLnNlc3Npb24sICdhPWdyb3VwOicpKSB7XG4gICAgICAgIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMuc2Vzc2lvbiwgJ2E9Z3JvdXA6Jyk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgdG1wID0gbGluZXNbaV0uc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSB0bXAuc2hpZnQoKS5zdWJzdHIoOCk7XG4gICAgICAgICAgICBlbGVtLmMoJ2dyb3VwJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6Z3JvdXBpbmc6MCcsIHNlbWFudGljczpzZW1hbnRpY3N9KTtcbiAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCB0bXAubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICBlbGVtLmMoJ2NvbnRlbnQnLCB7bmFtZTogdG1wW2pdfSkudXAoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBmb3IgKGkgPSAwOyBpIDwgdGhpcy5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICBtbGluZSA9IFNEUFV0aWwucGFyc2VfbWxpbmUodGhpcy5tZWRpYVtpXS5zcGxpdCgnXFxyXFxuJylbMF0pO1xuICAgICAgICBpZiAoIShtbGluZS5tZWRpYSA9PT0gJ2F1ZGlvJyB8fFxuICAgICAgICAgICAgICBtbGluZS5tZWRpYSA9PT0gJ3ZpZGVvJyB8fFxuICAgICAgICAgICAgICBtbGluZS5tZWRpYSA9PT0gJ2FwcGxpY2F0aW9uJykpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpKSB7XG4gICAgICAgICAgICBzc3JjID0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9c3NyYzonKS5zdWJzdHJpbmcoNykuc3BsaXQoJyAnKVswXTsgLy8gdGFrZSB0aGUgZmlyc3RcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmKHNzcmNzICYmIHNzcmNzW21saW5lLm1lZGlhXSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzc3JjID0gc3NyY3NbbWxpbmUubWVkaWFdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHNzcmMgPSBmYWxzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGVsZW0uYygnY29udGVudCcsIHtjcmVhdG9yOiB0aGVjcmVhdG9yLCBuYW1lOiBtbGluZS5tZWRpYX0pO1xuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9bWlkOicpKSB7XG4gICAgICAgICAgICAvLyBwcmVmZXIgaWRlbnRpZmllciBmcm9tIGE9bWlkIGlmIHByZXNlbnRcbiAgICAgICAgICAgIHZhciBtaWQgPSBTRFBVdGlsLnBhcnNlX21pZChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1taWQ6JykpO1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7IG5hbWU6IG1pZCB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1ydHBtYXA6JykubGVuZ3RoKVxuICAgICAgICB7XG4gICAgICAgICAgICBlbGVtLmMoJ2Rlc2NyaXB0aW9uJyxcbiAgICAgICAgICAgICAgICB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScsXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhOiBtbGluZS5tZWRpYSB9KTtcbiAgICAgICAgICAgIGlmIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7c3NyYzogc3NyY30pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IG1saW5lLmZtdC5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIHJ0cG1hcCA9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0cG1hcDonICsgbWxpbmUuZm10W2pdKTtcbiAgICAgICAgICAgICAgICBlbGVtLmMoJ3BheWxvYWQtdHlwZScsIFNEUFV0aWwucGFyc2VfcnRwbWFwKHJ0cG1hcCkpO1xuICAgICAgICAgICAgICAgIC8vIHB1dCBhbnkgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdIGxpbmVzIGludG8gPHBhcmFtIG5hbWU9Zm9vIHZhbHVlPWJhci8+XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWZtdHA6JyArIG1saW5lLmZtdFtqXSkpIHtcbiAgICAgICAgICAgICAgICAgICAgdG1wID0gU0RQVXRpbC5wYXJzZV9mbXRwKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWZtdHA6JyArIG1saW5lLmZtdFtqXSkpO1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGsgPSAwOyBrIDwgdG1wLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicsIHRtcFtrXSkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLlJ0Y3BGYlRvSmluZ2xlKGksIGVsZW0sIG1saW5lLmZtdFtqXSk7IC8vIFhFUC0wMjkzIC0tIG1hcCBhPXJ0Y3AtZmJcblxuICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1jcnlwdG86JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnZW5jcnlwdGlvbicsIHtyZXF1aXJlZDogMX0pO1xuICAgICAgICAgICAgICAgIHZhciBjcnlwdG8gPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVtpXSwgJ2E9Y3J5cHRvOicsIHRoaXMuc2Vzc2lvbik7XG4gICAgICAgICAgICAgICAgY3J5cHRvLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ2NyeXB0bycsIFNEUFV0aWwucGFyc2VfY3J5cHRvKGxpbmUpKS51cCgpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGVsZW0udXAoKTsgLy8gZW5kIG9mIGVuY3J5cHRpb25cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHNzcmMpIHtcbiAgICAgICAgICAgICAgICAvLyBuZXcgc3R5bGUgbWFwcGluZ1xuICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBncm91cCBieSBzc3JjIGFuZCBzdXBwb3J0IG11bHRpcGxlIGRpZmZlcmVudCBzc3Jjc1xuICAgICAgICAgICAgICAgIHZhciBzc3JjbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVtpXSwgJ2E9c3NyYzonKTtcbiAgICAgICAgICAgICAgICBpZihzc3JjbGluZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICBzc3JjbGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWR4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgbGluZXNzcmMgPSBsaW5lLnN1YnN0cigwLCBpZHgpLnN1YnN0cig3KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChsaW5lc3NyYyAhPSBzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNzcmMgPSBsaW5lc3NyYztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3NvdXJjZScsIHsgc3NyYzogc3NyYywgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBrdiA9IGxpbmUuc3Vic3RyKGlkeCArIDEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChrdi5pbmRleE9mKCc6JykgPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHsgbmFtZToga3YgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeyBuYW1lOiBrdi5zcGxpdCgnOicsIDIpWzBdIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeyB2YWx1ZToga3Yuc3BsaXQoJzonLCAyKVsxXSB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3NvdXJjZScsIHsgc3NyYzogc3NyYywgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7bmFtZTogXCJjbmFtZVwiLCB2YWx1ZTpNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoNyl9KTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgbXNpZCA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIGlmKG1saW5lLm1lZGlhID09IFwiYXVkaW9cIilcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IEFQUC5SVEMubG9jYWxBdWRpby5nZXRJZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IEFQUC5SVEMubG9jYWxWaWRlby5nZXRJZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmKG1zaWQgIT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IG1zaWQucmVwbGFjZSgvW1xceyxcXH1dL2csXCJcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7bmFtZTogXCJtc2lkXCIsIHZhbHVlOm1zaWR9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcIm1zbGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe25hbWU6IFwibGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFhFUC0wMzM5IGhhbmRsZSBzc3JjLWdyb3VwIGF0dHJpYnV0ZXNcbiAgICAgICAgICAgICAgICB2YXIgc3NyY19ncm91cF9saW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjLWdyb3VwOicpO1xuICAgICAgICAgICAgICAgIHNzcmNfZ3JvdXBfbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGlkeCA9IGxpbmUuaW5kZXhPZignICcpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gbGluZS5zdWJzdHIoMCwgaWR4KS5zdWJzdHIoMTMpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc3NyY3MgPSBsaW5lLnN1YnN0cigxNCArIHNlbWFudGljcy5sZW5ndGgpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdzc3JjLWdyb3VwJywgeyBzZW1hbnRpY3M6IHNlbWFudGljcywgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0Y3AtbXV4JykpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmMoJ3J0Y3AtbXV4JykudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYjoqXG4gICAgICAgICAgICB0aGlzLlJ0Y3BGYlRvSmluZ2xlKGksIGVsZW0sICcqJyk7XG5cbiAgICAgICAgICAgIC8vIFhFUC0wMjk0XG4gICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpO1xuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBsaW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2V4dG1hcChsaW5lc1tqXSk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygncnRwLWhkcmV4dCcsIHsgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogdG1wLnVyaSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGlkOiB0bXAudmFsdWUgfSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0bXAuaGFzT3duUHJvcGVydHkoJ2RpcmVjdGlvbicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHRtcC5kaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzZW5kb25seSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdyZXNwb25kZXInfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3JlY3Zvbmx5JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2luaXRpYXRvcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnc2VuZHJlY3YnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnYm90aCd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnaW5hY3RpdmUnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnbm9uZSd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy8gVE9ETzogaGFuZGxlIHBhcmFtc1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZGVzY3JpcHRpb25cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG1hcCBpY2UtdWZyYWcvcHdkLCBkdGxzIGZpbmdlcnByaW50LCBjYW5kaWRhdGVzXG4gICAgICAgIHRoaXMuVHJhbnNwb3J0VG9KaW5nbGUoaSwgZWxlbSk7XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNlbmRyZWN2JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2JvdGgnfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9c2VuZG9ubHknLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnaW5pdGlhdG9yJ30pO1xuICAgICAgICB9IGVsc2UgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJlY3Zvbmx5JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ3Jlc3BvbmRlcid9KTtcbiAgICAgICAgfSBlbHNlIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1pbmFjdGl2ZScsIHRoaXMuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdub25lJ30pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtbGluZS5wb3J0ID09ICcwJykge1xuICAgICAgICAgICAgLy8gZXN0b3MgaGFjayB0byByZWplY3QgYW4gbS1saW5lXG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAncmVqZWN0ZWQnfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH1cbiAgICBlbGVtLnVwKCk7XG4gICAgcmV0dXJuIGVsZW07XG59O1xuXG5TRFAucHJvdG90eXBlLlRyYW5zcG9ydFRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0pIHtcbiAgICB2YXIgaSA9IG1lZGlhaW5kZXg7XG4gICAgdmFyIHRtcDtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgZWxlbS5jKCd0cmFuc3BvcnQnKTtcblxuICAgIC8vIFhFUC0wMzQzIERUTFMvU0NUUFxuICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zY3RwbWFwOicpLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHZhciBzY3RwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUoXG4gICAgICAgICAgICB0aGlzLm1lZGlhW2ldLCAnYT1zY3RwbWFwOicsIHNlbGYuc2Vzc2lvbik7XG4gICAgICAgIGlmIChzY3RwbWFwKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgc2N0cEF0dHJzID0gU0RQVXRpbC5wYXJzZV9zY3RwbWFwKHNjdHBtYXApO1xuICAgICAgICAgICAgZWxlbS5jKCdzY3RwbWFwJyxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6ZHRscy1zY3RwOjEnLFxuICAgICAgICAgICAgICAgICAgICBudW1iZXI6IHNjdHBBdHRyc1swXSwgLyogU0NUUCBwb3J0ICovXG4gICAgICAgICAgICAgICAgICAgIHByb3RvY29sOiBzY3RwQXR0cnNbMV0sIC8qIHByb3RvY29sICovXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBPcHRpb25hbCBzdHJlYW0gY291bnQgYXR0cmlidXRlXG4gICAgICAgICAgICBpZiAoc2N0cEF0dHJzLmxlbmd0aCA+IDIpXG4gICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHN0cmVhbXM6IHNjdHBBdHRyc1syXX0pO1xuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8vIFhFUC0wMzIwXG4gICAgdmFyIGZpbmdlcnByaW50cyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1maW5nZXJwcmludDonLCB0aGlzLnNlc3Npb24pO1xuICAgIGZpbmdlcnByaW50cy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChsaW5lKTtcbiAgICAgICAgdG1wLnhtbG5zID0gJ3Vybjp4bXBwOmppbmdsZTphcHBzOmR0bHM6MCc7XG4gICAgICAgIGVsZW0uYygnZmluZ2VycHJpbnQnKS50KHRtcC5maW5nZXJwcmludCk7XG4gICAgICAgIGRlbGV0ZSB0bXAuZmluZ2VycHJpbnQ7XG4gICAgICAgIGxpbmUgPSBTRFBVdGlsLmZpbmRfbGluZShzZWxmLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zZXR1cDonLCBzZWxmLnNlc3Npb24pO1xuICAgICAgICBpZiAobGluZSkge1xuICAgICAgICAgICAgdG1wLnNldHVwID0gbGluZS5zdWJzdHIoOCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICBlbGVtLnVwKCk7IC8vIGVuZCBvZiBmaW5nZXJwcmludFxuICAgIH0pO1xuICAgIHRtcCA9IFNEUFV0aWwuaWNlcGFyYW1zKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sIHRoaXMuc2Vzc2lvbik7XG4gICAgaWYgKHRtcCkge1xuICAgICAgICB0bXAueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICAvLyBYRVAtMDE3NlxuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVttZWRpYWluZGV4XSwgJ2E9Y2FuZGlkYXRlOicsIHRoaXMuc2Vzc2lvbikpIHsgLy8gYWRkIGFueSBhPWNhbmRpZGF0ZSBsaW5lc1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPWNhbmRpZGF0ZTonLCB0aGlzLnNlc3Npb24pO1xuICAgICAgICAgICAgbGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY2FuZGlkYXRlJywgU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShsaW5lKSkudXAoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsZW0udXAoKTsgLy8gZW5kIG9mIHRyYW5zcG9ydFxufVxuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYlRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0sIHBheWxvYWR0eXBlKSB7IC8vIFhFUC0wMjkzXG4gICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlKTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciB0bXAgPSBTRFBVdGlsLnBhcnNlX3J0Y3BmYihsaW5lKTtcbiAgICAgICAgaWYgKHRtcC50eXBlID09ICd0cnItaW50Jykge1xuICAgICAgICAgICAgZWxlbS5jKCdydGNwLWZiLXRyci1pbnQnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRjcC1mYjowJywgdmFsdWU6IHRtcC5wYXJhbXNbMF19KTtcbiAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGVsZW0uYygncnRjcC1mYicsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnLCB0eXBlOiB0bXAudHlwZX0pO1xuICAgICAgICAgICAgaWYgKHRtcC5wYXJhbXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeydzdWJ0eXBlJzogdG1wLnBhcmFtc1swXX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfSk7XG59O1xuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYkZyb21KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgcGF5bG9hZHR5cGUpIHsgLy8gWEVQLTAyOTNcbiAgICB2YXIgbWVkaWEgPSAnJztcbiAgICB2YXIgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYi10cnItaW50W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MFwiXScpO1xuICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArICcqJyArICcgJyArICd0cnItaW50JyArICcgJztcbiAgICAgICAgaWYgKHRtcC5hdHRyKCd2YWx1ZScpKSB7XG4gICAgICAgICAgICBtZWRpYSArPSB0bXAuYXR0cigndmFsdWUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICcwJztcbiAgICAgICAgfVxuICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG4gICAgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYlt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlICsgJyAnICsgJCh0aGlzKS5hdHRyKCd0eXBlJyk7XG4gICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ3N1YnR5cGUnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgJCh0aGlzKS5hdHRyKCdzdWJ0eXBlJyk7XG4gICAgICAgIH1cbiAgICAgICAgbWVkaWEgKz0gJ1xcclxcbic7XG4gICAgfSk7XG4gICAgcmV0dXJuIG1lZGlhO1xufTtcblxuLy8gY29uc3RydWN0IGFuIFNEUCBmcm9tIGEgamluZ2xlIHN0YW56YVxuU0RQLnByb3RvdHlwZS5mcm9tSmluZ2xlID0gZnVuY3Rpb24gKGppbmdsZSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnJhdyA9ICd2PTBcXHJcXG4nICtcbiAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgJ3M9LVxcclxcbicgK1xuICAgICAgICAndD0wIDBcXHJcXG4nO1xuICAgIC8vIGh0dHA6Ly90b29scy5pZXRmLm9yZy9odG1sL2RyYWZ0LWlldGYtbW11c2ljLXNkcC1idW5kbGUtbmVnb3RpYXRpb24tMDQjc2VjdGlvbi04XG4gICAgaWYgKCQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykuZWFjaChmdW5jdGlvbiAoaWR4LCBncm91cCkge1xuICAgICAgICAgICAgdmFyIGNvbnRlbnRzID0gJChncm91cCkuZmluZCgnPmNvbnRlbnQnKS5tYXAoZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBjb250ZW50LmdldEF0dHJpYnV0ZSgnbmFtZScpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICBpZiAoY29udGVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHNlbGYucmF3ICs9ICdhPWdyb3VwOicgKyAoZ3JvdXAuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKSB8fCBncm91cC5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSkgKyAnICcgKyBjb250ZW50cy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5zZXNzaW9uID0gdGhpcy5yYXc7XG4gICAgamluZ2xlLmZpbmQoJz5jb250ZW50JykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBtID0gc2VsZi5qaW5nbGUybWVkaWEoJCh0aGlzKSk7XG4gICAgICAgIHNlbGYubWVkaWEucHVzaChtKTtcbiAgICB9KTtcblxuICAgIC8vIHJlY29uc3RydWN0IG1zaWQtc2VtYW50aWMgLS0gYXBwYXJlbnRseSBub3QgbmVjZXNzYXJ5XG4gICAgLypcbiAgICAgdmFyIG1zaWQgPSBTRFBVdGlsLnBhcnNlX3NzcmModGhpcy5yYXcpO1xuICAgICBpZiAobXNpZC5oYXNPd25Qcm9wZXJ0eSgnbXNsYWJlbCcpKSB7XG4gICAgIHRoaXMuc2Vzc2lvbiArPSBcImE9bXNpZC1zZW1hbnRpYzogV01TIFwiICsgbXNpZC5tc2xhYmVsICsgXCJcXHJcXG5cIjtcbiAgICAgfVxuICAgICAqL1xuXG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xufTtcblxuLy8gdHJhbnNsYXRlIGEgamluZ2xlIGNvbnRlbnQgZWxlbWVudCBpbnRvIGFuIGFuIFNEUCBtZWRpYSBwYXJ0XG5TRFAucHJvdG90eXBlLmppbmdsZTJtZWRpYSA9IGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgdmFyIG1lZGlhID0gJycsXG4gICAgICAgIGRlc2MgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uJyksXG4gICAgICAgIHNzcmMgPSBkZXNjLmF0dHIoJ3NzcmMnKSxcbiAgICAgICAgc2VsZiA9IHRoaXMsXG4gICAgICAgIHRtcDtcbiAgICB2YXIgc2N0cCA9IGNvbnRlbnQuZmluZChcbiAgICAgICAgJz50cmFuc3BvcnQ+c2N0cG1hcFt4bWxucz1cInVybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmR0bHMtc2N0cDoxXCJdJyk7XG5cbiAgICB0bXAgPSB7IG1lZGlhOiBkZXNjLmF0dHIoJ21lZGlhJykgfTtcbiAgICB0bXAucG9ydCA9ICcxJztcbiAgICBpZiAoY29udGVudC5hdHRyKCdzZW5kZXJzJykgPT0gJ3JlamVjdGVkJykge1xuICAgICAgICAvLyBlc3RvcyBoYWNrIHRvIHJlamVjdCBhbiBtLWxpbmUuXG4gICAgICAgIHRtcC5wb3J0ID0gJzAnO1xuICAgIH1cbiAgICBpZiAoY29udGVudC5maW5kKCc+dHJhbnNwb3J0PmZpbmdlcnByaW50JykubGVuZ3RoIHx8IGRlc2MuZmluZCgnZW5jcnlwdGlvbicpLmxlbmd0aCkge1xuICAgICAgICBpZiAoc2N0cC5sZW5ndGgpXG4gICAgICAgICAgICB0bXAucHJvdG8gPSAnRFRMUy9TQ1RQJztcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9TQVZQRic7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9BVlBGJztcbiAgICB9XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHRtcC5mbXQgPSBkZXNjLmZpbmQoJ3BheWxvYWQtdHlwZScpLm1hcChcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpOyB9KS5nZXQoKTtcbiAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9tbGluZSh0bXApICsgJ1xcclxcbic7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIG1lZGlhICs9ICdtPWFwcGxpY2F0aW9uIDEgRFRMUy9TQ1RQICcgKyBzY3RwLmF0dHIoJ251bWJlcicpICsgJ1xcclxcbic7XG4gICAgICAgIG1lZGlhICs9ICdhPXNjdHBtYXA6JyArIHNjdHAuYXR0cignbnVtYmVyJykgK1xuICAgICAgICAgICAgJyAnICsgc2N0cC5hdHRyKCdwcm90b2NvbCcpO1xuXG4gICAgICAgIHZhciBzdHJlYW1Db3VudCA9IHNjdHAuYXR0cignc3RyZWFtcycpO1xuICAgICAgICBpZiAoc3RyZWFtQ291bnQpXG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyBzdHJlYW1Db3VudCArICdcXHJcXG4nO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG5cbiAgICBtZWRpYSArPSAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbic7XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcDoxIElOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJz50cmFuc3BvcnRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjFcIl0nKTtcbiAgICBpZiAodG1wLmxlbmd0aCkge1xuICAgICAgICBpZiAodG1wLmF0dHIoJ3VmcmFnJykpIHtcbiAgICAgICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfaWNldWZyYWcodG1wLmF0dHIoJ3VmcmFnJykpICsgJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRtcC5hdHRyKCdwd2QnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9pY2Vwd2QodG1wLmF0dHIoJ3B3ZCcpKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIHRtcC5maW5kKCc+ZmluZ2VycHJpbnQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBjaGVjayBuYW1lc3BhY2UgYXQgc29tZSBwb2ludFxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdoYXNoJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyAkKHRoaXMpLnRleHQoKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJ2E9c2V0dXA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBzd2l0Y2ggKGNvbnRlbnQuYXR0cignc2VuZGVycycpKSB7XG4gICAgICAgIGNhc2UgJ2luaXRpYXRvcic6XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zZW5kb25seVxcclxcbic7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAncmVzcG9uZGVyJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXJlY3Zvbmx5XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdub25lJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdib3RoJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNlbmRyZWN2XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBtZWRpYSArPSAnYT1taWQ6JyArIGNvbnRlbnQuYXR0cignbmFtZScpICsgJ1xcclxcbic7XG5cbiAgICAvLyA8ZGVzY3JpcHRpb24+PHJ0Y3AtbXV4Lz48L2Rlc2NyaXB0aW9uPlxuICAgIC8vIHNlZSBodHRwOi8vY29kZS5nb29nbGUuY29tL3AvbGliamluZ2xlL2lzc3Vlcy9kZXRhaWw/aWQ9MzA5IC0tIG5vIHNwZWMgdGhvdWdoXG4gICAgLy8gYW5kIGh0dHA6Ly9tYWlsLmphYmJlci5vcmcvcGlwZXJtYWlsL2ppbmdsZS8yMDExLURlY2VtYmVyLzAwMTc2MS5odG1sXG4gICAgaWYgKGRlc2MuZmluZCgncnRjcC1tdXgnKS5sZW5ndGgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuICAgIH1cblxuICAgIGlmIChkZXNjLmZpbmQoJ2VuY3J5cHRpb24nKS5sZW5ndGgpIHtcbiAgICAgICAgZGVzYy5maW5kKCdlbmNyeXB0aW9uPmNyeXB0bycpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9Y3J5cHRvOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgndGFnJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgnY3J5cHRvLXN1aXRlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgna2V5LXBhcmFtcycpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXNzaW9uLXBhcmFtcycpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3Nlc3Npb24tcGFyYW1zJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIGRlc2MuZmluZCgncGF5bG9hZC10eXBlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfcnRwbWFwKHRoaXMpICsgJ1xcclxcbic7XG4gICAgICAgIGlmICgkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWZtdHA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpICsgJyAnO1xuICAgICAgICAgICAgbWVkaWEgKz0gJCh0aGlzKS5maW5kKCdwYXJhbWV0ZXInKS5tYXAoZnVuY3Rpb24gKCkgeyByZXR1cm4gKHRoaXMuZ2V0QXR0cmlidXRlKCduYW1lJykgPyAodGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICc9JykgOiAnJykgKyB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKTsgfSkuZ2V0KCkuam9pbignOyAnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIC8vIHhlcC0wMjkzXG4gICAgICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZSgkKHRoaXMpLCB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSk7XG4gICAgfSk7XG5cbiAgICAvLyB4ZXAtMDI5M1xuICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZShkZXNjLCAnKicpO1xuXG4gICAgLy8geGVwLTAyOTRcbiAgICB0bXAgPSBkZXNjLmZpbmQoJz5ydHAtaGRyZXh0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0cC1oZHJleHQ6MFwiXScpO1xuICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9ZXh0bWFwOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSArICcgJyArIHRoaXMuZ2V0QXR0cmlidXRlKCd1cmknKSArICdcXHJcXG4nO1xuICAgIH0pO1xuXG4gICAgY29udGVudC5maW5kKCc+dHJhbnNwb3J0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxXCJdPmNhbmRpZGF0ZScpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICBtZWRpYSArPSBTRFBVdGlsLmNhbmRpZGF0ZUZyb21KaW5nbGUodGhpcyk7XG4gICAgfSk7XG5cbiAgICAvLyBYRVAtMDMzOSBoYW5kbGUgc3NyYy1ncm91cCBhdHRyaWJ1dGVzXG4gICAgdG1wID0gY29udGVudC5maW5kKCdkZXNjcmlwdGlvbj5zc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgIHZhciBzc3JjcyA9ICQodGhpcykuZmluZCgnPnNvdXJjZScpLm1hcChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICB9KS5nZXQoKTtcblxuICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uPnNvdXJjZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzc3JjID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgJCh0aGlzKS5maW5kKCc+cGFyYW1ldGVyJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKTtcbiAgICAgICAgICAgIGlmICh0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKSAmJiB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJzonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3ZhbHVlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gbWVkaWE7XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gU0RQO1xuXG4iLCJmdW5jdGlvbiBTRFBEaWZmZXIobXlTRFAsIG90aGVyU0RQKSB7XG4gICAgdGhpcy5teVNEUCA9IG15U0RQO1xuICAgIHRoaXMub3RoZXJTRFAgPSBvdGhlclNEUDtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIG1hcCBvZiBNZWRpYUNoYW5uZWwgdGhhdCBjb250YWlucyBvbmx5IG1lZGlhIG5vdCBjb250YWluZWQgaW4gPHR0Pm90aGVyU2RwPC90dD4uIE1hcHBlZCBieSBjaGFubmVsIGlkeC5cbiAqIEBwYXJhbSBvdGhlclNkcCB0aGUgb3RoZXIgU0RQIHRvIGNoZWNrIHNzcmMgd2l0aC5cbiAqL1xuU0RQRGlmZmVyLnByb3RvdHlwZS5nZXROZXdNZWRpYSA9IGZ1bmN0aW9uKCkge1xuXG4gICAgLy8gdGhpcyBjb3VsZCBiZSB1c2VmdWwgaW4gQXJyYXkucHJvdG90eXBlLlxuICAgIGZ1bmN0aW9uIGFycmF5RXF1YWxzKGFycmF5KSB7XG4gICAgICAgIC8vIGlmIHRoZSBvdGhlciBhcnJheSBpcyBhIGZhbHN5IHZhbHVlLCByZXR1cm5cbiAgICAgICAgaWYgKCFhcnJheSlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICAvLyBjb21wYXJlIGxlbmd0aHMgLSBjYW4gc2F2ZSBhIGxvdCBvZiB0aW1lXG4gICAgICAgIGlmICh0aGlzLmxlbmd0aCAhPSBhcnJheS5sZW5ndGgpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGw9dGhpcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgICAgaWYgKHRoaXNbaV0gaW5zdGFuY2VvZiBBcnJheSAmJiBhcnJheVtpXSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmVjdXJzZSBpbnRvIHRoZSBuZXN0ZWQgYXJyYXlzXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzW2ldLmVxdWFscyhhcnJheVtpXSkpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKHRoaXNbaV0gIT0gYXJyYXlbaV0pIHtcbiAgICAgICAgICAgICAgICAvLyBXYXJuaW5nIC0gdHdvIGRpZmZlcmVudCBvYmplY3QgaW5zdGFuY2VzIHdpbGwgbmV2ZXIgYmUgZXF1YWw6IHt4OjIwfSAhPSB7eDoyMH1cbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgdmFyIG15TWVkaWFzID0gdGhpcy5teVNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgb3RoZXJzTWVkaWFzID0gdGhpcy5vdGhlclNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgbmV3TWVkaWEgPSB7fTtcbiAgICBPYmplY3Qua2V5cyhvdGhlcnNNZWRpYXMpLmZvckVhY2goZnVuY3Rpb24ob3RoZXJzTWVkaWFJZHgpIHtcbiAgICAgICAgdmFyIG15TWVkaWEgPSBteU1lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIHZhciBvdGhlcnNNZWRpYSA9IG90aGVyc01lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIGlmKCFteU1lZGlhICYmIG90aGVyc01lZGlhKSB7XG4gICAgICAgICAgICAvLyBBZGQgd2hvbGUgY2hhbm5lbFxuICAgICAgICAgICAgbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdID0gb3RoZXJzTWVkaWE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gTG9vayBmb3IgbmV3IHNzcmNzIGFjY3Jvc3MgdGhlIGNoYW5uZWxcbiAgICAgICAgT2JqZWN0LmtleXMob3RoZXJzTWVkaWEuc3NyY3MpLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgaWYoT2JqZWN0LmtleXMobXlNZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIHNzcmMgdGhhdCBkb2Vzbid0IGV4aXN0IGluIG91ciBjaGFubmVsXG4gICAgICAgICAgICAgICAgaWYoIW5ld01lZGlhW290aGVyc01lZGlhSWR4XSl7XG4gICAgICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhaW5kZXg6IG90aGVyc01lZGlhLm1lZGlhaW5kZXgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtaWQ6IG90aGVyc01lZGlhLm1pZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XS5zc3Jjc1tzc3JjXSA9IG90aGVyc01lZGlhLnNzcmNzW3NzcmNdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBMb29rIGZvciBuZXcgc3NyYyBncm91cHMgYWNyb3NzIHRoZSBjaGFubmVsc1xuICAgICAgICBvdGhlcnNNZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24ob3RoZXJTc3JjR3JvdXApe1xuXG4gICAgICAgICAgICAvLyB0cnkgdG8gbWF0Y2ggdGhlIG90aGVyIHNzcmMtZ3JvdXAgd2l0aCBhbiBzc3JjLWdyb3VwIG9mIG91cnNcbiAgICAgICAgICAgIHZhciBtYXRjaGVkID0gZmFsc2U7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG15TWVkaWEuc3NyY0dyb3Vwcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBteVNzcmNHcm91cCA9IG15TWVkaWEuc3NyY0dyb3Vwc1tpXTtcbiAgICAgICAgICAgICAgICBpZiAob3RoZXJTc3JjR3JvdXAuc2VtYW50aWNzID09IG15U3NyY0dyb3VwLnNlbWFudGljc1xuICAgICAgICAgICAgICAgICAgICAmJiBhcnJheUVxdWFscy5hcHBseShvdGhlclNzcmNHcm91cC5zc3JjcywgW215U3NyY0dyb3VwLnNzcmNzXSkpIHtcblxuICAgICAgICAgICAgICAgICAgICBtYXRjaGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIW1hdGNoZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIGFuIHNzcmMtZ3JvdXAgdGhhdCBkb2Vzbid0XG4gICAgICAgICAgICAgICAgLy8gZXhpc3QgaW4gb3VyIGNoYW5uZWxcblxuICAgICAgICAgICAgICAgIGlmKCFuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0pe1xuICAgICAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0gPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZWRpYWluZGV4OiBvdGhlcnNNZWRpYS5tZWRpYWluZGV4LFxuICAgICAgICAgICAgICAgICAgICAgICAgbWlkOiBvdGhlcnNNZWRpYS5taWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3Jjczoge30sXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3JjR3JvdXBzOiBbXVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0uc3NyY0dyb3Vwcy5wdXNoKG90aGVyU3NyY0dyb3VwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ld01lZGlhO1xufTtcblxuLyoqXG4gKiBTZW5kcyBTU1JDIHVwZGF0ZSBJUS5cbiAqIEBwYXJhbSBzZHBNZWRpYVNzcmNzIFNTUkNzIG1hcCBvYnRhaW5lZCBmcm9tIFNEUC5nZXROZXdNZWRpYS4gQ250YWlucyBTU1JDcyB0byBhZGQvcmVtb3ZlLlxuICogQHBhcmFtIHNpZCBzZXNzaW9uIGlkZW50aWZpZXIgdGhhdCB3aWxsIGJlIHB1dCBpbnRvIHRoZSBJUS5cbiAqIEBwYXJhbSBpbml0aWF0b3IgaW5pdGlhdG9yIGlkZW50aWZpZXIuXG4gKiBAcGFyYW0gdG9KaWQgZGVzdGluYXRpb24gSmlkXG4gKiBAcGFyYW0gaXNBZGQgaW5kaWNhdGVzIGlmIHRoaXMgaXMgcmVtb3ZlIG9yIGFkZCBvcGVyYXRpb24uXG4gKi9cblNEUERpZmZlci5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbihtb2RpZnkpIHtcbiAgICB2YXIgc2RwTWVkaWFTc3JjcyA9IHRoaXMuZ2V0TmV3TWVkaWEoKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBGSVhNRTogb25seSBhbm5vdW5jZSB2aWRlbyBzc3JjcyBzaW5jZSB3ZSBtaXggYXVkaW8gYW5kIGRvbnQgbmVlZFxuICAgIC8vICAgICAgdGhlIGF1ZGlvIHNzcmNzIHRoZXJlZm9yZVxuICAgIHZhciBtb2RpZmllZCA9IGZhbHNlO1xuICAgIE9iamVjdC5rZXlzKHNkcE1lZGlhU3NyY3MpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIG1vZGlmaWVkID0gdHJ1ZTtcbiAgICAgICAgdmFyIG1lZGlhID0gc2RwTWVkaWFTc3Jjc1ttZWRpYWluZGV4XTtcbiAgICAgICAgbW9kaWZ5LmMoJ2NvbnRlbnQnLCB7bmFtZTogbWVkaWEubWlkfSk7XG5cbiAgICAgICAgbW9kaWZ5LmMoJ2Rlc2NyaXB0aW9uJywge3htbG5zOid1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScsIG1lZGlhOiBtZWRpYS5taWR9KTtcbiAgICAgICAgLy8gRklYTUU6IG5vdCBjb21wbGV0bHkgc3VyZSB0aGlzIG9wZXJhdGVzIG9uIGJsb2NrcyBhbmQgLyBvciBoYW5kbGVzIGRpZmZlcmVudCBzc3JjcyBjb3JyZWN0bHlcbiAgICAgICAgLy8gZ2VuZXJhdGUgc291cmNlcyBmcm9tIGxpbmVzXG4gICAgICAgIE9iamVjdC5rZXlzKG1lZGlhLnNzcmNzKS5mb3JFYWNoKGZ1bmN0aW9uKHNzcmNOdW0pIHtcbiAgICAgICAgICAgIHZhciBtZWRpYVNzcmMgPSBtZWRpYS5zc3Jjc1tzc3JjTnVtXTtcbiAgICAgICAgICAgIG1vZGlmeS5jKCdzb3VyY2UnLCB7IHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICBtb2RpZnkuYXR0cnMoe3NzcmM6IG1lZGlhU3NyYy5zc3JjfSk7XG4gICAgICAgICAgICAvLyBpdGVyYXRlIG92ZXIgc3NyYyBsaW5lc1xuICAgICAgICAgICAgbWVkaWFTc3JjLmxpbmVzLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgaWR4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgbW9kaWZ5LmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgIGlmIChrdi5pbmRleE9mKCc6JykgPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3YgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3Yuc3BsaXQoJzonLCAyKVswXSB9KTtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgdmFsdWU6IGt2LnNwbGl0KCc6JywgMilbMV0gfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgcGFyYW1ldGVyXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc291cmNlXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIGdlbmVyYXRlIHNvdXJjZSBncm91cHMgZnJvbSBsaW5lc1xuICAgICAgICBtZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24oc3NyY0dyb3VwKSB7XG4gICAgICAgICAgICBpZiAoc3NyY0dyb3VwLnNzcmNzLmxlbmd0aCAhPSAwKSB7XG5cbiAgICAgICAgICAgICAgICBtb2RpZnkuYygnc3NyYy1ncm91cCcsIHtcbiAgICAgICAgICAgICAgICAgICAgc2VtYW50aWNzOiBzc3JjR3JvdXAuc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBzc3JjR3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAudXAoKTsgLy8gZW5kIG9mIHNvdXJjZVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc3NyYy1ncm91cFxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBtb2RpZnkudXAoKTsgLy8gZW5kIG9mIGRlc2NyaXB0aW9uXG4gICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIG1vZGlmaWVkO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTRFBEaWZmZXI7IiwiU0RQVXRpbCA9IHtcbiAgICBpY2VwYXJhbXM6IGZ1bmN0aW9uIChtZWRpYWRlc2MsIHNlc3Npb25kZXNjKSB7XG4gICAgICAgIHZhciBkYXRhID0gbnVsbDtcbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKG1lZGlhZGVzYywgJ2E9aWNlLXVmcmFnOicsIHNlc3Npb25kZXNjKSAmJlxuICAgICAgICAgICAgU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSkge1xuICAgICAgICAgICAgZGF0YSA9IHtcbiAgICAgICAgICAgICAgICB1ZnJhZzogU0RQVXRpbC5wYXJzZV9pY2V1ZnJhZyhTRFBVdGlsLmZpbmRfbGluZShtZWRpYWRlc2MsICdhPWljZS11ZnJhZzonLCBzZXNzaW9uZGVzYykpLFxuICAgICAgICAgICAgICAgIHB3ZDogU0RQVXRpbC5wYXJzZV9pY2Vwd2QoU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSlcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBwYXJzZV9pY2V1ZnJhZzogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgcmV0dXJuIGxpbmUuc3Vic3RyaW5nKDEyKTtcbiAgICB9LFxuICAgIGJ1aWxkX2ljZXVmcmFnOiBmdW5jdGlvbiAoZnJhZykge1xuICAgICAgICByZXR1cm4gJ2E9aWNlLXVmcmFnOicgKyBmcmFnO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlcHdkOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICByZXR1cm4gbGluZS5zdWJzdHJpbmcoMTApO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlcHdkOiBmdW5jdGlvbiAocHdkKSB7XG4gICAgICAgIHJldHVybiAnYT1pY2UtcHdkOicgKyBwd2Q7XG4gICAgfSxcbiAgICBwYXJzZV9taWQ6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHJldHVybiBsaW5lLnN1YnN0cmluZyg2KTtcbiAgICB9LFxuICAgIHBhcnNlX21saW5lOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygyKS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLm1lZGlhID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wb3J0ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wcm90byA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChwYXJ0c1twYXJ0cy5sZW5ndGggLSAxXSA9PT0gJycpIHsgLy8gdHJhaWxpbmcgd2hpdGVzcGFjZVxuICAgICAgICAgICAgcGFydHMucG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgZGF0YS5mbXQgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBidWlsZF9tbGluZTogZnVuY3Rpb24gKG1saW5lKSB7XG4gICAgICAgIHJldHVybiAnbT0nICsgbWxpbmUubWVkaWEgKyAnICcgKyBtbGluZS5wb3J0ICsgJyAnICsgbWxpbmUucHJvdG8gKyAnICcgKyBtbGluZS5mbXQuam9pbignICcpO1xuICAgIH0sXG4gICAgcGFyc2VfcnRwbWFwOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmlkID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuICAgICAgICBkYXRhLm5hbWUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmNsb2NrcmF0ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPyBwYXJ0cy5zaGlmdCgpIDogJzEnO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIFBhcnNlcyBTRFAgbGluZSBcImE9c2N0cG1hcDouLi5cIiBhbmQgZXh0cmFjdHMgU0NUUCBwb3J0IGZyb20gaXQuXG4gICAgICogQHBhcmFtIGxpbmUgZWcuIFwiYT1zY3RwbWFwOjUwMDAgd2VicnRjLWRhdGFjaGFubmVsXCJcbiAgICAgKiBAcmV0dXJucyBbU0NUUCBwb3J0IG51bWJlciwgcHJvdG9jb2wsIHN0cmVhbXNdXG4gICAgICovXG4gICAgcGFyc2Vfc2N0cG1hcDogZnVuY3Rpb24gKGxpbmUpXG4gICAge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMCkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIHNjdHBQb3J0ID0gcGFydHNbMF07XG4gICAgICAgIHZhciBwcm90b2NvbCA9IHBhcnRzWzFdO1xuICAgICAgICAvLyBTdHJlYW0gY291bnQgaXMgb3B0aW9uYWxcbiAgICAgICAgdmFyIHN0cmVhbUNvdW50ID0gcGFydHMubGVuZ3RoID4gMiA/IHBhcnRzWzJdIDogbnVsbDtcbiAgICAgICAgcmV0dXJuIFtzY3RwUG9ydCwgcHJvdG9jb2wsIHN0cmVhbUNvdW50XTsvLyBTQ1RQIHBvcnRcbiAgICB9LFxuICAgIGJ1aWxkX3J0cG1hcDogZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9cnRwbWFwOicgKyBlbC5nZXRBdHRyaWJ1dGUoJ2lkJykgKyAnICcgKyBlbC5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICcvJyArIGVsLmdldEF0dHJpYnV0ZSgnY2xvY2tyYXRlJyk7XG4gICAgICAgIGlmIChlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJykgJiYgZWwuZ2V0QXR0cmlidXRlKCdjaGFubmVscycpICE9ICcxJykge1xuICAgICAgICAgICAgbGluZSArPSAnLycgKyBlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxpbmU7XG4gICAgfSxcbiAgICBwYXJzZV9jcnlwdG86IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEudGFnID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YVsnY3J5cHRvLXN1aXRlJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhWydrZXktcGFyYW1zJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBpZiAocGFydHMubGVuZ3RoKSB7XG4gICAgICAgICAgICBkYXRhWydzZXNzaW9uLXBhcmFtcyddID0gcGFydHMuam9pbignICcpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfZmluZ2VycHJpbnQ6IGZ1bmN0aW9uIChsaW5lKSB7IC8vIFJGQyA0NTcyXG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDE0KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmhhc2ggPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmZpbmdlcnByaW50ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgLy8gVE9ETyBhc3NlcnQgdGhhdCBmaW5nZXJwcmludCBzYXRpc2ZpZXMgMlVIRVggKihcIjpcIiAyVUhFWCkgP1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2ZtdHA6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3BsaXQoJyAnKSxcbiAgICAgICAgICAgIGksIGtleSwgdmFsdWUsXG4gICAgICAgICAgICBkYXRhID0gW107XG4gICAgICAgIHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIHBhcnRzID0gcGFydHMuam9pbignICcpLnNwbGl0KCc7Jyk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAga2V5ID0gcGFydHNbaV0uc3BsaXQoJz0nKVswXTtcbiAgICAgICAgICAgIHdoaWxlIChrZXkubGVuZ3RoICYmIGtleVswXSA9PSAnICcpIHtcbiAgICAgICAgICAgICAgICBrZXkgPSBrZXkuc3Vic3RyaW5nKDEpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFsdWUgPSBwYXJ0c1tpXS5zcGxpdCgnPScpWzFdO1xuICAgICAgICAgICAgaWYgKGtleSAmJiB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIGRhdGEucHVzaCh7bmFtZToga2V5LCB2YWx1ZTogdmFsdWV9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmZjIDQ3MzMgKERUTUYpIHN0eWxlIHN0dWZmXG4gICAgICAgICAgICAgICAgZGF0YS5wdXNoKHtuYW1lOiAnJywgdmFsdWU6IGtleX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgY2FuZGlkYXRlID0ge30sXG4gICAgICAgICAgICBlbGVtcyA9IGxpbmUuc3BsaXQoJyAnKTtcbiAgICAgICAgY2FuZGlkYXRlLmZvdW5kYXRpb24gPSBlbGVtc1swXS5zdWJzdHJpbmcoMTIpO1xuICAgICAgICBjYW5kaWRhdGUuY29tcG9uZW50ID0gZWxlbXNbMV07XG4gICAgICAgIGNhbmRpZGF0ZS5wcm90b2NvbCA9IGVsZW1zWzJdLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNhbmRpZGF0ZS5wcmlvcml0eSA9IGVsZW1zWzNdO1xuICAgICAgICBjYW5kaWRhdGUuaXAgPSBlbGVtc1s0XTtcbiAgICAgICAgY2FuZGlkYXRlLnBvcnQgPSBlbGVtc1s1XTtcbiAgICAgICAgLy8gZWxlbXNbNl0gPT4gXCJ0eXBcIlxuICAgICAgICBjYW5kaWRhdGUudHlwZSA9IGVsZW1zWzddO1xuICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IDA7IC8vIGRlZmF1bHQgdmFsdWUsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKHZhciBpID0gODsgaSA8IGVsZW1zLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICBzd2l0Y2ggKGVsZW1zW2ldKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAncmFkZHInOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1hZGRyJ10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlWydyZWwtcG9ydCddID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdnZW5lcmF0aW9uJzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RjcHR5cGUnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUudGNwdHlwZSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDogLy8gVE9ET1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VfaWNlY2FuZGlkYXRlIG5vdCB0cmFuc2xhdGluZyBcIicgKyBlbGVtc1tpXSArICdcIiA9IFwiJyArIGVsZW1zW2kgKyAxXSArICdcIicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZS5uZXR3b3JrID0gJzEnO1xuICAgICAgICBjYW5kaWRhdGUuaWQgPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApOyAvLyBub3QgYXBwbGljYWJsZSB0byBTRFAgLS0gRklYTUU6IHNob3VsZCBiZSB1bmlxdWUsIG5vdCBqdXN0IHJhbmRvbVxuICAgICAgICByZXR1cm4gY2FuZGlkYXRlO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAoY2FuZCkge1xuICAgICAgICB2YXIgbGluZSA9IFsnYT1jYW5kaWRhdGU6JyArIGNhbmQuZm91bmRhdGlvbiwgY2FuZC5jb21wb25lbnQsIGNhbmQucHJvdG9jb2wsIGNhbmQucHJpb3JpdHksIGNhbmQuaXAsIGNhbmQucG9ydCwgJ3R5cCcsIGNhbmQudHlwZV0uam9pbignICcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgc3dpdGNoIChjYW5kLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtYWRkciddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncnBvcnQnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtcG9ydCddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNhbmQuaGFzT3duQXR0cmlidXRlKCd0Y3B0eXBlJykpIHtcbiAgICAgICAgICAgIGxpbmUgKz0gJ3RjcHR5cGUnO1xuICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICBsaW5lICs9IGNhbmQudGNwdHlwZTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmhhc093bkF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpID8gY2FuZC5nZW5lcmF0aW9uIDogJzAnO1xuICAgICAgICByZXR1cm4gbGluZTtcbiAgICB9LFxuICAgIHBhcnNlX3NzcmM6IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgICAgIC8vIHByb3ByaWV0YXJ5IG1hcHBpbmcgb2YgYT1zc3JjIGxpbmVzXG4gICAgICAgIC8vIFRPRE86IHNlZSBcIkppbmdsZSBSVFAgU291cmNlIERlc2NyaXB0aW9uXCIgYnkgSnViZXJ0aSBhbmQgUC4gVGhhdGNoZXIgb24gZ29vZ2xlIGRvY3NcbiAgICAgICAgLy8gYW5kIHBhcnNlIGFjY29yZGluZyB0byB0aGF0XG4gICAgICAgIHZhciBsaW5lcyA9IGRlc2Muc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIDcpID09ICdhPXNzcmM6Jykge1xuICAgICAgICAgICAgICAgIHZhciBpZHggPSBsaW5lc1tpXS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgZGF0YVtsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVswXV0gPSBsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVsxXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX3J0Y3BmYjogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoMTApLnNwbGl0KCcgJyk7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGRhdGEucHQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnR5cGUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnBhcmFtcyA9IHBhcnRzO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2V4dG1hcDogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIGRhdGEgPSB7fTtcbiAgICAgICAgZGF0YS52YWx1ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSAhPSAtMSkge1xuICAgICAgICAgICAgZGF0YS5kaXJlY3Rpb24gPSBkYXRhLnZhbHVlLnN1YnN0cihkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSArIDEpO1xuICAgICAgICAgICAgZGF0YS52YWx1ZSA9IGRhdGEudmFsdWUuc3Vic3RyKDAsIGRhdGEudmFsdWUuaW5kZXhPZignLycpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRhdGEuZGlyZWN0aW9uID0gJ2JvdGgnO1xuICAgICAgICB9XG4gICAgICAgIGRhdGEudXJpID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wYXJhbXMgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBmaW5kX2xpbmU6IGZ1bmN0aW9uIChoYXlzdGFjaywgbmVlZGxlLCBzZXNzaW9ucGFydCkge1xuICAgICAgICB2YXIgbGluZXMgPSBoYXlzdGFjay5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGxpbmVzW2ldO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICghc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBzZWFyY2ggc2Vzc2lvbiBwYXJ0XG4gICAgICAgIGxpbmVzID0gc2Vzc2lvbnBhcnQuc3BsaXQoJ1xcclxcbicpO1xuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsaW5lc1tqXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBmaW5kX2xpbmVzOiBmdW5jdGlvbiAoaGF5c3RhY2ssIG5lZWRsZSwgc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgdmFyIGxpbmVzID0gaGF5c3RhY2suc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgbmVlZGxlcyA9IFtdO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSlcbiAgICAgICAgICAgICAgICBuZWVkbGVzLnB1c2gobGluZXNbaV0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChuZWVkbGVzLmxlbmd0aCB8fCAhc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBuZWVkbGVzO1xuICAgICAgICB9XG4gICAgICAgIC8vIHNlYXJjaCBzZXNzaW9uIHBhcnRcbiAgICAgICAgbGluZXMgPSBzZXNzaW9ucGFydC5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tqXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgbmVlZGxlcy5wdXNoKGxpbmVzW2pdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmVlZGxlcztcbiAgICB9LFxuICAgIGNhbmRpZGF0ZVRvSmluZ2xlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAvLyBhPWNhbmRpZGF0ZToyOTc5MTY2NjYyIDEgdWRwIDIxMTM5MzcxNTEgMTkyLjE2OC4yLjEwMCA1NzY5OCB0eXAgaG9zdCBnZW5lcmF0aW9uIDBcbiAgICAgICAgLy8gICAgICA8Y2FuZGlkYXRlIGNvbXBvbmVudD0uLi4gZm91bmRhdGlvbj0uLi4gZ2VuZXJhdGlvbj0uLi4gaWQ9Li4uIGlwPS4uLiBuZXR3b3JrPS4uLiBwb3J0PS4uLiBwcmlvcml0eT0uLi4gcHJvdG9jb2w9Li4uIHR5cGU9Li4uLz5cbiAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignY2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICBsaW5lID0gJ2E9JyArIGxpbmU7XG4gICAgICAgIH0gZWxzZSBpZiAobGluZS5zdWJzdHJpbmcoMCwgMTIpICE9ICdhPWNhbmRpZGF0ZTonKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VDYW5kaWRhdGUgY2FsbGVkIHdpdGggYSBsaW5lIHRoYXQgaXMgbm90IGEgY2FuZGlkYXRlIGxpbmUnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGxpbmUuc3Vic3RyaW5nKGxpbmUubGVuZ3RoIC0gMikgPT0gJ1xcclxcbicpIC8vIGNob21wIGl0XG4gICAgICAgICAgICBsaW5lID0gbGluZS5zdWJzdHJpbmcoMCwgbGluZS5sZW5ndGggLSAyKTtcbiAgICAgICAgdmFyIGNhbmRpZGF0ZSA9IHt9LFxuICAgICAgICAgICAgZWxlbXMgPSBsaW5lLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBpO1xuICAgICAgICBpZiAoZWxlbXNbNl0gIT0gJ3R5cCcpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdkaWQgbm90IGZpbmQgdHlwIGluIHRoZSByaWdodCBwbGFjZScpO1xuICAgICAgICAgICAgY29uc29sZS5sb2cobGluZSk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUuZm91bmRhdGlvbiA9IGVsZW1zWzBdLnN1YnN0cmluZygxMik7XG4gICAgICAgIGNhbmRpZGF0ZS5jb21wb25lbnQgPSBlbGVtc1sxXTtcbiAgICAgICAgY2FuZGlkYXRlLnByb3RvY29sID0gZWxlbXNbMl0udG9Mb3dlckNhc2UoKTtcbiAgICAgICAgY2FuZGlkYXRlLnByaW9yaXR5ID0gZWxlbXNbM107XG4gICAgICAgIGNhbmRpZGF0ZS5pcCA9IGVsZW1zWzRdO1xuICAgICAgICBjYW5kaWRhdGUucG9ydCA9IGVsZW1zWzVdO1xuICAgICAgICAvLyBlbGVtc1s2XSA9PiBcInR5cFwiXG4gICAgICAgIGNhbmRpZGF0ZS50eXBlID0gZWxlbXNbN107XG5cbiAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSAnMCc7IC8vIGRlZmF1bHQsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKGkgPSA4OyBpIDwgZWxlbXMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgICAgICAgIHN3aXRjaCAoZWxlbXNbaV0pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdyYWRkcic6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZVsncmVsLWFkZHInXSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncnBvcnQnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1wb3J0J10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2dlbmVyYXRpb24nOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZS50Y3B0eXBlID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBUT0RPXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdub3QgdHJhbnNsYXRpbmcgXCInICsgZWxlbXNbaV0gKyAnXCIgPSBcIicgKyBlbGVtc1tpICsgMV0gKyAnXCInKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUubmV0d29yayA9ICcxJztcbiAgICAgICAgY2FuZGlkYXRlLmlkID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDEwKTsgLy8gbm90IGFwcGxpY2FibGUgdG8gU0RQIC0tIEZJWE1FOiBzaG91bGQgYmUgdW5pcXVlLCBub3QganVzdCByYW5kb21cbiAgICAgICAgcmV0dXJuIGNhbmRpZGF0ZTtcbiAgICB9LFxuICAgIGNhbmRpZGF0ZUZyb21KaW5nbGU6IGZ1bmN0aW9uIChjYW5kKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9Y2FuZGlkYXRlOic7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2ZvdW5kYXRpb24nKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2NvbXBvbmVudCcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJvdG9jb2wnKTsgLy8udG9VcHBlckNhc2UoKTsgLy8gY2hyb21lIE0yMyBkb2Vzbid0IGxpa2UgdGhpc1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJpb3JpdHknKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2lwJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdwb3J0Jyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9ICd0eXAnO1xuICAgICAgICBsaW5lICs9ICcgJyArIGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBzd2l0Y2ggKGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncmVsLWFkZHInKTtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJ3Jwb3J0JztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3Byb3RvY29sJykudG9Mb3dlckNhc2UoKSA9PSAndGNwJykge1xuICAgICAgICAgICAgbGluZSArPSAndGNwdHlwZSc7XG4gICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3RjcHR5cGUnKTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpIHx8ICcwJztcbiAgICAgICAgcmV0dXJuIGxpbmUgKyAnXFxyXFxuJztcbiAgICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBTRFBVdGlsOyIsImZ1bmN0aW9uIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKGljZV9jb25maWcsIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBSVENQZWVyY29ubmVjdGlvbiA9IG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEgPyBtb3pSVENQZWVyQ29ubmVjdGlvbiA6IHdlYmtpdFJUQ1BlZXJDb25uZWN0aW9uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSBuZXcgUlRDUGVlcmNvbm5lY3Rpb24oaWNlX2NvbmZpZywgY29uc3RyYWludHMpO1xuICAgIHRoaXMudXBkYXRlTG9nID0gW107XG4gICAgdGhpcy5zdGF0cyA9IHt9O1xuICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgdGhpcy5tYXhzdGF0cyA9IDA7IC8vIGxpbWl0IHRvIDMwMCB2YWx1ZXMsIGkuZS4gNSBtaW51dGVzOyBzZXQgdG8gMCB0byBkaXNhYmxlXG4gICAgdmFyIEludGVyb3AgPSByZXF1aXJlKCdzZHAtaW50ZXJvcCcpLkludGVyb3A7XG4gICAgdGhpcy5pbnRlcm9wID0gbmV3IEludGVyb3AoKTtcblxuICAgIC8vIG92ZXJyaWRlIGFzIGRlc2lyZWRcbiAgICB0aGlzLnRyYWNlID0gZnVuY3Rpb24gKHdoYXQsIGluZm8pIHtcbiAgICAgICAgLy9jb25zb2xlLndhcm4oJ1dUUkFDRScsIHdoYXQsIGluZm8pO1xuICAgICAgICBzZWxmLnVwZGF0ZUxvZy5wdXNoKHtcbiAgICAgICAgICAgIHRpbWU6IG5ldyBEYXRlKCksXG4gICAgICAgICAgICB0eXBlOiB3aGF0LFxuICAgICAgICAgICAgdmFsdWU6IGluZm8gfHwgXCJcIlxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2VjYW5kaWRhdGUgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25pY2VjYW5kaWRhdGUnLCBKU09OLnN0cmluZ2lmeShldmVudC5jYW5kaWRhdGUsIG51bGwsICcgJykpO1xuICAgICAgICBpZiAoc2VsZi5vbmljZWNhbmRpZGF0ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25hZGRzdHJlYW0gPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25hZGRzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25hZGRzdHJlYW0nLCBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBpZiAoc2VsZi5vbmFkZHN0cmVhbSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbmFkZHN0cmVhbShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25yZW1vdmVzdHJlYW0gPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25yZW1vdmVzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25yZW1vdmVzdHJlYW0nLCBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBpZiAoc2VsZi5vbnJlbW92ZXN0cmVhbSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbnJlbW92ZXN0cmVhbShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uc2lnbmFsaW5nc3RhdGVjaGFuZ2UnLCBzZWxmLnNpZ25hbGluZ1N0YXRlKTtcbiAgICAgICAgaWYgKHNlbGYub25zaWduYWxpbmdzdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZScsIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICAgICAgaWYgKHNlbGYub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25uZWdvdGlhdGlvbm5lZWRlZCA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbm5lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgIGlmIChzZWxmLm9ubmVnb3RpYXRpb25uZWVkZWQgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25uZWdvdGlhdGlvbm5lZWRlZChldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHNlbGYub25kYXRhY2hhbm5lbCA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmRhdGFjaGFubmVsID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uZGF0YWNoYW5uZWwnLCBldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uZGF0YWNoYW5uZWwgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25kYXRhY2hhbm5lbChldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIGlmICghbmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSAmJiB0aGlzLm1heHN0YXRzKSB7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24oc3RhdHMpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVzdWx0cyA9IHN0YXRzLnJlc3VsdCgpO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzdWx0cy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKHJlc3VsdHNbaV0udHlwZSwgcmVzdWx0c1tpXS5pZCwgcmVzdWx0c1tpXS5uYW1lcygpKVxuICAgICAgICAgICAgICAgICAgICB2YXIgbm93ID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0c1tpXS5uYW1lcygpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpZCA9IHJlc3VsdHNbaV0uaWQgKyAnLScgKyBuYW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFzZWxmLnN0YXRzW2lkXSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydFRpbWU6IG5vdyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kVGltZTogbm93LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM6IFtdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lczogW11cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udmFsdWVzLnB1c2gocmVzdWx0c1tpXS5zdGF0KG5hbWUpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLnRpbWVzLnB1c2gobm93LmdldFRpbWUoKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5zdGF0c1tpZF0udmFsdWVzLmxlbmd0aCA+IHNlbGYubWF4c3RhdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS52YWx1ZXMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS50aW1lcy5zaGlmdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0uZW5kVGltZSA9IG5vdztcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgfSwgMTAwMCk7XG4gICAgfVxufTtcblxuZHVtcFNEUCA9IGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgaWYgKHR5cGVvZiBkZXNjcmlwdGlvbiA9PT0gJ3VuZGVmaW5lZCcgfHwgZGVzY3JpcHRpb24gPT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gJyc7XG4gICAgfVxuXG4gICAgcmV0dXJuICd0eXBlOiAnICsgZGVzY3JpcHRpb24udHlwZSArICdcXHJcXG4nICsgZGVzY3JpcHRpb24uc2RwO1xufTtcblxuaWYgKFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fICE9PSB1bmRlZmluZWQpIHtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnc2lnbmFsaW5nU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGU7IH0pO1xuICAgIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKCdpY2VDb25uZWN0aW9uU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlOyB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnbG9jYWxEZXNjcmlwdGlvbicsIGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLnRyYWNlKCdnZXRMb2NhbERlc2NyaXB0aW9uOjpwcmVUcmFuc2Zvcm0gKFBsYW4gQSknLCBkdW1wU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbikpO1xuICAgICAgICAvLyBpZiB3ZSdyZSBydW5uaW5nIG9uIEZGLCB0cmFuc2Zvcm0gdG8gUGxhbiBCIGZpcnN0LlxuICAgICAgICB2YXIgZGVzYyA9IHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbjtcbiAgICAgICAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgICAgIGRlc2MgPSB0aGlzLmludGVyb3AudG9QbGFuQihkZXNjKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRlc2MgPSBBUFAuc2ltdWxjYXN0LnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbik7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy50cmFjZSgnZ2V0TG9jYWxEZXNjcmlwdGlvbjo6cG9zdFRyYW5zZm9ybSAoUGxhbiBCKScsIGR1bXBTRFAoZGVzYykpO1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygncmVtb3RlRGVzY3JpcHRpb24nLCBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy50cmFjZSgnZ2V0UmVtb3RlRGVzY3JpcHRpb246OnByZVRyYW5zZm9ybSAoUGxhbiBBKScsIGR1bXBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbikpO1xuICAgICAgICAvLyBpZiB3ZSdyZSBydW5uaW5nIG9uIEZGLCB0cmFuc2Zvcm0gdG8gUGxhbiBCIGZpcnN0LlxuICAgICAgICB2YXIgZGVzYyA9IHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb247XG4gICAgICAgIGlmIChuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhKSB7XG4gICAgICAgICAgICBkZXNjID0gdGhpcy5pbnRlcm9wLnRvUGxhbkIoZGVzYyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkZXNjID0gQVBQLnNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24odGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbik7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy50cmFjZSgnZ2V0UmVtb3RlRGVzY3JpcHRpb246OnBvc3RUcmFuc2Zvcm0gKFBsYW4gQiknLCBkdW1wU0RQKGRlc2MpKTtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfSk7XG59XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgdGhpcy50cmFjZSgnYWRkU3RyZWFtJywgc3RyZWFtLmlkKTtcbiAgICBBUFAuc2ltdWxjYXN0LnJlc2V0U2VuZGVyKCk7XG4gICAgdHJ5XG4gICAge1xuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmFkZFN0cmVhbShzdHJlYW0pO1xuICAgIH1cbiAgICBjYXRjaCAoZSlcbiAgICB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSwgc3RvcFN0cmVhbXMpIHtcbiAgICB0aGlzLnRyYWNlKCdyZW1vdmVTdHJlYW0nLCBzdHJlYW0uaWQpO1xuICAgIEFQUC5zaW11bGNhc3QucmVzZXRTZW5kZXIoKTtcbiAgICBpZihzdG9wU3RyZWFtcykge1xuICAgICAgICBzdHJlYW0uZ2V0QXVkaW9UcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAgICAgdHJhY2suc3RvcCgpO1xuICAgICAgICB9KTtcbiAgICAgICAgc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgICAgIHRyYWNrLnN0b3AoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgICAgLy8gRkYgZG9lc24ndCBzdXBwb3J0IHRoaXMgeWV0LlxuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW92ZVN0cmVhbShzdHJlYW0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICB9XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlRGF0YUNoYW5uZWwgPSBmdW5jdGlvbiAobGFiZWwsIG9wdHMpIHtcbiAgICB0aGlzLnRyYWNlKCdjcmVhdGVEYXRhQ2hhbm5lbCcsIGxhYmVsLCBvcHRzKTtcbiAgICByZXR1cm4gdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVEYXRhQ2hhbm5lbChsYWJlbCwgb3B0cyk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjcmlwdGlvbiwgc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICB0aGlzLnRyYWNlKCdzZXRMb2NhbERlc2NyaXB0aW9uOjpwcmVUcmFuc2Zvcm0gKFBsYW4gQiknLCBkdW1wU0RQKGRlc2NyaXB0aW9uKSk7XG4gICAgLy8gaWYgd2UncmUgcnVubmluZyBvbiBGRiwgdHJhbnNmb3JtIHRvIFBsYW4gQSBmaXJzdC5cbiAgICBpZiAobmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSkge1xuICAgICAgICBkZXNjcmlwdGlvbiA9IHRoaXMuaW50ZXJvcC50b1BsYW5BKGRlc2NyaXB0aW9uKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBkZXNjcmlwdGlvbiA9IEFQUC5zaW11bGNhc3QudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihkZXNjcmlwdGlvbik7XG4gICAgfVxuICAgIHRoaXMudHJhY2UoJ3NldExvY2FsRGVzY3JpcHRpb246OnBvc3RUcmFuc2Zvcm0gKFBsYW4gQSknLCBkdW1wU0RQKGRlc2NyaXB0aW9uKSk7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihkZXNjcmlwdGlvbixcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0TG9jYWxEZXNjcmlwdGlvbk9uU3VjY2VzcycpO1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldExvY2FsRGVzY3JpcHRpb25PbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICApO1xuICAgIC8qXG4gICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5tYXhzdGF0cyA+IDApIHtcbiAgICAgLy8gc3RhcnQgZ2F0aGVyaW5nIHN0YXRzXG4gICAgIH1cbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjcmlwdGlvbiwgc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICB0aGlzLnRyYWNlKCdzZXRSZW1vdGVEZXNjcmlwdGlvbjo6cHJlVHJhbnNmb3JtIChQbGFuIEIpJywgZHVtcFNEUChkZXNjcmlwdGlvbikpO1xuICAgIC8vIGlmIHdlJ3JlIHJ1bm5pbmcgb24gRkYsIHRyYW5zZm9ybSB0byBQbGFuIEEgZmlyc3QuXG4gICAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgZGVzY3JpcHRpb24gPSB0aGlzLmludGVyb3AudG9QbGFuQShkZXNjcmlwdGlvbik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBkZXNjcmlwdGlvbiA9IEFQUC5zaW11bGNhc3QudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24oZGVzY3JpcHRpb24pO1xuICAgIH1cbiAgICB0aGlzLnRyYWNlKCdzZXRSZW1vdGVEZXNjcmlwdGlvbjo6cG9zdFRyYW5zZm9ybSAoUGxhbiBBKScsIGR1bXBTRFAoZGVzY3JpcHRpb24pKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRSZW1vdGVEZXNjcmlwdGlvbihkZXNjcmlwdGlvbixcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0UmVtb3RlRGVzY3JpcHRpb25PblN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdzZXRSZW1vdGVEZXNjcmlwdGlvbk9uRmFpbHVyZScsIGVycik7XG4gICAgICAgICAgICBmYWlsdXJlQ2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICk7XG4gICAgLypcbiAgICAgaWYgKHRoaXMuc3RhdHNpbnRlcnZhbCA9PT0gbnVsbCAmJiB0aGlzLm1heHN0YXRzID4gMCkge1xuICAgICAvLyBzdGFydCBnYXRoZXJpbmcgc3RhdHNcbiAgICAgfVxuICAgICAqL1xufTtcblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNsb3NlID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMudHJhY2UoJ3N0b3AnKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uIChzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaywgY29uc3RyYWludHMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy50cmFjZSgnY3JlYXRlT2ZmZXInLCBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cywgbnVsbCwgJyAnKSk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVPZmZlcihcbiAgICAgICAgZnVuY3Rpb24gKG9mZmVyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVPZmZlck9uU3VjY2Vzczo6cHJlVHJhbnNmb3JtIChQbGFuIEEpJywgZHVtcFNEUChvZmZlcikpO1xuICAgICAgICAgICAgLy8gaWYgd2UncmUgcnVubmluZyBvbiBGRiwgdHJhbnNmb3JtIHRvIFBsYW4gQiBmaXJzdC5cbiAgICAgICAgICAgIGlmIChuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhKSB7XG4gICAgICAgICAgICAgICAgb2ZmZXIgPSBzZWxmLmludGVyb3AudG9QbGFuQihvZmZlcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVPZmZlck9uU3VjY2Vzczo6cG9zdFRyYW5zZm9ybSAoUGxhbiBCKScsIGR1bXBTRFAob2ZmZXIpKTtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjayhvZmZlcik7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlT2ZmZXJPbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVBbnN3ZXIgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2ssIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZUFuc3dlcicsIEpTT04uc3RyaW5naWZ5KGNvbnN0cmFpbnRzLCBudWxsLCAnICcpKTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgZnVuY3Rpb24gKGFuc3dlcikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlQW5zd2VyT25TdWNjZXNzOjpwcmVUcmFuc2ZvbSAoUGxhbiBBKScsIGR1bXBTRFAoYW5zd2VyKSk7XG4gICAgICAgICAgICAvLyBpZiB3ZSdyZSBydW5uaW5nIG9uIEZGLCB0cmFuc2Zvcm0gdG8gUGxhbiBBIGZpcnN0LlxuICAgICAgICAgICAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgICAgICAgICBhbnN3ZXIgPSBzZWxmLmludGVyb3AudG9QbGFuQihhbnN3ZXIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBhbnN3ZXIgPSBBUFAuc2ltdWxjYXN0LnRyYW5zZm9ybUFuc3dlcihhbnN3ZXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlQW5zd2VyT25TdWNjZXNzOjpwb3N0VHJhbnNmb20gKFBsYW4gQiknLCBkdW1wU0RQKGFuc3dlcikpO1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKGFuc3dlcik7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlQW5zd2VyT25GYWlsdXJlJywgZXJyKTtcbiAgICAgICAgICAgIGZhaWx1cmVDYWxsYmFjayhlcnIpO1xuICAgICAgICB9LFxuICAgICAgICBjb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24gKGNhbmRpZGF0ZSwgc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy50cmFjZSgnYWRkSWNlQ2FuZGlkYXRlJywgSlNPTi5zdHJpbmdpZnkoY2FuZGlkYXRlLCBudWxsLCAnICcpKTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmFkZEljZUNhbmRpZGF0ZShjYW5kaWRhdGUpO1xuICAgIC8qIG1heWJlIGxhdGVyXG4gICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSxcbiAgICAgZnVuY3Rpb24gKCkge1xuICAgICBzZWxmLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGVPblN1Y2Nlc3MnKTtcbiAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgIH0sXG4gICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgc2VsZi50cmFjZSgnYWRkSWNlQ2FuZGlkYXRlT25GYWlsdXJlJywgZXJyKTtcbiAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgIH1cbiAgICAgKTtcbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uKGNhbGxiYWNrLCBlcnJiYWNrKSB7XG4gICAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgLy8gaWdub3JlIGZvciBub3cuLi5cbiAgICAgICAgaWYoIWVycmJhY2spXG4gICAgICAgICAgICBlcnJiYWNrID0gZnVuY3Rpb24gKCkge1xuXG4gICAgICAgICAgICB9XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMobnVsbCxjYWxsYmFjayxlcnJiYWNrKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKGNhbGxiYWNrKTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uO1xuXG4iLCIvKiBnbG9iYWwgJCwgJGlxLCBBUFAsIGNvbmZpZywgY29ubmVjdGlvbiwgVUksIG1lc3NhZ2VIYW5kbGVyLFxuIHJvb21OYW1lLCBzZXNzaW9uVGVybWluYXRlZCwgU3Ryb3BoZSwgVXRpbCAqL1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi4vc2V0dGluZ3MvU2V0dGluZ3NcIik7XG5cbnZhciBBdXRoZW50aWNhdGlvbkV2ZW50c1xuICAgID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvYXV0aGVudGljYXRpb24vQXV0aGVudGljYXRpb25FdmVudHNcIik7XG5cbi8qKlxuICogQ29udGFpbnMgbG9naWMgcmVzcG9uc2libGUgZm9yIGVuYWJsaW5nL2Rpc2FibGluZyBmdW5jdGlvbmFsaXR5IGF2YWlsYWJsZVxuICogb25seSB0byBtb2RlcmF0b3IgdXNlcnMuXG4gKi9cbnZhciBjb25uZWN0aW9uID0gbnVsbDtcbnZhciBmb2N1c1VzZXJKaWQ7XG5cbmZ1bmN0aW9uIGNyZWF0ZUV4cEJhY2tvZmZUaW1lcihzdGVwKSB7XG4gICAgdmFyIGNvdW50ID0gMTtcbiAgICByZXR1cm4gZnVuY3Rpb24gKHJlc2V0KSB7XG4gICAgICAgIC8vIFJlc2V0IGNhbGxcbiAgICAgICAgaWYgKHJlc2V0KSB7XG4gICAgICAgICAgICBjb3VudCA9IDE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2FsY3VsYXRlIG5leHQgdGltZW91dFxuICAgICAgICB2YXIgdGltZW91dCA9IE1hdGgucG93KDIsIGNvdW50IC0gMSk7XG4gICAgICAgIGNvdW50ICs9IDE7XG4gICAgICAgIHJldHVybiB0aW1lb3V0ICogc3RlcDtcbiAgICB9O1xufVxuXG52YXIgZ2V0TmV4dFRpbWVvdXQgPSBjcmVhdGVFeHBCYWNrb2ZmVGltZXIoMTAwMCk7XG52YXIgZ2V0TmV4dEVycm9yVGltZW91dCA9IGNyZWF0ZUV4cEJhY2tvZmZUaW1lcigxMDAwKTtcbi8vIEV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIHN0dWZmXG52YXIgZXh0ZXJuYWxBdXRoRW5hYmxlZCA9IGZhbHNlO1xuLy8gU2lwIGdhdGV3YXkgY2FuIGJlIGVuYWJsZWQgYnkgY29uZmlndXJpbmcgSmlnYXNpIGhvc3QgaW4gY29uZmlnLmpzIG9yXG4vLyBpdCB3aWxsIGJlIGVuYWJsZWQgYXV0b21hdGljYWxseSBpZiBmb2N1cyBkZXRlY3RzIHRoZSBjb21wb25lbnQgdGhyb3VnaFxuLy8gc2VydmljZSBkaXNjb3ZlcnkuXG52YXIgc2lwR2F0ZXdheUVuYWJsZWQgPSBjb25maWcuaG9zdHMuY2FsbF9jb250cm9sICE9PSB1bmRlZmluZWQ7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBudWxsO1xuXG52YXIgTW9kZXJhdG9yID0ge1xuICAgIGlzTW9kZXJhdG9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uICYmIGNvbm5lY3Rpb24uZW11Yy5pc01vZGVyYXRvcigpO1xuICAgIH0sXG5cbiAgICBpc1BlZXJNb2RlcmF0b3I6IGZ1bmN0aW9uIChwZWVySmlkKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uICYmXG4gICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuZ2V0TWVtYmVyUm9sZShwZWVySmlkKSA9PT0gJ21vZGVyYXRvcic7XG4gICAgfSxcblxuICAgIGlzRXh0ZXJuYWxBdXRoRW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gZXh0ZXJuYWxBdXRoRW5hYmxlZDtcbiAgICB9LFxuXG4gICAgaXNTaXBHYXRld2F5RW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gc2lwR2F0ZXdheUVuYWJsZWQ7XG4gICAgfSxcblxuICAgIHNldENvbm5lY3Rpb246IGZ1bmN0aW9uIChjb24pIHtcbiAgICAgICAgY29ubmVjdGlvbiA9IGNvbjtcbiAgICB9LFxuXG4gICAgaW5pdDogZnVuY3Rpb24gKHhtcHAsIGVtaXR0ZXIpIHtcbiAgICAgICAgdGhpcy54bXBwU2VydmljZSA9IHhtcHA7XG4gICAgICAgIGV2ZW50RW1pdHRlciA9IGVtaXR0ZXI7XG5cbiAgICAgICAgLy8gTWVzc2FnZSBsaXN0ZW5lciB0aGF0IHRhbGtzIHRvIFBPUFVQIHdpbmRvd1xuICAgICAgICBmdW5jdGlvbiBsaXN0ZW5lcihldmVudCkge1xuICAgICAgICAgICAgaWYgKGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5zZXNzaW9uSWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoZXZlbnQub3JpZ2luICE9PSB3aW5kb3cubG9jYXRpb24ub3JpZ2luKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiSWdub3Jpbmcgc2Vzc2lvbklkIGZyb20gZGlmZmVyZW50IG9yaWdpbjogXCIgKyBldmVudC5vcmlnaW4pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCdzZXNzaW9uSWQnLCBldmVudC5kYXRhLnNlc3Npb25JZCk7XG4gICAgICAgICAgICAgICAgLy8gQWZ0ZXIgcG9wdXAgaXMgY2xvc2VkIHdlIHdpbGwgYXV0aGVudGljYXRlXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gUmVnaXN0ZXJcbiAgICAgICAgaWYgKHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKSB7XG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgbGlzdGVuZXIsIGZhbHNlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHdpbmRvdy5hdHRhY2hFdmVudChcIm9ubWVzc2FnZVwiLCBsaXN0ZW5lcik7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgb25NdWNMZWZ0OiBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlNvbWVvbmUgbGVmdCBpcyBpdCBmb2N1cyA/IFwiICsgamlkKTtcbiAgICAgICAgdmFyIHJlc291cmNlID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgaWYgKHJlc291cmNlID09PSAnZm9jdXMnICYmICF0aGlzLnhtcHBTZXJ2aWNlLnNlc3Npb25UZXJtaW5hdGVkKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXG4gICAgICAgICAgICAgICAgXCJGb2N1cyBoYXMgbGVmdCB0aGUgcm9vbSAtIGxlYXZpbmcgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgICAgIC8vaGFuZ1VwKCk7XG4gICAgICAgICAgICAvLyBXZSdkIHJhdGhlciByZWxvYWQgdG8gaGF2ZSBldmVyeXRoaW5nIHJlLWluaXRpYWxpemVkXG4gICAgICAgICAgICAvLyBGSVhNRTogc2hvdyBzb21lIG1lc3NhZ2UgYmVmb3JlIHJlbG9hZFxuICAgICAgICAgICAgbG9jYXRpb24ucmVsb2FkKCk7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIFxuICAgIHNldEZvY3VzVXNlckppZDogZnVuY3Rpb24gKGZvY3VzSmlkKSB7XG4gICAgICAgIGlmICghZm9jdXNVc2VySmlkKSB7XG4gICAgICAgICAgICBmb2N1c1VzZXJKaWQgPSBmb2N1c0ppZDtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkZvY3VzIGppZCBzZXQgdG86IFwiICsgZm9jdXNVc2VySmlkKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBnZXRGb2N1c1VzZXJKaWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGZvY3VzVXNlckppZDtcbiAgICB9LFxuXG4gICAgZ2V0Rm9jdXNDb21wb25lbnQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gR2V0IGZvY3VzIGNvbXBvbmVudCBhZGRyZXNzXG4gICAgICAgIHZhciBmb2N1c0NvbXBvbmVudCA9IGNvbmZpZy5ob3N0cy5mb2N1cztcbiAgICAgICAgLy8gSWYgbm90IHNwZWNpZmllZCB1c2UgZGVmYXVsdDogJ2ZvY3VzLmRvbWFpbidcbiAgICAgICAgaWYgKCFmb2N1c0NvbXBvbmVudCkge1xuICAgICAgICAgICAgZm9jdXNDb21wb25lbnQgPSAnZm9jdXMuJyArIGNvbmZpZy5ob3N0cy5kb21haW47XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZvY3VzQ29tcG9uZW50O1xuICAgIH0sXG5cbiAgICBjcmVhdGVDb25mZXJlbmNlSXE6IGZ1bmN0aW9uIChyb29tTmFtZSkge1xuICAgICAgICAvLyBHZW5lcmF0ZSBjcmVhdGUgY29uZmVyZW5jZSBJUVxuICAgICAgICB2YXIgZWxlbSA9ICRpcSh7dG86IE1vZGVyYXRvci5nZXRGb2N1c0NvbXBvbmVudCgpLCB0eXBlOiAnc2V0J30pO1xuXG4gICAgICAgIC8vIFNlc3Npb24gSWQgdXNlZCBmb3IgYXV0aGVudGljYXRpb25cbiAgICAgICAgdmFyIHNlc3Npb25JZCA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdzZXNzaW9uSWQnKTtcbiAgICAgICAgdmFyIG1hY2hpbmVVSUQgPSBTZXR0aW5ncy5nZXRTZXR0aW5ncygpLnVpZDtcblxuICAgICAgICBjb25zb2xlLmluZm8oXG4gICAgICAgICAgICBcIlNlc3Npb24gSUQ6IFwiICsgc2Vzc2lvbklkICsgXCIgbWFjaGluZSBVSUQ6IFwiICsgbWFjaGluZVVJRCk7XG5cbiAgICAgICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lLFxuICAgICAgICAgICAgJ21hY2hpbmUtdWlkJzogbWFjaGluZVVJRFxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoc2Vzc2lvbklkKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHsgJ3Nlc3Npb24taWQnOiBzZXNzaW9uSWR9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb25maWcuaG9zdHMuYnJpZGdlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2JyaWRnZScsIHZhbHVlOiBjb25maWcuaG9zdHMuYnJpZGdlfSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBUZWxsIHRoZSBmb2N1cyB3ZSBoYXZlIEppZ2FzaSBjb25maWd1cmVkXG4gICAgICAgIGlmIChjb25maWcuaG9zdHMuY2FsbF9jb250cm9sICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2NhbGxfY29udHJvbCcsIHZhbHVlOiBjb25maWcuaG9zdHMuY2FsbF9jb250cm9sfSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLmNoYW5uZWxMYXN0TiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjaGFubmVsTGFzdE4nLCB2YWx1ZTogY29uZmlnLmNoYW5uZWxMYXN0Tn0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5hZGFwdGl2ZUxhc3ROICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2FkYXB0aXZlTGFzdE4nLCB2YWx1ZTogY29uZmlnLmFkYXB0aXZlTGFzdE59KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuYWRhcHRpdmVTaW11bGNhc3QgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnYWRhcHRpdmVTaW11bGNhc3QnLCB2YWx1ZTogY29uZmlnLmFkYXB0aXZlU2ltdWxjYXN0fSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLm9wZW5TY3RwICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ29wZW5TY3RwJywgdmFsdWU6IGNvbmZpZy5vcGVuU2N0cH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHJvb21OYW1lID0gQVBQLlVJLmdlbmVyYXRlUm9vbU5hbWUoKTtcbiAgICAgICAgaWYgKHR5cGVvZiByb29tTmFtZSAhPT0gJ3N0cmluZycpIHJvb21OYW1lID0gJyc7XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlRmlyZWZveFN1cHBvcnQgIT09IHVuZGVmaW5lZCAmJiByb29tTmFtZS5pbmRleE9mKCdyZW1ic29uQCcpID09PSAtMSkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnZW5hYmxlRmlyZWZveEhhY2tzJyxcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGNvbmZpZy5lbmFibGVGaXJlZm94U3VwcG9ydH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICByZXR1cm4gZWxlbTtcbiAgICB9LFxuXG4gICAgcGFyc2VTZXNzaW9uSWQ6IGZ1bmN0aW9uIChyZXN1bHRJcSkge1xuICAgICAgICB2YXIgc2Vzc2lvbklkID0gJChyZXN1bHRJcSkuZmluZCgnY29uZmVyZW5jZScpLmF0dHIoJ3Nlc3Npb24taWQnKTtcbiAgICAgICAgaWYgKHNlc3Npb25JZCkge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKCdSZWNlaXZlZCBzZXNzaW9uSWQ6ICcgKyBzZXNzaW9uSWQpO1xuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ3Nlc3Npb25JZCcsIHNlc3Npb25JZCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgcGFyc2VDb25maWdPcHRpb25zOiBmdW5jdGlvbiAocmVzdWx0SXEpIHtcblxuICAgICAgICBNb2RlcmF0b3Iuc2V0Rm9jdXNVc2VySmlkKFxuICAgICAgICAgICAgJChyZXN1bHRJcSkuZmluZCgnY29uZmVyZW5jZScpLmF0dHIoJ2ZvY3VzamlkJykpO1xuXG4gICAgICAgIHZhciBhdXRoZW50aWNhdGlvbkVuYWJsZWRcbiAgICAgICAgICAgID0gJChyZXN1bHRJcSkuZmluZChcbiAgICAgICAgICAgICAgICAnPmNvbmZlcmVuY2U+cHJvcGVydHknICtcbiAgICAgICAgICAgICAgICAnW25hbWU9XFwnYXV0aGVudGljYXRpb25cXCddW3ZhbHVlPVxcJ3RydWVcXCddJykubGVuZ3RoID4gMDtcblxuICAgICAgICBjb25zb2xlLmluZm8oXCJBdXRoZW50aWNhdGlvbiBlbmFibGVkOiBcIiArIGF1dGhlbnRpY2F0aW9uRW5hYmxlZCk7XG5cbiAgICAgICAgZXh0ZXJuYWxBdXRoRW5hYmxlZFxuICAgICAgICAgICAgPSAkKHJlc3VsdElxKS5maW5kKFxuICAgICAgICAgICAgICAgICc+Y29uZmVyZW5jZT5wcm9wZXJ0eScgK1xuICAgICAgICAgICAgICAgICdbbmFtZT1cXCdleHRlcm5hbEF1dGhcXCddW3ZhbHVlPVxcJ3RydWVcXCddJykubGVuZ3RoID4gMDtcblxuICAgICAgICBjb25zb2xlLmluZm8oJ0V4dGVybmFsIGF1dGhlbnRpY2F0aW9uIGVuYWJsZWQ6ICcgKyBleHRlcm5hbEF1dGhFbmFibGVkKTtcblxuICAgICAgICBpZiAoIWV4dGVybmFsQXV0aEVuYWJsZWQpIHtcbiAgICAgICAgICAgIC8vIFdlIGV4cGVjdCB0byByZWNlaXZlIHNlc3Npb25JZCBpbiAnaW50ZXJuYWwnIGF1dGhlbnRpY2F0aW9uIG1vZGVcbiAgICAgICAgICAgIE1vZGVyYXRvci5wYXJzZVNlc3Npb25JZChyZXN1bHRJcSk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgYXV0aElkZW50aXR5ID0gJChyZXN1bHRJcSkuZmluZCgnPmNvbmZlcmVuY2UnKS5hdHRyKCdpZGVudGl0eScpO1xuXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KEF1dGhlbnRpY2F0aW9uRXZlbnRzLklERU5USVRZX1VQREFURUQsXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbkVuYWJsZWQsIGF1dGhJZGVudGl0eSk7XG4gICAgXG4gICAgICAgIC8vIENoZWNrIGlmIGZvY3VzIGhhcyBhdXRvLWRldGVjdGVkIEppZ2FzaSBjb21wb25lbnQodGhpcyB3aWxsIGJlIGFsc29cbiAgICAgICAgLy8gaW5jbHVkZWQgaWYgd2UgaGF2ZSBwYXNzZWQgb3VyIGhvc3QgZnJvbSB0aGUgY29uZmlnKVxuICAgICAgICBpZiAoJChyZXN1bHRJcSkuZmluZChcbiAgICAgICAgICAgICc+Y29uZmVyZW5jZT5wcm9wZXJ0eScgK1xuICAgICAgICAgICAgJ1tuYW1lPVxcJ3NpcEdhdGV3YXlFbmFibGVkXFwnXVt2YWx1ZT1cXCd0cnVlXFwnXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2lwR2F0ZXdheUVuYWJsZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlNpcCBnYXRld2F5IGVuYWJsZWQ6IFwiICsgc2lwR2F0ZXdheUVuYWJsZWQpO1xuICAgIH0sXG5cbiAgICAvLyBGSVhNRTogd2UgbmVlZCB0byBzaG93IHRoZSBmYWN0IHRoYXQgd2UncmUgd2FpdGluZyBmb3IgdGhlIGZvY3VzXG4gICAgLy8gdG8gdGhlIHVzZXIob3IgdGhhdCBmb2N1cyBpcyBub3QgYXZhaWxhYmxlKVxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbiAocm9vbU5hbWUsIGNhbGxiYWNrKSB7XG4gICAgICAgIC8vIFRyeSB0byB1c2UgZm9jdXMgdXNlciBKSUQgZnJvbSB0aGUgY29uZmlnXG4gICAgICAgIE1vZGVyYXRvci5zZXRGb2N1c1VzZXJKaWQoY29uZmlnLmZvY3VzVXNlckppZCk7XG4gICAgICAgIC8vIFNlbmQgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGlxID0gTW9kZXJhdG9yLmNyZWF0ZUNvbmZlcmVuY2VJcShyb29tTmFtZSk7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICBpcSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcblxuICAgICAgICAgICAgICAgIC8vIFNldHVwIGNvbmZpZyBvcHRpb25zXG4gICAgICAgICAgICAgICAgTW9kZXJhdG9yLnBhcnNlQ29uZmlnT3B0aW9ucyhyZXN1bHQpO1xuXG4gICAgICAgICAgICAgICAgaWYgKCd0cnVlJyA9PT0gJChyZXN1bHQpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdyZWFkeScpKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFJlc2V0IGJvdGggdGltZXJzXG4gICAgICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBnZXROZXh0RXJyb3JUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAvLyBFeGVjIGNhbGxiYWNrXG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHdhaXRNcyA9IGdldE5leHRUaW1lb3V0KCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIldhaXRpbmcgZm9yIHRoZSBmb2N1cy4uLiBcIiArIHdhaXRNcyk7XG4gICAgICAgICAgICAgICAgICAgIC8vIFJlc2V0IGVycm9yIHRpbWVvdXRcbiAgICAgICAgICAgICAgICAgICAgZ2V0TmV4dEVycm9yVGltZW91dCh0cnVlKTtcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb29tTmFtZSwgY2FsbGJhY2spO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSwgd2FpdE1zKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgLy8gSW52YWxpZCBzZXNzaW9uID8gcmVtb3ZlIGFuZCB0cnkgYWdhaW5cbiAgICAgICAgICAgICAgICAvLyB3aXRob3V0IHNlc3Npb24gSUQgdG8gZ2V0IGEgbmV3IG9uZVxuICAgICAgICAgICAgICAgIHZhciBpbnZhbGlkU2Vzc2lvblxuICAgICAgICAgICAgICAgICAgICA9ICQoZXJyb3IpLmZpbmQoJz5lcnJvcj5zZXNzaW9uLWludmFsaWQnKS5sZW5ndGg7XG4gICAgICAgICAgICAgICAgaWYgKGludmFsaWRTZXNzaW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIlNlc3Npb24gZXhwaXJlZCEgLSByZW1vdmluZ1wiKTtcbiAgICAgICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oXCJzZXNzaW9uSWRcIik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICgkKGVycm9yKS5maW5kKCc+ZXJyb3I+Z3JhY2VmdWwtc2h1dGRvd24nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5HUkFDRUZVTF9TSFVURE9XTik7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIGVycm9yIHJldHVybmVkIGJ5IHRoZSByZXNlcnZhdGlvbiBzeXN0ZW1cbiAgICAgICAgICAgICAgICB2YXIgcmVzZXJ2YXRpb25FcnIgPSAkKGVycm9yKS5maW5kKCc+ZXJyb3I+cmVzZXJ2YXRpb24tZXJyb3InKTtcbiAgICAgICAgICAgICAgICBpZiAocmVzZXJ2YXRpb25FcnIubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFRyaWdnZXIgZXJyb3IgZXZlbnRcbiAgICAgICAgICAgICAgICAgICAgdmFyIGVycm9yQ29kZSA9IHJlc2VydmF0aW9uRXJyLmF0dHIoJ2Vycm9yLWNvZGUnKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGVycm9yTXNnO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChlcnJvcikuZmluZCgnPmVycm9yPnRleHQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JNc2cgPSAkKGVycm9yKS5maW5kKCc+ZXJyb3I+dGV4dCcpLnRleHQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChcbiAgICAgICAgICAgICAgICAgICAgICAgIFhNUFBFdmVudHMuUkVTRVJWQVRJT05fRVJST1IsIGVycm9yQ29kZSwgZXJyb3JNc2cpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIE5vdCBhdXRob3JpemVkIHRvIGNyZWF0ZSBuZXcgcm9vbVxuICAgICAgICAgICAgICAgIGlmICgkKGVycm9yKS5maW5kKCc+ZXJyb3I+bm90LWF1dGhvcml6ZWQnKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiVW5hdXRob3JpemVkIHRvIHN0YXJ0IHRoZSBjb25mZXJlbmNlXCIsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHRvRG9tYWluXG4gICAgICAgICAgICAgICAgICAgICAgICA9IFN0cm9waGUuZ2V0RG9tYWluRnJvbUppZChlcnJvci5nZXRBdHRyaWJ1dGUoJ3RvJykpO1xuICAgICAgICAgICAgICAgICAgICBpZiAodG9Eb21haW4gIT09IGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBcImlzIGV4dGVybmFsXCIgc2hvdWxkIGNvbWUgZWl0aGVyIGZyb21cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoZSBmb2N1cyBvciBjb25maWcuanNcbiAgICAgICAgICAgICAgICAgICAgICAgIGV4dGVybmFsQXV0aEVuYWJsZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFxuICAgICAgICAgICAgICAgICAgICAgICAgWE1QUEV2ZW50cy5BVVRIRU5USUNBVElPTl9SRVFVSVJFRCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dEVycm9yVGltZW91dCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGb2N1cyBlcnJvciwgcmV0cnkgYWZ0ZXIgXCIgKyB3YWl0TXMsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAvLyBTaG93IG1lc3NhZ2VcbiAgICAgICAgICAgICAgICB2YXIgZm9jdXNDb21wb25lbnQgPSBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKTtcbiAgICAgICAgICAgICAgICB2YXIgcmV0cnlTZWMgPSB3YWl0TXMgLyAxMDAwO1xuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBtZXNzYWdlIGlzIGR1cGxpY2F0ZWQgP1xuICAgICAgICAgICAgICAgIC8vIERvIG5vdCBzaG93IGluIGNhc2Ugb2Ygc2Vzc2lvbiBpbnZhbGlkXG4gICAgICAgICAgICAgICAgLy8gd2hpY2ggbWVhbnMganVzdCBhIHJldHJ5XG4gICAgICAgICAgICAgICAgaWYgKCFpbnZhbGlkU2Vzc2lvbikge1xuICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIubm90aWZ5KFxuICAgICAgICAgICAgICAgICAgICAgICAgbnVsbCwgXCJub3RpZnkuZm9jdXNcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICdkaXNjb25uZWN0ZWQnLCBcIm5vdGlmeS5mb2N1c0ZhaWxcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIHtjb21wb25lbnQ6IGZvY3VzQ29tcG9uZW50LCBtczogcmV0cnlTZWN9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gUmVzZXQgcmVzcG9uc2UgdGltZW91dFxuICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMocm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgfSwgd2FpdE1zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuXG4gICAgZ2V0TG9naW5Vcmw6IGZ1bmN0aW9uIChyb29tTmFtZSwgdXJsQ2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGlxID0gJGlxKHt0bzogTW9kZXJhdG9yLmdldEZvY3VzQ29tcG9uZW50KCksIHR5cGU6ICdnZXQnfSk7XG4gICAgICAgIGlxLmMoJ2xvZ2luLXVybCcsIHtcbiAgICAgICAgICAgIHhtbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9wcm90b2NvbC9mb2N1cycsXG4gICAgICAgICAgICByb29tOiByb29tTmFtZSxcbiAgICAgICAgICAgICdtYWNoaW5lLXVpZCc6IFNldHRpbmdzLmdldFNldHRpbmdzKCkudWlkXG4gICAgICAgIH0pO1xuICAgICAgICBjb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgIGlxLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSAkKHJlc3VsdCkuZmluZCgnbG9naW4tdXJsJykuYXR0cigndXJsJyk7XG4gICAgICAgICAgICAgICAgdXJsID0gdXJsID0gZGVjb2RlVVJJQ29tcG9uZW50KHVybCk7XG4gICAgICAgICAgICAgICAgaWYgKHVybCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJHb3QgYXV0aCB1cmw6IFwiICsgdXJsKTtcbiAgICAgICAgICAgICAgICAgICAgdXJsQ2FsbGJhY2sodXJsKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJGYWlsZWQgdG8gZ2V0IGF1dGggdXJsIGZyb20gdGhlIGZvY3VzXCIsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJHZXQgYXV0aCB1cmwgZXJyb3JcIiwgZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sXG4gICAgZ2V0UG9wdXBMb2dpblVybDogZnVuY3Rpb24gKHJvb21OYW1lLCB1cmxDYWxsYmFjaykge1xuICAgICAgICB2YXIgaXEgPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ2dldCd9KTtcbiAgICAgICAgaXEuYygnbG9naW4tdXJsJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lLFxuICAgICAgICAgICAgJ21hY2hpbmUtdWlkJzogU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKS51aWQsXG4gICAgICAgICAgICBwb3B1cDogdHJ1ZVxuICAgICAgICB9KTtcbiAgICAgICAgY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICBpcSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICB2YXIgdXJsID0gJChyZXN1bHQpLmZpbmQoJ2xvZ2luLXVybCcpLmF0dHIoJ3VybCcpO1xuICAgICAgICAgICAgICAgIHVybCA9IHVybCA9IGRlY29kZVVSSUNvbXBvbmVudCh1cmwpO1xuICAgICAgICAgICAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiR290IFBPUFVQIGF1dGggdXJsOiBcIiArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIHVybENhbGxiYWNrKHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiRmFpbGVkIHRvIGdldCBQT1BVUCBhdXRoIHVybCBmcm9tIHRoZSBmb2N1c1wiLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdHZXQgUE9QVVAgYXV0aCB1cmwgZXJyb3InLCBlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSxcbiAgICBsb2dvdXQ6IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICB2YXIgaXEgPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ3NldCd9KTtcbiAgICAgICAgdmFyIHNlc3Npb25JZCA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdzZXNzaW9uSWQnKTtcbiAgICAgICAgaWYgKCFzZXNzaW9uSWQpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaXEuYygnbG9nb3V0Jywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgICdzZXNzaW9uLWlkJzogc2Vzc2lvbklkXG4gICAgICAgIH0pO1xuICAgICAgICBjb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgIGlxLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgIHZhciBsb2dvdXRVcmwgPSAkKHJlc3VsdCkuZmluZCgnbG9nb3V0JykuYXR0cignbG9nb3V0LXVybCcpO1xuICAgICAgICAgICAgICAgIGlmIChsb2dvdXRVcmwpIHtcbiAgICAgICAgICAgICAgICAgICAgbG9nb3V0VXJsID0gZGVjb2RlVVJJQ29tcG9uZW50KGxvZ291dFVybCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkxvZyBvdXQgT0ssIHVybDogXCIgKyBsb2dvdXRVcmwsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oJ3Nlc3Npb25JZCcpO1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGxvZ291dFVybCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkxvZ291dCBlcnJvclwiLCBlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBNb2RlcmF0b3I7XG5cblxuXG4iLCIvKiBnbG9iYWwgJCwgJGlxLCBjb25maWcsIGNvbm5lY3Rpb24sIGZvY3VzTXVjSmlkLCBtZXNzYWdlSGFuZGxlciwgTW9kZXJhdG9yLFxuICAgVG9vbGJhciwgVXRpbCAqL1xudmFyIE1vZGVyYXRvciA9IHJlcXVpcmUoXCIuL21vZGVyYXRvclwiKTtcblxuXG52YXIgcmVjb3JkaW5nVG9rZW4gPSBudWxsO1xudmFyIHJlY29yZGluZ0VuYWJsZWQ7XG5cbi8qKlxuICogV2hldGhlciB0byB1c2UgYSBqaXJlY29uIGNvbXBvbmVudCBmb3IgcmVjb3JkaW5nLCBvciB1c2UgdGhlIHZpZGVvYnJpZGdlXG4gKiB0aHJvdWdoIENPTElCUkkuXG4gKi9cbnZhciB1c2VKaXJlY29uID0gKHR5cGVvZiBjb25maWcuaG9zdHMuamlyZWNvbiAhPSBcInVuZGVmaW5lZFwiKTtcblxuLyoqXG4gKiBUaGUgSUQgb2YgdGhlIGppcmVjb24gcmVjb3JkaW5nIHNlc3Npb24uIEppcmVjb24gZ2VuZXJhdGVzIGl0IHdoZW4gd2VcbiAqIGluaXRpYWxseSBzdGFydCByZWNvcmRpbmcsIGFuZCBpdCBuZWVkcyB0byBiZSB1c2VkIGluIHN1YnNlcXVlbnQgcmVxdWVzdHNcbiAqIHRvIGppcmVjb24uXG4gKi9cbnZhciBqaXJlY29uUmlkID0gbnVsbDtcblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nVG9rZW4odG9rZW4pIHtcbiAgICByZWNvcmRpbmdUb2tlbiA9IHRva2VuO1xufVxuXG5mdW5jdGlvbiBzZXRSZWNvcmRpbmcoc3RhdGUsIHRva2VuLCBjYWxsYmFjaywgY29ubmVjdGlvbikge1xuICAgIGlmICh1c2VKaXJlY29uKXtcbiAgICAgICAgc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBzZXRSZWNvcmRpbmdDb2xpYnJpKHN0YXRlLCB0b2tlbiwgY2FsbGJhY2ssIGNvbm5lY3Rpb24pO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKSB7XG4gICAgaWYgKHN0YXRlID09IHJlY29yZGluZ0VuYWJsZWQpe1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGlxID0gJGlxKHt0bzogY29uZmlnLmhvc3RzLmppcmVjb24sIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ3JlY29yZGluZycsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvamlyZWNvbicsXG4gICAgICAgICAgICBhY3Rpb246IHN0YXRlID8gJ3N0YXJ0JyA6ICdzdG9wJyxcbiAgICAgICAgICAgIG11Y2ppZDogY29ubmVjdGlvbi5lbXVjLnJvb21qaWR9KTtcbiAgICBpZiAoIXN0YXRlKXtcbiAgICAgICAgaXEuYXR0cnMoe3JpZDogamlyZWNvblJpZH0pO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdTdGFydCByZWNvcmRpbmcnKTtcblxuICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICBpcSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgLy8gVE9ETyB3YWl0IGZvciBhbiBJUSB3aXRoIHRoZSByZWFsIHN0YXR1cywgc2luY2UgdGhpcyBpc1xuICAgICAgICAgICAgLy8gcHJvdmlzaW9uYWw/XG4gICAgICAgICAgICBqaXJlY29uUmlkID0gJChyZXN1bHQpLmZpbmQoJ3JlY29yZGluZycpLmF0dHIoJ3JpZCcpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1JlY29yZGluZyAnICsgKHN0YXRlID8gJ3N0YXJ0ZWQnIDogJ3N0b3BwZWQnKSArXG4gICAgICAgICAgICAgICAgJyhqaXJlY29uKScgKyByZXN1bHQpO1xuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IHN0YXRlO1xuICAgICAgICAgICAgaWYgKCFzdGF0ZSl7XG4gICAgICAgICAgICAgICAgamlyZWNvblJpZCA9IG51bGw7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKHN0YXRlKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRmFpbGVkIHRvIHN0YXJ0IHJlY29yZGluZywgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9KTtcbn1cblxuLy8gU2VuZHMgYSBDT0xJQlJJIG1lc3NhZ2Ugd2hpY2ggZW5hYmxlcyBvciBkaXNhYmxlcyAoYWNjb3JkaW5nIHRvICdzdGF0ZScpXG4vLyB0aGUgcmVjb3JkaW5nIG9uIHRoZSBicmlkZ2UuIFdhaXRzIGZvciB0aGUgcmVzdWx0IElRIGFuZCBjYWxscyAnY2FsbGJhY2snXG4vLyB3aXRoIHRoZSBuZXcgcmVjb3JkaW5nIHN0YXRlLCBhY2NvcmRpbmcgdG8gdGhlIElRLlxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nQ29saWJyaShzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKSB7XG4gICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBjb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQsIHR5cGU6ICdzZXQnfSk7XG4gICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvY29saWJyaSdcbiAgICB9KTtcbiAgICBlbGVtLmMoJ3JlY29yZGluZycsIHtzdGF0ZTogc3RhdGUsIHRva2VuOiB0b2tlbn0pO1xuXG4gICAgY29ubmVjdGlvbi5zZW5kSVEoZWxlbSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1NldCByZWNvcmRpbmcgXCInLCBzdGF0ZSwgJ1wiLiBSZXN1bHQ6JywgcmVzdWx0KTtcbiAgICAgICAgICAgIHZhciByZWNvcmRpbmdFbGVtID0gJChyZXN1bHQpLmZpbmQoJz5jb25mZXJlbmNlPnJlY29yZGluZycpO1xuICAgICAgICAgICAgdmFyIG5ld1N0YXRlID0gKCd0cnVlJyA9PT0gcmVjb3JkaW5nRWxlbS5hdHRyKCdzdGF0ZScpKTtcblxuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IG5ld1N0YXRlO1xuICAgICAgICAgICAgY2FsbGJhY2sobmV3U3RhdGUpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihlcnJvcik7XG4gICAgICAgICAgICBjYWxsYmFjayhyZWNvcmRpbmdFbmFibGVkKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cbnZhciBSZWNvcmRpbmcgPSB7XG4gICAgdG9nZ2xlUmVjb3JkaW5nOiBmdW5jdGlvbiAodG9rZW5FbXB0eUNhbGxiYWNrLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjaywgY29ubmVjdGlvbikge1xuICAgICAgICBpZiAoIU1vZGVyYXRvci5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgJ25vbi1mb2N1cywgb3IgY29uZmVyZW5jZSBub3QgeWV0IG9yZ2FuaXplZDonICtcbiAgICAgICAgICAgICAgICAgICAgJyBub3QgZW5hYmxpbmcgcmVjb3JkaW5nJyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgIC8vIEppcmVjb24gZG9lcyBub3QgKGN1cnJlbnRseSkgc3VwcG9ydCBhIHRva2VuLlxuICAgICAgICBpZiAoIXJlY29yZGluZ1Rva2VuICYmICF1c2VKaXJlY29uKSB7XG4gICAgICAgICAgICB0b2tlbkVtcHR5Q2FsbGJhY2soZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgc2V0UmVjb3JkaW5nVG9rZW4odmFsdWUpO1xuICAgICAgICAgICAgICAgIHNlbGYudG9nZ2xlUmVjb3JkaW5nKHRva2VuRW1wdHlDYWxsYmFjayxcbiAgICAgICAgICAgICAgICAgICAgc3RhcnRpbmdDYWxsYmFjaywgc3RhcnRlZENhbGxiYWNrLCBjb25uZWN0aW9uKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgb2xkU3RhdGUgPSByZWNvcmRpbmdFbmFibGVkO1xuICAgICAgICBzdGFydGluZ0NhbGxiYWNrKCFvbGRTdGF0ZSk7XG4gICAgICAgIHNldFJlY29yZGluZyghb2xkU3RhdGUsXG4gICAgICAgICAgICByZWNvcmRpbmdUb2tlbixcbiAgICAgICAgICAgIGZ1bmN0aW9uIChzdGF0ZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiTmV3IHJlY29yZGluZyBzdGF0ZTogXCIsIHN0YXRlKTtcbiAgICAgICAgICAgICAgICBpZiAoc3RhdGUgPT09IG9sZFN0YXRlKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBuZXcgZm9jdXM6XG4gICAgICAgICAgICAgICAgICAgIC8vIHRoaXMgd2lsbCBub3Qgd29yayB3aGVuIG1vZGVyYXRvciBjaGFuZ2VzXG4gICAgICAgICAgICAgICAgICAgIC8vIGR1cmluZyBhY3RpdmUgc2Vzc2lvbi4gVGhlbiBpdCB3aWxsIGFzc3VtZSB0aGF0XG4gICAgICAgICAgICAgICAgICAgIC8vIHJlY29yZGluZyBzdGF0dXMgaGFzIGNoYW5nZWQgdG8gdHJ1ZSwgYnV0IGl0IG1pZ2h0IGhhdmVcbiAgICAgICAgICAgICAgICAgICAgLy8gYmVlbiBhbHJlYWR5IHRydWUoYW5kIHdlIG9ubHkgcmVjZWl2ZWQgYWN0dWFsIHN0YXR1cyBmcm9tXG4gICAgICAgICAgICAgICAgICAgIC8vIHRoZSBmb2N1cykuXG4gICAgICAgICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgICAgICAgIC8vIFNPIHdlIHN0YXJ0IHdpdGggc3RhdHVzIG51bGwsIHNvIHRoYXQgaXQgaXMgaW5pdGlhbGl6ZWRcbiAgICAgICAgICAgICAgICAgICAgLy8gaGVyZSBhbmQgd2lsbCBmYWlsIG9ubHkgYWZ0ZXIgc2Vjb25kIGNsaWNrLCBzbyBpZiBpbnZhbGlkXG4gICAgICAgICAgICAgICAgICAgIC8vIHRva2VuIHdhcyB1c2VkIHdlIGhhdmUgdG8gcHJlc3MgdGhlIGJ1dHRvbiB0d2ljZSBiZWZvcmVcbiAgICAgICAgICAgICAgICAgICAgLy8gY3VycmVudCBzdGF0dXMgd2lsbCBiZSBmZXRjaGVkIGFuZCB0b2tlbiB3aWxsIGJlIHJlc2V0LlxuICAgICAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgICAgICAvLyBSZWxpYWJsZSB3YXkgd291bGQgYmUgdG8gcmV0dXJuIGF1dGhlbnRpY2F0aW9uIGVycm9yLlxuICAgICAgICAgICAgICAgICAgICAvLyBPciBzdGF0dXMgdXBkYXRlIHdoZW4gbW9kZXJhdG9yIGNvbm5lY3RzLlxuICAgICAgICAgICAgICAgICAgICAvLyBPciB3ZSBoYXZlIHRvIHN0b3AgcmVjb3JkaW5nIHNlc3Npb24gd2hlbiBjdXJyZW50XG4gICAgICAgICAgICAgICAgICAgIC8vIG1vZGVyYXRvciBsZWF2ZXMgdGhlIHJvb20uXG5cbiAgICAgICAgICAgICAgICAgICAgLy8gRmFpbGVkIHRvIGNoYW5nZSwgcmVzZXQgdGhlIHRva2VuIGJlY2F1c2UgaXQgbWlnaHRcbiAgICAgICAgICAgICAgICAgICAgLy8gaGF2ZSBiZWVuIHdyb25nXG4gICAgICAgICAgICAgICAgICAgIHNldFJlY29yZGluZ1Rva2VuKG51bGwpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBzdGFydGVkQ2FsbGJhY2soc3RhdGUpO1xuXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgY29ubmVjdGlvblxuICAgICAgICApO1xuICAgIH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFJlY29yZGluZzsiLCIvKiBqc2hpbnQgLVcxMTcgKi9cbi8qIGEgc2ltcGxlIE1VQyBjb25uZWN0aW9uIHBsdWdpblxuICogY2FuIG9ubHkgaGFuZGxlIGEgc2luZ2xlIE1VQyByb29tXG4gKi9cbnZhciBYTVBQRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UveG1wcC9YTVBQRXZlbnRzXCIpO1xudmFyIE1vZGVyYXRvciA9IHJlcXVpcmUoXCIuL21vZGVyYXRvclwiKTtcbnZhciBKaW5nbGVTZXNzaW9uID0gcmVxdWlyZShcIi4vSmluZ2xlU2Vzc2lvblwiKTtcblxudmFyIGJyaWRnZUlzRG93biA9IGZhbHNlO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKFhNUFAsIGV2ZW50RW1pdHRlcikge1xuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignZW11YycsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgcm9vbWppZDogbnVsbCxcbiAgICAgICAgbXlyb29tamlkOiBudWxsLFxuICAgICAgICBtZW1iZXJzOiB7fSxcbiAgICAgICAgbGlzdF9tZW1iZXJzOiBbXSwgLy8gc28gd2UgY2FuIGVsZWN0IGEgbmV3IGZvY3VzXG4gICAgICAgIHByZXNNYXA6IHt9LFxuICAgICAgICBwcmV6aU1hcDoge30sXG4gICAgICAgIGpvaW5lZDogZmFsc2UsXG4gICAgICAgIGlzT3duZXI6IGZhbHNlLFxuICAgICAgICByb2xlOiBudWxsLFxuICAgICAgICBmb2N1c011Y0ppZDogbnVsbCxcbiAgICAgICAgc3NyYzJqaWQ6IHt9LFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdFByZXNlbmNlTWFwOiBmdW5jdGlvbiAobXlyb29tamlkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3RvJ10gPSBteXJvb21qaWQ7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3hucyddID0gJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211Yyc7XG4gICAgICAgIH0sXG4gICAgICAgIGRvSm9pbjogZnVuY3Rpb24gKGppZCwgcGFzc3dvcmQpIHtcbiAgICAgICAgICAgIHRoaXMubXlyb29tamlkID0gamlkO1xuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJKb2luZWQgTVVDIGFzIFwiICsgdGhpcy5teXJvb21qaWQpO1xuXG4gICAgICAgICAgICB0aGlzLmluaXRQcmVzZW5jZU1hcCh0aGlzLm15cm9vbWppZCk7XG5cbiAgICAgICAgICAgIGlmICghdGhpcy5yb29tamlkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yb29tamlkID0gU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgICAgIC8vIGFkZCBoYW5kbGVycyAoanVzdCBvbmNlKVxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25QcmVzZW5jZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCBudWxsLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VVbmF2YWlsYWJsZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAndW5hdmFpbGFibGUnLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VFcnJvci5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAnZXJyb3InLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uTWVzc2FnZS5iaW5kKHRoaXMpLCBudWxsLCAnbWVzc2FnZScsIG51bGwsIG51bGwsIHRoaXMucm9vbWppZCwge21hdGNoQmFyZTogdHJ1ZX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHBhc3N3b3JkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10gPSBwYXNzd29yZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuc2VuZFByZXNlbmNlKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGRvTGVhdmU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiZG8gbGVhdmVcIiwgdGhpcy5teXJvb21qaWQpO1xuICAgICAgICAgICAgdmFyIHByZXMgPSAkcHJlcyh7dG86IHRoaXMubXlyb29tamlkLCB0eXBlOiAndW5hdmFpbGFibGUnIH0pO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwLmxlbmd0aCA9IDA7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChwcmVzKTtcbiAgICAgICAgfSxcbiAgICAgICAgY3JlYXRlTm9uQW5vbnltb3VzUm9vbTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAwNDUuaHRtbCNjcmVhdGVyb29tLXJlc2VydmVkXG5cbiAgICAgICAgICAgIHZhciBnZXRGb3JtID0gJGlxKHt0eXBlOiAnZ2V0JywgdG86IHRoaXMucm9vbWppZH0pXG4gICAgICAgICAgICAgICAgLmMoJ3F1ZXJ5Jywge3htbG5zOiAnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI293bmVyJ30pXG4gICAgICAgICAgICAgICAgLmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKGdldEZvcm0sIGZ1bmN0aW9uIChmb3JtKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoISQoZm9ybSkuZmluZChcbiAgICAgICAgICAgICAgICAgICAgICAgICc+cXVlcnk+eFt4bWxucz1cImphYmJlcjp4OmRhdGFcIl0nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICc+ZmllbGRbdmFyPVwibXVjI3Jvb21jb25maWdfd2hvaXNcIl0nKS5sZW5ndGgpIHtcblxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdub24tYW5vbnltb3VzIHJvb21zIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBmb3JtU3VibWl0ID0gJGlxKHt0bzogdGhpcy5yb29tamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KTtcblxuICAgICAgICAgICAgICAgIGZvcm1TdWJtaXQuYygneCcsIHt4bWxuczogJ2phYmJlcjp4OmRhdGEnLCB0eXBlOiAnc3VibWl0J30pO1xuXG4gICAgICAgICAgICAgICAgZm9ybVN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ0ZPUk1fVFlQRSd9KVxuICAgICAgICAgICAgICAgICAgICAuYygndmFsdWUnKVxuICAgICAgICAgICAgICAgICAgICAudCgnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI3Jvb21jb25maWcnKS51cCgpLnVwKCk7XG5cbiAgICAgICAgICAgICAgICBmb3JtU3VibWl0LmMoJ2ZpZWxkJywgeyd2YXInOiAnbXVjI3Jvb21jb25maWdfd2hvaXMnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3ZhbHVlJykudCgnYW55b25lJykudXAoKS51cCgpO1xuXG4gICAgICAgICAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShmb3JtU3VibWl0KTtcblxuICAgICAgICAgICAgfSwgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGdldHRpbmcgcm9vbSBjb25maWd1cmF0aW9uIGZvcm1cIik7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZTogZnVuY3Rpb24gKHByZXMpIHtcbiAgICAgICAgICAgIHZhciBmcm9tID0gcHJlcy5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcblxuICAgICAgICAgICAgLy8gV2hhdCBpcyB0aGlzIGZvcj8gQSB3b3JrYXJvdW5kIGZvciBzb21ldGhpbmc/XG4gICAgICAgICAgICBpZiAocHJlcy5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBldGhlcnBhZCB0YWcuXG4gICAgICAgICAgICB2YXIgZXRoZXJwYWQgPSAkKHByZXMpLmZpbmQoJz5ldGhlcnBhZCcpO1xuICAgICAgICAgICAgaWYgKGV0aGVycGFkLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGlmIChjb25maWcuZXRoZXJwYWRfYmFzZSAmJiAhTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5FVEhFUlBBRCwgZXRoZXJwYWQudGV4dCgpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFBhcnNlIHByZXppIHRhZy5cbiAgICAgICAgICAgIHZhciBwcmVzZW50YXRpb24gPSAkKHByZXMpLmZpbmQoJz5wcmV6aScpO1xuICAgICAgICAgICAgaWYgKHByZXNlbnRhdGlvbi5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB2YXIgdXJsID0gcHJlc2VudGF0aW9uLmF0dHIoJ3VybCcpO1xuICAgICAgICAgICAgICAgIHZhciBjdXJyZW50ID0gcHJlc2VudGF0aW9uLmZpbmQoJz5jdXJyZW50JykudGV4dCgpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3ByZXNlbnRhdGlvbiBpbmZvIHJlY2VpdmVkIGZyb20nLCBmcm9tLCB1cmwpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMucHJlemlNYXBbZnJvbV0gPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnByZXppTWFwW2Zyb21dID0gdXJsO1xuXG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ByZXNlbnRhdGlvbmFkZGVkLm11YycsIFtmcm9tLCB1cmwsIGN1cnJlbnRdKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2dvdG9zbGlkZS5tdWMnLCBbZnJvbSwgdXJsLCBjdXJyZW50XSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAodGhpcy5wcmV6aU1hcFtmcm9tXSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHVybCA9IHRoaXMucHJlemlNYXBbZnJvbV07XG4gICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMucHJlemlNYXBbZnJvbV07XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncHJlc2VudGF0aW9ucmVtb3ZlZC5tdWMnLCBbZnJvbSwgdXJsXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFBhcnNlIGF1ZGlvIGluZm8gdGFnLlxuICAgICAgICAgICAgdmFyIGF1ZGlvTXV0ZWQgPSAkKHByZXMpLmZpbmQoJz5hdWRpb211dGVkJyk7XG4gICAgICAgICAgICBpZiAoYXVkaW9NdXRlZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhdWRpb211dGVkLm11YycsIFtmcm9tLCBhdWRpb011dGVkLnRleHQoKV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSB2aWRlbyBpbmZvIHRhZy5cbiAgICAgICAgICAgIHZhciB2aWRlb011dGVkID0gJChwcmVzKS5maW5kKCc+dmlkZW9tdXRlZCcpO1xuICAgICAgICAgICAgaWYgKHZpZGVvTXV0ZWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigndmlkZW9tdXRlZC5tdWMnLCBbZnJvbSwgdmlkZW9NdXRlZC50ZXh0KCldKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGRldmljZXMgPSAkKHByZXMpLmZpbmQoJz5kZXZpY2VzJyk7XG4gICAgICAgICAgICBpZihkZXZpY2VzLmxlbmd0aClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB2YXIgYXVkaW8gPSBkZXZpY2VzLmZpbmQoJz5hdWRpbycpO1xuICAgICAgICAgICAgICAgIHZhciB2aWRlbyA9IGRldmljZXMuZmluZCgnPnZpZGVvJyk7XG4gICAgICAgICAgICAgICAgdmFyIGRldmljZXNWYWx1ZXMgPSB7YXVkaW86IGZhbHNlLCB2aWRlbzogZmFsc2V9O1xuICAgICAgICAgICAgICAgIGlmKGF1ZGlvLmxlbmd0aCAmJiBhdWRpby50ZXh0KCkgPT09IFwidHJ1ZVwiKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZGV2aWNlc1ZhbHVlcy5hdWRpbyA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYodmlkZW8ubGVuZ3RoICYmIHZpZGVvLnRleHQoKSA9PT0gXCJ0cnVlXCIpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkZXZpY2VzVmFsdWVzLnZpZGVvID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5ERVZJQ0VfQVZBSUxBQkxFLFxuICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKSwgZGV2aWNlc1ZhbHVlcyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBzdGF0cyA9ICQocHJlcykuZmluZCgnPnN0YXRzJyk7XG4gICAgICAgICAgICBpZiAoc3RhdHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN0YXRzT2JqID0ge307XG4gICAgICAgICAgICAgICAgU3Ryb3BoZS5mb3JFYWNoQ2hpbGQoc3RhdHNbMF0sIFwic3RhdFwiLCBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RhdHNPYmpbZWwuZ2V0QXR0cmlidXRlKFwibmFtZVwiKV0gPSBlbC5nZXRBdHRyaWJ1dGUoXCJ2YWx1ZVwiKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLlJFTU9URV9TVEFUUywgZnJvbSwgc3RhdHNPYmopO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBzdGF0dXMuXG4gICAgICAgICAgICBpZiAoJChwcmVzKS5maW5kKCc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdPnN0YXR1c1tjb2RlPVwiMjAxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmNyZWF0ZU5vbkFub255bW91c1Jvb20oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2Ugcm9sZXMuXG4gICAgICAgICAgICB2YXIgbWVtYmVyID0ge307XG4gICAgICAgICAgICBtZW1iZXIuc2hvdyA9ICQocHJlcykuZmluZCgnPnNob3cnKS50ZXh0KCk7XG4gICAgICAgICAgICBtZW1iZXIuc3RhdHVzID0gJChwcmVzKS5maW5kKCc+c3RhdHVzJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIHRtcCA9ICQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5pdGVtJyk7XG4gICAgICAgICAgICBtZW1iZXIuYWZmaWxpYXRpb24gPSB0bXAuYXR0cignYWZmaWxpYXRpb24nKTtcbiAgICAgICAgICAgIG1lbWJlci5yb2xlID0gdG1wLmF0dHIoJ3JvbGUnKTtcblxuICAgICAgICAgICAgLy8gRm9jdXMgcmVjb2duaXRpb25cbiAgICAgICAgICAgIG1lbWJlci5qaWQgPSB0bXAuYXR0cignamlkJyk7XG4gICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IGZhbHNlO1xuICAgICAgICAgICAgaWYgKG1lbWJlci5qaWRcbiAgICAgICAgICAgICAgICAmJiBtZW1iZXIuamlkLmluZGV4T2YoTW9kZXJhdG9yLmdldEZvY3VzVXNlckppZCgpICsgXCIvXCIpID09IDApIHtcbiAgICAgICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBuaWNrdGFnID0gJChwcmVzKS5maW5kKCc+bmlja1t4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2tcIl0nKTtcbiAgICAgICAgICAgIG1lbWJlci5kaXNwbGF5TmFtZSA9IChuaWNrdGFnLmxlbmd0aCA+IDAgPyBuaWNrdGFnLmh0bWwoKSA6IG51bGwpO1xuXG4gICAgICAgICAgICBpZiAoZnJvbSA9PSB0aGlzLm15cm9vbWppZCkge1xuICAgICAgICAgICAgICAgIGlmIChtZW1iZXIuYWZmaWxpYXRpb24gPT0gJ293bmVyJykgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5yb2xlICE9PSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJvbGUgPSBtZW1iZXIucm9sZTtcblxuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkxPQ0FMUk9MRV9DSEFOR0VELFxuICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSwgbWVtYmVyLCBwcmVzLCBNb2RlcmF0b3IuaXNNb2RlcmF0b3IoKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5qb2luZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5qb2luZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19KT0lORUQsIGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnB1c2goZnJvbSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLm1lbWJlcnNbZnJvbV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIC8vIG5ldyBwYXJ0aWNpcGFudFxuICAgICAgICAgICAgICAgIHRoaXMubWVtYmVyc1tmcm9tXSA9IG1lbWJlcjtcbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RfbWVtYmVycy5wdXNoKGZyb20pO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdlbnRlcmVkJywgZnJvbSwgbWVtYmVyKTtcbiAgICAgICAgICAgICAgICBpZiAobWVtYmVyLmlzRm9jdXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5mb2N1c011Y0ppZCA9IGZyb207XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIklnbm9yZSBmb2N1czogXCIgKyBmcm9tICsgXCIsIHJlYWwgSklEOiBcIiArIG1lbWJlci5qaWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGlkID0gJChwcmVzKS5maW5kKCc+dXNlcklEJykudGV4dCgpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgZW1haWwgPSAkKHByZXMpLmZpbmQoJz5lbWFpbCcpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZW1haWwubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSBlbWFpbC50ZXh0KCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5NVUNfRU5URVIsIGZyb20sIGlkLCBtZW1iZXIuZGlzcGxheU5hbWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gUHJlc2VuY2UgdXBkYXRlIGZvciBleGlzdGluZyBwYXJ0aWNpcGFudFxuICAgICAgICAgICAgICAgIC8vIFdhdGNoIHJvbGUgY2hhbmdlOlxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm1lbWJlcnNbZnJvbV0ucm9sZSAhPSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm1lbWJlcnNbZnJvbV0ucm9sZSA9IG1lbWJlci5yb2xlO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19ST0xFX0NIQU5HRUQsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZW1iZXIucm9sZSwgbWVtYmVyLmRpc3BsYXlOYW1lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIEFsd2F5cyB0cmlnZ2VyIHByZXNlbmNlIHRvIHVwZGF0ZSBiaW5kaW5nc1xuICAgICAgICAgICAgdGhpcy5wYXJzZVByZXNlbmNlKGZyb20sIG1lbWJlciwgcHJlcyk7XG5cbiAgICAgICAgICAgIC8vIFRyaWdnZXIgc3RhdHVzIG1lc3NhZ2UgdXBkYXRlXG4gICAgICAgICAgICBpZiAobWVtYmVyLnN0YXR1cykge1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuUFJFU0VOQ0VfU1RBVFVTLCBmcm9tLCBtZW1iZXIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZVVuYXZhaWxhYmxlOiBmdW5jdGlvbiAocHJlcykge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBwcmVzLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgLy8gcm9vbSBkZXN0cm95ZWQgP1xuICAgICAgICAgICAgaWYgKCQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXScgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPmRlc3Ryb3knKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVhc29uO1xuICAgICAgICAgICAgICAgIHZhciByZWFzb25TZWxlY3QgPSAkKHByZXMpLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgICc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdJyArXG4gICAgICAgICAgICAgICAgICAgICc+ZGVzdHJveT5yZWFzb24nKTtcbiAgICAgICAgICAgICAgICBpZiAocmVhc29uU2VsZWN0Lmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICByZWFzb24gPSByZWFzb25TZWxlY3QudGV4dCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBYTVBQLmRpc3Bvc2VDb25mZXJlbmNlKGZhbHNlKTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19ERVNUUk9ZRUQsIHJlYXNvbik7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBTdGF0dXMgY29kZSAxMTAgaW5kaWNhdGVzIHRoYXQgdGhpcyBub3RpZmljYXRpb24gaXMgXCJzZWxmLXByZXNlbmNlXCIuXG4gICAgICAgICAgICBpZiAoISQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5zdGF0dXNbY29kZT1cIjExMFwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbZnJvbV07XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0X21lbWJlcnMuc3BsaWNlKHRoaXMubGlzdF9tZW1iZXJzLmluZGV4T2YoZnJvbSksIDEpO1xuICAgICAgICAgICAgICAgIHRoaXMub25QYXJ0aWNpcGFudExlZnQoZnJvbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBJZiB0aGUgc3RhdHVzIGNvZGUgaXMgMTEwIHRoaXMgbWVhbnMgd2UncmUgbGVhdmluZyBhbmQgd2Ugd291bGQgbGlrZVxuICAgICAgICAgICAgLy8gdG8gcmVtb3ZlIGV2ZXJ5b25lIGVsc2UgZnJvbSBvdXIgdmlldywgc28gd2UgdHJpZ2dlciB0aGUgZXZlbnQuXG4gICAgICAgICAgICBlbHNlIGlmICh0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWVtYmVyID0gdGhpcy5saXN0X21lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnNwbGljZShpLCAxKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblBhcnRpY2lwYW50TGVmdChtZW1iZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICgkKHByZXMpLmZpbmQoJz54W3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI3VzZXJcIl0+c3RhdHVzW2NvZGU9XCIzMDdcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdraWNrZWQubXVjJywgW2Zyb21dKTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5teXJvb21qaWQgPT09IGZyb20pIHtcbiAgICAgICAgICAgICAgICAgICAgWE1QUC5kaXNwb3NlQ29uZmVyZW5jZShmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuS0lDS0VEKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZUVycm9yOiBmdW5jdGlvbiAocHJlcykge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBwcmVzLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgaWYgKCQocHJlcykuZmluZCgnPmVycm9yW3R5cGU9XCJhdXRoXCJdPm5vdC1hdXRob3JpemVkW3htbG5zPVwidXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXNcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnb24gcGFzc3dvcmQgcmVxdWlyZWQnLCBmcm9tKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5QQVNTV09SRF9SRVFVSVJFRCwgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuZG9Kb2luKGZyb20sIHZhbHVlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoJChwcmVzKS5maW5kKFxuICAgICAgICAgICAgICAgICc+ZXJyb3JbdHlwZT1cImNhbmNlbFwiXT5ub3QtYWxsb3dlZFt4bWxucz1cInVybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRvRG9tYWluID0gU3Ryb3BoZS5nZXREb21haW5Gcm9tSmlkKHByZXMuZ2V0QXR0cmlidXRlKCd0bycpKTtcbiAgICAgICAgICAgICAgICBpZiAodG9Eb21haW4gPT09IGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4pIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gZW50ZXIgdGhlIHJvb20gYnkgcmVwbHlpbmcgd2l0aCAnbm90LWF1dGhvcml6ZWQnLiBUaGlzIHdvdWxkXG4gICAgICAgICAgICAgICAgICAgIC8vIHJlc3VsdCBpbiByZWNvbm5lY3Rpb24gZnJvbSBhdXRob3JpemVkIGRvbWFpbi5cbiAgICAgICAgICAgICAgICAgICAgLy8gV2UncmUgZWl0aGVyIG1pc3NpbmcgSmljb2ZvL1Byb3NvZHkgY29uZmlnIGZvciBhbm9ueW1vdXNcbiAgICAgICAgICAgICAgICAgICAgLy8gZG9tYWlucyBvciBzb21ldGhpbmcgaXMgd3JvbmcuXG4vLyAgICAgICAgICAgICAgICAgICAgWE1QUC5wcm9tcHRMb2dpbigpO1xuICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJkaWFsb2cuam9pbkVycm9yXCIsIHByZXMpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignb25QcmVzRXJyb3IgJywgcHJlcyk7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5vcGVuUmVwb3J0RGlhbG9nKG51bGwsXG4gICAgICAgICAgICAgICAgICAgICAgICBcImRpYWxvZy5jb25uZWN0RXJyb3JcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdvblByZXNFcnJvciAnLCBwcmVzKTtcbiAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICBcImRpYWxvZy5jb25uZWN0RXJyb3JcIixcbiAgICAgICAgICAgICAgICAgICAgcHJlcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZE1lc3NhZ2U6IGZ1bmN0aW9uIChib2R5LCBuaWNrbmFtZSkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnYm9keScsIGJvZHkpLnVwKCk7XG4gICAgICAgICAgICBpZiAobmlja25hbWUpIHtcbiAgICAgICAgICAgICAgICBtc2cuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSkudChuaWNrbmFtZSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuU0VORElOR19DSEFUX01FU1NBR0UsIGJvZHkpO1xuICAgICAgICB9LFxuICAgICAgICBzZXRTdWJqZWN0OiBmdW5jdGlvbiAoc3ViamVjdCkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnc3ViamVjdCcsIHN1YmplY3QpO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwidG9waWMgY2hhbmdlZCB0byBcIiArIHN1YmplY3QpO1xuICAgICAgICB9LFxuICAgICAgICBvbk1lc3NhZ2U6IGZ1bmN0aW9uIChtc2cpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiB0aGlzIGlzIGEgaGFjay4gYnV0IGppbmdsZSBvbiBtdWMgbWFrZXMgbmlja2NoYW5nZXMgaGFyZFxuICAgICAgICAgICAgdmFyIGZyb20gPSBtc2cuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICB2YXIgbmljayA9XG4gICAgICAgICAgICAgICAgJChtc2cpLmZpbmQoJz5uaWNrW3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmlja1wiXScpXG4gICAgICAgICAgICAgICAgICAgIC50ZXh0KCkgfHxcbiAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKTtcblxuICAgICAgICAgICAgdmFyIHR4dCA9ICQobXNnKS5maW5kKCc+Ym9keScpLnRleHQoKTtcbiAgICAgICAgICAgIHZhciB0eXBlID0gbXNnLmdldEF0dHJpYnV0ZShcInR5cGVcIik7XG4gICAgICAgICAgICBpZiAodHlwZSA9PSBcImVycm9yXCIpIHtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkNIQVRfRVJST1JfUkVDRUlWRUQsXG4gICAgICAgICAgICAgICAgICAgICQobXNnKS5maW5kKCc+dGV4dCcpLnRleHQoKSwgdHh0KTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHN1YmplY3QgPSAkKG1zZykuZmluZCgnPnN1YmplY3QnKTtcbiAgICAgICAgICAgIGlmIChzdWJqZWN0Lmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBzdWJqZWN0VGV4dCA9IHN1YmplY3QudGV4dCgpO1xuICAgICAgICAgICAgICAgIGlmIChzdWJqZWN0VGV4dCB8fCBzdWJqZWN0VGV4dCA9PSBcIlwiKSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuU1VCSkVDVF9DSEFOR0VELCBzdWJqZWN0VGV4dCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiU3ViamVjdCBpcyBjaGFuZ2VkIHRvIFwiICsgc3ViamVjdFRleHQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICBpZiAodHh0KSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ2NoYXQnLCBuaWNrLCB0eHQpO1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuTUVTU0FHRV9SRUNFSVZFRCxcbiAgICAgICAgICAgICAgICAgICAgZnJvbSwgbmljaywgdHh0LCB0aGlzLm15cm9vbWppZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgbG9ja1Jvb206IGZ1bmN0aW9uIChrZXksIG9uU3VjY2Vzcywgb25FcnJvciwgb25Ob3RTdXBwb3J0ZWQpIHtcbiAgICAgICAgICAgIC8vaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAwNDUuaHRtbCNyb29tY29uZmlnXG4gICAgICAgICAgICB2YXIgb2IgPSB0aGlzO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUSgkaXEoe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdnZXQnfSkuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSksXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChyZXMpLmZpbmQoJz5xdWVyeT54W3htbG5zPVwiamFiYmVyOng6ZGF0YVwiXT5maWVsZFt2YXI9XCJtdWMjcm9vbWNvbmZpZ19yb29tc2VjcmV0XCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZm9ybXN1Ym1pdCA9ICRpcSh7dG86IG9iLnJvb21qaWQsIHR5cGU6ICdzZXQnfSkuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3Jtc3VibWl0LmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdGT1JNX1RZUEUnfSkuYygndmFsdWUnKS50KCdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjcm9vbWNvbmZpZycpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdtdWMjcm9vbWNvbmZpZ19yb29tc2VjcmV0J30pLmMoJ3ZhbHVlJykudChrZXkpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZpeGVzIGEgYnVnIGluIHByb3NvZHkgMC45LisgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9seG1wcGQvaXNzdWVzL2RldGFpbD9pZD0zNzNcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdtdWMjcm9vbWNvbmZpZ193aG9pcyd9KS5jKCd2YWx1ZScpLnQoJ2FueW9uZScpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBpcyBtdWMjcm9vbWNvbmZpZ19wYXNzd29yZHByb3RlY3RlZHJvb20gcmVxdWlyZWQ/XG4gICAgICAgICAgICAgICAgICAgICAgICBvYi5jb25uZWN0aW9uLnNlbmRJUShmb3Jtc3VibWl0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uU3VjY2VzcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbkVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9uTm90U3VwcG9ydGVkKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LCBvbkVycm9yKTtcbiAgICAgICAgfSxcbiAgICAgICAga2ljazogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAgICAgdmFyIGtpY2tJUSA9ICRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNhZG1pbid9KVxuICAgICAgICAgICAgICAgIC5jKCdpdGVtJywge25pY2s6IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCksIHJvbGU6ICdub25lJ30pXG4gICAgICAgICAgICAgICAgLmMoJ3JlYXNvbicpLnQoJ1lvdSBoYXZlIGJlZW4ga2lja2VkLicpLnVwKCkudXAoKS51cCgpO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgIGtpY2tJUSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdLaWNrIHBhcnRpY2lwYW50IHdpdGggamlkOiAnLCBqaWQsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ0tpY2sgcGFydGljaXBhbnQgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZFByZXNlbmNlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcHJlcyA9ICRwcmVzKHt0bzogdGhpcy5wcmVzTWFwWyd0byddIH0pO1xuICAgICAgICAgICAgcHJlcy5jKCd4Jywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3hucyddfSk7XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3Bhc3N3b3JkJykudCh0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMudXAoKTtcblxuICAgICAgICAgICAgLy8gU2VuZCBYRVAtMDExNSAnYycgc3RhbnphIHRoYXQgY29udGFpbnMgb3VyIGNhcGFiaWxpdGllcyBpbmZvXG4gICAgICAgICAgICBpZiAodGhpcy5jb25uZWN0aW9uLmNhcHMpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY2Fwcy5ub2RlID0gY29uZmlnLmNsaWVudE5vZGU7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdjJywgdGhpcy5jb25uZWN0aW9uLmNhcHMuZ2VuZXJhdGVDYXBzQXR0cnMoKSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcHJlcy5jKCd1c2VyLWFnZW50Jywge3htbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L3VzZXItYWdlbnQnfSlcbiAgICAgICAgICAgICAgICAudChuYXZpZ2F0b3IudXNlckFnZW50KS51cCgpO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydicmlkZ2VJc0Rvd24nXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnYnJpZGdlSXNEb3duJykudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZW1haWwnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnZW1haWwnKS50KHRoaXMucHJlc01hcFsnZW1haWwnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsndXNlcklkJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3VzZXJJZCcpLnQodGhpcy5wcmVzTWFwWyd1c2VySWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZGlzcGxheU5hbWUnXSkge1xuICAgICAgICAgICAgICAgIC8vIFhFUC0wMTcyXG4gICAgICAgICAgICAgICAgcHJlcy5jKCduaWNrJywge3htbG5zOiAnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmljayd9KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKHRoaXMucHJlc01hcFtcImRldmljZXNcIl0pXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdkZXZpY2VzJykuYygnYXVkaW8nKS50KHRoaXMucHJlc01hcFsnZGV2aWNlcyddLmF1ZGlvKS51cCgpXG4gICAgICAgICAgICAgICAgICAgIC5jKCd2aWRlbycpLnQodGhpcy5wcmVzTWFwWydkZXZpY2VzJ10udmlkZW8pLnVwKCkudXAoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2F1ZGlvbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnYXVkaW9tdXRlZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWydhdWRpb25zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2F1ZGlvbXV0ZWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsndmlkZW9ucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCd2aWRlb211dGVkJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3ZpZGVvbnMnXX0pXG4gICAgICAgICAgICAgICAgICAgIC50KHRoaXMucHJlc01hcFsndmlkZW9tdXRlZCddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydzdGF0c25zJ10pIHtcbiAgICAgICAgICAgICAgICB2YXIgc3RhdHMgPSBwcmVzLmMoJ3N0YXRzJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3N0YXRzbnMnXX0pO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIHN0YXQgaW4gdGhpcy5wcmVzTWFwW1wic3RhdHNcIl0pXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbXCJzdGF0c1wiXVtzdGF0XSAhPSBudWxsKVxuICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHMuYyhcInN0YXRcIiwge25hbWU6IHN0YXQsIHZhbHVlOiB0aGlzLnByZXNNYXBbXCJzdGF0c1wiXVtzdGF0XX0pLnVwKCk7XG4gICAgICAgICAgICAgICAgcHJlcy51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydwcmV6aW5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3ByZXppJyxcbiAgICAgICAgICAgICAgICAgICAge3htbG5zOiB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICd1cmwnOiB0aGlzLnByZXNNYXBbJ3ByZXppdXJsJ119KVxuICAgICAgICAgICAgICAgICAgICAuYygnY3VycmVudCcpLnQodGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydldGhlcnBhZG5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2V0aGVycGFkJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ2V0aGVycGFkbnMnXX0pXG4gICAgICAgICAgICAgICAgICAgIC50KHRoaXMucHJlc01hcFsnZXRoZXJwYWRuYW1lJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnbWVkaWEnLCB7eG1sbnM6IHRoaXMucHJlc01hcFsnbWVkaWFucyddfSk7XG4gICAgICAgICAgICAgICAgdmFyIHNvdXJjZU51bWJlciA9IDA7XG4gICAgICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5wcmVzTWFwKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCdzb3VyY2UnKSA+PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VOdW1iZXIrKztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGlmIChzb3VyY2VOdW1iZXIgPiAwKVxuICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8PSBzb3VyY2VOdW1iZXIgLyAzOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXMuYygnc291cmNlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7dHlwZTogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfdHlwZSddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzc3JjOiB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBpICsgJ19zc3JjJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfZGlyZWN0aW9uJ11cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8ICdzZW5kcmVjdicgfVxuICAgICAgICAgICAgICAgICAgICAgICAgKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMudXAoKTtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKHByZXMpO1xuICAgICAgICB9LFxuICAgICAgICBhZGREaXNwbGF5TmFtZVRvUHJlc2VuY2U6IGZ1bmN0aW9uIChkaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydkaXNwbGF5TmFtZSddID0gZGlzcGxheU5hbWU7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZE1lZGlhVG9QcmVzZW5jZTogZnVuY3Rpb24gKHNvdXJjZU51bWJlciwgbXR5cGUsIHNzcmNzLCBkaXJlY3Rpb24pIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5wcmVzTWFwWydtZWRpYW5zJ10pXG4gICAgICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydtZWRpYW5zJ10gPSAnaHR0cDovL2VzdG9zLmRlL25zL21qcyc7XG5cbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc291cmNlJyArIHNvdXJjZU51bWJlciArICdfdHlwZSddID0gbXR5cGU7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBzb3VyY2VOdW1iZXIgKyAnX3NzcmMnXSA9IHNzcmNzO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgc291cmNlTnVtYmVyICsgJ19kaXJlY3Rpb24nXSA9IGRpcmVjdGlvbjtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkRGV2aWNlc1RvUHJlc2VuY2U6IGZ1bmN0aW9uIChkZXZpY2VzKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2RldmljZXMnXSA9IGRldmljZXM7XG4gICAgICAgIH0sXG4gICAgICAgIGNsZWFyUHJlc2VuY2VNZWRpYTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5wcmVzTWFwKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJ3NvdXJjZScpICE9IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzZWxmLnByZXNNYXBba2V5XTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkUHJlemlUb1ByZXNlbmNlOiBmdW5jdGlvbiAodXJsLCBjdXJyZW50U2xpZGUpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsncHJlemlucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9wcmV6aSc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ByZXppdXJsJ10gPSB1cmw7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ByZXppY3VycmVudCddID0gY3VycmVudFNsaWRlO1xuICAgICAgICB9LFxuICAgICAgICByZW1vdmVQcmV6aUZyb21QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMucHJlc01hcFsncHJlemlucyddO1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMucHJlc01hcFsncHJleml1cmwnXTtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXNNYXBbJ3ByZXppY3VycmVudCddO1xuICAgICAgICB9LFxuICAgICAgICBhZGRDdXJyZW50U2xpZGVUb1ByZXNlbmNlOiBmdW5jdGlvbiAoY3VycmVudFNsaWRlKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ByZXppY3VycmVudCddID0gY3VycmVudFNsaWRlO1xuICAgICAgICB9LFxuICAgICAgICBnZXRQcmV6aTogZnVuY3Rpb24gKHJvb21qaWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnByZXppTWFwW3Jvb21qaWRdO1xuICAgICAgICB9LFxuICAgICAgICBhZGRFdGhlcnBhZFRvUHJlc2VuY2U6IGZ1bmN0aW9uIChldGhlcnBhZE5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnZXRoZXJwYWRucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9ldGhlcnBhZCc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2V0aGVycGFkbmFtZSddID0gZXRoZXJwYWROYW1lO1xuICAgICAgICB9LFxuICAgICAgICBhZGRBdWRpb0luZm9Ub1ByZXNlbmNlOiBmdW5jdGlvbiAoaXNNdXRlZCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydhdWRpb25zJ10gPSAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L2F1ZGlvJztcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnYXVkaW9tdXRlZCddID0gaXNNdXRlZC50b1N0cmluZygpO1xuICAgICAgICB9LFxuICAgICAgICBhZGRWaWRlb0luZm9Ub1ByZXNlbmNlOiBmdW5jdGlvbiAoaXNNdXRlZCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWyd2aWRlb25zJ10gPSAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L3ZpZGVvJztcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsndmlkZW9tdXRlZCddID0gaXNNdXRlZC50b1N0cmluZygpO1xuICAgICAgICB9LFxuICAgICAgICBhZGRDb25uZWN0aW9uSW5mb1RvUHJlc2VuY2U6IGZ1bmN0aW9uIChzdGF0cykge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzdGF0c25zJ10gPSAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L3N0YXRzJztcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc3RhdHMnXSA9IHN0YXRzO1xuICAgICAgICB9LFxuICAgICAgICBmaW5kSmlkRnJvbVJlc291cmNlOiBmdW5jdGlvbiAocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgICAgIGlmIChyZXNvdXJjZUppZCAmJlxuICAgICAgICAgICAgICAgIHJlc291cmNlSmlkID09PSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZCh0aGlzLm15cm9vbWppZCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5teXJvb21qaWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgcGVlckppZCA9IG51bGw7XG4gICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLm1lbWJlcnMpLnNvbWUoZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAgICAgICAgIHBlZXJKaWQgPSBqaWQ7XG4gICAgICAgICAgICAgICAgcmV0dXJuIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgPT09IHJlc291cmNlSmlkO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gcGVlckppZDtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQnJpZGdlSXNEb3duVG9QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydicmlkZ2VJc0Rvd24nXSA9IHRydWU7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEVtYWlsVG9QcmVzZW5jZTogZnVuY3Rpb24gKGVtYWlsKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2VtYWlsJ10gPSBlbWFpbDtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkVXNlcklkVG9QcmVzZW5jZTogZnVuY3Rpb24gKHVzZXJJZCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWyd1c2VySWQnXSA9IHVzZXJJZDtcbiAgICAgICAgfSxcbiAgICAgICAgaXNNb2RlcmF0b3I6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJvbGUgPT09ICdtb2RlcmF0b3InO1xuICAgICAgICB9LFxuICAgICAgICBnZXRNZW1iZXJSb2xlOiBmdW5jdGlvbiAocGVlckppZCkge1xuICAgICAgICAgICAgaWYgKHRoaXMubWVtYmVyc1twZWVySmlkXSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLm1lbWJlcnNbcGVlckppZF0ucm9sZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9LFxuICAgICAgICBvblBhcnRpY2lwYW50TGVmdDogZnVuY3Rpb24gKGppZCkge1xuXG4gICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19MRUZULCBqaWQpO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uamluZ2xlLnRlcm1pbmF0ZUJ5SmlkKGppZCk7XG5cbiAgICAgICAgICAgIGlmICh0aGlzLmdldFByZXppKGppZCkpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwcmVzZW50YXRpb25yZW1vdmVkLm11YycsXG4gICAgICAgICAgICAgICAgICAgIFtqaWQsIHRoaXMuZ2V0UHJlemkoamlkKV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBNb2RlcmF0b3Iub25NdWNMZWZ0KGppZCk7XG4gICAgICAgIH0sXG4gICAgICAgIHBhcnNlUHJlc2VuY2U6IGZ1bmN0aW9uIChmcm9tLCBtZW1lYmVyLCBwcmVzKSB7XG4gICAgICAgICAgICBpZigkKHByZXMpLmZpbmQoXCI+YnJpZGdlSXNEb3duXCIpLmxlbmd0aCA+IDAgJiYgIWJyaWRnZUlzRG93bikge1xuICAgICAgICAgICAgICAgIGJyaWRnZUlzRG93biA9IHRydWU7XG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5CUklER0VfRE9XTik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKG1lbWViZXIuaXNGb2N1cylcbiAgICAgICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgIC8vIFJlbW92ZSBvbGQgc3NyY3MgY29taW5nIGZyb20gdGhlIGppZFxuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5zc3JjMmppZCkuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLnNzcmMyamlkW3NzcmNdID09IGZyb20pIHtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHNlbGYuc3NyYzJqaWRbc3NyY107XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHZhciBjaGFuZ2VkU3RyZWFtcyA9IFtdO1xuICAgICAgICAgICAgJChwcmVzKS5maW5kKCc+bWVkaWFbeG1sbnM9XCJodHRwOi8vZXN0b3MuZGUvbnMvbWpzXCJdPnNvdXJjZScpLmVhY2goZnVuY3Rpb24gKGlkeCwgc3NyYykge1xuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coamlkLCAnYXNzb2Mgc3NyYycsIHNzcmMuZ2V0QXR0cmlidXRlKCd0eXBlJyksIHNzcmMuZ2V0QXR0cmlidXRlKCdzc3JjJykpO1xuICAgICAgICAgICAgICAgIHZhciBzc3JjViA9IHNzcmMuZ2V0QXR0cmlidXRlKCdzc3JjJyk7XG4gICAgICAgICAgICAgICAgc2VsZi5zc3JjMmppZFtzc3JjVl0gPSBmcm9tO1xuICAgICAgICAgICAgICAgIEppbmdsZVNlc3Npb24ubm90UmVjZWl2ZWRTU1JDcy5wdXNoKHNzcmNWKTtcblxuXG4gICAgICAgICAgICAgICAgdmFyIHR5cGUgPSBzc3JjLmdldEF0dHJpYnV0ZSgndHlwZScpO1xuXG4gICAgICAgICAgICAgICAgdmFyIGRpcmVjdGlvbiA9IHNzcmMuZ2V0QXR0cmlidXRlKCdkaXJlY3Rpb24nKTtcblxuICAgICAgICAgICAgICAgIGNoYW5nZWRTdHJlYW1zLnB1c2goe3R5cGU6IHR5cGUsIGRpcmVjdGlvbjogZGlyZWN0aW9ufSk7XG5cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkNIQU5HRURfU1RSRUFNUywgZnJvbSwgY2hhbmdlZFN0cmVhbXMpO1xuXG4gICAgICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSAhY29uZmlnLmRpc3BsYXlKaWRzXG4gICAgICAgICAgICAgICAgPyBtZW1lYmVyLmRpc3BsYXlOYW1lIDogU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoZnJvbSk7XG5cbiAgICAgICAgICAgIGlmIChkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGggPiAwKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuRElTUExBWV9OQU1FX0NIQU5HRUQsIGZyb20sIGRpc3BsYXlOYW1lKTtcbiAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICB2YXIgaWQgPSAkKHByZXMpLmZpbmQoJz51c2VySUQnKS50ZXh0KCk7XG4gICAgICAgICAgICB2YXIgZW1haWwgPSAkKHByZXMpLmZpbmQoJz5lbWFpbCcpO1xuICAgICAgICAgICAgaWYoZW1haWwubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGlkID0gZW1haWwudGV4dCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLlVTRVJfSURfQ0hBTkdFRCwgZnJvbSwgaWQpO1xuICAgICAgICB9XG4gICAgfSk7XG59O1xuXG4iLCIvKiBqc2hpbnQgLVcxMTcgKi9cblxudmFyIEppbmdsZVNlc3Npb24gPSByZXF1aXJlKFwiLi9KaW5nbGVTZXNzaW9uXCIpO1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihYTVBQLCBldmVudEVtaXR0ZXIpXG57XG4gICAgZnVuY3Rpb24gQ2FsbEluY29taW5nSmluZ2xlKHNpZCwgY29ubmVjdGlvbikge1xuICAgICAgICB2YXIgc2VzcyA9IGNvbm5lY3Rpb24uamluZ2xlLnNlc3Npb25zW3NpZF07XG5cbiAgICAgICAgLy8gVE9ETzogZG8gd2UgY2hlY2sgYWN0aXZlY2FsbCA9PSBudWxsP1xuICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5hY3RpdmVjYWxsID0gc2VzcztcblxuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkNBTExfSU5DT01JTkcsIHNlc3MpO1xuXG4gICAgICAgIC8vIFRPRE86IGNoZWNrIGFmZmlsaWF0aW9uIGFuZC9vciByb2xlXG4gICAgICAgIGNvbnNvbGUubG9nKCdlbXVjIGRhdGEgZm9yJywgc2Vzcy5wZWVyamlkLCBjb25uZWN0aW9uLmVtdWMubWVtYmVyc1tzZXNzLnBlZXJqaWRdKTtcbiAgICAgICAgc2Vzcy51c2VkcmlwID0gdHJ1ZTsgLy8gbm90LXNvLW5haXZlIHRyaWNrbGUgaWNlXG4gICAgICAgIHNlc3Muc2VuZEFuc3dlcigpO1xuICAgICAgICBzZXNzLmFjY2VwdCgpO1xuXG4gICAgfTtcblxuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignamluZ2xlJywge1xuICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICBzZXNzaW9uczoge30sXG4gICAgICAgIGppZDJzZXNzaW9uOiB7fSxcbiAgICAgICAgaWNlX2NvbmZpZzoge2ljZVNlcnZlcnM6IFtdfSxcbiAgICAgICAgcGNfY29uc3RyYWludHM6IHt9LFxuICAgICAgICBhY3RpdmVjYWxsOiBudWxsLFxuICAgICAgICBtZWRpYV9jb25zdHJhaW50czoge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgICAgJ09mZmVyVG9SZWNlaXZlQXVkaW8nOiB0cnVlLFxuICAgICAgICAgICAgICAgICdPZmZlclRvUmVjZWl2ZVZpZGVvJzogdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gTW96RG9udE9mZmVyRGF0YUNoYW5uZWw6IHRydWUgd2hlbiB0aGlzIGlzIGZpcmVmb3hcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG4gICAgICAgICAgICBpZiAodGhpcy5jb25uZWN0aW9uLmRpc2NvKSB7XG4gICAgICAgICAgICAgICAgLy8gaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAxNjcuaHRtbCNzdXBwb3J0XG4gICAgICAgICAgICAgICAgLy8gaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAxNzYuaHRtbCNzdXBwb3J0XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZToxJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDoxJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmljZS11ZHA6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czpkdGxzLXNjdHA6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6YXVkaW8nKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnZpZGVvJyk7XG5cblxuICAgICAgICAgICAgICAgIC8vIHRoaXMgaXMgZGVhbHQgd2l0aCBieSBTRFAgTy9BIHNvIHdlIGRvbid0IG5lZWQgdG8gYW5ub3VjZSB0aGlzXG4gICAgICAgICAgICAgICAgLy90aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MCcpOyAvLyBYRVAtMDI5M1xuICAgICAgICAgICAgICAgIC8vdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydHAtaGRyZXh0OjAnKTsgLy8gWEVQLTAyOTRcbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLnVzZVJ0Y3BNdXgpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3VybjppZXRmOnJmYzo1NzYxJyk7IC8vIHJ0Y3AtbXV4XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChjb25maWcudXNlQnVuZGxlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46aWV0ZjpyZmM6NTg4OCcpOyAvLyBhPWdyb3VwLCBlLmcuIGJ1bmRsZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvL3RoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46aWV0ZjpyZmM6NTU3NicpOyAvLyBhPXNzcmNcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25KaW5nbGUuYmluZCh0aGlzKSwgJ3Vybjp4bXBwOmppbmdsZToxJywgJ2lxJywgJ3NldCcsIG51bGwsIG51bGwpO1xuICAgICAgICB9LFxuICAgICAgICBvbkppbmdsZTogZnVuY3Rpb24gKGlxKSB7XG4gICAgICAgICAgICB2YXIgc2lkID0gJChpcSkuZmluZCgnamluZ2xlJykuYXR0cignc2lkJyk7XG4gICAgICAgICAgICB2YXIgYWN0aW9uID0gJChpcSkuZmluZCgnamluZ2xlJykuYXR0cignYWN0aW9uJyk7XG4gICAgICAgICAgICB2YXIgZnJvbUppZCA9IGlxLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgLy8gc2VuZCBhY2sgZmlyc3RcbiAgICAgICAgICAgIHZhciBhY2sgPSAkaXEoe3R5cGU6ICdyZXN1bHQnLFxuICAgICAgICAgICAgICAgIHRvOiBmcm9tSmlkLFxuICAgICAgICAgICAgICAgIGlkOiBpcS5nZXRBdHRyaWJ1dGUoJ2lkJylcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ29uIGppbmdsZSAnICsgYWN0aW9uICsgJyBmcm9tICcgKyBmcm9tSmlkLCBpcSk7XG4gICAgICAgICAgICB2YXIgc2VzcyA9IHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgIGlmICgnc2Vzc2lvbi1pbml0aWF0ZScgIT0gYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNlc3MgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgYWNrLnR5cGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgICAgICBhY2suYygnZXJyb3InLCB7dHlwZTogJ2NhbmNlbCd9KVxuICAgICAgICAgICAgICAgICAgICAgICAgLmMoJ2l0ZW0tbm90LWZvdW5kJywge3htbG5zOiAndXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXMnfSkudXAoKVxuICAgICAgICAgICAgICAgICAgICAgICAgLmMoJ3Vua25vd24tc2Vzc2lvbicsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTplcnJvcnM6MSd9KTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIGNvbXBhcmUgZnJvbSB0byBzZXNzLnBlZXJqaWQgKGJhcmUgamlkIGNvbXBhcmlzb24gZm9yIGxhdGVyIGNvbXBhdCB3aXRoIG1lc3NhZ2UtbW9kZSlcbiAgICAgICAgICAgICAgICAvLyBsb2NhbCBqaWQgaXMgbm90IGNoZWNrZWRcbiAgICAgICAgICAgICAgICBpZiAoU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChmcm9tSmlkKSAhPSBTdHJvcGhlLmdldEJhcmVKaWRGcm9tSmlkKHNlc3MucGVlcmppZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdqaWQgbWlzbWF0Y2ggZm9yIHNlc3Npb24gaWQnLCBzaWQsIGZyb21KaWQsIHNlc3MucGVlcmppZCk7XG4gICAgICAgICAgICAgICAgICAgIGFjay50eXBlID0gJ2Vycm9yJztcbiAgICAgICAgICAgICAgICAgICAgYWNrLmMoJ2Vycm9yJywge3R5cGU6ICdjYW5jZWwnfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCdpdGVtLW5vdC1mb3VuZCcsIHt4bWxuczogJ3VybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzJ30pLnVwKClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCd1bmtub3duLXNlc3Npb24nLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6ZXJyb3JzOjEnfSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoc2VzcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgLy8gZXhpc3Rpbmcgc2Vzc2lvbiB3aXRoIHNhbWUgc2Vzc2lvbiBpZFxuICAgICAgICAgICAgICAgIC8vIHRoaXMgbWlnaHQgYmUgb3V0LW9mLW9yZGVyIGlmIHRoZSBzZXNzLnBlZXJqaWQgaXMgdGhlIHNhbWUgYXMgZnJvbVxuICAgICAgICAgICAgICAgIGFjay50eXBlID0gJ2Vycm9yJztcbiAgICAgICAgICAgICAgICBhY2suYygnZXJyb3InLCB7dHlwZTogJ2NhbmNlbCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygnc2VydmljZS11bmF2YWlsYWJsZScsIHt4bWxuczogJ3VybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzJ30pLnVwKCk7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdkdXBsaWNhdGUgc2Vzc2lvbiBpZCcsIHNpZCk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoYWNrKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEZJWE1FOiBjaGVjayBmb3IgYSBkZWZpbmVkIGFjdGlvblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoYWNrKTtcbiAgICAgICAgICAgIC8vIHNlZSBodHRwOi8veG1wcC5vcmcvZXh0ZW5zaW9ucy94ZXAtMDE2Ni5odG1sI2NvbmNlcHRzLXNlc3Npb25cbiAgICAgICAgICAgIHN3aXRjaCAoYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi1pbml0aWF0ZSc6XG4gICAgICAgICAgICAgICAgICAgIHNlc3MgPSBuZXcgSmluZ2xlU2Vzc2lvbihcbiAgICAgICAgICAgICAgICAgICAgICAgICQoaXEpLmF0dHIoJ3RvJyksICQoaXEpLmZpbmQoJ2ppbmdsZScpLmF0dHIoJ3NpZCcpLFxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLCBYTVBQKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gY29uZmlndXJlIHNlc3Npb25cblxuICAgICAgICAgICAgICAgICAgICBzZXNzLm1lZGlhX2NvbnN0cmFpbnRzID0gdGhpcy5tZWRpYV9jb25zdHJhaW50cztcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5wY19jb25zdHJhaW50cyA9IHRoaXMucGNfY29uc3RyYWludHM7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuaWNlX2NvbmZpZyA9IHRoaXMuaWNlX2NvbmZpZztcblxuICAgICAgICAgICAgICAgICAgICBzZXNzLmluaXRpYXRlKGZyb21KaWQsIGZhbHNlKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IHNldFJlbW90ZURlc2NyaXB0aW9uIHNob3VsZCBvbmx5IGJlIGRvbmUgd2hlbiB0aGlzIGNhbGwgaXMgdG8gYmUgYWNjZXB0ZWRcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5zZXRSZW1vdGVEZXNjcmlwdGlvbigkKGlxKS5maW5kKCc+amluZ2xlJyksICdvZmZlcicpO1xuXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdID0gc2VzcztcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5qaWQyc2Vzc2lvbltzZXNzLnBlZXJqaWRdID0gc2VzcztcblxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgY2FsbGJhY2sgc2hvdWxkIGVpdGhlclxuICAgICAgICAgICAgICAgICAgICAvLyAuc2VuZEFuc3dlciBhbmQgLmFjY2VwdFxuICAgICAgICAgICAgICAgICAgICAvLyBvciAuc2VuZFRlcm1pbmF0ZSAtLSBub3QgbmVjZXNzYXJpbHkgc3luY2hyb251c1xuICAgICAgICAgICAgICAgICAgICBDYWxsSW5jb21pbmdKaW5nbGUoc2Vzcy5zaWQsIHRoaXMuY29ubmVjdGlvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Nlc3Npb24tYWNjZXB0JzpcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5zZXRSZW1vdGVEZXNjcmlwdGlvbigkKGlxKS5maW5kKCc+amluZ2xlJyksICdhbnN3ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5hY2NlcHQoKTtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignY2FsbGFjY2VwdGVkLmppbmdsZScsIFtzZXNzLnNpZF0pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdzZXNzaW9uLXRlcm1pbmF0ZSc6XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHRoaXMgaXMgbm90IHRoZSBmb2N1cyBzZW5kaW5nIHRoZSB0ZXJtaW5hdGUsIHdlIGhhdmVcbiAgICAgICAgICAgICAgICAgICAgLy8gbm90aGluZyBtb3JlIHRvIGRvIGhlcmUuXG4gICAgICAgICAgICAgICAgICAgIGlmIChPYmplY3Qua2V5cyh0aGlzLnNlc3Npb25zKS5sZW5ndGggPCAxXG4gICAgICAgICAgICAgICAgICAgICAgICB8fCAhKHRoaXMuc2Vzc2lvbnNbT2JqZWN0LmtleXModGhpcy5zZXNzaW9ucylbMF1dXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5zdGFuY2VvZiBKaW5nbGVTZXNzaW9uKSlcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3Rlcm1pbmF0aW5nLi4uJywgc2Vzcy5zaWQpO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRlcm1pbmF0ZShzZXNzLnNpZCk7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPnJlYXNvbicpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignY2FsbHRlcm1pbmF0ZWQuamluZ2xlJywgW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlc3Muc2lkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlc3MucGVlcmppZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkKGlxKS5maW5kKCc+amluZ2xlPnJlYXNvbj46Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICQoaXEpLmZpbmQoJz5qaW5nbGU+cmVhc29uPnRleHQnKS50ZXh0KClcbiAgICAgICAgICAgICAgICAgICAgICAgIF0pO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignY2FsbHRlcm1pbmF0ZWQuamluZ2xlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbc2Vzcy5zaWQsIHNlc3MucGVlcmppZF0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RyYW5zcG9ydC1pbmZvJzpcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5hZGRJY2VDYW5kaWRhdGUoJChpcSkuZmluZCgnPmppbmdsZT5jb250ZW50JykpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdzZXNzaW9uLWluZm8nOlxuICAgICAgICAgICAgICAgICAgICB2YXIgYWZmZWN0ZWQ7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPnJpbmdpbmdbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdyaW5naW5nLmppbmdsZScsIFtzZXNzLnNpZF0pO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCQoaXEpLmZpbmQoJz5qaW5nbGU+bXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFmZmVjdGVkID0gJChpcSkuZmluZCgnPmppbmdsZT5tdXRlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ211dGUuamluZ2xlJywgW3Nlc3Muc2lkLCBhZmZlY3RlZF0pO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCQoaXEpLmZpbmQoJz5qaW5nbGU+dW5tdXRlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYWZmZWN0ZWQgPSAkKGlxKS5maW5kKCc+amluZ2xlPnVubXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCd1bm11dGUuamluZ2xlJywgW3Nlc3Muc2lkLCBhZmZlY3RlZF0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2FkZHNvdXJjZSc6IC8vIEZJWE1FOiBwcm9wcmlldGFyeSwgdW4tamluZ2xlaXNoXG4gICAgICAgICAgICAgICAgY2FzZSAnc291cmNlLWFkZCc6IC8vIEZJWE1FOiBwcm9wcmlldGFyeVxuICAgICAgICAgICAgICAgICAgICBzZXNzLmFkZFNvdXJjZSgkKGlxKS5maW5kKCc+amluZ2xlPmNvbnRlbnQnKSwgZnJvbUppZCk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3JlbW92ZXNvdXJjZSc6IC8vIEZJWE1FOiBwcm9wcmlldGFyeSwgdW4tamluZ2xlaXNoXG4gICAgICAgICAgICAgICAgY2FzZSAnc291cmNlLXJlbW92ZSc6IC8vIEZJWE1FOiBwcm9wcmlldGFyeVxuICAgICAgICAgICAgICAgICAgICBzZXNzLnJlbW92ZVNvdXJjZSgkKGlxKS5maW5kKCc+amluZ2xlPmNvbnRlbnQnKSwgZnJvbUppZCk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignamluZ2xlIGFjdGlvbiBub3QgaW1wbGVtZW50ZWQnLCBhY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBpbml0aWF0ZTogZnVuY3Rpb24gKHBlZXJqaWQsIG15amlkKSB7IC8vIGluaXRpYXRlIGEgbmV3IGppbmdsZXNlc3Npb24gdG8gcGVlcmppZFxuICAgICAgICAgICAgdmFyIHNlc3MgPSBuZXcgSmluZ2xlU2Vzc2lvbihteWppZCB8fCB0aGlzLmNvbm5lY3Rpb24uamlkLFxuICAgICAgICAgICAgICAgIE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCAxMiksIC8vIHJhbmRvbSBzdHJpbmdcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24sIFhNUFApO1xuICAgICAgICAgICAgLy8gY29uZmlndXJlIHNlc3Npb25cblxuICAgICAgICAgICAgc2Vzcy5tZWRpYV9jb25zdHJhaW50cyA9IHRoaXMubWVkaWFfY29uc3RyYWludHM7XG4gICAgICAgICAgICBzZXNzLnBjX2NvbnN0cmFpbnRzID0gdGhpcy5wY19jb25zdHJhaW50cztcbiAgICAgICAgICAgIHNlc3MuaWNlX2NvbmZpZyA9IHRoaXMuaWNlX2NvbmZpZztcblxuICAgICAgICAgICAgc2Vzcy5pbml0aWF0ZShwZWVyamlkLCB0cnVlKTtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdID0gc2VzcztcbiAgICAgICAgICAgIHRoaXMuamlkMnNlc3Npb25bc2Vzcy5wZWVyamlkXSA9IHNlc3M7XG4gICAgICAgICAgICBzZXNzLnNlbmRPZmZlcigpO1xuICAgICAgICAgICAgcmV0dXJuIHNlc3M7XG4gICAgICAgIH0sXG4gICAgICAgIHRlcm1pbmF0ZTogZnVuY3Rpb24gKHNpZCwgcmVhc29uLCB0ZXh0KSB7IC8vIHRlcm1pbmF0ZSBieSBzZXNzaW9uaWQgKG9yIGFsbCBzZXNzaW9ucylcbiAgICAgICAgICAgIGlmIChzaWQgPT09IG51bGwgfHwgc2lkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHNpZCBpbiB0aGlzLnNlc3Npb25zKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnNlc3Npb25zW3NpZF0uc3RhdGUgIT0gJ2VuZGVkJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnNlbmRUZXJtaW5hdGUocmVhc29uIHx8ICghdGhpcy5zZXNzaW9uc1tzaWRdLmFjdGl2ZSgpKSA/ICdjYW5jZWwnIDogbnVsbCwgdGV4dCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3NpZF0udGVybWluYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuamlkMnNlc3Npb25bdGhpcy5zZXNzaW9uc1tzaWRdLnBlZXJqaWRdO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5zZXNzaW9uc1tzaWRdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5zZXNzaW9ucy5oYXNPd25Qcm9wZXJ0eShzaWQpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc2Vzc2lvbnNbc2lkXS5zdGF0ZSAhPSAnZW5kZWQnKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2lkXS5zZW5kVGVybWluYXRlKHJlYXNvbiB8fCAoIXRoaXMuc2Vzc2lvbnNbc2lkXS5hY3RpdmUoKSkgPyAnY2FuY2VsJyA6IG51bGwsIHRleHQpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3NpZF0udGVybWluYXRlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmppZDJzZXNzaW9uW3RoaXMuc2Vzc2lvbnNbc2lkXS5wZWVyamlkXTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5zZXNzaW9uc1tzaWRdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICAvLyBVc2VkIHRvIHRlcm1pbmF0ZSBhIHNlc3Npb24gd2hlbiBhbiB1bmF2YWlsYWJsZSBwcmVzZW5jZSBpcyByZWNlaXZlZC5cbiAgICAgICAgdGVybWluYXRlQnlKaWQ6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmppZDJzZXNzaW9uLmhhc093blByb3BlcnR5KGppZCkpIHtcbiAgICAgICAgICAgICAgICB2YXIgc2VzcyA9IHRoaXMuamlkMnNlc3Npb25bamlkXTtcbiAgICAgICAgICAgICAgICBpZiAoc2Vzcykge1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncGVlciB3ZW50IGF3YXkgc2lsZW50bHknLCBqaWQpO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5zZXNzaW9uc1tzZXNzLnNpZF07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmppZDJzZXNzaW9uW2ppZF07XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsXG4gICAgICAgICAgICAgICAgICAgICAgICBbc2Vzcy5zaWQsIGppZF0sICdnb25lJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICB0ZXJtaW5hdGVSZW1vdGVCeUppZDogZnVuY3Rpb24gKGppZCwgcmVhc29uKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5qaWQyc2Vzc2lvbi5oYXNPd25Qcm9wZXJ0eShqaWQpKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlc3MgPSB0aGlzLmppZDJzZXNzaW9uW2ppZF07XG4gICAgICAgICAgICAgICAgaWYgKHNlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5zZW5kVGVybWluYXRlKHJlYXNvbiB8fCAoIXNlc3MuYWN0aXZlKCkpID8gJ2tpY2snIDogbnVsbCk7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MudGVybWluYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0ZXJtaW5hdGUgcGVlciB3aXRoIGppZCcsIHNlc3Muc2lkLCBqaWQpO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5zZXNzaW9uc1tzZXNzLnNpZF07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmppZDJzZXNzaW9uW2ppZF07XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsXG4gICAgICAgICAgICAgICAgICAgICAgICBbc2Vzcy5zaWQsIGppZCwgJ2tpY2tlZCddKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGdldFN0dW5BbmRUdXJuQ3JlZGVudGlhbHM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIGdldCBzdHVuIGFuZCB0dXJuIGNvbmZpZ3VyYXRpb24gZnJvbSBzZXJ2ZXIgdmlhIHhlcC0wMjE1XG4gICAgICAgICAgICAvLyB1c2VzIHRpbWUtbGltaXRlZCBjcmVkZW50aWFscyBhcyBkZXNjcmliZWQgaW5cbiAgICAgICAgICAgIC8vIGh0dHA6Ly90b29scy5pZXRmLm9yZy9odG1sL2RyYWZ0LXViZXJ0aS1iZWhhdmUtdHVybi1yZXN0LTAwXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gc2VlIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvcHJvc29keS1tb2R1bGVzL3NvdXJjZS9icm93c2UvbW9kX3R1cm5jcmVkZW50aWFscy9tb2RfdHVybmNyZWRlbnRpYWxzLmx1YVxuICAgICAgICAgICAgLy8gZm9yIGEgcHJvc29keSBtb2R1bGUgd2hpY2ggaW1wbGVtZW50cyB0aGlzXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gY3VycmVudGx5LCB0aGlzIGRvZXNuJ3Qgd29yayB3aXRoIHVwZGF0ZUljZSBhbmQgdGhlcmVmb3JlIGNyZWRlbnRpYWxzIHdpdGggYSBsb25nXG4gICAgICAgICAgICAvLyB2YWxpZGl0eSBoYXZlIHRvIGJlIGZldGNoZWQgYmVmb3JlIGNyZWF0aW5nIHRoZSBwZWVyY29ubmVjdGlvblxuICAgICAgICAgICAgLy8gVE9ETzogaW1wbGVtZW50IHJlZnJlc2ggdmlhIHVwZGF0ZUljZSBhcyBkZXNjcmliZWQgaW5cbiAgICAgICAgICAgIC8vICAgICAgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD0xNjUwXG4gICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgICRpcSh7dHlwZTogJ2dldCcsIHRvOiB0aGlzLmNvbm5lY3Rpb24uZG9tYWlufSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3NlcnZpY2VzJywge3htbG5zOiAndXJuOnhtcHA6ZXh0ZGlzY286MSd9KS5jKCdzZXJ2aWNlJywge2hvc3Q6ICd0dXJuLicgKyB0aGlzLmNvbm5lY3Rpb24uZG9tYWlufSksXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgaWNlc2VydmVycyA9IFtdO1xuICAgICAgICAgICAgICAgICAgICAkKHJlcykuZmluZCgnPnNlcnZpY2VzPnNlcnZpY2UnKS5lYWNoKGZ1bmN0aW9uIChpZHgsIGVsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbCA9ICQoZWwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGRpY3QgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0eXBlID0gZWwuYXR0cigndHlwZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnc3R1bic6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsID0gJ3N0dW46JyArIGVsLmF0dHIoJ2hvc3QnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVsLmF0dHIoJ3BvcnQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gJzonICsgZWwuYXR0cigncG9ydCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGljZXNlcnZlcnMucHVzaChkaWN0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAndHVybic6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAndHVybnMnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCA9IHR5cGUgKyAnOic7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCd1c2VybmFtZScpKSB7IC8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9MTUwOFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0Nocm9tKGV8aXVtKVxcLyhbMC05XSspXFwuLykgJiYgcGFyc2VJbnQobmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvQ2hyb20oZXxpdW0pXFwvKFswLTldKylcXC4vKVsyXSwgMTApIDwgMjgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCArPSBlbC5hdHRyKCd1c2VybmFtZScpICsgJ0AnO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVzZXJuYW1lID0gZWwuYXR0cigndXNlcm5hbWUnKTsgLy8gb25seSB3b3JrcyBpbiBNMjhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCArPSBlbC5hdHRyKCdob3N0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCdwb3J0JykgJiYgZWwuYXR0cigncG9ydCcpICE9ICczNDc4Jykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gJzonICsgZWwuYXR0cigncG9ydCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCd0cmFuc3BvcnQnKSAmJiBlbC5hdHRyKCd0cmFuc3BvcnQnKSAhPSAndWRwJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gJz90cmFuc3BvcnQ9JyArIGVsLmF0dHIoJ3RyYW5zcG9ydCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCdwYXNzd29yZCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LmNyZWRlbnRpYWwgPSBlbC5hdHRyKCdwYXNzd29yZCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGljZXNlcnZlcnMucHVzaChkaWN0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmljZV9jb25maWcuaWNlU2VydmVycyA9IGljZXNlcnZlcnM7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignZ2V0dGluZyB0dXJuIGNyZWRlbnRpYWxzIGZhaWxlZCcsIGVycik7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignaXMgbW9kX3R1cm5jcmVkZW50aWFscyBvciBzaW1pbGFyIGluc3RhbGxlZD8nKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgLy8gaW1wbGVtZW50IHB1c2g/XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFBvcHVsYXRlcyB0aGUgbG9nIGRhdGFcbiAgICAgICAgICovXG4gICAgICAgIHBvcHVsYXRlRGF0YTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGRhdGEgPSB7fTtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMuc2Vzc2lvbnMpLmZvckVhY2goZnVuY3Rpb24gKHNpZCkge1xuICAgICAgICAgICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5zZXNzaW9uc1tzaWRdO1xuICAgICAgICAgICAgICAgIGlmIChzZXNzaW9uLnBlZXJjb25uZWN0aW9uICYmIHNlc3Npb24ucGVlcmNvbm5lY3Rpb24udXBkYXRlTG9nKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBzaG91bGQgcHJvYmFibHkgYmUgYSAuZHVtcCBjYWxsXG4gICAgICAgICAgICAgICAgICAgIGRhdGFbXCJqaW5nbGVfXCIgKyBzZXNzaW9uLnNpZF0gPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVMb2c6IHNlc3Npb24ucGVlcmNvbm5lY3Rpb24udXBkYXRlTG9nLFxuICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHM6IHNlc3Npb24ucGVlcmNvbm5lY3Rpb24uc3RhdHMsXG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHdpbmRvdy5sb2NhdGlvbi5ocmVmXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gZGF0YTtcbiAgICAgICAgfVxuICAgIH0pO1xufTtcblxuIiwiLyogZ2xvYmFsIFN0cm9waGUgKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkge1xuXG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdsb2dnZXInLCB7XG4gICAgICAgIC8vIGxvZ3MgcmF3IHN0YW56YXMgYW5kIG1ha2VzIHRoZW0gYXZhaWxhYmxlIGZvciBkb3dubG9hZCBhcyBKU09OXG4gICAgICAgIGNvbm5lY3Rpb246IG51bGwsXG4gICAgICAgIGxvZzogW10sXG4gICAgICAgIGluaXQ6IGZ1bmN0aW9uIChjb25uKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnJhd0lucHV0ID0gdGhpcy5sb2dfaW5jb21pbmcuYmluZCh0aGlzKTtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5yYXdPdXRwdXQgPSB0aGlzLmxvZ19vdXRnb2luZy5iaW5kKHRoaXMpO1xuICAgICAgICB9LFxuICAgICAgICBsb2dfaW5jb21pbmc6IGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLnB1c2goW25ldyBEYXRlKCkuZ2V0VGltZSgpLCAnaW5jb21pbmcnLCBzdGFuemFdKTtcbiAgICAgICAgfSxcbiAgICAgICAgbG9nX291dGdvaW5nOiBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5wdXNoKFtuZXcgRGF0ZSgpLmdldFRpbWUoKSwgJ291dGdvaW5nJywgc3RhbnphXSk7XG4gICAgICAgIH1cbiAgICB9KTtcbn07IiwiLyogZ2xvYmFsICQsICRpcSwgY29uZmlnLCBjb25uZWN0aW9uLCBmb2N1c011Y0ppZCwgZm9yY2VNdXRlZCxcbiAgIHNldEF1ZGlvTXV0ZWQsIFN0cm9waGUgKi9cbi8qKlxuICogTW9kZXJhdGUgY29ubmVjdGlvbiBwbHVnaW4uXG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKFhNUFApIHtcbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ21vZGVyYXRlJywge1xuICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmFkZEhhbmRsZXIodGhpcy5vbk11dGUuYmluZCh0aGlzKSxcbiAgICAgICAgICAgICAgICAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L2F1ZGlvJyxcbiAgICAgICAgICAgICAgICAnaXEnLFxuICAgICAgICAgICAgICAgICdzZXQnLFxuICAgICAgICAgICAgICAgIG51bGwsXG4gICAgICAgICAgICAgICAgbnVsbCk7XG4gICAgICAgIH0sXG4gICAgICAgIHNldE11dGU6IGZ1bmN0aW9uIChqaWQsIG11dGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcInNldCBtdXRlXCIsIG11dGUpO1xuICAgICAgICAgICAgdmFyIGlxVG9Gb2N1cyA9ICRpcSh7dG86IHRoaXMuY29ubmVjdGlvbi5lbXVjLmZvY3VzTXVjSmlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgICAgICAgICAgLmMoJ211dGUnLCB7XG4gICAgICAgICAgICAgICAgICAgIHhtbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L2F1ZGlvJyxcbiAgICAgICAgICAgICAgICAgICAgamlkOiBqaWRcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC50KG11dGUudG9TdHJpbmcoKSlcbiAgICAgICAgICAgICAgICAudXAoKTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICBpcVRvRm9jdXMsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0IG11dGUnLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzZXQgbXV0ZSBlcnJvcicsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgb25NdXRlOiBmdW5jdGlvbiAoaXEpIHtcbiAgICAgICAgICAgIHZhciBmcm9tID0gaXEuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICBpZiAoZnJvbSAhPT0gdGhpcy5jb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJJZ25vcmVkIG11dGUgZnJvbSBub24gZm9jdXMgcGVlclwiKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgbXV0ZSA9ICQoaXEpLmZpbmQoJ211dGUnKTtcbiAgICAgICAgICAgIGlmIChtdXRlLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBkb011dGVBdWRpbyA9IG11dGUudGV4dCgpID09PSBcInRydWVcIjtcbiAgICAgICAgICAgICAgICBBUFAuVUkuc2V0QXVkaW9NdXRlZChkb011dGVBdWRpbyk7XG4gICAgICAgICAgICAgICAgWE1QUC5mb3JjZU11dGVkID0gZG9NdXRlQXVkaW87XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgZWplY3Q6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIC8vIFdlJ3JlIG5vdCB0aGUgZm9jdXMsIHNvIGNhbid0IHRlcm1pbmF0ZVxuICAgICAgICAgICAgLy9jb25uZWN0aW9uLmppbmdsZS50ZXJtaW5hdGVSZW1vdGVCeUppZChqaWQsICdraWNrJyk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5raWNrKGppZCk7XG4gICAgICAgIH1cbiAgICB9KTtcbn0iLCIvKiBqc2hpbnQgLVcxMTcgKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdyYXlvJyxcbiAgICAgICAge1xuICAgICAgICAgICAgUkFZT19YTUxOUzogJ3Vybjp4bXBwOnJheW86MScsXG4gICAgICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24uZGlzY28pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOnJheW86Y2xpZW50OjEnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcihcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblJheW8uYmluZCh0aGlzKSwgdGhpcy5SQVlPX1hNTE5TLCAnaXEnLCAnc2V0JywgbnVsbCwgbnVsbCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25SYXlvOiBmdW5jdGlvbiAoaXEpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJSYXlvIElRXCIsIGlxKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkaWFsOiBmdW5jdGlvbiAodG8sIGZyb20sIHJvb21OYW1lLCByb29tUGFzcykge1xuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiB0aGlzLmNvbm5lY3Rpb24uZW11Yy5mb2N1c011Y0ppZFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXEuYygnZGlhbCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlMsXG4gICAgICAgICAgICAgICAgICAgICAgICB0bzogdG8sXG4gICAgICAgICAgICAgICAgICAgICAgICBmcm9tOiBmcm9tXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJlcS5jKCdoZWFkZXInLFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiAnSnZiUm9vbU5hbWUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21OYW1lXG4gICAgICAgICAgICAgICAgICAgIH0pLnVwKCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocm9vbVBhc3MgIT09IG51bGwgJiYgcm9vbVBhc3MubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgcmVxLmMoJ2hlYWRlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogJ0p2YlJvb21QYXNzd29yZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21QYXNzXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS51cCgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgICAgIHJlcSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdEaWFsIHJlc3VsdCAnLCByZXN1bHQpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSAkKHJlc3VsdCkuZmluZCgncmVmJykuYXR0cigndXJpJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNhbGxfcmVzb3VyY2UgPSByZXNvdXJjZS5zdWJzdHIoJ3htcHA6Jy5sZW5ndGgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIlJlY2VpdmVkIGNhbGwgcmVzb3VyY2U6IFwiICsgdGhpcy5jYWxsX3Jlc291cmNlKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0RpYWwgZXJyb3IgJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBoYW5nX3VwOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmNhbGxfcmVzb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTm8gY2FsbCBpbiBwcm9ncmVzc1wiKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiB0aGlzLmNhbGxfcmVzb3VyY2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgcmVxLmMoJ2hhbmd1cCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlNcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgICAgICByZXEsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnSGFuZ3VwIHJlc3VsdCAnLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jYWxsX3Jlc291cmNlID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0hhbmd1cCBlcnJvciAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNhbGxfcmVzb3VyY2UgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICk7XG59O1xuIiwiLyoqXG4gKiBTdHJvcGhlIGxvZ2dlciBpbXBsZW1lbnRhdGlvbi4gTG9ncyBmcm9tIGxldmVsIFdBUk4gYW5kIGFib3ZlLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIFN0cm9waGUubG9nID0gZnVuY3Rpb24gKGxldmVsLCBtc2cpIHtcbiAgICAgICAgc3dpdGNoIChsZXZlbCkge1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLldBUk46XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiU3Ryb3BoZTogXCIgKyBtc2cpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkVSUk9SOlxuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkZBVEFMOlxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJTdHJvcGhlOiBcIiArIG1zZyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgU3Ryb3BoZS5nZXRTdGF0dXNTdHJpbmcgPSBmdW5jdGlvbiAoc3RhdHVzKSB7XG4gICAgICAgIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkVSUk9SOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkVSUk9SXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkNPTk5FQ1RJTkc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJDT05ORkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRU5USUNBVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRU5USUNBVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORUNURUQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVEVEXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkRJU0NPTk5FQ1RFRDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNURURcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNUSU5HXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkFUVEFDSEVEOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkFUVEFDSEVEXCI7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiBcInVua25vd25cIjtcbiAgICAgICAgfVxuICAgIH07XG59O1xuIiwiLyogZ2xvYmFsICQsIEFQUCwgY29uZmlnLCBTdHJvcGhlKi9cbnZhciBNb2RlcmF0b3IgPSByZXF1aXJlKFwiLi9tb2RlcmF0b3JcIik7XG52YXIgRXZlbnRFbWl0dGVyID0gcmVxdWlyZShcImV2ZW50c1wiKTtcbnZhciBSZWNvcmRpbmcgPSByZXF1aXJlKFwiLi9yZWNvcmRpbmdcIik7XG52YXIgU0RQID0gcmVxdWlyZShcIi4vU0RQXCIpO1xudmFyIFNldHRpbmdzID0gcmVxdWlyZShcIi4uL3NldHRpbmdzL1NldHRpbmdzXCIpO1xudmFyIFBha28gPSByZXF1aXJlKFwicGFrb1wiKTtcbnZhciBTdHJlYW1FdmVudFR5cGVzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1N0cmVhbUV2ZW50VHlwZXNcIik7XG52YXIgUlRDRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1JUQ0V2ZW50c1wiKTtcbnZhciBVSUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1VJL1VJRXZlbnRzXCIpO1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgY29ubmVjdGlvbiA9IG51bGw7XG52YXIgYXV0aGVudGljYXRlZFVzZXIgPSBmYWxzZTtcblxuZnVuY3Rpb24gY29ubmVjdChqaWQsIHBhc3N3b3JkKSB7XG4gICAgY29ubmVjdGlvbiA9IFhNUFAuY3JlYXRlQ29ubmVjdGlvbigpO1xuICAgIE1vZGVyYXRvci5zZXRDb25uZWN0aW9uKGNvbm5lY3Rpb24pO1xuXG4gICAgaWYgKGNvbm5lY3Rpb24uZGlzY28pIHtcbiAgICAgICAgLy8gZm9yIGNocm9tZSwgYWRkIG11bHRpc3RyZWFtIGNhcFxuICAgIH1cbiAgICBjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cyA9IEFQUC5SVEMuZ2V0UENDb25zdHJhaW50cygpO1xuICAgIGlmIChjb25maWcudXNlSVB2Nikge1xuICAgICAgICAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTI4MjhcbiAgICAgICAgaWYgKCFjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cy5vcHRpb25hbClcbiAgICAgICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzLm9wdGlvbmFsID0gW107XG4gICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzLm9wdGlvbmFsLnB1c2goe2dvb2dJUHY2OiB0cnVlfSk7XG4gICAgfVxuXG4gICAgLy8gSW5jbHVkZSB1c2VyIGluZm8gaW4gTVVDIHByZXNlbmNlXG4gICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICBpZiAoc2V0dGluZ3MuZW1haWwpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEVtYWlsVG9QcmVzZW5jZShzZXR0aW5ncy5lbWFpbCk7XG4gICAgfVxuICAgIGlmIChzZXR0aW5ncy51aWQpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZFVzZXJJZFRvUHJlc2VuY2Uoc2V0dGluZ3MudWlkKTtcbiAgICB9XG4gICAgaWYgKHNldHRpbmdzLmRpc3BsYXlOYW1lKSB7XG4gICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGREaXNwbGF5TmFtZVRvUHJlc2VuY2Uoc2V0dGluZ3MuZGlzcGxheU5hbWUpO1xuICAgIH1cblxuICAgIHZhciBhbm9ueW1vdXNDb25uZWN0aW9uRmFpbGVkID0gZmFsc2U7XG4gICAgY29ubmVjdGlvbi5jb25uZWN0KGppZCwgcGFzc3dvcmQsIGZ1bmN0aW9uIChzdGF0dXMsIG1zZykge1xuICAgICAgICBjb25zb2xlLmxvZygnU3Ryb3BoZSBzdGF0dXMgY2hhbmdlZCB0bycsXG4gICAgICAgICAgICBTdHJvcGhlLmdldFN0YXR1c1N0cmluZyhzdGF0dXMpKTtcbiAgICAgICAgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICBpZiAoY29uZmlnLnVzZVN0dW5UdXJuKSB7XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5qaW5nbGUuZ2V0U3R1bkFuZFR1cm5DcmVkZW50aWFscygpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJNeSBKYWJiZXIgSUQ6IFwiICsgY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZihwYXNzd29yZClcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkVXNlciA9IHRydWU7XG4gICAgICAgICAgICBtYXliZURvSm9pbigpO1xuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQ09OTkZBSUwpIHtcbiAgICAgICAgICAgIGlmKG1zZyA9PT0gJ3gtc3Ryb3BoZS1iYWQtbm9uLWFub24tamlkJykge1xuICAgICAgICAgICAgICAgIGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICBpZihhbm9ueW1vdXNDb25uZWN0aW9uRmFpbGVkKSB7XG4gICAgICAgICAgICAgICAgLy8gcHJvbXB0IHVzZXIgZm9yIHVzZXJuYW1lIGFuZCBwYXNzd29yZFxuICAgICAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChzdGF0dXMgPT09IFN0cm9waGUuU3RhdHVzLkFVVEhGQUlMKSB7XG4gICAgICAgICAgICAvLyB3cm9uZyBwYXNzd29yZCBvciB1c2VybmFtZSwgcHJvbXB0IHVzZXJcbiAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcblxuICAgICAgICB9XG4gICAgfSk7XG59XG5cblxuXG5mdW5jdGlvbiBtYXliZURvSm9pbigpIHtcbiAgICBpZiAoY29ubmVjdGlvbiAmJiBjb25uZWN0aW9uLmNvbm5lY3RlZCAmJlxuICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChjb25uZWN0aW9uLmppZClcbiAgICAgICAgJiYgKEFQUC5SVEMubG9jYWxBdWRpbyB8fCBBUFAuUlRDLmxvY2FsVmlkZW8pKSB7XG4gICAgICAgIC8vIC5jb25uZWN0ZWQgaXMgdHJ1ZSB3aGlsZSBjb25uZWN0aW5nP1xuICAgICAgICBkb0pvaW4oKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGRvSm9pbigpIHtcbiAgICB2YXIgcm9vbU5hbWUgPSBBUFAuVUkuZ2VuZXJhdGVSb29tTmFtZSgpO1xuXG4gICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKFxuICAgICAgICByb29tTmFtZSwgQVBQLlVJLmNoZWNrRm9yTmlja25hbWVBbmRKb2luKTtcbn1cblxuZnVuY3Rpb24gaW5pdFN0cm9waGVQbHVnaW5zKClcbntcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLmVtdWNcIikoWE1QUCwgZXZlbnRFbWl0dGVyKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLmppbmdsZVwiKShYTVBQLCBldmVudEVtaXR0ZXIpO1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUubW9kZXJhdGVcIikoWE1QUCk7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS51dGlsXCIpKCk7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS5yYXlvXCIpKCk7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS5sb2dnZXJcIikoKTtcbn1cblxuZnVuY3Rpb24gcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihtYXliZURvSm9pbixcbiAgICAgICAgU3RyZWFtRXZlbnRUeXBlcy5FVkVOVF9UWVBFX0xPQ0FMX0NSRUFURUQpO1xuICAgIEFQUC5SVEMuYWRkTGlzdGVuZXIoUlRDRXZlbnRzLkFWQUlMQUJMRV9ERVZJQ0VTX0NIQU5HRUQsIGZ1bmN0aW9uIChkZXZpY2VzKSB7XG4gICAgICAgIFhNUFAuYWRkVG9QcmVzZW5jZShcImRldmljZXNcIiwgZGV2aWNlcyk7XG4gICAgfSlcbiAgICBBUFAuVUkuYWRkTGlzdGVuZXIoVUlFdmVudHMuTklDS05BTUVfQ0hBTkdFRCwgZnVuY3Rpb24gKG5pY2tuYW1lKSB7XG4gICAgICAgIFhNUFAuYWRkVG9QcmVzZW5jZShcImRpc3BsYXlOYW1lXCIsIG5pY2tuYW1lKTtcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gc2V0dXBFdmVudHMoKSB7XG4gICAgJCh3aW5kb3cpLmJpbmQoJ2JlZm9yZXVubG9hZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5jb25uZWN0ZWQpIHtcbiAgICAgICAgICAgIC8vIGVuc3VyZSBzaWdub3V0XG4gICAgICAgICAgICAkLmFqYXgoe1xuICAgICAgICAgICAgICAgIHR5cGU6ICdQT1NUJyxcbiAgICAgICAgICAgICAgICB1cmw6IGNvbmZpZy5ib3NoLFxuICAgICAgICAgICAgICAgIGFzeW5jOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgY29udGVudFR5cGU6ICdhcHBsaWNhdGlvbi94bWwnLFxuICAgICAgICAgICAgICAgIGRhdGE6IFwiPGJvZHkgcmlkPSdcIiArIChjb25uZWN0aW9uLnJpZCB8fCBjb25uZWN0aW9uLl9wcm90by5yaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHhtbG5zPSdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9odHRwYmluZCcgc2lkPSdcIlxuICAgICAgICAgICAgICAgICAgICArIChjb25uZWN0aW9uLnNpZCB8fCBjb25uZWN0aW9uLl9wcm90by5zaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHR5cGU9J3Rlcm1pbmF0ZSc+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjxwcmVzZW5jZSB4bWxucz0namFiYmVyOmNsaWVudCcgdHlwZT0ndW5hdmFpbGFibGUnLz5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC9ib2R5PlwiLFxuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzaWduZWQgb3V0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZXJyb3I6IGZ1bmN0aW9uIChYTUxIdHRwUmVxdWVzdCwgdGV4dFN0YXR1cywgZXJyb3JUaHJvd24pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NpZ25vdXQgZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRTdGF0dXMgKyAnICgnICsgZXJyb3JUaHJvd24gKyAnKScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIFhNUFAuZGlzcG9zZUNvbmZlcmVuY2UodHJ1ZSk7XG4gICAgfSk7XG59XG5cbnZhciBYTVBQID0ge1xuICAgIHNlc3Npb25UZXJtaW5hdGVkOiBmYWxzZSxcblxuICAgIC8qKlxuICAgICAqIFhNUFAgY29ubmVjdGlvbiBzdGF0dXNcbiAgICAgKi9cbiAgICBTdGF0dXM6IFN0cm9waGUuU3RhdHVzLFxuXG4gICAgLyoqXG4gICAgICogUmVtZW1iZXJzIGlmIHdlIHdlcmUgbXV0ZWQgYnkgdGhlIGZvY3VzLlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgIGZvcmNlTXV0ZWQ6IGZhbHNlLFxuICAgIHN0YXJ0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHNldHVwRXZlbnRzKCk7XG4gICAgICAgIGluaXRTdHJvcGhlUGx1Z2lucygpO1xuICAgICAgICByZWdpc3Rlckxpc3RlbmVycygpO1xuICAgICAgICBNb2RlcmF0b3IuaW5pdCh0aGlzLCBldmVudEVtaXR0ZXIpO1xuICAgICAgICB2YXIgY29uZmlnRG9tYWluID0gY29uZmlnLmhvc3RzLmFub255bW91c2RvbWFpbiB8fCBjb25maWcuaG9zdHMuZG9tYWluO1xuICAgICAgICAvLyBGb3JjZSBhdXRoZW50aWNhdGVkIGRvbWFpbiBpZiByb29tIGlzIGFwcGVuZGVkIHdpdGggJz9sb2dpbj10cnVlJ1xuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmFub255bW91c2RvbWFpbiAmJlxuICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLnNlYXJjaC5pbmRleE9mKFwibG9naW49dHJ1ZVwiKSAhPT0gLTEpIHtcbiAgICAgICAgICAgIGNvbmZpZ0RvbWFpbiA9IGNvbmZpZy5ob3N0cy5kb21haW47XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGppZCA9IGNvbmZpZ0RvbWFpbiB8fCB3aW5kb3cubG9jYXRpb24uaG9zdG5hbWU7XG4gICAgICAgIGNvbm5lY3QoamlkLCBudWxsKTtcbiAgICB9LFxuICAgIGNyZWF0ZUNvbm5lY3Rpb246IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGJvc2ggPSBjb25maWcuYm9zaCB8fCAnL2h0dHAtYmluZCc7XG5cbiAgICAgICAgcmV0dXJuIG5ldyBTdHJvcGhlLkNvbm5lY3Rpb24oYm9zaCk7XG4gICAgfSxcbiAgICBnZXRTdGF0dXNTdHJpbmc6IGZ1bmN0aW9uIChzdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIFN0cm9waGUuZ2V0U3RhdHVzU3RyaW5nKHN0YXR1cyk7XG4gICAgfSxcbiAgICBwcm9tcHRMb2dpbjogZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBGSVhNRTogcmUtdXNlIExvZ2luRGlhbG9nIHdoaWNoIHN1cHBvcnRzIHJldHJpZXNcbiAgICAgICAgQVBQLlVJLnNob3dMb2dpblBvcHVwKGNvbm5lY3QpO1xuICAgIH0sXG4gICAgam9pblJvb206IGZ1bmN0aW9uKHJvb21OYW1lLCB1c2VOaWNrcywgbmljaylcbiAgICB7XG4gICAgICAgIHZhciByb29tamlkO1xuICAgICAgICByb29tamlkID0gcm9vbU5hbWU7XG5cbiAgICAgICAgaWYgKHVzZU5pY2tzKSB7XG4gICAgICAgICAgICBpZiAobmljaykge1xuICAgICAgICAgICAgICAgIHJvb21qaWQgKz0gJy8nICsgbmljaztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyBTdHJvcGhlLmdldE5vZGVGcm9tSmlkKGNvbm5lY3Rpb24uamlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgdmFyIHRtcEppZCA9IFN0cm9waGUuZ2V0Tm9kZUZyb21KaWQoY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZighYXV0aGVudGljYXRlZFVzZXIpXG4gICAgICAgICAgICAgICAgdG1wSmlkID0gdG1wSmlkLnN1YnN0cigwLCA4KTtcblxuICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyB0bXBKaWQ7XG4gICAgICAgIH1cbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvSm9pbihyb29tamlkKTtcbiAgICB9LFxuICAgIG15SmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmVtdWMubXlyb29tamlkO1xuICAgIH0sXG4gICAgbXlSZXNvdXJjZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZighY29ubmVjdGlvbiB8fCAhIGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpO1xuICAgIH0sXG4gICAgZGlzcG9zZUNvbmZlcmVuY2U6IGZ1bmN0aW9uIChvblVubG9hZCkge1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkRJU1BPU0VfQ09ORkVSRU5DRSwgb25VbmxvYWQpO1xuICAgICAgICB2YXIgaGFuZGxlciA9IGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGw7XG4gICAgICAgIGlmIChoYW5kbGVyICYmIGhhbmRsZXIucGVlcmNvbm5lY3Rpb24pIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBwcm9iYWJseSByZW1vdmluZyBzdHJlYW1zIGlzIG5vdCByZXF1aXJlZCBhbmQgY2xvc2UoKSBzaG91bGRcbiAgICAgICAgICAgIC8vIGJlIGVub3VnaFxuICAgICAgICAgICAgaWYgKEFQUC5SVEMubG9jYWxBdWRpbykge1xuICAgICAgICAgICAgICAgIGhhbmRsZXIucGVlcmNvbm5lY3Rpb24ucmVtb3ZlU3RyZWFtKFxuICAgICAgICAgICAgICAgICAgICBBUFAuUlRDLmxvY2FsQXVkaW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKEFQUC5SVEMubG9jYWxWaWRlbykge1xuICAgICAgICAgICAgICAgIGhhbmRsZXIucGVlcmNvbm5lY3Rpb24ucmVtb3ZlU3RyZWFtKFxuICAgICAgICAgICAgICAgICAgICBBUFAuUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwgPSBudWxsO1xuICAgICAgICBpZighb25VbmxvYWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIHJlbW92ZUxpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbihyb29tTmFtZSwgY2FsbGJhY2spIHtcbiAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgfSxcbiAgICBnZXRMb2dpblVybDogZnVuY3Rpb24gKHJvb21OYW1lLCBjYWxsYmFjaykge1xuICAgICAgICBNb2RlcmF0b3IuZ2V0TG9naW5Vcmwocm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICB9LFxuICAgIGdldFBvcHVwTG9naW5Vcmw6IGZ1bmN0aW9uIChyb29tTmFtZSwgY2FsbGJhY2spIHtcbiAgICAgICAgTW9kZXJhdG9yLmdldFBvcHVwTG9naW5Vcmwocm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICB9LFxuICAgIGlzTW9kZXJhdG9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNNb2RlcmF0b3IoKTtcbiAgICB9LFxuICAgIGlzU2lwR2F0ZXdheUVuYWJsZWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIE1vZGVyYXRvci5pc1NpcEdhdGV3YXlFbmFibGVkKCk7XG4gICAgfSxcbiAgICBpc0V4dGVybmFsQXV0aEVuYWJsZWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIE1vZGVyYXRvci5pc0V4dGVybmFsQXV0aEVuYWJsZWQoKTtcbiAgICB9LFxuICAgIHN3aXRjaFN0cmVhbXM6IGZ1bmN0aW9uIChzdHJlYW0sIG9sZFN0cmVhbSwgY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5qaW5nbGUuYWN0aXZlY2FsbCkge1xuICAgICAgICAgICAgLy8gRklYTUU6IHdpbGwgYmxvY2sgc3dpdGNoSW5Qcm9ncmVzcyBvbiB0cnVlIHZhbHVlIGluIGNhc2Ugb2YgZXhjZXB0aW9uXG4gICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5hY3RpdmVjYWxsLnN3aXRjaFN0cmVhbXMoc3RyZWFtLCBvbGRTdHJlYW0sIGNhbGxiYWNrKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIFdlIGFyZSBkb25lIGltbWVkaWF0ZWx5XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJObyBjb25mZXJlbmNlIGhhbmRsZXIgb3IgY29uZmVyZW5jZSBub3Qgc3RhcnRlZCB5ZXRcIik7XG4gICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZW5kVmlkZW9JbmZvUHJlc2VuY2U6IGZ1bmN0aW9uIChtdXRlKSB7XG4gICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRWaWRlb0luZm9Ub1ByZXNlbmNlKG11dGUpO1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2VuZFByZXNlbmNlKCk7XG4gICAgfSxcbiAgICBzZXRWaWRlb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgICAgICBpZighY29ubmVjdGlvbilcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICB2YXIgbG9jYWxDYWxsYmFjayA9IGZ1bmN0aW9uIChtdXRlKSB7XG4gICAgICAgICAgICBzZWxmLnNlbmRWaWRlb0luZm9QcmVzZW5jZShtdXRlKTtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhtdXRlKTtcbiAgICAgICAgfTtcblxuICAgICAgICBpZihjb25uZWN0aW9uLmppbmdsZS5hY3RpdmVjYWxsKVxuICAgICAgICB7XG4gICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5hY3RpdmVjYWxsLnNldFZpZGVvTXV0ZShcbiAgICAgICAgICAgICAgICBtdXRlLCBsb2NhbENhbGxiYWNrLCBvcHRpb25zKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxvY2FsQ2FsbGJhY2sobXV0ZSk7XG4gICAgICAgIH1cblxuICAgIH0sXG4gICAgc2V0QXVkaW9NdXRlOiBmdW5jdGlvbiAobXV0ZSwgY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKCEoY29ubmVjdGlvbiAmJiBBUFAuUlRDLmxvY2FsQXVkaW8pKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuXG4gICAgICAgIGlmICh0aGlzLmZvcmNlTXV0ZWQgJiYgIW11dGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkFza2luZyBmb2N1cyBmb3IgdW5tdXRlXCIpO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQsIG11dGUpO1xuICAgICAgICAgICAgLy8gRklYTUU6IHdhaXQgZm9yIHJlc3VsdCBiZWZvcmUgcmVzZXR0aW5nIG11dGVkIHN0YXR1c1xuICAgICAgICAgICAgdGhpcy5mb3JjZU11dGVkID0gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobXV0ZSA9PSBBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAvLyBOb3RoaW5nIHRvIGRvXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSByaWdodCB3YXkgdG8gaGFuZGxlIG11bHRpcGxlIHRyYWNrcy5cbiAgICAgICAgLy8gU28gYXQgbGVhc3QgbWFrZSBzdXJlIHRoYXQgdGhleSBhcmUgYWxsIG11dGVkIG9yIGFsbCB1bm11dGVkIGFuZFxuICAgICAgICAvLyB0aGF0IHdlIHNlbmQgcHJlc2VuY2UganVzdCBvbmNlLlxuICAgICAgICBBUFAuUlRDLmxvY2FsQXVkaW8ubXV0ZSgpO1xuICAgICAgICAvLyBpc011dGVkIGlzIHRoZSBvcHBvc2l0ZSBvZiBhdWRpb0VuYWJsZWRcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEF1ZGlvSW5mb1RvUHJlc2VuY2UobXV0ZSk7XG4gICAgICAgIGNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSxcbiAgICAvLyBSZWFsbHkgbXV0ZSB2aWRlbywgaS5lLiBkb250IGV2ZW4gc2VuZCBibGFjayBmcmFtZXNcbiAgICBtdXRlVmlkZW86IGZ1bmN0aW9uIChwYywgdW5tdXRlKSB7XG4gICAgICAgIC8vIEZJWE1FOiB0aGlzIHByb2JhYmx5IG5lZWRzIGFub3RoZXIgb2YgdGhvc2UgbG92ZWx5IHN0YXRlIHNhZmVndWFyZHMuLi5cbiAgICAgICAgLy8gd2hpY2ggY2hlY2tzIGZvciBpY2Vjb25uID09IGNvbm5lY3RlZCBhbmQgc2lnc3RhdGUgPT0gc3RhYmxlXG4gICAgICAgIHBjLnNldFJlbW90ZURlc2NyaXB0aW9uKHBjLnJlbW90ZURlc2NyaXB0aW9uLFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHBjLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGFuc3dlcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHNkcCA9IG5ldyBTRFAoYW5zd2VyLnNkcCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2RwLm1lZGlhLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodW5tdXRlKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1zZW5kcmVjdicsICdhPXJlY3Zvbmx5Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2RwLnJhdyA9IHNkcC5zZXNzaW9uICsgc2RwLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuc3dlci5zZHAgPSBzZHAucmF3O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgcGMuc2V0TG9jYWxEZXNjcmlwdGlvbihhbnN3ZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnbXV0ZSBTTEQgb2snKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnbXV0ZSBTTEQgZXJyb3InKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcihcImRpYWxvZy5lcnJvclwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJkaWFsb2cuU0xERmFpbHVyZVwiKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ211dGVWaWRlbyBTUkQgZXJyb3InKTtcbiAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKFwiZGlhbG9nLmVycm9yXCIsXG4gICAgICAgICAgICAgICAgICAgIFwiZGlhbG9nLlNSREZhaWx1cmVcIik7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuICAgIHRvZ2dsZVJlY29yZGluZzogZnVuY3Rpb24gKHRva2VuRW1wdHlDYWxsYmFjayxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydGluZ0NhbGxiYWNrLCBzdGFydGVkQ2FsbGJhY2spIHtcbiAgICAgICAgUmVjb3JkaW5nLnRvZ2dsZVJlY29yZGluZyh0b2tlbkVtcHR5Q2FsbGJhY2ssXG4gICAgICAgICAgICBzdGFydGluZ0NhbGxiYWNrLCBzdGFydGVkQ2FsbGJhY2ssIGNvbm5lY3Rpb24pO1xuICAgIH0sXG4gICAgYWRkVG9QcmVzZW5jZTogZnVuY3Rpb24gKG5hbWUsIHZhbHVlLCBkb250U2VuZCkge1xuICAgICAgICBzd2l0Y2ggKG5hbWUpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNhc2UgXCJkaXNwbGF5TmFtZVwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGREaXNwbGF5TmFtZVRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcImV0aGVycGFkXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEV0aGVycGFkVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwicHJlemlcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkUHJlemlUb1ByZXNlbmNlKHZhbHVlLCAwKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJwcmV6aVNsaWRlXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEN1cnJlbnRTbGlkZVRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcImNvbm5lY3Rpb25RdWFsaXR5XCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZENvbm5lY3Rpb25JbmZvVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiZW1haWxcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRW1haWxUb1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJkZXZpY2VzXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZERldmljZXNUb1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQgOlxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiVW5rbm93biB0YWcgZm9yIHByZXNlbmNlOiBcIiArIG5hbWUpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWRvbnRTZW5kKVxuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogU2VuZHMgJ2RhdGEnIGFzIGEgbG9nIG1lc3NhZ2UgdG8gdGhlIGZvY3VzLiBSZXR1cm5zIHRydWUgaWZmIGEgbWVzc2FnZVxuICAgICAqIHdhcyBzZW50LlxuICAgICAqIEBwYXJhbSBkYXRhXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59IHRydWUgaWZmIGEgbWVzc2FnZSB3YXMgc2VudC5cbiAgICAgKi9cbiAgICBzZW5kTG9nczogZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgaWYoIWNvbm5lY3Rpb24uZW11Yy5mb2N1c011Y0ppZClcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICB2YXIgZGVmbGF0ZSA9IHRydWU7XG5cbiAgICAgICAgdmFyIGNvbnRlbnQgPSBKU09OLnN0cmluZ2lmeShkYXRhKTtcbiAgICAgICAgaWYgKGRlZmxhdGUpIHtcbiAgICAgICAgICAgIGNvbnRlbnQgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIFBha28uZGVmbGF0ZVJhdyhjb250ZW50KSk7XG4gICAgICAgIH1cbiAgICAgICAgY29udGVudCA9IEJhc2U2NC5lbmNvZGUoY29udGVudCk7XG4gICAgICAgIC8vIFhFUC0wMzM3LWlzaFxuICAgICAgICB2YXIgbWVzc2FnZSA9ICRtc2coe3RvOiBjb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQsIHR5cGU6ICdub3JtYWwnfSk7XG4gICAgICAgIG1lc3NhZ2UuYygnbG9nJywgeyB4bWxuczogJ3Vybjp4bXBwOmV2ZW50bG9nJyxcbiAgICAgICAgICAgIGlkOiAnUGVlckNvbm5lY3Rpb25TdGF0cyd9KTtcbiAgICAgICAgbWVzc2FnZS5jKCdtZXNzYWdlJykudChjb250ZW50KS51cCgpO1xuICAgICAgICBpZiAoZGVmbGF0ZSkge1xuICAgICAgICAgICAgbWVzc2FnZS5jKCd0YWcnLCB7bmFtZTogXCJkZWZsYXRlZFwiLCB2YWx1ZTogXCJ0cnVlXCJ9KS51cCgpO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2UudXAoKTtcblxuICAgICAgICBjb25uZWN0aW9uLnNlbmQobWVzc2FnZSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH0sXG4gICAgcG9wdWxhdGVEYXRhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGlmIChjb25uZWN0aW9uLmppbmdsZSkge1xuICAgICAgICAgICAgZGF0YSA9IGNvbm5lY3Rpb24uamluZ2xlLnBvcHVsYXRlRGF0YSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgZ2V0TG9nZ2VyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKGNvbm5lY3Rpb24ubG9nZ2VyKVxuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24ubG9nZ2VyLmxvZztcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSxcbiAgICBnZXRQcmV6aTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmdldFByZXppKHRoaXMubXlKaWQoKSk7XG4gICAgfSxcbiAgICByZW1vdmVQcmV6aUZyb21QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgc2VuZENoYXRNZXNzYWdlOiBmdW5jdGlvbiAobWVzc2FnZSwgbmlja25hbWUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRNZXNzYWdlKG1lc3NhZ2UsIG5pY2tuYW1lKTtcbiAgICB9LFxuICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uICh0b3BpYykge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2V0U3ViamVjdCh0b3BpYyk7XG4gICAgfSxcbiAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMubG9ja1Jvb20oa2V5LCBvblN1Y2Nlc3MsIG9uRXJyb3IsIG9uTm90U3VwcG9ydGVkKTtcbiAgICB9LFxuICAgIGRpYWw6IGZ1bmN0aW9uICh0bywgZnJvbSwgcm9vbU5hbWUscm9vbVBhc3MpIHtcbiAgICAgICAgY29ubmVjdGlvbi5yYXlvLmRpYWwodG8sIGZyb20sIHJvb21OYW1lLHJvb21QYXNzKTtcbiAgICB9LFxuICAgIHNldE11dGU6IGZ1bmN0aW9uIChqaWQsIG11dGUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGppZCwgbXV0ZSk7XG4gICAgfSxcbiAgICBlamVjdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25uZWN0aW9uLm1vZGVyYXRlLmVqZWN0KGppZCk7XG4gICAgfSxcbiAgICBsb2dvdXQ6IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICBNb2RlcmF0b3IubG9nb3V0KGNhbGxiYWNrKTtcbiAgICB9LFxuICAgIGZpbmRKaWRGcm9tUmVzb3VyY2U6IGZ1bmN0aW9uIChyZXNvdXJjZSkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2UpO1xuICAgIH0sXG4gICAgZ2V0TWVtYmVyczogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLm1lbWJlcnM7XG4gICAgfSxcbiAgICBnZXRKaWRGcm9tU1NSQzogZnVuY3Rpb24gKHNzcmMpIHtcbiAgICAgICAgaWYoIWNvbm5lY3Rpb24pXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24uZW11Yy5zc3JjMmppZFtzc3JjXTtcbiAgICB9LFxuICAgIGdldE1VQ0pvaW5lZDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmpvaW5lZDtcbiAgICB9LFxuICAgIGdldFNlc3Npb25zOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmppbmdsZS5zZXNzaW9ucztcbiAgICB9LFxuICAgIHJlbW92ZVN0cmVhbTogZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICBpZighY29ubmVjdGlvbiB8fCAhY29ubmVjdGlvbi5qaW5nbGUuYWN0aXZlY2FsbCB8fFxuICAgICAgICAgICAgIWNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwucGVlcmNvbm5lY3Rpb24pXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwucGVlcmNvbm5lY3Rpb24ucmVtb3ZlU3RyZWFtKHN0cmVhbSk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBYTVBQO1xuIiwiLy8gaTE4bmV4dCwgdjEuNy43XG4vLyBDb3B5cmlnaHQgKGMpMjAxNCBKYW4gTcO8aGxlbWFubiAoamFtdWhsKS5cbi8vIERpc3RyaWJ1dGVkIHVuZGVyIE1JVCBsaWNlbnNlXG4vLyBodHRwOi8vaTE4bmV4dC5jb21cbihmdW5jdGlvbigpIHtcblxuICAgIC8vIGFkZCBpbmRleE9mIHRvIG5vbiBFQ01BLTI2MiBzdGFuZGFyZCBjb21wbGlhbnQgYnJvd3NlcnNcbiAgICBpZiAoIUFycmF5LnByb3RvdHlwZS5pbmRleE9mKSB7XG4gICAgICAgIEFycmF5LnByb3RvdHlwZS5pbmRleE9mID0gZnVuY3Rpb24gKHNlYXJjaEVsZW1lbnQgLyosIGZyb21JbmRleCAqLyApIHtcbiAgICAgICAgICAgIFwidXNlIHN0cmljdFwiO1xuICAgICAgICAgICAgaWYgKHRoaXMgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciB0ID0gT2JqZWN0KHRoaXMpO1xuICAgICAgICAgICAgdmFyIGxlbiA9IHQubGVuZ3RoID4+PiAwO1xuICAgICAgICAgICAgaWYgKGxlbiA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBuID0gMDtcbiAgICAgICAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIG4gPSBOdW1iZXIoYXJndW1lbnRzWzFdKTtcbiAgICAgICAgICAgICAgICBpZiAobiAhPSBuKSB7IC8vIHNob3J0Y3V0IGZvciB2ZXJpZnlpbmcgaWYgaXQncyBOYU5cbiAgICAgICAgICAgICAgICAgICAgbiA9IDA7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChuICE9IDAgJiYgbiAhPSBJbmZpbml0eSAmJiBuICE9IC1JbmZpbml0eSkge1xuICAgICAgICAgICAgICAgICAgICBuID0gKG4gPiAwIHx8IC0xKSAqIE1hdGguZmxvb3IoTWF0aC5hYnMobikpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChuID49IGxlbikge1xuICAgICAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBrID0gbiA+PSAwID8gbiA6IE1hdGgubWF4KGxlbiAtIE1hdGguYWJzKG4pLCAwKTtcbiAgICAgICAgICAgIGZvciAoOyBrIDwgbGVuOyBrKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoayBpbiB0ICYmIHRba10gPT09IHNlYXJjaEVsZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8vIGFkZCBsYXN0SW5kZXhPZiB0byBub24gRUNNQS0yNjIgc3RhbmRhcmQgY29tcGxpYW50IGJyb3dzZXJzXG4gICAgaWYgKCFBcnJheS5wcm90b3R5cGUubGFzdEluZGV4T2YpIHtcbiAgICAgICAgQXJyYXkucHJvdG90eXBlLmxhc3RJbmRleE9mID0gZnVuY3Rpb24oc2VhcmNoRWxlbWVudCAvKiwgZnJvbUluZGV4Ki8pIHtcbiAgICAgICAgICAgIFwidXNlIHN0cmljdFwiO1xuICAgICAgICAgICAgaWYgKHRoaXMgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciB0ID0gT2JqZWN0KHRoaXMpO1xuICAgICAgICAgICAgdmFyIGxlbiA9IHQubGVuZ3RoID4+PiAwO1xuICAgICAgICAgICAgaWYgKGxlbiA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBuID0gbGVuO1xuICAgICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgbiA9IE51bWJlcihhcmd1bWVudHNbMV0pO1xuICAgICAgICAgICAgICAgIGlmIChuICE9IG4pIHtcbiAgICAgICAgICAgICAgICAgICAgbiA9IDA7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChuICE9IDAgJiYgbiAhPSAoMSAvIDApICYmIG4gIT0gLSgxIC8gMCkpIHtcbiAgICAgICAgICAgICAgICAgICAgbiA9IChuID4gMCB8fCAtMSkgKiBNYXRoLmZsb29yKE1hdGguYWJzKG4pKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgayA9IG4gPj0gMCA/IE1hdGgubWluKG4sIGxlbiAtIDEpIDogbGVuIC0gTWF0aC5hYnMobik7XG4gICAgICAgICAgICBmb3IgKDsgayA+PSAwOyBrLS0pIHtcbiAgICAgICAgICAgICAgICBpZiAoayBpbiB0ICYmIHRba10gPT09IHNlYXJjaEVsZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICB9O1xuICAgIH1cbiAgICBcbiAgICAvLyBBZGQgc3RyaW5nIHRyaW0gZm9yIElFOC5cbiAgICBpZiAodHlwZW9mIFN0cmluZy5wcm90b3R5cGUudHJpbSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBTdHJpbmcucHJvdG90eXBlLnRyaW0gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJlcGxhY2UoL15cXHMrfFxccyskL2csICcnKTsgXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgcm9vdCA9IHRoaXNcbiAgICAgICwgJCA9IHJvb3QualF1ZXJ5IHx8IHJvb3QuWmVwdG9cbiAgICAgICwgaTE4biA9IHt9XG4gICAgICAsIHJlc1N0b3JlID0ge31cbiAgICAgICwgY3VycmVudExuZ1xuICAgICAgLCByZXBsYWNlbWVudENvdW50ZXIgPSAwXG4gICAgICAsIGxhbmd1YWdlcyA9IFtdXG4gICAgICAsIGluaXRpYWxpemVkID0gZmFsc2VcbiAgICAgICwgc3luYyA9IHt9O1xuXG5cblxuICAgIC8vIEV4cG9ydCB0aGUgaTE4bmV4dCBvYmplY3QgZm9yICoqQ29tbW9uSlMqKi4gXG4gICAgLy8gSWYgd2UncmUgbm90IGluIENvbW1vbkpTLCBhZGQgYGkxOG5gIHRvIHRoZVxuICAgIC8vIGdsb2JhbCBvYmplY3Qgb3IgdG8ganF1ZXJ5LlxuICAgIGlmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJyAmJiBtb2R1bGUuZXhwb3J0cykge1xuICAgICAgICBpZiAoISQpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgJCA9IHJlcXVpcmUoJ2pxdWVyeScpO1xuICAgICAgICAgIH0gY2F0Y2goZSkge1xuICAgICAgICAgICAgLy8ganVzdCBpZ25vcmVcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCQpIHtcbiAgICAgICAgICAgICQuaTE4biA9ICQuaTE4biB8fCBpMThuO1xuICAgICAgICB9XG4gICAgICAgIG1vZHVsZS5leHBvcnRzID0gaTE4bjtcbiAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoJCkge1xuICAgICAgICAgICAgJC5pMThuID0gJC5pMThuIHx8IGkxOG47XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHJvb3QuaTE4biA9IHJvb3QuaTE4biB8fCBpMThuO1xuICAgIH1cbiAgICBzeW5jID0ge1xuICAgIFxuICAgICAgICBsb2FkOiBmdW5jdGlvbihsbmdzLCBvcHRpb25zLCBjYikge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMudXNlTG9jYWxTdG9yYWdlKSB7XG4gICAgICAgICAgICAgICAgc3luYy5fbG9hZExvY2FsKGxuZ3MsIG9wdGlvbnMsIGZ1bmN0aW9uKGVyciwgc3RvcmUpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG1pc3NpbmdMbmdzID0gW107XG4gICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXN0b3JlW2xuZ3NbaV1dKSBtaXNzaW5nTG5ncy5wdXNoKGxuZ3NbaV0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIGlmIChtaXNzaW5nTG5ncy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzeW5jLl9mZXRjaChtaXNzaW5nTG5ncywgb3B0aW9ucywgZnVuY3Rpb24oZXJyLCBmZXRjaGVkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZi5leHRlbmQoc3RvcmUsIGZldGNoZWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bmMuX3N0b3JlTG9jYWwoZmV0Y2hlZCk7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2IobnVsbCwgc3RvcmUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYihudWxsLCBzdG9yZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc3luYy5fZmV0Y2gobG5ncywgb3B0aW9ucywgZnVuY3Rpb24oZXJyLCBzdG9yZSl7XG4gICAgICAgICAgICAgICAgICAgIGNiKG51bGwsIHN0b3JlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgX2xvYWRMb2NhbDogZnVuY3Rpb24obG5ncywgb3B0aW9ucywgY2IpIHtcbiAgICAgICAgICAgIHZhciBzdG9yZSA9IHt9XG4gICAgICAgICAgICAgICwgbm93TVMgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcbiAgICBcbiAgICAgICAgICAgIGlmKHdpbmRvdy5sb2NhbFN0b3JhZ2UpIHtcbiAgICBcbiAgICAgICAgICAgICAgICB2YXIgdG9kbyA9IGxuZ3MubGVuZ3RoO1xuICAgIFxuICAgICAgICAgICAgICAgIGYuZWFjaChsbmdzLCBmdW5jdGlvbihrZXksIGxuZykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbG9jYWwgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3Jlc18nICsgbG5nKTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxvY2FsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsb2NhbCA9IEpTT04ucGFyc2UobG9jYWwpO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGxvY2FsLmkxOG5TdGFtcCAmJiBsb2NhbC5pMThuU3RhbXAgKyBvcHRpb25zLmxvY2FsU3RvcmFnZUV4cGlyYXRpb25UaW1lID4gbm93TVMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9yZVtsbmddID0gbG9jYWw7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAgICAgdG9kby0tOyAvLyB3YWl0IGZvciBhbGwgZG9uZSBiZWZvciBjYWxsYmFja1xuICAgICAgICAgICAgICAgICAgICBpZiAodG9kbyA9PT0gMCkgY2IobnVsbCwgc3RvcmUpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgIFxuICAgICAgICBfc3RvcmVMb2NhbDogZnVuY3Rpb24oc3RvcmUpIHtcbiAgICAgICAgICAgIGlmKHdpbmRvdy5sb2NhbFN0b3JhZ2UpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBtIGluIHN0b3JlKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0b3JlW21dLmkxOG5TdGFtcCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuICAgICAgICAgICAgICAgICAgICBmLmxvY2FsU3RvcmFnZS5zZXRJdGVtKCdyZXNfJyArIG0sIEpTT04uc3RyaW5naWZ5KHN0b3JlW21dKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9LFxuICAgIFxuICAgICAgICBfZmV0Y2g6IGZ1bmN0aW9uKGxuZ3MsIG9wdGlvbnMsIGNiKSB7XG4gICAgICAgICAgICB2YXIgbnMgPSBvcHRpb25zLm5zXG4gICAgICAgICAgICAgICwgc3RvcmUgPSB7fTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKCFvcHRpb25zLmR5bmFtaWNMb2FkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRvZG8gPSBucy5uYW1lc3BhY2VzLmxlbmd0aCAqIGxuZ3MubGVuZ3RoXG4gICAgICAgICAgICAgICAgICAsIGVycm9ycztcbiAgICBcbiAgICAgICAgICAgICAgICAvLyBsb2FkIGVhY2ggZmlsZSBpbmRpdmlkdWFsXG4gICAgICAgICAgICAgICAgZi5lYWNoKG5zLm5hbWVzcGFjZXMsIGZ1bmN0aW9uKG5zSW5kZXgsIG5zVmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgZi5lYWNoKGxuZ3MsIGZ1bmN0aW9uKGxuZ0luZGV4LCBsbmdWYWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDYWxsIHRoaXMgb25jZSBvdXIgdHJhbnNsYXRpb24gaGFzIHJldHVybmVkLlxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxvYWRDb21wbGV0ZSA9IGZ1bmN0aW9uKGVyciwgZGF0YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JzID0gZXJyb3JzIHx8IFtdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcnMucHVzaChlcnIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9yZVtsbmdWYWx1ZV0gPSBzdG9yZVtsbmdWYWx1ZV0gfHwge307XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmVbbG5nVmFsdWVdW25zVmFsdWVdID0gZGF0YTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2RvLS07IC8vIHdhaXQgZm9yIGFsbCBkb25lIGJlZm9yIGNhbGxiYWNrXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRvZG8gPT09IDApIGNiKGVycm9ycywgc3RvcmUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgaWYodHlwZW9mIG9wdGlvbnMuY3VzdG9tTG9hZCA9PSAnZnVuY3Rpb24nKXtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBVc2UgdGhlIHNwZWNpZmllZCBjdXN0b20gY2FsbGJhY2suXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2FkKGxuZ1ZhbHVlLCBuc1ZhbHVlLCBvcHRpb25zLCBsb2FkQ29tcGxldGUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL34gLy8gVXNlIG91ciBpbmJ1aWx0IHN5bmMuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3luYy5fZmV0Y2hPbmUobG5nVmFsdWUsIG5zVmFsdWUsIG9wdGlvbnMsIGxvYWRDb21wbGV0ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBDYWxsIHRoaXMgb25jZSBvdXIgdHJhbnNsYXRpb24gaGFzIHJldHVybmVkLlxuICAgICAgICAgICAgICAgIHZhciBsb2FkQ29tcGxldGUgPSBmdW5jdGlvbihlcnIsIGRhdGEpIHtcbiAgICAgICAgICAgICAgICAgICAgY2IobnVsbCwgZGF0YSk7XG4gICAgICAgICAgICAgICAgfTtcbiAgICBcbiAgICAgICAgICAgICAgICBpZih0eXBlb2Ygb3B0aW9ucy5jdXN0b21Mb2FkID09ICdmdW5jdGlvbicpe1xuICAgICAgICAgICAgICAgICAgICAvLyBVc2UgdGhlIHNwZWNpZmllZCBjdXN0b20gY2FsbGJhY2suXG4gICAgICAgICAgICAgICAgICAgIG9wdGlvbnMuY3VzdG9tTG9hZChsbmdzLCBucy5uYW1lc3BhY2VzLCBvcHRpb25zLCBsb2FkQ29tcGxldGUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB1cmwgPSBhcHBseVJlcGxhY2VtZW50KG9wdGlvbnMucmVzR2V0UGF0aCwgeyBsbmc6IGxuZ3Muam9pbignKycpLCBuczogbnMubmFtZXNwYWNlcy5qb2luKCcrJykgfSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGxvYWQgYWxsIG5lZWRlZCBzdHVmZiBvbmNlXG4gICAgICAgICAgICAgICAgICAgIGYuYWpheCh7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHVybCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uKGRhdGEsIHN0YXR1cywgeGhyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2coJ2xvYWRlZDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9hZENvbXBsZXRlKG51bGwsIGRhdGEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yIDogZnVuY3Rpb24oeGhyLCBzdGF0dXMsIGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2coJ2ZhaWxlZCBsb2FkaW5nOiAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkQ29tcGxldGUoJ2ZhaWxlZCBsb2FkaW5nIHJlc291cmNlLmpzb24gZXJyb3I6ICcgKyBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YVR5cGU6IFwianNvblwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgYXN5bmMgOiBvcHRpb25zLmdldEFzeW5jXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gICAgXG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIF9mZXRjaE9uZTogZnVuY3Rpb24obG5nLCBucywgb3B0aW9ucywgZG9uZSkge1xuICAgICAgICAgICAgdmFyIHVybCA9IGFwcGx5UmVwbGFjZW1lbnQob3B0aW9ucy5yZXNHZXRQYXRoLCB7IGxuZzogbG5nLCBuczogbnMgfSk7XG4gICAgICAgICAgICBmLmFqYXgoe1xuICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uKGRhdGEsIHN0YXR1cywgeGhyKSB7XG4gICAgICAgICAgICAgICAgICAgIGYubG9nKCdsb2FkZWQ6ICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgICAgICBkb25lKG51bGwsIGRhdGEpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZXJyb3IgOiBmdW5jdGlvbih4aHIsIHN0YXR1cywgZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKChzdGF0dXMgJiYgc3RhdHVzID09IDIwMCkgfHwgKHhociAmJiB4aHIuc3RhdHVzICYmIHhoci5zdGF0dXMgPT0gMjAwKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gZmlsZSBsb2FkZWQgYnV0IGludmFsaWQganNvbiwgc3RvcCB3YXN0ZSB0aW1lICFcbiAgICAgICAgICAgICAgICAgICAgICAgIGYuZXJyb3IoJ1RoZXJlIGlzIGEgdHlwbyBpbjogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoKHN0YXR1cyAmJiBzdGF0dXMgPT0gNDA0KSB8fCAoeGhyICYmIHhoci5zdGF0dXMgJiYgeGhyLnN0YXR1cyA9PSA0MDQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmLmxvZygnRG9lcyBub3QgZXhpc3Q6ICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRoZVN0YXR1cyA9IHN0YXR1cyA/IHN0YXR1cyA6ICgoeGhyICYmIHhoci5zdGF0dXMpID8geGhyLnN0YXR1cyA6IG51bGwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2codGhlU3RhdHVzICsgJyB3aGVuIGxvYWRpbmcgJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHt9KTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGRhdGFUeXBlOiBcImpzb25cIixcbiAgICAgICAgICAgICAgICBhc3luYyA6IG9wdGlvbnMuZ2V0QXN5bmNcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9LFxuICAgIFxuICAgICAgICBwb3N0TWlzc2luZzogZnVuY3Rpb24obG5nLCBucywga2V5LCBkZWZhdWx0VmFsdWUsIGxuZ3MpIHtcbiAgICAgICAgICAgIHZhciBwYXlsb2FkID0ge307XG4gICAgICAgICAgICBwYXlsb2FkW2tleV0gPSBkZWZhdWx0VmFsdWU7XG4gICAgXG4gICAgICAgICAgICB2YXIgdXJscyA9IFtdO1xuICAgIFxuICAgICAgICAgICAgaWYgKG8uc2VuZE1pc3NpbmdUbyA9PT0gJ2ZhbGxiYWNrJyAmJiBvLmZhbGxiYWNrTG5nWzBdICE9PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgby5mYWxsYmFja0xuZy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB1cmxzLnB1c2goe2xuZzogby5mYWxsYmFja0xuZ1tpXSwgdXJsOiBhcHBseVJlcGxhY2VtZW50KG8ucmVzUG9zdFBhdGgsIHsgbG5nOiBvLmZhbGxiYWNrTG5nW2ldLCBuczogbnMgfSl9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG8uc2VuZE1pc3NpbmdUbyA9PT0gJ2N1cnJlbnQnIHx8IChvLnNlbmRNaXNzaW5nVG8gPT09ICdmYWxsYmFjaycgJiYgby5mYWxsYmFja0xuZ1swXSA9PT0gZmFsc2UpICkge1xuICAgICAgICAgICAgICAgIHVybHMucHVzaCh7bG5nOiBsbmcsIHVybDogYXBwbHlSZXBsYWNlbWVudChvLnJlc1Bvc3RQYXRoLCB7IGxuZzogbG5nLCBuczogbnMgfSl9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoby5zZW5kTWlzc2luZ1RvID09PSAnYWxsJykge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwLCBsID0gbG5ncy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdXJscy5wdXNoKHtsbmc6IGxuZ3NbaV0sIHVybDogYXBwbHlSZXBsYWNlbWVudChvLnJlc1Bvc3RQYXRoLCB7IGxuZzogbG5nc1tpXSwgbnM6IG5zIH0pfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgZm9yICh2YXIgeSA9IDAsIGxlbiA9IHVybHMubGVuZ3RoOyB5IDwgbGVuOyB5KyspIHtcbiAgICAgICAgICAgICAgICB2YXIgaXRlbSA9IHVybHNbeV07XG4gICAgICAgICAgICAgICAgZi5hamF4KHtcbiAgICAgICAgICAgICAgICAgICAgdXJsOiBpdGVtLnVybCxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogby5zZW5kVHlwZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YTogcGF5bG9hZCxcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCB4aHIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGYubG9nKCdwb3N0ZWQgbWlzc2luZyBrZXkgXFwnJyArIGtleSArICdcXCcgdG86ICcgKyBpdGVtLnVybCk7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBhZGQga2V5IHRvIHJlc1N0b3JlXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIga2V5cyA9IGtleS5zcGxpdCgnLicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHggPSAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHZhbHVlID0gcmVzU3RvcmVbaXRlbS5sbmddW25zXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHdoaWxlIChrZXlzW3hdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHggPT09IGtleXMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlW2tleXNbeF1dID0gZGVmYXVsdFZhbHVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWVba2V5c1t4XV0gPSB2YWx1ZVtrZXlzW3hdXSB8fCB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeCsrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBlcnJvciA6IGZ1bmN0aW9uKHhociwgc3RhdHVzLCBlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2coJ2ZhaWxlZCBwb3N0aW5nIG1pc3Npbmcga2V5IFxcJycgKyBrZXkgKyAnXFwnIHRvOiAnICsgaXRlbS51cmwpO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBkYXRhVHlwZTogXCJqc29uXCIsXG4gICAgICAgICAgICAgICAgICAgIGFzeW5jIDogby5wb3N0QXN5bmNcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgcmVsb2FkOiByZWxvYWRcbiAgICB9O1xuICAgIC8vIGRlZmF1bHRzXG4gICAgdmFyIG8gPSB7XG4gICAgICAgIGxuZzogdW5kZWZpbmVkLFxuICAgICAgICBsb2FkOiAnYWxsJyxcbiAgICAgICAgcHJlbG9hZDogW10sXG4gICAgICAgIGxvd2VyQ2FzZUxuZzogZmFsc2UsXG4gICAgICAgIHJldHVybk9iamVjdFRyZWVzOiBmYWxzZSxcbiAgICAgICAgZmFsbGJhY2tMbmc6IFsnZGV2J10sXG4gICAgICAgIGZhbGxiYWNrTlM6IFtdLFxuICAgICAgICBkZXRlY3RMbmdRUzogJ3NldExuZycsXG4gICAgICAgIGRldGVjdExuZ0Zyb21Mb2NhbFN0b3JhZ2U6IGZhbHNlLFxuICAgICAgICBuczogJ3RyYW5zbGF0aW9uJyxcbiAgICAgICAgZmFsbGJhY2tPbk51bGw6IHRydWUsXG4gICAgICAgIGZhbGxiYWNrT25FbXB0eTogZmFsc2UsXG4gICAgICAgIGZhbGxiYWNrVG9EZWZhdWx0TlM6IGZhbHNlLFxuICAgICAgICBuc3NlcGFyYXRvcjogJzonLFxuICAgICAgICBrZXlzZXBhcmF0b3I6ICcuJyxcbiAgICAgICAgc2VsZWN0b3JBdHRyOiAnZGF0YS1pMThuJyxcbiAgICAgICAgZGVidWc6IGZhbHNlLFxuICAgICAgICBcbiAgICAgICAgcmVzR2V0UGF0aDogJ2xvY2FsZXMvX19sbmdfXy9fX25zX18uanNvbicsXG4gICAgICAgIHJlc1Bvc3RQYXRoOiAnbG9jYWxlcy9hZGQvX19sbmdfXy9fX25zX18nLFxuICAgIFxuICAgICAgICBnZXRBc3luYzogdHJ1ZSxcbiAgICAgICAgcG9zdEFzeW5jOiB0cnVlLFxuICAgIFxuICAgICAgICByZXNTdG9yZTogdW5kZWZpbmVkLFxuICAgICAgICB1c2VMb2NhbFN0b3JhZ2U6IGZhbHNlLFxuICAgICAgICBsb2NhbFN0b3JhZ2VFeHBpcmF0aW9uVGltZTogNyoyNCo2MCo2MCoxMDAwLFxuICAgIFxuICAgICAgICBkeW5hbWljTG9hZDogZmFsc2UsXG4gICAgICAgIHNlbmRNaXNzaW5nOiBmYWxzZSxcbiAgICAgICAgc2VuZE1pc3NpbmdUbzogJ2ZhbGxiYWNrJywgLy8gY3VycmVudCB8IGFsbFxuICAgICAgICBzZW5kVHlwZTogJ1BPU1QnLFxuICAgIFxuICAgICAgICBpbnRlcnBvbGF0aW9uUHJlZml4OiAnX18nLFxuICAgICAgICBpbnRlcnBvbGF0aW9uU3VmZml4OiAnX18nLFxuICAgICAgICBkZWZhdWx0VmFyaWFibGVzOiBmYWxzZSxcbiAgICAgICAgcmV1c2VQcmVmaXg6ICckdCgnLFxuICAgICAgICByZXVzZVN1ZmZpeDogJyknLFxuICAgICAgICBwbHVyYWxTdWZmaXg6ICdfcGx1cmFsJyxcbiAgICAgICAgcGx1cmFsTm90Rm91bmQ6IFsncGx1cmFsX25vdF9mb3VuZCcsIE1hdGgucmFuZG9tKCldLmpvaW4oJycpLFxuICAgICAgICBjb250ZXh0Tm90Rm91bmQ6IFsnY29udGV4dF9ub3RfZm91bmQnLCBNYXRoLnJhbmRvbSgpXS5qb2luKCcnKSxcbiAgICAgICAgZXNjYXBlSW50ZXJwb2xhdGlvbjogZmFsc2UsXG4gICAgICAgIGluZGVmaW5pdGVTdWZmaXg6ICdfaW5kZWZpbml0ZScsXG4gICAgICAgIGluZGVmaW5pdGVOb3RGb3VuZDogWydpbmRlZmluaXRlX25vdF9mb3VuZCcsIE1hdGgucmFuZG9tKCldLmpvaW4oJycpLFxuICAgIFxuICAgICAgICBzZXRKcXVlcnlFeHQ6IHRydWUsXG4gICAgICAgIGRlZmF1bHRWYWx1ZUZyb21Db250ZW50OiB0cnVlLFxuICAgICAgICB1c2VEYXRhQXR0ck9wdGlvbnM6IGZhbHNlLFxuICAgICAgICBjb29raWVFeHBpcmF0aW9uVGltZTogdW5kZWZpbmVkLFxuICAgICAgICB1c2VDb29raWU6IHRydWUsXG4gICAgICAgIGNvb2tpZU5hbWU6ICdpMThuZXh0JyxcbiAgICAgICAgY29va2llRG9tYWluOiB1bmRlZmluZWQsXG4gICAgXG4gICAgICAgIG9iamVjdFRyZWVLZXlIYW5kbGVyOiB1bmRlZmluZWQsXG4gICAgICAgIHBvc3RQcm9jZXNzOiB1bmRlZmluZWQsXG4gICAgICAgIHBhcnNlTWlzc2luZ0tleTogdW5kZWZpbmVkLFxuICAgICAgICBtaXNzaW5nS2V5SGFuZGxlcjogc3luYy5wb3N0TWlzc2luZyxcbiAgICBcbiAgICAgICAgc2hvcnRjdXRGdW5jdGlvbjogJ3NwcmludGYnIC8vIG9yOiBkZWZhdWx0VmFsdWVcbiAgICB9O1xuICAgIGZ1bmN0aW9uIF9leHRlbmQodGFyZ2V0LCBzb3VyY2UpIHtcbiAgICAgICAgaWYgKCFzb3VyY2UgfHwgdHlwZW9mIHNvdXJjZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgcmV0dXJuIHRhcmdldDtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBmb3IgKHZhciBhdHRyIGluIHNvdXJjZSkgeyB0YXJnZXRbYXR0cl0gPSBzb3VyY2VbYXR0cl07IH1cbiAgICAgICAgcmV0dXJuIHRhcmdldDtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gX2RlZXBFeHRlbmQodGFyZ2V0LCBzb3VyY2UpIHtcbiAgICAgICAgZm9yICh2YXIgcHJvcCBpbiBzb3VyY2UpXG4gICAgICAgICAgICBpZiAocHJvcCBpbiB0YXJnZXQpXG4gICAgICAgICAgICAgICAgX2RlZXBFeHRlbmQodGFyZ2V0W3Byb3BdLCBzb3VyY2VbcHJvcF0pO1xuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHRhcmdldFtwcm9wXSA9IHNvdXJjZVtwcm9wXTtcbiAgICAgICAgcmV0dXJuIHRhcmdldDtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gX2VhY2gob2JqZWN0LCBjYWxsYmFjaywgYXJncykge1xuICAgICAgICB2YXIgbmFtZSwgaSA9IDAsXG4gICAgICAgICAgICBsZW5ndGggPSBvYmplY3QubGVuZ3RoLFxuICAgICAgICAgICAgaXNPYmogPSBsZW5ndGggPT09IHVuZGVmaW5lZCB8fCBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmFwcGx5KG9iamVjdCkgIT09ICdbb2JqZWN0IEFycmF5XScgfHwgdHlwZW9mIG9iamVjdCA9PT0gXCJmdW5jdGlvblwiO1xuICAgIFxuICAgICAgICBpZiAoYXJncykge1xuICAgICAgICAgICAgaWYgKGlzT2JqKSB7XG4gICAgICAgICAgICAgICAgZm9yIChuYW1lIGluIG9iamVjdCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoY2FsbGJhY2suYXBwbHkob2JqZWN0W25hbWVdLCBhcmdzKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBsZW5ndGg7ICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoY2FsbGJhY2suYXBwbHkob2JqZWN0W2krK10sIGFyZ3MpID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIEEgc3BlY2lhbCwgZmFzdCwgY2FzZSBmb3IgdGhlIG1vc3QgY29tbW9uIHVzZSBvZiBlYWNoXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAoaXNPYmopIHtcbiAgICAgICAgICAgICAgICBmb3IgKG5hbWUgaW4gb2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjYWxsYmFjay5jYWxsKG9iamVjdFtuYW1lXSwgbmFtZSwgb2JqZWN0W25hbWVdKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBsZW5ndGg7ICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoY2FsbGJhY2suY2FsbChvYmplY3RbaV0sIGksIG9iamVjdFtpKytdKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJldHVybiBvYmplY3Q7XG4gICAgfVxuICAgIFxuICAgIHZhciBfZW50aXR5TWFwID0ge1xuICAgICAgICBcIiZcIjogXCImYW1wO1wiLFxuICAgICAgICBcIjxcIjogXCImbHQ7XCIsXG4gICAgICAgIFwiPlwiOiBcIiZndDtcIixcbiAgICAgICAgJ1wiJzogJyZxdW90OycsXG4gICAgICAgIFwiJ1wiOiAnJiMzOTsnLFxuICAgICAgICBcIi9cIjogJyYjeDJGOydcbiAgICB9O1xuICAgIFxuICAgIGZ1bmN0aW9uIF9lc2NhcGUoZGF0YSkge1xuICAgICAgICBpZiAodHlwZW9mIGRhdGEgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4gZGF0YS5yZXBsYWNlKC9bJjw+XCInXFwvXS9nLCBmdW5jdGlvbiAocykge1xuICAgICAgICAgICAgICAgIHJldHVybiBfZW50aXR5TWFwW3NdO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1lbHNle1xuICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gX2FqYXgob3B0aW9ucykge1xuICAgIFxuICAgICAgICAvLyB2MC41LjAgb2YgaHR0cHM6Ly9naXRodWIuY29tL2dvbG9yb2Rlbi9odHRwLmpzXG4gICAgICAgIHZhciBnZXRYaHIgPSBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgICAgICAgIC8vIFVzZSB0aGUgbmF0aXZlIFhIUiBvYmplY3QgaWYgdGhlIGJyb3dzZXIgc3VwcG9ydHMgaXQuXG4gICAgICAgICAgICBpZiAod2luZG93LlhNTEh0dHBSZXF1ZXN0KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG51bGwsIG5ldyBYTUxIdHRwUmVxdWVzdCgpKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAod2luZG93LkFjdGl2ZVhPYmplY3QpIHtcbiAgICAgICAgICAgICAgICAvLyBJbiBJbnRlcm5ldCBFeHBsb3JlciBjaGVjayBmb3IgQWN0aXZlWCB2ZXJzaW9ucyBvZiB0aGUgWEhSIG9iamVjdC5cbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobnVsbCwgbmV3IEFjdGl2ZVhPYmplY3QoXCJNc3htbDIuWE1MSFRUUFwiKSk7XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobnVsbCwgbmV3IEFjdGl2ZVhPYmplY3QoXCJNaWNyb3NvZnQuWE1MSFRUUFwiKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgLy8gSWYgbm8gWEhSIHN1cHBvcnQgd2FzIGZvdW5kLCB0aHJvdyBhbiBlcnJvci5cbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhuZXcgRXJyb3IoKSk7XG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIHZhciBlbmNvZGVVc2luZ1VybEVuY29kaW5nID0gZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgICAgIGlmKHR5cGVvZiBkYXRhID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IFtdO1xuICAgICAgICAgICAgZm9yKHZhciBkYXRhSXRlbSBpbiBkYXRhKSB7XG4gICAgICAgICAgICAgICAgaWYoZGF0YS5oYXNPd25Qcm9wZXJ0eShkYXRhSXRlbSkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KGRhdGFJdGVtKSArICc9JyArIGVuY29kZVVSSUNvbXBvbmVudChkYXRhW2RhdGFJdGVtXSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQuam9pbignJicpO1xuICAgICAgICB9O1xuICAgIFxuICAgICAgICB2YXIgdXRmOCA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgICAgICAgICB0ZXh0ID0gdGV4dC5yZXBsYWNlKC9cXHJcXG4vZywgJ1xcbicpO1xuICAgICAgICAgICAgdmFyIHJlc3VsdCA9ICcnO1xuICAgIFxuICAgICAgICAgICAgZm9yKHZhciBpID0gMDsgaSA8IHRleHQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgYyA9IHRleHQuY2hhckNvZGVBdChpKTtcbiAgICBcbiAgICAgICAgICAgICAgICBpZihjIDwgMTI4KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYoKGMgPiAxMjcpICYmIChjIDwgMjA0OCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChjID4+IDYpIHwgMTkyKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChjICYgNjMpIHwgMTI4KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoKGMgPj4gMTIpIHwgMjI0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCgoYyA+PiA2KSAmIDYzKSB8IDEyOCk7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSgoYyAmIDYzKSB8IDEyOCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgdmFyIGJhc2U2NCA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgICAgICAgICB2YXIga2V5U3RyID0gJ0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5Ky89JztcbiAgICBcbiAgICAgICAgICAgIHRleHQgPSB1dGY4KHRleHQpO1xuICAgICAgICAgICAgdmFyIHJlc3VsdCA9ICcnLFxuICAgICAgICAgICAgICAgICAgICBjaHIxLCBjaHIyLCBjaHIzLFxuICAgICAgICAgICAgICAgICAgICBlbmMxLCBlbmMyLCBlbmMzLCBlbmM0LFxuICAgICAgICAgICAgICAgICAgICBpID0gMDtcbiAgICBcbiAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgICBjaHIxID0gdGV4dC5jaGFyQ29kZUF0KGkrKyk7XG4gICAgICAgICAgICAgICAgY2hyMiA9IHRleHQuY2hhckNvZGVBdChpKyspO1xuICAgICAgICAgICAgICAgIGNocjMgPSB0ZXh0LmNoYXJDb2RlQXQoaSsrKTtcbiAgICBcbiAgICAgICAgICAgICAgICBlbmMxID0gY2hyMSA+PiAyO1xuICAgICAgICAgICAgICAgIGVuYzIgPSAoKGNocjEgJiAzKSA8PCA0KSB8IChjaHIyID4+IDQpO1xuICAgICAgICAgICAgICAgIGVuYzMgPSAoKGNocjIgJiAxNSkgPDwgMikgfCAoY2hyMyA+PiA2KTtcbiAgICAgICAgICAgICAgICBlbmM0ID0gY2hyMyAmIDYzO1xuICAgIFxuICAgICAgICAgICAgICAgIGlmKGlzTmFOKGNocjIpKSB7XG4gICAgICAgICAgICAgICAgICAgIGVuYzMgPSBlbmM0ID0gNjQ7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmKGlzTmFOKGNocjMpKSB7XG4gICAgICAgICAgICAgICAgICAgIGVuYzQgPSA2NDtcbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgcmVzdWx0ICs9XG4gICAgICAgICAgICAgICAgICAgIGtleVN0ci5jaGFyQXQoZW5jMSkgK1xuICAgICAgICAgICAgICAgICAgICBrZXlTdHIuY2hhckF0KGVuYzIpICtcbiAgICAgICAgICAgICAgICAgICAga2V5U3RyLmNoYXJBdChlbmMzKSArXG4gICAgICAgICAgICAgICAgICAgIGtleVN0ci5jaGFyQXQoZW5jNCk7XG4gICAgICAgICAgICAgICAgY2hyMSA9IGNocjIgPSBjaHIzID0gJyc7XG4gICAgICAgICAgICAgICAgZW5jMSA9IGVuYzIgPSBlbmMzID0gZW5jNCA9ICcnO1xuICAgICAgICAgICAgfSB3aGlsZShpIDwgdGV4dC5sZW5ndGgpO1xuICAgIFxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgdmFyIG1lcmdlSGVhZGVycyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIFVzZSB0aGUgZmlyc3QgaGVhZGVyIG9iamVjdCBhcyBiYXNlLlxuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IGFyZ3VtZW50c1swXTtcbiAgICBcbiAgICAgICAgICAgIC8vIEl0ZXJhdGUgdGhyb3VnaCB0aGUgcmVtYWluaW5nIGhlYWRlciBvYmplY3RzIGFuZCBhZGQgdGhlbS5cbiAgICAgICAgICAgIGZvcih2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudEhlYWRlcnMgPSBhcmd1bWVudHNbaV07XG4gICAgICAgICAgICAgICAgZm9yKHZhciBoZWFkZXIgaW4gY3VycmVudEhlYWRlcnMpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYoY3VycmVudEhlYWRlcnMuaGFzT3duUHJvcGVydHkoaGVhZGVyKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0W2hlYWRlcl0gPSBjdXJyZW50SGVhZGVyc1toZWFkZXJdO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgLy8gUmV0dXJuIHRoZSBtZXJnZWQgaGVhZGVycy5cbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIHZhciBhamF4ID0gZnVuY3Rpb24gKG1ldGhvZCwgdXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgLy8gQWRqdXN0IHBhcmFtZXRlcnMuXG4gICAgICAgICAgICBpZih0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrID0gb3B0aW9ucztcbiAgICAgICAgICAgICAgICBvcHRpb25zID0ge307XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAvLyBTZXQgZGVmYXVsdCBwYXJhbWV0ZXIgdmFsdWVzLlxuICAgICAgICAgICAgb3B0aW9ucy5jYWNoZSA9IG9wdGlvbnMuY2FjaGUgfHwgZmFsc2U7XG4gICAgICAgICAgICBvcHRpb25zLmRhdGEgPSBvcHRpb25zLmRhdGEgfHwge307XG4gICAgICAgICAgICBvcHRpb25zLmhlYWRlcnMgPSBvcHRpb25zLmhlYWRlcnMgfHwge307XG4gICAgICAgICAgICBvcHRpb25zLmpzb25wID0gb3B0aW9ucy5qc29ucCB8fCBmYWxzZTtcbiAgICAgICAgICAgIG9wdGlvbnMuYXN5bmMgPSBvcHRpb25zLmFzeW5jID09PSB1bmRlZmluZWQgPyB0cnVlIDogb3B0aW9ucy5hc3luYztcbiAgICBcbiAgICAgICAgICAgIC8vIE1lcmdlIHRoZSB2YXJpb3VzIGhlYWRlciBvYmplY3RzLlxuICAgICAgICAgICAgdmFyIGhlYWRlcnMgPSBtZXJnZUhlYWRlcnMoe1xuICAgICAgICAgICAgICAgICdhY2NlcHQnOiAnKi8qJyxcbiAgICAgICAgICAgICAgICAnY29udGVudC10eXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDtjaGFyc2V0PVVURi04J1xuICAgICAgICAgICAgfSwgYWpheC5oZWFkZXJzLCBvcHRpb25zLmhlYWRlcnMpO1xuICAgIFxuICAgICAgICAgICAgLy8gRW5jb2RlIHRoZSBkYXRhIGFjY29yZGluZyB0byB0aGUgY29udGVudC10eXBlLlxuICAgICAgICAgICAgdmFyIHBheWxvYWQ7XG4gICAgICAgICAgICBpZiAoaGVhZGVyc1snY29udGVudC10eXBlJ10gPT09ICdhcHBsaWNhdGlvbi9qc29uJykge1xuICAgICAgICAgICAgICAgIHBheWxvYWQgPSBKU09OLnN0cmluZ2lmeShvcHRpb25zLmRhdGEpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwYXlsb2FkID0gZW5jb2RlVXNpbmdVcmxFbmNvZGluZyhvcHRpb25zLmRhdGEpO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgLy8gU3BlY2lhbGx5IHByZXBhcmUgR0VUIHJlcXVlc3RzOiBTZXR1cCB0aGUgcXVlcnkgc3RyaW5nLCBoYW5kbGUgY2FjaGluZyBhbmQgbWFrZSBhIEpTT05QIGNhbGxcbiAgICAgICAgICAgIC8vIGlmIG5lY2Nlc3NhcnkuXG4gICAgICAgICAgICBpZihtZXRob2QgPT09ICdHRVQnKSB7XG4gICAgICAgICAgICAgICAgLy8gU2V0dXAgdGhlIHF1ZXJ5IHN0cmluZy5cbiAgICAgICAgICAgICAgICB2YXIgcXVlcnlTdHJpbmcgPSBbXTtcbiAgICAgICAgICAgICAgICBpZihwYXlsb2FkKSB7XG4gICAgICAgICAgICAgICAgICAgIHF1ZXJ5U3RyaW5nLnB1c2gocGF5bG9hZCk7XG4gICAgICAgICAgICAgICAgICAgIHBheWxvYWQgPSBudWxsO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgY2FjaGluZy5cbiAgICAgICAgICAgICAgICBpZighb3B0aW9ucy5jYWNoZSkge1xuICAgICAgICAgICAgICAgICAgICBxdWVyeVN0cmluZy5wdXNoKCdfPScgKyAobmV3IERhdGUoKSkuZ2V0VGltZSgpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gSWYgbmVjY2Vzc2FyeSBwcmVwYXJlIHRoZSBxdWVyeSBzdHJpbmcgZm9yIGEgSlNPTlAgY2FsbC5cbiAgICAgICAgICAgICAgICBpZihvcHRpb25zLmpzb25wKSB7XG4gICAgICAgICAgICAgICAgICAgIHF1ZXJ5U3RyaW5nLnB1c2goJ2NhbGxiYWNrPScgKyBvcHRpb25zLmpzb25wKTtcbiAgICAgICAgICAgICAgICAgICAgcXVlcnlTdHJpbmcucHVzaCgnanNvbnA9JyArIG9wdGlvbnMuanNvbnApO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAvLyBNZXJnZSB0aGUgcXVlcnkgc3RyaW5nIGFuZCBhdHRhY2ggaXQgdG8gdGhlIHVybC5cbiAgICAgICAgICAgICAgICBxdWVyeVN0cmluZyA9IHF1ZXJ5U3RyaW5nLmpvaW4oJyYnKTtcbiAgICAgICAgICAgICAgICBpZiAocXVlcnlTdHJpbmcubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAodXJsLmluZGV4T2YoJz8nKSA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJyYnICsgcXVlcnlTdHJpbmc7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJz8nICsgcXVlcnlTdHJpbmc7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gTWFrZSBhIEpTT05QIGNhbGwgaWYgbmVjY2Vzc2FyeS5cbiAgICAgICAgICAgICAgICBpZihvcHRpb25zLmpzb25wKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBoZWFkID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2hlYWQnKVswXTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNjcmlwdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpO1xuICAgICAgICAgICAgICAgICAgICBzY3JpcHQudHlwZSA9ICd0ZXh0L2phdmFzY3JpcHQnO1xuICAgICAgICAgICAgICAgICAgICBzY3JpcHQuc3JjID0gdXJsO1xuICAgICAgICAgICAgICAgICAgICBoZWFkLmFwcGVuZENoaWxkKHNjcmlwdCk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAvLyBTaW5jZSB3ZSBnb3QgaGVyZSwgaXQgaXMgbm8gSlNPTlAgcmVxdWVzdCwgc28gbWFrZSBhIG5vcm1hbCBYSFIgcmVxdWVzdC5cbiAgICAgICAgICAgIGdldFhocihmdW5jdGlvbiAoZXJyLCB4aHIpIHtcbiAgICAgICAgICAgICAgICBpZihlcnIpIHJldHVybiBjYWxsYmFjayhlcnIpO1xuICAgIFxuICAgICAgICAgICAgICAgIC8vIE9wZW4gdGhlIHJlcXVlc3QuXG4gICAgICAgICAgICAgICAgeGhyLm9wZW4obWV0aG9kLCB1cmwsIG9wdGlvbnMuYXN5bmMpO1xuICAgIFxuICAgICAgICAgICAgICAgIC8vIFNldCB0aGUgcmVxdWVzdCBoZWFkZXJzLlxuICAgICAgICAgICAgICAgIGZvcih2YXIgaGVhZGVyIGluIGhlYWRlcnMpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYoaGVhZGVycy5oYXNPd25Qcm9wZXJ0eShoZWFkZXIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihoZWFkZXIsIGhlYWRlcnNbaGVhZGVyXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gSGFuZGxlIHRoZSByZXF1ZXN0IGV2ZW50cy5cbiAgICAgICAgICAgICAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBpZih4aHIucmVhZHlTdGF0ZSA9PT0gNCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGRhdGEgPSB4aHIucmVzcG9uc2VUZXh0IHx8ICcnO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgbm8gY2FsbGJhY2sgaXMgZ2l2ZW4sIHJldHVybi5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFJldHVybiBhbiBvYmplY3QgdGhhdCBwcm92aWRlcyBhY2Nlc3MgdG8gdGhlIGRhdGEgYXMgdGV4dCBhbmQgSlNPTi5cbiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKHhoci5zdGF0dXMsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAganNvbjogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZi5lcnJvcignQ2FuIG5vdCBwYXJzZSBKU09OLiBVUkw6ICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9O1xuICAgIFxuICAgICAgICAgICAgICAgIC8vIEFjdHVhbGx5IHNlbmQgdGhlIFhIUiByZXF1ZXN0LlxuICAgICAgICAgICAgICAgIHhoci5zZW5kKHBheWxvYWQpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIC8vIERlZmluZSB0aGUgZXh0ZXJuYWwgaW50ZXJmYWNlLlxuICAgICAgICB2YXIgaHR0cCA9IHtcbiAgICAgICAgICAgIGF1dGhCYXNpYzogZnVuY3Rpb24gKHVzZXJuYW1lLCBwYXNzd29yZCkge1xuICAgICAgICAgICAgICAgIGFqYXguaGVhZGVyc1snQXV0aG9yaXphdGlvbiddID0gJ0Jhc2ljICcgKyBiYXNlNjQodXNlcm5hbWUgKyAnOicgKyBwYXNzd29yZCk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgY29ubmVjdDogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnQ09OTkVDVCcsIHVybCwgb3B0aW9ucywgY2FsbGJhY2spO1xuICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgIGRlbDogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnREVMRVRFJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgZ2V0OiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdHRVQnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBoZWFkOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdIRUFEJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgaGVhZGVyczogZnVuY3Rpb24gKGhlYWRlcnMpIHtcbiAgICAgICAgICAgICAgICBhamF4LmhlYWRlcnMgPSBoZWFkZXJzIHx8IHt9O1xuICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgIGlzQWxsb3dlZDogZnVuY3Rpb24gKHVybCwgdmVyYiwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9wdGlvbnModXJsLCBmdW5jdGlvbiAoc3RhdHVzLCBkYXRhKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKGRhdGEudGV4dCgpLmluZGV4T2YodmVyYikgIT09IC0xKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBvcHRpb25zOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdPUFRJT05TJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgcGF0Y2g6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFqYXgoJ1BBVENIJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgcG9zdDogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnUE9TVCcsIHVybCwgb3B0aW9ucywgY2FsbGJhY2spO1xuICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgIHB1dDogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnUFVUJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgdHJhY2U6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFqYXgoJ1RSQUNFJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgXG4gICAgXG4gICAgICAgIHZhciBtZXRob2RlID0gb3B0aW9ucy50eXBlID8gb3B0aW9ucy50eXBlLnRvTG93ZXJDYXNlKCkgOiAnZ2V0JztcbiAgICBcbiAgICAgICAgaHR0cFttZXRob2RlXShvcHRpb25zLnVybCwgb3B0aW9ucywgZnVuY3Rpb24gKHN0YXR1cywgZGF0YSkge1xuICAgICAgICAgICAgLy8gZmlsZTogcHJvdG9jb2wgYWx3YXlzIGdpdmVzIHN0YXR1cyBjb2RlIDAsIHNvIGNoZWNrIGZvciBkYXRhXG4gICAgICAgICAgICBpZiAoc3RhdHVzID09PSAyMDAgfHwgKHN0YXR1cyA9PT0gMCAmJiBkYXRhLnRleHQoKSkpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zLnN1Y2Nlc3MoZGF0YS5qc29uKCksIHN0YXR1cywgbnVsbCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMuZXJyb3IoZGF0YS50ZXh0KCksIHN0YXR1cywgbnVsbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBcbiAgICB2YXIgX2Nvb2tpZSA9IHtcbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbihuYW1lLHZhbHVlLG1pbnV0ZXMsZG9tYWluKSB7XG4gICAgICAgICAgICB2YXIgZXhwaXJlcztcbiAgICAgICAgICAgIGlmIChtaW51dGVzKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRhdGUgPSBuZXcgRGF0ZSgpO1xuICAgICAgICAgICAgICAgIGRhdGUuc2V0VGltZShkYXRlLmdldFRpbWUoKSsobWludXRlcyo2MCoxMDAwKSk7XG4gICAgICAgICAgICAgICAgZXhwaXJlcyA9IFwiOyBleHBpcmVzPVwiK2RhdGUudG9HTVRTdHJpbmcoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgZXhwaXJlcyA9IFwiXCI7XG4gICAgICAgICAgICBkb21haW4gPSAoZG9tYWluKT8gXCJkb21haW49XCIrZG9tYWluK1wiO1wiIDogXCJcIjtcbiAgICAgICAgICAgIGRvY3VtZW50LmNvb2tpZSA9IG5hbWUrXCI9XCIrdmFsdWUrZXhwaXJlcytcIjtcIitkb21haW4rXCJwYXRoPS9cIjtcbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgcmVhZDogZnVuY3Rpb24obmFtZSkge1xuICAgICAgICAgICAgdmFyIG5hbWVFUSA9IG5hbWUgKyBcIj1cIjtcbiAgICAgICAgICAgIHZhciBjYSA9IGRvY3VtZW50LmNvb2tpZS5zcGxpdCgnOycpO1xuICAgICAgICAgICAgZm9yKHZhciBpPTA7aSA8IGNhLmxlbmd0aDtpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgYyA9IGNhW2ldO1xuICAgICAgICAgICAgICAgIHdoaWxlIChjLmNoYXJBdCgwKT09JyAnKSBjID0gYy5zdWJzdHJpbmcoMSxjLmxlbmd0aCk7XG4gICAgICAgICAgICAgICAgaWYgKGMuaW5kZXhPZihuYW1lRVEpID09PSAwKSByZXR1cm4gYy5zdWJzdHJpbmcobmFtZUVRLmxlbmd0aCxjLmxlbmd0aCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgcmVtb3ZlOiBmdW5jdGlvbihuYW1lKSB7XG4gICAgICAgICAgICB0aGlzLmNyZWF0ZShuYW1lLFwiXCIsLTEpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBcbiAgICB2YXIgY29va2llX25vb3AgPSB7XG4gICAgICAgIGNyZWF0ZTogZnVuY3Rpb24obmFtZSx2YWx1ZSxtaW51dGVzLGRvbWFpbikge30sXG4gICAgICAgIHJlYWQ6IGZ1bmN0aW9uKG5hbWUpIHsgcmV0dXJuIG51bGw7IH0sXG4gICAgICAgIHJlbW92ZTogZnVuY3Rpb24obmFtZSkge31cbiAgICB9O1xuICAgIFxuICAgIFxuICAgIFxuICAgIC8vIG1vdmUgZGVwZW5kZW50IGZ1bmN0aW9ucyB0byBhIGNvbnRhaW5lciBzbyB0aGF0XG4gICAgLy8gdGhleSBjYW4gYmUgb3ZlcnJpZGVuIGVhc2llciBpbiBubyBqcXVlcnkgZW52aXJvbm1lbnQgKG5vZGUuanMpXG4gICAgdmFyIGYgPSB7XG4gICAgICAgIGV4dGVuZDogJCA/ICQuZXh0ZW5kIDogX2V4dGVuZCxcbiAgICAgICAgZGVlcEV4dGVuZDogX2RlZXBFeHRlbmQsXG4gICAgICAgIGVhY2g6ICQgPyAkLmVhY2ggOiBfZWFjaCxcbiAgICAgICAgYWpheDogJCA/ICQuYWpheCA6ICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnID8gX2FqYXggOiBmdW5jdGlvbigpIHt9KSxcbiAgICAgICAgY29va2llOiB0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnID8gX2Nvb2tpZSA6IGNvb2tpZV9ub29wLFxuICAgICAgICBkZXRlY3RMYW5ndWFnZTogZGV0ZWN0TGFuZ3VhZ2UsXG4gICAgICAgIGVzY2FwZTogX2VzY2FwZSxcbiAgICAgICAgbG9nOiBmdW5jdGlvbihzdHIpIHtcbiAgICAgICAgICAgIGlmIChvLmRlYnVnICYmIHR5cGVvZiBjb25zb2xlICE9PSBcInVuZGVmaW5lZFwiKSBjb25zb2xlLmxvZyhzdHIpO1xuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogZnVuY3Rpb24oc3RyKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIGNvbnNvbGUgIT09IFwidW5kZWZpbmVkXCIpIGNvbnNvbGUuZXJyb3Ioc3RyKTtcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0Q291bnR5SW5kZXhPZkxuZzogZnVuY3Rpb24obG5nKSB7XG4gICAgICAgICAgICB2YXIgbG5nX2luZGV4ID0gMDtcbiAgICAgICAgICAgIGlmIChsbmcgPT09ICduYi1OTycgfHwgbG5nID09PSAnbm4tTk8nIHx8IGxuZyA9PT0gJ25iLW5vJyB8fCBsbmcgPT09ICdubi1ubycpIGxuZ19pbmRleCA9IDE7XG4gICAgICAgICAgICByZXR1cm4gbG5nX2luZGV4O1xuICAgICAgICB9LFxuICAgICAgICB0b0xhbmd1YWdlczogZnVuY3Rpb24obG5nKSB7XG4gICAgICAgICAgICB2YXIgbG9nID0gdGhpcy5sb2c7XG4gICAgXG4gICAgICAgICAgICBmdW5jdGlvbiBhcHBseUNhc2UobCkge1xuICAgICAgICAgICAgICAgIHZhciByZXQgPSBsO1xuICAgIFxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgbCA9PT0gJ3N0cmluZycgJiYgbC5pbmRleE9mKCctJykgPiAtMSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgcGFydHMgPSBsLnNwbGl0KCctJyk7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIHJldCA9IG8ubG93ZXJDYXNlTG5nID9cbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRzWzBdLnRvTG93ZXJDYXNlKCkgKyAgJy0nICsgcGFydHNbMV0udG9Mb3dlckNhc2UoKSA6XG4gICAgICAgICAgICAgICAgICAgICAgICBwYXJ0c1swXS50b0xvd2VyQ2FzZSgpICsgICctJyArIHBhcnRzWzFdLnRvVXBwZXJDYXNlKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0ID0gby5sb3dlckNhc2VMbmcgPyBsLnRvTG93ZXJDYXNlKCkgOiBsO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICByZXR1cm4gcmV0O1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgdmFyIGxhbmd1YWdlcyA9IFtdO1xuICAgICAgICAgICAgdmFyIHdoaXRlbGlzdCA9IG8ubG5nV2hpdGVsaXN0IHx8IGZhbHNlO1xuICAgICAgICAgICAgdmFyIGFkZExhbmd1YWdlID0gZnVuY3Rpb24obGFuZ3VhZ2Upe1xuICAgICAgICAgICAgICAvL3JlamVjdCBsYW5ncyBub3Qgd2hpdGVsaXN0ZWRcbiAgICAgICAgICAgICAgaWYoIXdoaXRlbGlzdCB8fCB3aGl0ZWxpc3QuaW5kZXhPZihsYW5ndWFnZSkgPiAtMSl7XG4gICAgICAgICAgICAgICAgbGFuZ3VhZ2VzLnB1c2gobGFuZ3VhZ2UpO1xuICAgICAgICAgICAgICB9ZWxzZXtcbiAgICAgICAgICAgICAgICBsb2coJ3JlamVjdGluZyBub24td2hpdGVsaXN0ZWQgbGFuZ3VhZ2U6ICcgKyBsYW5ndWFnZSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBpZiAodHlwZW9mIGxuZyA9PT0gJ3N0cmluZycgJiYgbG5nLmluZGV4T2YoJy0nKSA+IC0xKSB7XG4gICAgICAgICAgICAgICAgdmFyIHBhcnRzID0gbG5nLnNwbGl0KCctJyk7XG4gICAgXG4gICAgICAgICAgICAgICAgaWYgKG8ubG9hZCAhPT0gJ3Vuc3BlY2lmaWMnKSBhZGRMYW5ndWFnZShhcHBseUNhc2UobG5nKSk7XG4gICAgICAgICAgICAgICAgaWYgKG8ubG9hZCAhPT0gJ2N1cnJlbnQnKSBhZGRMYW5ndWFnZShhcHBseUNhc2UocGFydHNbdGhpcy5nZXRDb3VudHlJbmRleE9mTG5nKGxuZyldKSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGFkZExhbmd1YWdlKGFwcGx5Q2FzZShsbmcpKTtcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgby5mYWxsYmFja0xuZy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGlmIChsYW5ndWFnZXMuaW5kZXhPZihvLmZhbGxiYWNrTG5nW2ldKSA9PT0gLTEgJiYgby5mYWxsYmFja0xuZ1tpXSkgbGFuZ3VhZ2VzLnB1c2goYXBwbHlDYXNlKG8uZmFsbGJhY2tMbmdbaV0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBsYW5ndWFnZXM7XG4gICAgICAgIH0sXG4gICAgICAgIHJlZ2V4RXNjYXBlOiBmdW5jdGlvbihzdHIpIHtcbiAgICAgICAgICAgIHJldHVybiBzdHIucmVwbGFjZSgvW1xcLVxcW1xcXVxcL1xce1xcfVxcKFxcKVxcKlxcK1xcP1xcLlxcXFxcXF5cXCRcXHxdL2csIFwiXFxcXCQmXCIpO1xuICAgICAgICB9LFxuICAgICAgICByZWdleFJlcGxhY2VtZW50RXNjYXBlOiBmdW5jdGlvbihzdHJPckZuKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHN0ck9yRm4gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHN0ck9yRm4ucmVwbGFjZSgvXFwkL2csIFwiJCQkJFwiKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHN0ck9yRm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGxvY2FsU3RvcmFnZToge1xuICAgICAgICAgICAgc2V0SXRlbTogZnVuY3Rpb24oa2V5LCB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIGlmICh3aW5kb3cubG9jYWxTdG9yYWdlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0oa2V5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGYubG9nKCdmYWlsZWQgdG8gc2V0IHZhbHVlIGZvciBrZXkgXCInICsga2V5ICsgJ1wiIHRvIGxvY2FsU3RvcmFnZS4nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG4gICAgZnVuY3Rpb24gaW5pdChvcHRpb25zLCBjYikge1xuICAgICAgICBcbiAgICAgICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjYiA9IG9wdGlvbnM7XG4gICAgICAgICAgICBvcHRpb25zID0ge307XG4gICAgICAgIH1cbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgIFxuICAgICAgICAvLyBvdmVycmlkZSBkZWZhdWx0cyB3aXRoIHBhc3NlZCBpbiBvcHRpb25zXG4gICAgICAgIGYuZXh0ZW5kKG8sIG9wdGlvbnMpO1xuICAgICAgICBkZWxldGUgby5maXhMbmc7IC8qIHBhc3NlZCBpbiBlYWNoIHRpbWUgKi9cbiAgICBcbiAgICAgICAgLy8gb3ZlcnJpZGUgZnVuY3Rpb25zOiAubG9nKCksIC5kZXRlY3RMYW5ndWFnZSgpLCBldGNcbiAgICAgICAgaWYgKG8uZnVuY3Rpb25zKSB7XG4gICAgICAgICAgICBkZWxldGUgby5mdW5jdGlvbnM7XG4gICAgICAgICAgICBmLmV4dGVuZChmLCBvcHRpb25zLmZ1bmN0aW9ucyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gY3JlYXRlIG5hbWVzcGFjZSBvYmplY3QgaWYgbmFtZXNwYWNlIGlzIHBhc3NlZCBpbiBhcyBzdHJpbmdcbiAgICAgICAgaWYgKHR5cGVvZiBvLm5zID09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBvLm5zID0geyBuYW1lc3BhY2VzOiBbby5uc10sIGRlZmF1bHROczogby5uc307XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZmFsbGJhY2sgbmFtZXNwYWNlc1xuICAgICAgICBpZiAodHlwZW9mIG8uZmFsbGJhY2tOUyA9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgby5mYWxsYmFja05TID0gW28uZmFsbGJhY2tOU107XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZmFsbGJhY2sgbGFuZ3VhZ2VzXG4gICAgICAgIGlmICh0eXBlb2Ygby5mYWxsYmFja0xuZyA9PSAnc3RyaW5nJyB8fCB0eXBlb2Ygby5mYWxsYmFja0xuZyA9PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgIG8uZmFsbGJhY2tMbmcgPSBbby5mYWxsYmFja0xuZ107XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZXNjYXBlIHByZWZpeC9zdWZmaXhcbiAgICAgICAgby5pbnRlcnBvbGF0aW9uUHJlZml4RXNjYXBlZCA9IGYucmVnZXhFc2NhcGUoby5pbnRlcnBvbGF0aW9uUHJlZml4KTtcbiAgICAgICAgby5pbnRlcnBvbGF0aW9uU3VmZml4RXNjYXBlZCA9IGYucmVnZXhFc2NhcGUoby5pbnRlcnBvbGF0aW9uU3VmZml4KTtcbiAgICBcbiAgICAgICAgaWYgKCFvLmxuZykgby5sbmcgPSBmLmRldGVjdExhbmd1YWdlKCk7XG4gICAgXG4gICAgICAgIGxhbmd1YWdlcyA9IGYudG9MYW5ndWFnZXMoby5sbmcpO1xuICAgICAgICBjdXJyZW50TG5nID0gbGFuZ3VhZ2VzWzBdO1xuICAgICAgICBmLmxvZygnY3VycmVudExuZyBzZXQgdG86ICcgKyBjdXJyZW50TG5nKTtcbiAgICBcbiAgICAgICAgaWYgKG8udXNlQ29va2llICYmIGYuY29va2llLnJlYWQoby5jb29raWVOYW1lKSAhPT0gY3VycmVudExuZyl7IC8vY29va2llIGlzIHVuc2V0IG9yIGludmFsaWRcbiAgICAgICAgICAgIGYuY29va2llLmNyZWF0ZShvLmNvb2tpZU5hbWUsIGN1cnJlbnRMbmcsIG8uY29va2llRXhwaXJhdGlvblRpbWUsIG8uY29va2llRG9tYWluKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoby5kZXRlY3RMbmdGcm9tTG9jYWxTdG9yYWdlICYmIHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmxvY2FsU3RvcmFnZSkge1xuICAgICAgICAgICAgZi5sb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnaTE4bmV4dF9sbmcnLCBjdXJyZW50TG5nKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICB2YXIgbG5nVHJhbnNsYXRlID0gdHJhbnNsYXRlO1xuICAgICAgICBpZiAob3B0aW9ucy5maXhMbmcpIHtcbiAgICAgICAgICAgIGxuZ1RyYW5zbGF0ZSA9IGZ1bmN0aW9uKGtleSwgb3B0aW9ucykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgICAgICAgICAgICAgIG9wdGlvbnMubG5nID0gb3B0aW9ucy5sbmcgfHwgbG5nVHJhbnNsYXRlLmxuZztcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJhbnNsYXRlKGtleSwgb3B0aW9ucyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgbG5nVHJhbnNsYXRlLmxuZyA9IGN1cnJlbnRMbmc7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcGx1cmFsRXh0ZW5zaW9ucy5zZXRDdXJyZW50TG5nKGN1cnJlbnRMbmcpO1xuICAgIFxuICAgICAgICAvLyBhZGQgSlF1ZXJ5IGV4dGVuc2lvbnNcbiAgICAgICAgaWYgKCQgJiYgby5zZXRKcXVlcnlFeHQpIGFkZEpxdWVyeUZ1bmN0KCk7XG4gICAgXG4gICAgICAgIC8vIGpRdWVyeSBkZWZlcnJlZFxuICAgICAgICB2YXIgZGVmZXJyZWQ7XG4gICAgICAgIGlmICgkICYmICQuRGVmZXJyZWQpIHtcbiAgICAgICAgICAgIGRlZmVycmVkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIHJldHVybiBpbW1pZGlhdGx5IGlmIHJlcyBhcmUgcGFzc2VkIGluXG4gICAgICAgIGlmIChvLnJlc1N0b3JlKSB7XG4gICAgICAgICAgICByZXNTdG9yZSA9IG8ucmVzU3RvcmU7XG4gICAgICAgICAgICBpbml0aWFsaXplZCA9IHRydWU7XG4gICAgICAgICAgICBpZiAoY2IpIGNiKGxuZ1RyYW5zbGF0ZSk7XG4gICAgICAgICAgICBpZiAoZGVmZXJyZWQpIGRlZmVycmVkLnJlc29sdmUobG5nVHJhbnNsYXRlKTtcbiAgICAgICAgICAgIGlmIChkZWZlcnJlZCkgcmV0dXJuIGRlZmVycmVkLnByb21pc2UoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBsYW5ndWFnZXMgdG8gbG9hZFxuICAgICAgICB2YXIgbG5nc1RvTG9hZCA9IGYudG9MYW5ndWFnZXMoby5sbmcpO1xuICAgICAgICBpZiAodHlwZW9mIG8ucHJlbG9hZCA9PT0gJ3N0cmluZycpIG8ucHJlbG9hZCA9IFtvLnByZWxvYWRdO1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgbCA9IG8ucHJlbG9hZC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgIHZhciBwcmVzID0gZi50b0xhbmd1YWdlcyhvLnByZWxvYWRbaV0pO1xuICAgICAgICAgICAgZm9yICh2YXIgeSA9IDAsIGxlbiA9IHByZXMubGVuZ3RoOyB5IDwgbGVuOyB5KyspIHtcbiAgICAgICAgICAgICAgICBpZiAobG5nc1RvTG9hZC5pbmRleE9mKHByZXNbeV0pIDwgMCkge1xuICAgICAgICAgICAgICAgICAgICBsbmdzVG9Mb2FkLnB1c2gocHJlc1t5XSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIGVsc2UgbG9hZCB0aGVtXG4gICAgICAgIGkxOG4uc3luYy5sb2FkKGxuZ3NUb0xvYWQsIG8sIGZ1bmN0aW9uKGVyciwgc3RvcmUpIHtcbiAgICAgICAgICAgIHJlc1N0b3JlID0gc3RvcmU7XG4gICAgICAgICAgICBpbml0aWFsaXplZCA9IHRydWU7XG4gICAgXG4gICAgICAgICAgICBpZiAoY2IpIGNiKGxuZ1RyYW5zbGF0ZSk7XG4gICAgICAgICAgICBpZiAoZGVmZXJyZWQpIGRlZmVycmVkLnJlc29sdmUobG5nVHJhbnNsYXRlKTtcbiAgICAgICAgfSk7XG4gICAgXG4gICAgICAgIGlmIChkZWZlcnJlZCkgcmV0dXJuIGRlZmVycmVkLnByb21pc2UoKTtcbiAgICB9XG4gICAgZnVuY3Rpb24gcHJlbG9hZChsbmdzLCBjYikge1xuICAgICAgICBpZiAodHlwZW9mIGxuZ3MgPT09ICdzdHJpbmcnKSBsbmdzID0gW2xuZ3NdO1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgbCA9IGxuZ3MubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoby5wcmVsb2FkLmluZGV4T2YobG5nc1tpXSkgPCAwKSB7XG4gICAgICAgICAgICAgICAgby5wcmVsb2FkLnB1c2gobG5nc1tpXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGluaXQoY2IpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBhZGRSZXNvdXJjZUJ1bmRsZShsbmcsIG5zLCByZXNvdXJjZXMsIGRlZXApIHtcbiAgICAgICAgaWYgKHR5cGVvZiBucyAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJlc291cmNlcyA9IG5zO1xuICAgICAgICAgICAgbnMgPSBvLm5zLmRlZmF1bHROcztcbiAgICAgICAgfSBlbHNlIGlmIChvLm5zLm5hbWVzcGFjZXMuaW5kZXhPZihucykgPCAwKSB7XG4gICAgICAgICAgICBvLm5zLm5hbWVzcGFjZXMucHVzaChucyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmVzU3RvcmVbbG5nXSA9IHJlc1N0b3JlW2xuZ10gfHwge307XG4gICAgICAgIHJlc1N0b3JlW2xuZ11bbnNdID0gcmVzU3RvcmVbbG5nXVtuc10gfHwge307XG4gICAgXG4gICAgICAgIGlmIChkZWVwKSB7XG4gICAgICAgICAgICBmLmRlZXBFeHRlbmQocmVzU3RvcmVbbG5nXVtuc10sIHJlc291cmNlcyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmLmV4dGVuZChyZXNTdG9yZVtsbmddW25zXSwgcmVzb3VyY2VzKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBoYXNSZXNvdXJjZUJ1bmRsZShsbmcsIG5zKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbnMgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBucyA9IG8ubnMuZGVmYXVsdE5zO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJlc1N0b3JlW2xuZ10gPSByZXNTdG9yZVtsbmddIHx8IHt9O1xuICAgICAgICB2YXIgcmVzID0gcmVzU3RvcmVbbG5nXVtuc10gfHwge307XG4gICAgXG4gICAgICAgIHZhciBoYXNWYWx1ZXMgPSBmYWxzZTtcbiAgICAgICAgZm9yKHZhciBwcm9wIGluIHJlcykge1xuICAgICAgICAgICAgaWYgKHJlcy5oYXNPd25Qcm9wZXJ0eShwcm9wKSkge1xuICAgICAgICAgICAgICAgIGhhc1ZhbHVlcyA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmV0dXJuIGhhc1ZhbHVlcztcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gcmVtb3ZlUmVzb3VyY2VCdW5kbGUobG5nLCBucykge1xuICAgICAgICBpZiAodHlwZW9mIG5zICE9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgbnMgPSBvLm5zLmRlZmF1bHROcztcbiAgICAgICAgfVxuICAgIFxuICAgICAgICByZXNTdG9yZVtsbmddID0gcmVzU3RvcmVbbG5nXSB8fCB7fTtcbiAgICAgICAgcmVzU3RvcmVbbG5nXVtuc10gPSB7fTtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gYWRkUmVzb3VyY2UobG5nLCBucywga2V5LCB2YWx1ZSkge1xuICAgICAgICBpZiAodHlwZW9mIG5zICE9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmVzb3VyY2UgPSBucztcbiAgICAgICAgICAgIG5zID0gby5ucy5kZWZhdWx0TnM7XG4gICAgICAgIH0gZWxzZSBpZiAoby5ucy5uYW1lc3BhY2VzLmluZGV4T2YobnMpIDwgMCkge1xuICAgICAgICAgICAgby5ucy5uYW1lc3BhY2VzLnB1c2gobnMpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJlc1N0b3JlW2xuZ10gPSByZXNTdG9yZVtsbmddIHx8IHt9O1xuICAgICAgICByZXNTdG9yZVtsbmddW25zXSA9IHJlc1N0b3JlW2xuZ11bbnNdIHx8IHt9O1xuICAgIFxuICAgICAgICB2YXIga2V5cyA9IGtleS5zcGxpdChvLmtleXNlcGFyYXRvcik7XG4gICAgICAgIHZhciB4ID0gMDtcbiAgICAgICAgdmFyIG5vZGUgPSByZXNTdG9yZVtsbmddW25zXTtcbiAgICAgICAgdmFyIG9yaWdSZWYgPSBub2RlO1xuICAgIFxuICAgICAgICB3aGlsZSAoa2V5c1t4XSkge1xuICAgICAgICAgICAgaWYgKHggPT0ga2V5cy5sZW5ndGggLSAxKVxuICAgICAgICAgICAgICAgIG5vZGVba2V5c1t4XV0gPSB2YWx1ZTtcbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmIChub2RlW2tleXNbeF1dID09IG51bGwpXG4gICAgICAgICAgICAgICAgICAgIG5vZGVba2V5c1t4XV0gPSB7fTtcbiAgICBcbiAgICAgICAgICAgICAgICBub2RlID0gbm9kZVtrZXlzW3hdXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHgrKztcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBhZGRSZXNvdXJjZXMobG5nLCBucywgcmVzb3VyY2VzKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbnMgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICByZXNvdXJjZSA9IG5zO1xuICAgICAgICAgICAgbnMgPSBvLm5zLmRlZmF1bHROcztcbiAgICAgICAgfSBlbHNlIGlmIChvLm5zLm5hbWVzcGFjZXMuaW5kZXhPZihucykgPCAwKSB7XG4gICAgICAgICAgICBvLm5zLm5hbWVzcGFjZXMucHVzaChucyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgZm9yICh2YXIgbSBpbiByZXNvdXJjZXMpIHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgcmVzb3VyY2VzW21dID09PSAnc3RyaW5nJykgYWRkUmVzb3VyY2UobG5nLCBucywgbSwgcmVzb3VyY2VzW21dKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBzZXREZWZhdWx0TmFtZXNwYWNlKG5zKSB7XG4gICAgICAgIG8ubnMuZGVmYXVsdE5zID0gbnM7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGxvYWROYW1lc3BhY2UobmFtZXNwYWNlLCBjYikge1xuICAgICAgICBsb2FkTmFtZXNwYWNlcyhbbmFtZXNwYWNlXSwgY2IpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBsb2FkTmFtZXNwYWNlcyhuYW1lc3BhY2VzLCBjYikge1xuICAgICAgICB2YXIgb3B0cyA9IHtcbiAgICAgICAgICAgIGR5bmFtaWNMb2FkOiBvLmR5bmFtaWNMb2FkLFxuICAgICAgICAgICAgcmVzR2V0UGF0aDogby5yZXNHZXRQYXRoLFxuICAgICAgICAgICAgZ2V0QXN5bmM6IG8uZ2V0QXN5bmMsXG4gICAgICAgICAgICBjdXN0b21Mb2FkOiBvLmN1c3RvbUxvYWQsXG4gICAgICAgICAgICBuczogeyBuYW1lc3BhY2VzOiBuYW1lc3BhY2VzLCBkZWZhdWx0TnM6ICcnfSAvKiBuZXcgbmFtZXNwYWNlcyB0byBsb2FkICovXG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIC8vIGxhbmd1YWdlcyB0byBsb2FkXG4gICAgICAgIHZhciBsbmdzVG9Mb2FkID0gZi50b0xhbmd1YWdlcyhvLmxuZyk7XG4gICAgICAgIGlmICh0eXBlb2Ygby5wcmVsb2FkID09PSAnc3RyaW5nJykgby5wcmVsb2FkID0gW28ucHJlbG9hZF07XG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBsID0gby5wcmVsb2FkLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgICAgdmFyIHByZXMgPSBmLnRvTGFuZ3VhZ2VzKG8ucHJlbG9hZFtpXSk7XG4gICAgICAgICAgICBmb3IgKHZhciB5ID0gMCwgbGVuID0gcHJlcy5sZW5ndGg7IHkgPCBsZW47IHkrKykge1xuICAgICAgICAgICAgICAgIGlmIChsbmdzVG9Mb2FkLmluZGV4T2YocHJlc1t5XSkgPCAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGxuZ3NUb0xvYWQucHVzaChwcmVzW3ldKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gY2hlY2sgaWYgd2UgaGF2ZSB0byBsb2FkXG4gICAgICAgIHZhciBsbmdOZWVkTG9hZCA9IFtdO1xuICAgICAgICBmb3IgKHZhciBhID0gMCwgbGVuQSA9IGxuZ3NUb0xvYWQubGVuZ3RoOyBhIDwgbGVuQTsgYSsrKSB7XG4gICAgICAgICAgICB2YXIgbmVlZExvYWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHZhciByZXNTZXQgPSByZXNTdG9yZVtsbmdzVG9Mb2FkW2FdXTtcbiAgICAgICAgICAgIGlmIChyZXNTZXQpIHtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBiID0gMCwgbGVuQiA9IG5hbWVzcGFjZXMubGVuZ3RoOyBiIDwgbGVuQjsgYisrKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICghcmVzU2V0W25hbWVzcGFjZXNbYl1dKSBuZWVkTG9hZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBuZWVkTG9hZCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBpZiAobmVlZExvYWQpIGxuZ05lZWRMb2FkLnB1c2gobG5nc1RvTG9hZFthXSk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKGxuZ05lZWRMb2FkLmxlbmd0aCkge1xuICAgICAgICAgICAgaTE4bi5zeW5jLl9mZXRjaChsbmdOZWVkTG9hZCwgb3B0cywgZnVuY3Rpb24oZXJyLCBzdG9yZSkge1xuICAgICAgICAgICAgICAgIHZhciB0b2RvID0gbmFtZXNwYWNlcy5sZW5ndGggKiBsbmdOZWVkTG9hZC5sZW5ndGg7XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gbG9hZCBlYWNoIGZpbGUgaW5kaXZpZHVhbFxuICAgICAgICAgICAgICAgIGYuZWFjaChuYW1lc3BhY2VzLCBmdW5jdGlvbihuc0luZGV4LCBuc1ZhbHVlKSB7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIC8vIGFwcGVuZCBuYW1lc3BhY2UgdG8gbmFtZXNwYWNlIGFycmF5XG4gICAgICAgICAgICAgICAgICAgIGlmIChvLm5zLm5hbWVzcGFjZXMuaW5kZXhPZihuc1ZhbHVlKSA8IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG8ubnMubmFtZXNwYWNlcy5wdXNoKG5zVmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIGYuZWFjaChsbmdOZWVkTG9hZCwgZnVuY3Rpb24obG5nSW5kZXgsIGxuZ1ZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNTdG9yZVtsbmdWYWx1ZV0gPSByZXNTdG9yZVtsbmdWYWx1ZV0gfHwge307XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNTdG9yZVtsbmdWYWx1ZV1bbnNWYWx1ZV0gPSBzdG9yZVtsbmdWYWx1ZV1bbnNWYWx1ZV07XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICB0b2RvLS07IC8vIHdhaXQgZm9yIGFsbCBkb25lIGJlZm9yIGNhbGxiYWNrXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodG9kbyA9PT0gMCAmJiBjYikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChvLnVzZUxvY2FsU3RvcmFnZSkgaTE4bi5zeW5jLl9zdG9yZUxvY2FsKHJlc1N0b3JlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKGNiKSBjYigpO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIHNldExuZyhsbmcsIG9wdGlvbnMsIGNiKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2IgPSBvcHRpb25zO1xuICAgICAgICAgICAgb3B0aW9ucyA9IHt9O1xuICAgICAgICB9IGVsc2UgaWYgKCFvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0ge307XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgb3B0aW9ucy5sbmcgPSBsbmc7XG4gICAgICAgIHJldHVybiBpbml0KG9wdGlvbnMsIGNiKTtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gbG5nKCkge1xuICAgICAgICByZXR1cm4gY3VycmVudExuZztcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gcmVsb2FkKGNiKSB7XG4gICAgICAgIHJlc1N0b3JlID0ge307XG4gICAgICAgIHNldExuZyhjdXJyZW50TG5nLCBjYik7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGFkZEpxdWVyeUZ1bmN0KCkge1xuICAgICAgICAvLyAkLnQgc2hvcnRjdXRcbiAgICAgICAgJC50ID0gJC50IHx8IHRyYW5zbGF0ZTtcbiAgICBcbiAgICAgICAgZnVuY3Rpb24gcGFyc2UoZWxlLCBrZXksIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGlmIChrZXkubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgXG4gICAgICAgICAgICB2YXIgYXR0ciA9ICd0ZXh0JztcbiAgICBcbiAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignWycpID09PSAwKSB7XG4gICAgICAgICAgICAgICAgdmFyIHBhcnRzID0ga2V5LnNwbGl0KCddJyk7XG4gICAgICAgICAgICAgICAga2V5ID0gcGFydHNbMV07XG4gICAgICAgICAgICAgICAgYXR0ciA9IHBhcnRzWzBdLnN1YnN0cigxLCBwYXJ0c1swXS5sZW5ndGgtMSk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJzsnKSA9PT0ga2V5Lmxlbmd0aC0xKSB7XG4gICAgICAgICAgICAgICAga2V5ID0ga2V5LnN1YnN0cigwLCBrZXkubGVuZ3RoLTIpO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgdmFyIG9wdGlvbnNUb1VzZTtcbiAgICAgICAgICAgIGlmIChhdHRyID09PSAnaHRtbCcpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zVG9Vc2UgPSBvLmRlZmF1bHRWYWx1ZUZyb21Db250ZW50ID8gJC5leHRlbmQoeyBkZWZhdWx0VmFsdWU6IGVsZS5odG1sKCkgfSwgb3B0aW9ucykgOiBvcHRpb25zO1xuICAgICAgICAgICAgICAgIGVsZS5odG1sKCQudChrZXksIG9wdGlvbnNUb1VzZSkpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChhdHRyID09PSAndGV4dCcpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zVG9Vc2UgPSBvLmRlZmF1bHRWYWx1ZUZyb21Db250ZW50ID8gJC5leHRlbmQoeyBkZWZhdWx0VmFsdWU6IGVsZS50ZXh0KCkgfSwgb3B0aW9ucykgOiBvcHRpb25zO1xuICAgICAgICAgICAgICAgIGVsZS50ZXh0KCQudChrZXksIG9wdGlvbnNUb1VzZSkpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChhdHRyID09PSAncHJlcGVuZCcpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zVG9Vc2UgPSBvLmRlZmF1bHRWYWx1ZUZyb21Db250ZW50ID8gJC5leHRlbmQoeyBkZWZhdWx0VmFsdWU6IGVsZS5odG1sKCkgfSwgb3B0aW9ucykgOiBvcHRpb25zO1xuICAgICAgICAgICAgICAgIGVsZS5wcmVwZW5kKCQudChrZXksIG9wdGlvbnNUb1VzZSkpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChhdHRyID09PSAnYXBwZW5kJykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnNUb1VzZSA9IG8uZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQgPyAkLmV4dGVuZCh7IGRlZmF1bHRWYWx1ZTogZWxlLmh0bWwoKSB9LCBvcHRpb25zKSA6IG9wdGlvbnM7XG4gICAgICAgICAgICAgICAgZWxlLmFwcGVuZCgkLnQoa2V5LCBvcHRpb25zVG9Vc2UpKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoYXR0ci5pbmRleE9mKFwiZGF0YS1cIikgPT09IDApIHtcbiAgICAgICAgICAgICAgICB2YXIgZGF0YUF0dHIgPSBhdHRyLnN1YnN0cigoXCJkYXRhLVwiKS5sZW5ndGgpO1xuICAgICAgICAgICAgICAgIG9wdGlvbnNUb1VzZSA9IG8uZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQgPyAkLmV4dGVuZCh7IGRlZmF1bHRWYWx1ZTogZWxlLmRhdGEoZGF0YUF0dHIpIH0sIG9wdGlvbnMpIDogb3B0aW9ucztcbiAgICAgICAgICAgICAgICB2YXIgdHJhbnNsYXRlZCA9ICQudChrZXksIG9wdGlvbnNUb1VzZSk7XG4gICAgICAgICAgICAgICAgLy93ZSBjaGFuZ2UgaW50byB0aGUgZGF0YSBjYWNoZVxuICAgICAgICAgICAgICAgIGVsZS5kYXRhKGRhdGFBdHRyLCB0cmFuc2xhdGVkKTtcbiAgICAgICAgICAgICAgICAvL3dlIGNoYW5nZSBpbnRvIHRoZSBkb21cbiAgICAgICAgICAgICAgICBlbGUuYXR0cihhdHRyLCB0cmFuc2xhdGVkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1RvVXNlID0gby5kZWZhdWx0VmFsdWVGcm9tQ29udGVudCA/ICQuZXh0ZW5kKHsgZGVmYXVsdFZhbHVlOiBlbGUuYXR0cihhdHRyKSB9LCBvcHRpb25zKSA6IG9wdGlvbnM7XG4gICAgICAgICAgICAgICAgZWxlLmF0dHIoYXR0ciwgJC50KGtleSwgb3B0aW9uc1RvVXNlKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgZnVuY3Rpb24gbG9jYWxpemUoZWxlLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIga2V5ID0gZWxlLmF0dHIoby5zZWxlY3RvckF0dHIpO1xuICAgICAgICAgICAgaWYgKCFrZXkgJiYgdHlwZW9mIGtleSAhPT0gJ3VuZGVmaW5lZCcgJiYga2V5ICE9PSBmYWxzZSkga2V5ID0gZWxlLnRleHQoKSB8fCBlbGUudmFsKCk7XG4gICAgICAgICAgICBpZiAoIWtleSkgcmV0dXJuO1xuICAgIFxuICAgICAgICAgICAgdmFyIHRhcmdldCA9IGVsZVxuICAgICAgICAgICAgICAsIHRhcmdldFNlbGVjdG9yID0gZWxlLmRhdGEoXCJpMThuLXRhcmdldFwiKTtcbiAgICAgICAgICAgIGlmICh0YXJnZXRTZWxlY3Rvcikge1xuICAgICAgICAgICAgICAgIHRhcmdldCA9IGVsZS5maW5kKHRhcmdldFNlbGVjdG9yKSB8fCBlbGU7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBpZiAoIW9wdGlvbnMgJiYgby51c2VEYXRhQXR0ck9wdGlvbnMgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zID0gZWxlLmRhdGEoXCJpMThuLW9wdGlvbnNcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICBcbiAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignOycpID49IDApIHtcbiAgICAgICAgICAgICAgICB2YXIga2V5cyA9IGtleS5zcGxpdCgnOycpO1xuICAgIFxuICAgICAgICAgICAgICAgICQuZWFjaChrZXlzLCBmdW5jdGlvbihtLCBrKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChrICE9PSAnJykgcGFyc2UodGFyZ2V0LCBrLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICBcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcGFyc2UodGFyZ2V0LCBrZXksIG9wdGlvbnMpO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgaWYgKG8udXNlRGF0YUF0dHJPcHRpb25zID09PSB0cnVlKSBlbGUuZGF0YShcImkxOG4tb3B0aW9uc1wiLCBvcHRpb25zKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBmblxuICAgICAgICAkLmZuLmkxOG4gPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAvLyBsb2NhbGl6ZSBlbGVtZW50IGl0c2VsZlxuICAgICAgICAgICAgICAgIGxvY2FsaXplKCQodGhpcyksIG9wdGlvbnMpO1xuICAgIFxuICAgICAgICAgICAgICAgIC8vIGxvY2FsaXplIGNoaWxkc1xuICAgICAgICAgICAgICAgIHZhciBlbGVtZW50cyA9ICAkKHRoaXMpLmZpbmQoJ1snICsgby5zZWxlY3RvckF0dHIgKyAnXScpO1xuICAgICAgICAgICAgICAgIGVsZW1lbnRzLmVhY2goZnVuY3Rpb24oKSB7IFxuICAgICAgICAgICAgICAgICAgICBsb2NhbGl6ZSgkKHRoaXMpLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuICAgIH1cbiAgICBmdW5jdGlvbiBhcHBseVJlcGxhY2VtZW50KHN0ciwgcmVwbGFjZW1lbnRIYXNoLCBuZXN0ZWRLZXksIG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKCFzdHIpIHJldHVybiBzdHI7XG4gICAgXG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHJlcGxhY2VtZW50SGFzaDsgLy8gZmlyc3QgY2FsbCB1c2VzIHJlcGxhY2VtZW50IGhhc2ggY29tYmluZWQgd2l0aCBvcHRpb25zXG4gICAgICAgIGlmIChzdHIuaW5kZXhPZihvcHRpb25zLmludGVycG9sYXRpb25QcmVmaXggfHwgby5pbnRlcnBvbGF0aW9uUHJlZml4KSA8IDApIHJldHVybiBzdHI7XG4gICAgXG4gICAgICAgIHZhciBwcmVmaXggPSBvcHRpb25zLmludGVycG9sYXRpb25QcmVmaXggPyBmLnJlZ2V4RXNjYXBlKG9wdGlvbnMuaW50ZXJwb2xhdGlvblByZWZpeCkgOiBvLmludGVycG9sYXRpb25QcmVmaXhFc2NhcGVkXG4gICAgICAgICAgLCBzdWZmaXggPSBvcHRpb25zLmludGVycG9sYXRpb25TdWZmaXggPyBmLnJlZ2V4RXNjYXBlKG9wdGlvbnMuaW50ZXJwb2xhdGlvblN1ZmZpeCkgOiBvLmludGVycG9sYXRpb25TdWZmaXhFc2NhcGVkXG4gICAgICAgICAgLCB1bkVzY2FwaW5nU3VmZml4ID0gJ0hUTUwnK3N1ZmZpeDtcbiAgICBcbiAgICAgICAgdmFyIGhhc2ggPSByZXBsYWNlbWVudEhhc2gucmVwbGFjZSAmJiB0eXBlb2YgcmVwbGFjZW1lbnRIYXNoLnJlcGxhY2UgPT09ICdvYmplY3QnID8gcmVwbGFjZW1lbnRIYXNoLnJlcGxhY2UgOiByZXBsYWNlbWVudEhhc2g7XG4gICAgICAgIGYuZWFjaChoYXNoLCBmdW5jdGlvbihrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICB2YXIgbmV4dEtleSA9IG5lc3RlZEtleSA/IG5lc3RlZEtleSArIG8ua2V5c2VwYXJhdG9yICsga2V5IDoga2V5O1xuICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBzdHIgPSBhcHBseVJlcGxhY2VtZW50KHN0ciwgdmFsdWUsIG5leHRLZXksIG9wdGlvbnMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAob3B0aW9ucy5lc2NhcGVJbnRlcnBvbGF0aW9uIHx8IG8uZXNjYXBlSW50ZXJwb2xhdGlvbikge1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSBzdHIucmVwbGFjZShuZXcgUmVnRXhwKFtwcmVmaXgsIG5leHRLZXksIHVuRXNjYXBpbmdTdWZmaXhdLmpvaW4oJycpLCAnZycpLCBmLnJlZ2V4UmVwbGFjZW1lbnRFc2NhcGUodmFsdWUpKTtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gc3RyLnJlcGxhY2UobmV3IFJlZ0V4cChbcHJlZml4LCBuZXh0S2V5LCBzdWZmaXhdLmpvaW4oJycpLCAnZycpLCBmLnJlZ2V4UmVwbGFjZW1lbnRFc2NhcGUoZi5lc2NhcGUodmFsdWUpKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gc3RyLnJlcGxhY2UobmV3IFJlZ0V4cChbcHJlZml4LCBuZXh0S2V5LCBzdWZmaXhdLmpvaW4oJycpLCAnZycpLCBmLnJlZ2V4UmVwbGFjZW1lbnRFc2NhcGUodmFsdWUpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gc3RyID0gb3B0aW9ucy5lc2NhcGVJbnRlcnBvbGF0aW9uO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHN0cjtcbiAgICB9XG4gICAgXG4gICAgLy8gYXBwZW5kIGl0IHRvIGZ1bmN0aW9uc1xuICAgIGYuYXBwbHlSZXBsYWNlbWVudCA9IGFwcGx5UmVwbGFjZW1lbnQ7XG4gICAgXG4gICAgZnVuY3Rpb24gYXBwbHlSZXVzZSh0cmFuc2xhdGVkLCBvcHRpb25zKSB7XG4gICAgICAgIHZhciBjb21tYSA9ICcsJztcbiAgICAgICAgdmFyIG9wdGlvbnNfb3BlbiA9ICd7JztcbiAgICAgICAgdmFyIG9wdGlvbnNfY2xvc2UgPSAnfSc7XG4gICAgXG4gICAgICAgIHZhciBvcHRzID0gZi5leHRlbmQoe30sIG9wdGlvbnMpO1xuICAgICAgICBkZWxldGUgb3B0cy5wb3N0UHJvY2VzcztcbiAgICBcbiAgICAgICAgd2hpbGUgKHRyYW5zbGF0ZWQuaW5kZXhPZihvLnJldXNlUHJlZml4KSAhPSAtMSkge1xuICAgICAgICAgICAgcmVwbGFjZW1lbnRDb3VudGVyKys7XG4gICAgICAgICAgICBpZiAocmVwbGFjZW1lbnRDb3VudGVyID4gby5tYXhSZWN1cnNpb24pIHsgYnJlYWs7IH0gLy8gc2FmZXR5IG5ldCBmb3IgdG9vIG11Y2ggcmVjdXJzaW9uXG4gICAgICAgICAgICB2YXIgaW5kZXhfb2Zfb3BlbmluZyA9IHRyYW5zbGF0ZWQubGFzdEluZGV4T2Yoby5yZXVzZVByZWZpeCk7XG4gICAgICAgICAgICB2YXIgaW5kZXhfb2ZfZW5kX29mX2Nsb3NpbmcgPSB0cmFuc2xhdGVkLmluZGV4T2Yoby5yZXVzZVN1ZmZpeCwgaW5kZXhfb2Zfb3BlbmluZykgKyBvLnJldXNlU3VmZml4Lmxlbmd0aDtcbiAgICAgICAgICAgIHZhciB0b2tlbiA9IHRyYW5zbGF0ZWQuc3Vic3RyaW5nKGluZGV4X29mX29wZW5pbmcsIGluZGV4X29mX2VuZF9vZl9jbG9zaW5nKTtcbiAgICAgICAgICAgIHZhciB0b2tlbl93aXRob3V0X3N5bWJvbHMgPSB0b2tlbi5yZXBsYWNlKG8ucmV1c2VQcmVmaXgsICcnKS5yZXBsYWNlKG8ucmV1c2VTdWZmaXgsICcnKTtcbiAgICBcbiAgICAgICAgICAgIGlmIChpbmRleF9vZl9lbmRfb2ZfY2xvc2luZyA8PSBpbmRleF9vZl9vcGVuaW5nKSB7XG4gICAgICAgICAgICAgICAgZi5lcnJvcigndGhlcmUgaXMgYW4gbWlzc2luZyBjbG9zaW5nIGluIGZvbGxvd2luZyB0cmFuc2xhdGlvbiB2YWx1ZScsIHRyYW5zbGF0ZWQpO1xuICAgICAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIGlmICh0b2tlbl93aXRob3V0X3N5bWJvbHMuaW5kZXhPZihjb21tYSkgIT0gLTEpIHtcbiAgICAgICAgICAgICAgICB2YXIgaW5kZXhfb2ZfdG9rZW5fZW5kX29mX2Nsb3NpbmcgPSB0b2tlbl93aXRob3V0X3N5bWJvbHMuaW5kZXhPZihjb21tYSk7XG4gICAgICAgICAgICAgICAgaWYgKHRva2VuX3dpdGhvdXRfc3ltYm9scy5pbmRleE9mKG9wdGlvbnNfb3BlbiwgaW5kZXhfb2ZfdG9rZW5fZW5kX29mX2Nsb3NpbmcpICE9IC0xICYmIHRva2VuX3dpdGhvdXRfc3ltYm9scy5pbmRleE9mKG9wdGlvbnNfY2xvc2UsIGluZGV4X29mX3Rva2VuX2VuZF9vZl9jbG9zaW5nKSAhPSAtMSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgaW5kZXhfb2Zfb3B0c19vcGVuaW5nID0gdG9rZW5fd2l0aG91dF9zeW1ib2xzLmluZGV4T2Yob3B0aW9uc19vcGVuLCBpbmRleF9vZl90b2tlbl9lbmRfb2ZfY2xvc2luZyk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBpbmRleF9vZl9vcHRzX2VuZF9vZl9jbG9zaW5nID0gdG9rZW5fd2l0aG91dF9zeW1ib2xzLmluZGV4T2Yob3B0aW9uc19jbG9zZSwgaW5kZXhfb2Zfb3B0c19vcGVuaW5nKSArIG9wdGlvbnNfY2xvc2UubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgb3B0cyA9IGYuZXh0ZW5kKG9wdHMsIEpTT04ucGFyc2UodG9rZW5fd2l0aG91dF9zeW1ib2xzLnN1YnN0cmluZyhpbmRleF9vZl9vcHRzX29wZW5pbmcsIGluZGV4X29mX29wdHNfZW5kX29mX2Nsb3NpbmcpKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0b2tlbl93aXRob3V0X3N5bWJvbHMgPSB0b2tlbl93aXRob3V0X3N5bWJvbHMuc3Vic3RyaW5nKDAsIGluZGV4X29mX3Rva2VuX2VuZF9vZl9jbG9zaW5nKTtcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgdmFyIHRyYW5zbGF0ZWRfdG9rZW4gPSBfdHJhbnNsYXRlKHRva2VuX3dpdGhvdXRfc3ltYm9scywgb3B0cyk7XG4gICAgICAgICAgICB0cmFuc2xhdGVkID0gdHJhbnNsYXRlZC5yZXBsYWNlKHRva2VuLCBmLnJlZ2V4UmVwbGFjZW1lbnRFc2NhcGUodHJhbnNsYXRlZF90b2tlbikpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cmFuc2xhdGVkO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBoYXNDb250ZXh0KG9wdGlvbnMpIHtcbiAgICAgICAgcmV0dXJuIChvcHRpb25zLmNvbnRleHQgJiYgKHR5cGVvZiBvcHRpb25zLmNvbnRleHQgPT0gJ3N0cmluZycgfHwgdHlwZW9mIG9wdGlvbnMuY29udGV4dCA9PSAnbnVtYmVyJykpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBuZWVkc1BsdXJhbChvcHRpb25zLCBsbmcpIHtcbiAgICAgICAgcmV0dXJuIChvcHRpb25zLmNvdW50ICE9PSB1bmRlZmluZWQgJiYgdHlwZW9mIG9wdGlvbnMuY291bnQgIT0gJ3N0cmluZycvKiAmJiBwbHVyYWxFeHRlbnNpb25zLm5lZWRzUGx1cmFsKGxuZywgb3B0aW9ucy5jb3VudCkqLyk7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIG5lZWRzSW5kZWZpbml0ZUFydGljbGUob3B0aW9ucykge1xuICAgICAgICByZXR1cm4gKG9wdGlvbnMuaW5kZWZpbml0ZV9hcnRpY2xlICE9PSB1bmRlZmluZWQgJiYgdHlwZW9mIG9wdGlvbnMuaW5kZWZpbml0ZV9hcnRpY2xlICE9ICdzdHJpbmcnICYmIG9wdGlvbnMuaW5kZWZpbml0ZV9hcnRpY2xlKTtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gZXhpc3RzKGtleSwgb3B0aW9ucykge1xuICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICBcbiAgICAgICAgdmFyIG5vdEZvdW5kID0gX2dldERlZmF1bHRWYWx1ZShrZXksIG9wdGlvbnMpXG4gICAgICAgICAgICAsIGZvdW5kID0gX2ZpbmQoa2V5LCBvcHRpb25zKTtcbiAgICBcbiAgICAgICAgcmV0dXJuIGZvdW5kICE9PSB1bmRlZmluZWQgfHwgZm91bmQgPT09IG5vdEZvdW5kO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiB0cmFuc2xhdGUoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgIFxuICAgICAgICBpZiAoIWluaXRpYWxpemVkKSB7XG4gICAgICAgICAgICBmLmxvZygnaTE4bmV4dCBub3QgZmluaXNoZWQgaW5pdGlhbGl6YXRpb24uIHlvdSBtaWdodCBoYXZlIGNhbGxlZCB0IGZ1bmN0aW9uIGJlZm9yZSBsb2FkaW5nIHJlc291cmNlcyBmaW5pc2hlZC4nKVxuICAgICAgICAgICAgcmV0dXJuIG9wdGlvbnMuZGVmYXVsdFZhbHVlIHx8ICcnO1xuICAgICAgICB9O1xuICAgICAgICByZXBsYWNlbWVudENvdW50ZXIgPSAwO1xuICAgICAgICByZXR1cm4gX3RyYW5zbGF0ZS5hcHBseShudWxsLCBhcmd1bWVudHMpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfZ2V0RGVmYXVsdFZhbHVlKGtleSwgb3B0aW9ucykge1xuICAgICAgICByZXR1cm4gKG9wdGlvbnMuZGVmYXVsdFZhbHVlICE9PSB1bmRlZmluZWQpID8gb3B0aW9ucy5kZWZhdWx0VmFsdWUgOiBrZXk7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIF9pbmplY3RTcHJpbnRmUHJvY2Vzc29yKCkge1xuICAgIFxuICAgICAgICB2YXIgdmFsdWVzID0gW107XG4gICAgXG4gICAgICAgIC8vIG1oOiBidWlsZCBhcnJheSBmcm9tIHNlY29uZCBhcmd1bWVudCBvbndhcmRzXG4gICAgICAgIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YWx1ZXMucHVzaChhcmd1bWVudHNbaV0pO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBwb3N0UHJvY2VzczogJ3NwcmludGYnLFxuICAgICAgICAgICAgc3ByaW50ZjogICAgIHZhbHVlc1xuICAgICAgICB9O1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfdHJhbnNsYXRlKHBvdGVudGlhbEtleXMsIG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKG9wdGlvbnMgJiYgdHlwZW9mIG9wdGlvbnMgIT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICBpZiAoby5zaG9ydGN1dEZ1bmN0aW9uID09PSAnc3ByaW50ZicpIHtcbiAgICAgICAgICAgICAgICAvLyBtaDogZ2V0dGV4dCBsaWtlIHNwcmludGYgc3ludGF4IGZvdW5kLCBhdXRvbWF0aWNhbGx5IGNyZWF0ZSBzcHJpbnRmIHByb2Nlc3NvclxuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBfaW5qZWN0U3ByaW50ZlByb2Nlc3Nvci5hcHBseShudWxsLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChvLnNob3J0Y3V0RnVuY3Rpb24gPT09ICdkZWZhdWx0VmFsdWUnKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiBvcHRpb25zXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKHR5cGVvZiBvLmRlZmF1bHRWYXJpYWJsZXMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gZi5leHRlbmQoe30sIG8uZGVmYXVsdFZhcmlhYmxlcywgb3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKHBvdGVudGlhbEtleXMgPT09IHVuZGVmaW5lZCB8fCBwb3RlbnRpYWxLZXlzID09PSBudWxsIHx8IHBvdGVudGlhbEtleXMgPT09ICcnKSByZXR1cm4gJyc7XG4gICAgXG4gICAgICAgIGlmICh0eXBlb2YgcG90ZW50aWFsS2V5cyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHBvdGVudGlhbEtleXMgPSBbcG90ZW50aWFsS2V5c107XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgdmFyIGtleSA9IHBvdGVudGlhbEtleXNbMF07XG4gICAgXG4gICAgICAgIGlmIChwb3RlbnRpYWxLZXlzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcG90ZW50aWFsS2V5cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGtleSA9IHBvdGVudGlhbEtleXNbaV07XG4gICAgICAgICAgICAgICAgaWYgKGV4aXN0cyhrZXksIG9wdGlvbnMpKSB7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICB2YXIgbm90Rm91bmQgPSBfZ2V0RGVmYXVsdFZhbHVlKGtleSwgb3B0aW9ucylcbiAgICAgICAgICAgICwgZm91bmQgPSBfZmluZChrZXksIG9wdGlvbnMpXG4gICAgICAgICAgICAsIGxuZ3MgPSBvcHRpb25zLmxuZyA/IGYudG9MYW5ndWFnZXMob3B0aW9ucy5sbmcsIG9wdGlvbnMuZmFsbGJhY2tMbmcpIDogbGFuZ3VhZ2VzXG4gICAgICAgICAgICAsIG5zID0gb3B0aW9ucy5ucyB8fCBvLm5zLmRlZmF1bHROc1xuICAgICAgICAgICAgLCBwYXJ0cztcbiAgICBcbiAgICAgICAgLy8gc3BsaXQgbnMgYW5kIGtleVxuICAgICAgICBpZiAoa2V5LmluZGV4T2Yoby5uc3NlcGFyYXRvcikgPiAtMSkge1xuICAgICAgICAgICAgcGFydHMgPSBrZXkuc3BsaXQoby5uc3NlcGFyYXRvcik7XG4gICAgICAgICAgICBucyA9IHBhcnRzWzBdO1xuICAgICAgICAgICAga2V5ID0gcGFydHNbMV07XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKGZvdW5kID09PSB1bmRlZmluZWQgJiYgby5zZW5kTWlzc2luZyAmJiB0eXBlb2Ygby5taXNzaW5nS2V5SGFuZGxlciA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMubG5nKSB7XG4gICAgICAgICAgICAgICAgby5taXNzaW5nS2V5SGFuZGxlcihsbmdzWzBdLCBucywga2V5LCBub3RGb3VuZCwgbG5ncyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG8ubWlzc2luZ0tleUhhbmRsZXIoby5sbmcsIG5zLCBrZXksIG5vdEZvdW5kLCBsbmdzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICB2YXIgcG9zdFByb2Nlc3NvciA9IG9wdGlvbnMucG9zdFByb2Nlc3MgfHwgby5wb3N0UHJvY2VzcztcbiAgICAgICAgaWYgKGZvdW5kICE9PSB1bmRlZmluZWQgJiYgcG9zdFByb2Nlc3Nvcikge1xuICAgICAgICAgICAgaWYgKHBvc3RQcm9jZXNzb3JzW3Bvc3RQcm9jZXNzb3JdKSB7XG4gICAgICAgICAgICAgICAgZm91bmQgPSBwb3N0UHJvY2Vzc29yc1twb3N0UHJvY2Vzc29yXShmb3VuZCwga2V5LCBvcHRpb25zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBwcm9jZXNzIG5vdEZvdW5kIGlmIGZ1bmN0aW9uIGV4aXN0c1xuICAgICAgICB2YXIgc3BsaXROb3RGb3VuZCA9IG5vdEZvdW5kO1xuICAgICAgICBpZiAobm90Rm91bmQuaW5kZXhPZihvLm5zc2VwYXJhdG9yKSA+IC0xKSB7XG4gICAgICAgICAgICBwYXJ0cyA9IG5vdEZvdW5kLnNwbGl0KG8ubnNzZXBhcmF0b3IpO1xuICAgICAgICAgICAgc3BsaXROb3RGb3VuZCA9IHBhcnRzWzFdO1xuICAgICAgICB9XG4gICAgICAgIGlmIChzcGxpdE5vdEZvdW5kID09PSBrZXkgJiYgby5wYXJzZU1pc3NpbmdLZXkpIHtcbiAgICAgICAgICAgIG5vdEZvdW5kID0gby5wYXJzZU1pc3NpbmdLZXkobm90Rm91bmQpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGlmIChmb3VuZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBub3RGb3VuZCA9IGFwcGx5UmVwbGFjZW1lbnQobm90Rm91bmQsIG9wdGlvbnMpO1xuICAgICAgICAgICAgbm90Rm91bmQgPSBhcHBseVJldXNlKG5vdEZvdW5kLCBvcHRpb25zKTtcbiAgICBcbiAgICAgICAgICAgIGlmIChwb3N0UHJvY2Vzc29yICYmIHBvc3RQcm9jZXNzb3JzW3Bvc3RQcm9jZXNzb3JdKSB7XG4gICAgICAgICAgICAgICAgdmFyIHZhbCA9IF9nZXREZWZhdWx0VmFsdWUoa2V5LCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICBmb3VuZCA9IHBvc3RQcm9jZXNzb3JzW3Bvc3RQcm9jZXNzb3JdKHZhbCwga2V5LCBvcHRpb25zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICByZXR1cm4gKGZvdW5kICE9PSB1bmRlZmluZWQpID8gZm91bmQgOiBub3RGb3VuZDtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gX2ZpbmQoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgIFxuICAgICAgICB2YXIgb3B0aW9uV2l0aG91dENvdW50LCB0cmFuc2xhdGVkXG4gICAgICAgICAgICAsIG5vdEZvdW5kID0gX2dldERlZmF1bHRWYWx1ZShrZXksIG9wdGlvbnMpXG4gICAgICAgICAgICAsIGxuZ3MgPSBsYW5ndWFnZXM7XG4gICAgXG4gICAgICAgIGlmICghcmVzU3RvcmUpIHsgcmV0dXJuIG5vdEZvdW5kOyB9IC8vIG5vIHJlc1N0b3JlIHRvIHRyYW5zbGF0ZSBmcm9tXG4gICAgXG4gICAgICAgIC8vIENJIG1vZGVcbiAgICAgICAgaWYgKGxuZ3NbMF0udG9Mb3dlckNhc2UoKSA9PT0gJ2NpbW9kZScpIHJldHVybiBub3RGb3VuZDtcbiAgICBcbiAgICAgICAgLy8gcGFzc2VkIGluIGxuZ1xuICAgICAgICBpZiAob3B0aW9ucy5sbmdzKSBsbmdzID0gb3B0aW9ucy5sbmdzO1xuICAgICAgICBpZiAob3B0aW9ucy5sbmcpIHtcbiAgICAgICAgICAgIGxuZ3MgPSBmLnRvTGFuZ3VhZ2VzKG9wdGlvbnMubG5nLCBvcHRpb25zLmZhbGxiYWNrTG5nKTtcbiAgICBcbiAgICAgICAgICAgIGlmICghcmVzU3RvcmVbbG5nc1swXV0pIHtcbiAgICAgICAgICAgICAgICB2YXIgb2xkQXN5bmMgPSBvLmdldEFzeW5jO1xuICAgICAgICAgICAgICAgIG8uZ2V0QXN5bmMgPSBmYWxzZTtcbiAgICBcbiAgICAgICAgICAgICAgICBpMThuLnN5bmMubG9hZChsbmdzLCBvLCBmdW5jdGlvbihlcnIsIHN0b3JlKSB7XG4gICAgICAgICAgICAgICAgICAgIGYuZXh0ZW5kKHJlc1N0b3JlLCBzdG9yZSk7XG4gICAgICAgICAgICAgICAgICAgIG8uZ2V0QXN5bmMgPSBvbGRBc3luYztcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICB2YXIgbnMgPSBvcHRpb25zLm5zIHx8IG8ubnMuZGVmYXVsdE5zO1xuICAgICAgICBpZiAoa2V5LmluZGV4T2Yoby5uc3NlcGFyYXRvcikgPiAtMSkge1xuICAgICAgICAgICAgdmFyIHBhcnRzID0ga2V5LnNwbGl0KG8ubnNzZXBhcmF0b3IpO1xuICAgICAgICAgICAgbnMgPSBwYXJ0c1swXTtcbiAgICAgICAgICAgIGtleSA9IHBhcnRzWzFdO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGlmIChoYXNDb250ZXh0KG9wdGlvbnMpKSB7XG4gICAgICAgICAgICBvcHRpb25XaXRob3V0Q291bnQgPSBmLmV4dGVuZCh7fSwgb3B0aW9ucyk7XG4gICAgICAgICAgICBkZWxldGUgb3B0aW9uV2l0aG91dENvdW50LmNvbnRleHQ7XG4gICAgICAgICAgICBvcHRpb25XaXRob3V0Q291bnQuZGVmYXVsdFZhbHVlID0gby5jb250ZXh0Tm90Rm91bmQ7XG4gICAgXG4gICAgICAgICAgICB2YXIgY29udGV4dEtleSA9IG5zICsgby5uc3NlcGFyYXRvciArIGtleSArICdfJyArIG9wdGlvbnMuY29udGV4dDtcbiAgICBcbiAgICAgICAgICAgIHRyYW5zbGF0ZWQgPSB0cmFuc2xhdGUoY29udGV4dEtleSwgb3B0aW9uV2l0aG91dENvdW50KTtcbiAgICAgICAgICAgIGlmICh0cmFuc2xhdGVkICE9IG8uY29udGV4dE5vdEZvdW5kKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFwcGx5UmVwbGFjZW1lbnQodHJhbnNsYXRlZCwgeyBjb250ZXh0OiBvcHRpb25zLmNvbnRleHQgfSk7IC8vIGFwcGx5IHJlcGxhY2VtZW50IGZvciBjb250ZXh0IG9ubHlcbiAgICAgICAgICAgIH0gLy8gZWxzZSBjb250aW51ZSB0cmFuc2xhdGlvbiB3aXRoIG9yaWdpbmFsL25vbkNvbnRleHQga2V5XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKG5lZWRzUGx1cmFsKG9wdGlvbnMsIGxuZ3NbMF0pKSB7XG4gICAgICAgICAgICBvcHRpb25XaXRob3V0Q291bnQgPSBmLmV4dGVuZCh7IGxuZ3M6IFtsbmdzWzBdXX0sIG9wdGlvbnMpO1xuICAgICAgICAgICAgZGVsZXRlIG9wdGlvbldpdGhvdXRDb3VudC5jb3VudDtcbiAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25XaXRob3V0Q291bnQubG5nO1xuICAgICAgICAgICAgb3B0aW9uV2l0aG91dENvdW50LmRlZmF1bHRWYWx1ZSA9IG8ucGx1cmFsTm90Rm91bmQ7XG4gICAgXG4gICAgICAgICAgICB2YXIgcGx1cmFsS2V5O1xuICAgICAgICAgICAgaWYgKCFwbHVyYWxFeHRlbnNpb25zLm5lZWRzUGx1cmFsKGxuZ3NbMF0sIG9wdGlvbnMuY291bnQpKSB7XG4gICAgICAgICAgICAgICAgcGx1cmFsS2V5ID0gbnMgKyBvLm5zc2VwYXJhdG9yICsga2V5O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwbHVyYWxLZXkgPSBucyArIG8ubnNzZXBhcmF0b3IgKyBrZXkgKyBvLnBsdXJhbFN1ZmZpeDtcbiAgICAgICAgICAgICAgICB2YXIgcGx1cmFsRXh0ZW5zaW9uID0gcGx1cmFsRXh0ZW5zaW9ucy5nZXQobG5nc1swXSwgb3B0aW9ucy5jb3VudCk7XG4gICAgICAgICAgICAgICAgaWYgKHBsdXJhbEV4dGVuc2lvbiA+PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHBsdXJhbEtleSA9IHBsdXJhbEtleSArICdfJyArIHBsdXJhbEV4dGVuc2lvbjtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHBsdXJhbEV4dGVuc2lvbiA9PT0gMSkge1xuICAgICAgICAgICAgICAgICAgICBwbHVyYWxLZXkgPSBucyArIG8ubnNzZXBhcmF0b3IgKyBrZXk7IC8vIHNpbmd1bGFyXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgdHJhbnNsYXRlZCA9IHRyYW5zbGF0ZShwbHVyYWxLZXksIG9wdGlvbldpdGhvdXRDb3VudCk7XG4gICAgXG4gICAgICAgICAgICBpZiAodHJhbnNsYXRlZCAhPSBvLnBsdXJhbE5vdEZvdW5kKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFwcGx5UmVwbGFjZW1lbnQodHJhbnNsYXRlZCwge1xuICAgICAgICAgICAgICAgICAgICBjb3VudDogb3B0aW9ucy5jb3VudCxcbiAgICAgICAgICAgICAgICAgICAgaW50ZXJwb2xhdGlvblByZWZpeDogb3B0aW9ucy5pbnRlcnBvbGF0aW9uUHJlZml4LFxuICAgICAgICAgICAgICAgICAgICBpbnRlcnBvbGF0aW9uU3VmZml4OiBvcHRpb25zLmludGVycG9sYXRpb25TdWZmaXhcbiAgICAgICAgICAgICAgICB9KTsgLy8gYXBwbHkgcmVwbGFjZW1lbnQgZm9yIGNvdW50IG9ubHlcbiAgICAgICAgICAgIH0gZWxzZSBpZiAobG5ncy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgLy8gcmVtb3ZlIGZhaWxlZCBsbmdcbiAgICAgICAgICAgICAgICB2YXIgY2xvbmUgPSBsbmdzLnNsaWNlKCk7XG4gICAgICAgICAgICAgICAgY2xvbmUuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICBvcHRpb25zID0gZi5leHRlbmQob3B0aW9ucywgeyBsbmdzOiBjbG9uZSB9KTtcbiAgICAgICAgICAgICAgICBkZWxldGUgb3B0aW9ucy5sbmc7XG4gICAgICAgICAgICAgICAgLy8gcmV0cnkgd2l0aCBmYWxsYmFja3NcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGVkID0gdHJhbnNsYXRlKG5zICsgby5uc3NlcGFyYXRvciArIGtleSwgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgaWYgKHRyYW5zbGF0ZWQgIT0gby5wbHVyYWxOb3RGb3VuZCkgcmV0dXJuIHRyYW5zbGF0ZWQ7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cmFuc2xhdGVkO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIGlmIChuZWVkc0luZGVmaW5pdGVBcnRpY2xlKG9wdGlvbnMpKSB7XG4gICAgICAgICAgICB2YXIgb3B0aW9uc1dpdGhvdXRJbmRlZiA9IGYuZXh0ZW5kKHt9LCBvcHRpb25zKTtcbiAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25zV2l0aG91dEluZGVmLmluZGVmaW5pdGVfYXJ0aWNsZTtcbiAgICAgICAgICAgIG9wdGlvbnNXaXRob3V0SW5kZWYuZGVmYXVsdFZhbHVlID0gby5pbmRlZmluaXRlTm90Rm91bmQ7XG4gICAgICAgICAgICAvLyBJZiB3ZSBkb24ndCBoYXZlIGEgY291bnQsIHdlIHdhbnQgdGhlIGluZGVmaW5pdGUsIGlmIHdlIGRvIGhhdmUgYSBjb3VudCwgYW5kIG5lZWRzUGx1cmFsIGlzIGZhbHNlXG4gICAgICAgICAgICB2YXIgaW5kZWZpbml0ZUtleSA9IG5zICsgby5uc3NlcGFyYXRvciArIGtleSArICgoKG9wdGlvbnMuY291bnQgJiYgIW5lZWRzUGx1cmFsKG9wdGlvbnMsIGxuZ3NbMF0pKSB8fCAhb3B0aW9ucy5jb3VudCkgPyBvLmluZGVmaW5pdGVTdWZmaXggOiBcIlwiKTtcbiAgICAgICAgICAgIHRyYW5zbGF0ZWQgPSB0cmFuc2xhdGUoaW5kZWZpbml0ZUtleSwgb3B0aW9uc1dpdGhvdXRJbmRlZik7XG4gICAgICAgICAgICBpZiAodHJhbnNsYXRlZCAhPSBvLmluZGVmaW5pdGVOb3RGb3VuZCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cmFuc2xhdGVkO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBmb3VuZDtcbiAgICAgICAgdmFyIGtleXMgPSBrZXkuc3BsaXQoby5rZXlzZXBhcmF0b3IpO1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgbGVuID0gbG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKyApIHtcbiAgICAgICAgICAgIGlmIChmb3VuZCAhPT0gdW5kZWZpbmVkKSBicmVhaztcbiAgICBcbiAgICAgICAgICAgIHZhciBsID0gbG5nc1tpXTtcbiAgICBcbiAgICAgICAgICAgIHZhciB4ID0gMDtcbiAgICAgICAgICAgIHZhciB2YWx1ZSA9IHJlc1N0b3JlW2xdICYmIHJlc1N0b3JlW2xdW25zXTtcbiAgICAgICAgICAgIHdoaWxlIChrZXlzW3hdKSB7XG4gICAgICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZSAmJiB2YWx1ZVtrZXlzW3hdXTtcbiAgICAgICAgICAgICAgICB4Kys7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHZhciB2YWx1ZVR5cGUgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmFwcGx5KHZhbHVlKTtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IGFwcGx5UmVwbGFjZW1lbnQodmFsdWUsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IGFwcGx5UmV1c2UodmFsdWUsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVUeXBlID09PSAnW29iamVjdCBBcnJheV0nICYmICFvLnJldHVybk9iamVjdFRyZWVzICYmICFvcHRpb25zLnJldHVybk9iamVjdFRyZWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWUuam9pbignXFxuJyk7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gYXBwbHlSZXBsYWNlbWVudCh2YWx1ZSwgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gYXBwbHlSZXVzZSh2YWx1ZSwgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZSA9PT0gbnVsbCAmJiBvLmZhbGxiYWNrT25OdWxsID09PSB0cnVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFvLnJldHVybk9iamVjdFRyZWVzICYmICFvcHRpb25zLnJldHVybk9iamVjdFRyZWVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoby5vYmplY3RUcmVlS2V5SGFuZGxlciAmJiB0eXBlb2Ygby5vYmplY3RUcmVlS2V5SGFuZGxlciA9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBvLm9iamVjdFRyZWVLZXlIYW5kbGVyKGtleSwgdmFsdWUsIGwsIG5zLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAna2V5IFxcJycgKyBucyArICc6JyArIGtleSArICcgKCcgKyBsICsgJylcXCcgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdyZXR1cm5lZCBhbiBvYmplY3QgaW5zdGVhZCBvZiBzdHJpbmcuJztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmLmxvZyh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVUeXBlICE9PSAnW29iamVjdCBOdW1iZXJdJyAmJiB2YWx1ZVR5cGUgIT09ICdbb2JqZWN0IEZ1bmN0aW9uXScgJiYgdmFsdWVUeXBlICE9PSAnW29iamVjdCBSZWdFeHBdJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGNvcHkgPSAodmFsdWVUeXBlID09PSAnW29iamVjdCBBcnJheV0nKSA/IFtdIDoge307IC8vIGFwcGx5IGNoaWxkIHRyYW5zbGF0aW9uIG9uIGEgY29weVxuICAgICAgICAgICAgICAgICAgICAgICAgZi5lYWNoKHZhbHVlLCBmdW5jdGlvbihtKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29weVttXSA9IF90cmFuc2xhdGUobnMgKyBvLm5zc2VwYXJhdG9yICsga2V5ICsgby5rZXlzZXBhcmF0b3IgKyBtLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBjb3B5O1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnRyaW0oKSA9PT0gJycgJiYgby5mYWxsYmFja09uRW1wdHkgPT09IHRydWUpXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdW5kZWZpbmVkO1xuICAgIFxuICAgICAgICAgICAgICAgIGZvdW5kID0gdmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKGZvdW5kID09PSB1bmRlZmluZWQgJiYgIW9wdGlvbnMuaXNGYWxsYmFja0xvb2t1cCAmJiAoby5mYWxsYmFja1RvRGVmYXVsdE5TID09PSB0cnVlIHx8IChvLmZhbGxiYWNrTlMgJiYgby5mYWxsYmFja05TLmxlbmd0aCA+IDApKSkge1xuICAgICAgICAgICAgLy8gc2V0IGZsYWcgZm9yIGZhbGxiYWNrIGxvb2t1cCAtIGF2b2lkIHJlY3Vyc2lvblxuICAgICAgICAgICAgb3B0aW9ucy5pc0ZhbGxiYWNrTG9va3VwID0gdHJ1ZTtcbiAgICBcbiAgICAgICAgICAgIGlmIChvLmZhbGxiYWNrTlMubGVuZ3RoKSB7XG4gICAgXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgeSA9IDAsIGxlblkgPSBvLmZhbGxiYWNrTlMubGVuZ3RoOyB5IDwgbGVuWTsgeSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvdW5kID0gX2ZpbmQoby5mYWxsYmFja05TW3ldICsgby5uc3NlcGFyYXRvciArIGtleSwgb3B0aW9ucyk7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCB8fCAoZm91bmQ9PT1cIlwiICYmIG8uZmFsbGJhY2tPbkVtcHR5ID09PSBmYWxzZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8qIGNvbXBhcmUgdmFsdWUgd2l0aG91dCBuYW1lc3BhY2UgKi9cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmb3VuZFZhbHVlID0gZm91bmQuaW5kZXhPZihvLm5zc2VwYXJhdG9yKSA+IC0xID8gZm91bmQuc3BsaXQoby5uc3NlcGFyYXRvcilbMV0gOiBmb3VuZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAsIG5vdEZvdW5kVmFsdWUgPSBub3RGb3VuZC5pbmRleE9mKG8ubnNzZXBhcmF0b3IpID4gLTEgPyBub3RGb3VuZC5zcGxpdChvLm5zc2VwYXJhdG9yKVsxXSA6IG5vdEZvdW5kO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGZvdW5kVmFsdWUgIT09IG5vdEZvdW5kVmFsdWUpIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmb3VuZCA9IF9maW5kKGtleSwgb3B0aW9ucyk7IC8vIGZhbGxiYWNrIHRvIGRlZmF1bHQgTlNcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9wdGlvbnMuaXNGYWxsYmFja0xvb2t1cCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJldHVybiBmb3VuZDtcbiAgICB9XG4gICAgZnVuY3Rpb24gZGV0ZWN0TGFuZ3VhZ2UoKSB7XG4gICAgICAgIHZhciBkZXRlY3RlZExuZztcbiAgICAgICAgdmFyIHdoaXRlbGlzdCA9IG8ubG5nV2hpdGVsaXN0IHx8IFtdO1xuICAgICAgICB2YXIgdXNlckxuZ0Nob2ljZXMgPSBbXTtcbiAgICBcbiAgICAgICAgLy8gZ2V0IGZyb20gcXNcbiAgICAgICAgdmFyIHFzUGFybSA9IFtdO1xuICAgICAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICB2YXIgcXVlcnkgPSB3aW5kb3cubG9jYXRpb24uc2VhcmNoLnN1YnN0cmluZygxKTtcbiAgICAgICAgICAgICAgICB2YXIgcGFyYW1zID0gcXVlcnkuc3BsaXQoJyYnKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpPTA7IGk8cGFyYW1zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwb3MgPSBwYXJhbXNbaV0uaW5kZXhPZignPScpO1xuICAgICAgICAgICAgICAgICAgICBpZiAocG9zID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGtleSA9IHBhcmFtc1tpXS5zdWJzdHJpbmcoMCxwb3MpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGtleSA9PSBvLmRldGVjdExuZ1FTKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlckxuZ0Nob2ljZXMucHVzaChwYXJhbXNbaV0uc3Vic3RyaW5nKHBvcysxKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KSgpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIGdldCBmcm9tIGNvb2tpZVxuICAgICAgICBpZiAoby51c2VDb29raWUgJiYgdHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgdmFyIGMgPSBmLmNvb2tpZS5yZWFkKG8uY29va2llTmFtZSk7XG4gICAgICAgICAgICBpZiAoYykgdXNlckxuZ0Nob2ljZXMucHVzaChjKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBnZXQgZnJvbSBsb2NhbFN0b3JhZ2VcbiAgICAgICAgaWYgKG8uZGV0ZWN0TG5nRnJvbUxvY2FsU3RvcmFnZSAmJiB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB3aW5kb3cubG9jYWxTdG9yYWdlKSB7XG4gICAgICAgICAgICB1c2VyTG5nQ2hvaWNlcy5wdXNoKHdpbmRvdy5sb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnaTE4bmV4dF9sbmcnKSk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZ2V0IGZyb20gbmF2aWdhdG9yXG4gICAgICAgIGlmICh0eXBlb2YgbmF2aWdhdG9yICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgaWYgKG5hdmlnYXRvci5sYW5ndWFnZXMpIHsgLy8gY2hyb21lIG9ubHk7IG5vdCBhbiBhcnJheSwgc28gY2FuJ3QgdXNlIC5wdXNoLmFwcGx5IGluc3RlYWQgb2YgaXRlcmF0aW5nXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaT0wO2k8bmF2aWdhdG9yLmxhbmd1YWdlcy5sZW5ndGg7aSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIHVzZXJMbmdDaG9pY2VzLnB1c2gobmF2aWdhdG9yLmxhbmd1YWdlc1tpXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG5hdmlnYXRvci51c2VyTGFuZ3VhZ2UpIHtcbiAgICAgICAgICAgICAgICB1c2VyTG5nQ2hvaWNlcy5wdXNoKG5hdmlnYXRvci51c2VyTGFuZ3VhZ2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG5hdmlnYXRvci5sYW5ndWFnZSkge1xuICAgICAgICAgICAgICAgIHVzZXJMbmdDaG9pY2VzLnB1c2gobmF2aWdhdG9yLmxhbmd1YWdlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICAoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpPTA7aTx1c2VyTG5nQ2hvaWNlcy5sZW5ndGg7aSsrKSB7XG4gICAgICAgICAgICAgICAgdmFyIGxuZyA9IHVzZXJMbmdDaG9pY2VzW2ldO1xuICAgIFxuICAgICAgICAgICAgICAgIGlmIChsbmcuaW5kZXhPZignLScpID4gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcnRzID0gbG5nLnNwbGl0KCctJyk7XG4gICAgICAgICAgICAgICAgICAgIGxuZyA9IG8ubG93ZXJDYXNlTG5nID9cbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRzWzBdLnRvTG93ZXJDYXNlKCkgKyAgJy0nICsgcGFydHNbMV0udG9Mb3dlckNhc2UoKSA6XG4gICAgICAgICAgICAgICAgICAgICAgICBwYXJ0c1swXS50b0xvd2VyQ2FzZSgpICsgICctJyArIHBhcnRzWzFdLnRvVXBwZXJDYXNlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgIGlmICh3aGl0ZWxpc3QubGVuZ3RoID09PSAwIHx8IHdoaXRlbGlzdC5pbmRleE9mKGxuZykgPiAtMSkge1xuICAgICAgICAgICAgICAgICAgICBkZXRlY3RlZExuZyA9IGxuZztcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KSgpO1xuICAgIFxuICAgICAgICAvL2ZhbGxiYWNrXG4gICAgICAgIGlmICghZGV0ZWN0ZWRMbmcpe1xuICAgICAgICAgIGRldGVjdGVkTG5nID0gby5mYWxsYmFja0xuZ1swXTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIGRldGVjdGVkTG5nO1xuICAgIH1cbiAgICAvLyBkZWZpbml0aW9uIGh0dHA6Ly90cmFuc2xhdGUuc291cmNlZm9yZ2UubmV0L3dpa2kvbDEwbi9wbHVyYWxmb3Jtc1xuICAgIFxuICAgIC8qIFtjb2RlLCBuYW1lLCBudW1iZXJzLCBwbHVyYWxzVHlwZV0gKi9cbiAgICB2YXIgX3J1bGVzID0gW1xuICAgICAgICBbXCJhY2hcIiwgXCJBY2hvbGlcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJhZlwiLCBcIkFmcmlrYWFuc1wiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wiYWtcIiwgXCJBa2FuXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wiYW1cIiwgXCJBbWhhcmljXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wiYW5cIiwgXCJBcmFnb25lc2VcIixbMSwyXSwgMl0sXG4gICAgICAgIFtcImFyXCIsIFwiQXJhYmljXCIsIFswLDEsMiwzLDExLDEwMF0sNV0sXG4gICAgICAgIFtcImFyblwiLCBcIk1hcHVkdW5ndW5cIixbMSwyXSwgMV0sXG4gICAgICAgIFtcImFzdFwiLCBcIkFzdHVyaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiYXlcIiwgXCJBeW1hcsOhXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImF6XCIsIFwiQXplcmJhaWphbmlcIixbMSwyXSwyXSxcbiAgICAgICAgW1wiYmVcIiwgXCJCZWxhcnVzaWFuXCIsWzEsMiw1XSw0XSxcbiAgICAgICAgW1wiYmdcIiwgXCJCdWxnYXJpYW5cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcImJuXCIsIFwiQmVuZ2FsaVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImJvXCIsIFwiVGliZXRhblwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJiclwiLCBcIkJyZXRvblwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcImJzXCIsIFwiQm9zbmlhblwiLCBbMSwyLDVdLDRdLFxuICAgICAgICBbXCJjYVwiLCBcIkNhdGFsYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJjZ2dcIiwgXCJDaGlnYVwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJjc1wiLCBcIkN6ZWNoXCIsIFsxLDIsNV0sNl0sXG4gICAgICAgIFtcImNzYlwiLCBcIkthc2h1YmlhblwiLFsxLDIsNV0sN10sXG4gICAgICAgIFtcImN5XCIsIFwiV2Vsc2hcIiwgWzEsMiwzLDhdLDhdLFxuICAgICAgICBbXCJkYVwiLCBcIkRhbmlzaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImRlXCIsIFwiR2VybWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZGV2XCIsIFwiRGV2ZWxvcG1lbnQgRmFsbGJhY2tcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJkelwiLCBcIkR6b25na2hhXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImVsXCIsIFwiR3JlZWtcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJlblwiLCBcIkVuZ2xpc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJlb1wiLCBcIkVzcGVyYW50b1wiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZXNcIiwgXCJTcGFuaXNoXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZXNfYXJcIixcIkFyZ2VudGluZWFuIFNwYW5pc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJldFwiLCBcIkVzdG9uaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZXVcIiwgXCJCYXNxdWVcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJmYVwiLCBcIlBlcnNpYW5cIiwgWzFdLCAzXSxcbiAgICAgICAgW1wiZmlcIiwgXCJGaW5uaXNoXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZmlsXCIsIFwiRmlsaXBpbm9cIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJmb1wiLCBcIkZhcm9lc2VcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJmclwiLCBcIkZyZW5jaFwiLCBbMSwyXSwgOV0sXG4gICAgICAgIFtcImZ1clwiLCBcIkZyaXVsaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZnlcIiwgXCJGcmlzaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZ2FcIiwgXCJJcmlzaFwiLCBbMSwyLDMsNywxMV0sMTBdLFxuICAgICAgICBbXCJnZFwiLCBcIlNjb3R0aXNoIEdhZWxpY1wiLFsxLDIsMywyMF0sMTFdLFxuICAgICAgICBbXCJnbFwiLCBcIkdhbGljaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZ3VcIiwgXCJHdWphcmF0aVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImd1blwiLCBcIkd1blwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcImhhXCIsIFwiSGF1c2FcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJoZVwiLCBcIkhlYnJld1wiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImhpXCIsIFwiSGluZGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJoclwiLCBcIkNyb2F0aWFuXCIsIFsxLDIsNV0sNF0sXG4gICAgICAgIFtcImh1XCIsIFwiSHVuZ2FyaWFuXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJoeVwiLCBcIkFybWVuaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiaWFcIiwgXCJJbnRlcmxpbmd1YVwiLFsxLDJdLDJdLFxuICAgICAgICBbXCJpZFwiLCBcIkluZG9uZXNpYW5cIixbMV0sIDNdLFxuICAgICAgICBbXCJpc1wiLCBcIkljZWxhbmRpY1wiLFsxLDJdLCAxMl0sXG4gICAgICAgIFtcIml0XCIsIFwiSXRhbGlhblwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImphXCIsIFwiSmFwYW5lc2VcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wiamJvXCIsIFwiTG9qYmFuXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImp2XCIsIFwiSmF2YW5lc2VcIiwgWzAsMV0sIDEzXSxcbiAgICAgICAgW1wia2FcIiwgXCJHZW9yZ2lhblwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJra1wiLCBcIkthemFraFwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJrbVwiLCBcIktobWVyXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImtuXCIsIFwiS2FubmFkYVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImtvXCIsIFwiS29yZWFuXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImt1XCIsIFwiS3VyZGlzaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImt3XCIsIFwiQ29ybmlzaFwiLCBbMSwyLDMsNF0sMTRdLFxuICAgICAgICBbXCJreVwiLCBcIkt5cmd5elwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJsYlwiLCBcIkxldHplYnVyZ2VzY2hcIixbMSwyXSwyXSxcbiAgICAgICAgW1wibG5cIiwgXCJMaW5nYWxhXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wibG9cIiwgXCJMYW9cIiwgWzFdLCAzXSxcbiAgICAgICAgW1wibHRcIiwgXCJMaXRodWFuaWFuXCIsWzEsMiwxMF0sMTVdLFxuICAgICAgICBbXCJsdlwiLCBcIkxhdHZpYW5cIiwgWzEsMiwwXSwxNl0sXG4gICAgICAgIFtcIm1haVwiLCBcIk1haXRoaWxpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wibWZlXCIsIFwiTWF1cml0aWFuIENyZW9sZVwiLFsxLDJdLDFdLFxuICAgICAgICBbXCJtZ1wiLCBcIk1hbGFnYXN5XCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wibWlcIiwgXCJNYW9yaVwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcIm1rXCIsIFwiTWFjZWRvbmlhblwiLFsxLDJdLDE3XSxcbiAgICAgICAgW1wibWxcIiwgXCJNYWxheWFsYW1cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcIm1uXCIsIFwiTW9uZ29saWFuXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJtbmtcIiwgXCJNYW5kaW5rYVwiLCBbMCwxLDJdLDE4XSxcbiAgICAgICAgW1wibXJcIiwgXCJNYXJhdGhpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wibXNcIiwgXCJNYWxheVwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJtdFwiLCBcIk1hbHRlc2VcIiwgWzEsMiwxMSwyMF0sMTldLFxuICAgICAgICBbXCJuYWhcIiwgXCJOYWh1YXRsXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wibmFwXCIsIFwiTmVhcG9saXRhblwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wibmJcIiwgXCJOb3J3ZWdpYW4gQm9rbWFsXCIsWzEsMl0sMl0sXG4gICAgICAgIFtcIm5lXCIsIFwiTmVwYWxpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wibmxcIiwgXCJEdXRjaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcIm5uXCIsIFwiTm9yd2VnaWFuIE55bm9yc2tcIixbMSwyXSwyXSxcbiAgICAgICAgW1wibm9cIiwgXCJOb3J3ZWdpYW5cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcIm5zb1wiLCBcIk5vcnRoZXJuIFNvdGhvXCIsWzEsMl0sMl0sXG4gICAgICAgIFtcIm9jXCIsIFwiT2NjaXRhblwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcIm9yXCIsIFwiT3JpeWFcIiwgWzIsMV0sIDJdLFxuICAgICAgICBbXCJwYVwiLCBcIlB1bmphYmlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJwYXBcIiwgXCJQYXBpYW1lbnRvXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJwbFwiLCBcIlBvbGlzaFwiLCBbMSwyLDVdLDddLFxuICAgICAgICBbXCJwbXNcIiwgXCJQaWVtb250ZXNlXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJwc1wiLCBcIlBhc2h0b1wiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInB0XCIsIFwiUG9ydHVndWVzZVwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wicHRfYnJcIixcIkJyYXppbGlhbiBQb3J0dWd1ZXNlXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJybVwiLCBcIlJvbWFuc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJyb1wiLCBcIlJvbWFuaWFuXCIsIFsxLDIsMjBdLDIwXSxcbiAgICAgICAgW1wicnVcIiwgXCJSdXNzaWFuXCIsIFsxLDIsNV0sNF0sXG4gICAgICAgIFtcInNhaFwiLCBcIllha3V0XCIsIFsxXSwgM10sXG4gICAgICAgIFtcInNjb1wiLCBcIlNjb3RzXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wic2VcIiwgXCJOb3J0aGVybiBTYW1pXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJzaVwiLCBcIlNpbmhhbGFcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJza1wiLCBcIlNsb3Zha1wiLCBbMSwyLDVdLDZdLFxuICAgICAgICBbXCJzbFwiLCBcIlNsb3ZlbmlhblwiLFs1LDEsMiwzXSwyMV0sXG4gICAgICAgIFtcInNvXCIsIFwiU29tYWxpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wic29uXCIsIFwiU29uZ2hheVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInNxXCIsIFwiQWxiYW5pYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJzclwiLCBcIlNlcmJpYW5cIiwgWzEsMiw1XSw0XSxcbiAgICAgICAgW1wic3VcIiwgXCJTdW5kYW5lc2VcIixbMV0sIDNdLFxuICAgICAgICBbXCJzdlwiLCBcIlN3ZWRpc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJzd1wiLCBcIlN3YWhpbGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ0YVwiLCBcIlRhbWlsXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1widGVcIiwgXCJUZWx1Z3VcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ0Z1wiLCBcIlRhamlrXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1widGhcIiwgXCJUaGFpXCIsIFsxXSwgM10sXG4gICAgICAgIFtcInRpXCIsIFwiVGlncmlueWFcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJ0a1wiLCBcIlR1cmttZW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ0clwiLCBcIlR1cmtpc2hcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJ0dFwiLCBcIlRhdGFyXCIsIFsxXSwgM10sXG4gICAgICAgIFtcInVnXCIsIFwiVXlnaHVyXCIsIFsxXSwgM10sXG4gICAgICAgIFtcInVrXCIsIFwiVWtyYWluaWFuXCIsWzEsMiw1XSw0XSxcbiAgICAgICAgW1widXJcIiwgXCJVcmR1XCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1widXpcIiwgXCJVemJla1wiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcInZpXCIsIFwiVmlldG5hbWVzZVwiLFsxXSwgM10sXG4gICAgICAgIFtcIndhXCIsIFwiV2FsbG9vblwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcIndvXCIsIFwiV29sb2ZcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wieW9cIiwgXCJZb3J1YmFcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ6aFwiLCBcIkNoaW5lc2VcIiwgWzFdLCAzXVxuICAgIF07XG4gICAgXG4gICAgdmFyIF9ydWxlc1BsdXJhbHNUeXBlcyA9IHtcbiAgICAgICAgMTogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiA+IDEpO30sXG4gICAgICAgIDI6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4gIT0gMSk7fSxcbiAgICAgICAgMzogZnVuY3Rpb24obikge3JldHVybiAwO30sXG4gICAgICAgIDQ6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4lMTA9PTEgJiYgbiUxMDAhPTExID8gMCA6IG4lMTA+PTIgJiYgbiUxMDw9NCAmJiAobiUxMDA8MTAgfHwgbiUxMDA+PTIwKSA/IDEgOiAyKTt9LFxuICAgICAgICA1OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuPT09MCA/IDAgOiBuPT0xID8gMSA6IG49PTIgPyAyIDogbiUxMDA+PTMgJiYgbiUxMDA8PTEwID8gMyA6IG4lMTAwPj0xMSA/IDQgOiA1KTt9LFxuICAgICAgICA2OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcigobj09MSkgPyAwIDogKG4+PTIgJiYgbjw9NCkgPyAxIDogMik7fSxcbiAgICAgICAgNzogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobj09MSA/IDAgOiBuJTEwPj0yICYmIG4lMTA8PTQgJiYgKG4lMTAwPDEwIHx8IG4lMTAwPj0yMCkgPyAxIDogMik7fSxcbiAgICAgICAgODogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIoKG49PTEpID8gMCA6IChuPT0yKSA/IDEgOiAobiAhPSA4ICYmIG4gIT0gMTEpID8gMiA6IDMpO30sXG4gICAgICAgIDk6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4gPj0gMik7fSxcbiAgICAgICAgMTA6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG49PTEgPyAwIDogbj09MiA/IDEgOiBuPDcgPyAyIDogbjwxMSA/IDMgOiA0KSA7fSxcbiAgICAgICAgMTE6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKChuPT0xIHx8IG49PTExKSA/IDAgOiAobj09MiB8fCBuPT0xMikgPyAxIDogKG4gPiAyICYmIG4gPCAyMCkgPyAyIDogMyk7fSxcbiAgICAgICAgMTI6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4lMTAhPTEgfHwgbiUxMDA9PTExKTt9LFxuICAgICAgICAxMzogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiAhPT0gMCk7fSxcbiAgICAgICAgMTQ6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKChuPT0xKSA/IDAgOiAobj09MikgPyAxIDogKG4gPT0gMykgPyAyIDogMyk7fSxcbiAgICAgICAgMTU6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4lMTA9PTEgJiYgbiUxMDAhPTExID8gMCA6IG4lMTA+PTIgJiYgKG4lMTAwPDEwIHx8IG4lMTAwPj0yMCkgPyAxIDogMik7fSxcbiAgICAgICAgMTY6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4lMTA9PTEgJiYgbiUxMDAhPTExID8gMCA6IG4gIT09IDAgPyAxIDogMik7fSxcbiAgICAgICAgMTc6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG49PTEgfHwgbiUxMD09MSA/IDAgOiAxKTt9LFxuICAgICAgICAxODogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIoMCA/IDAgOiBuPT0xID8gMSA6IDIpO30sXG4gICAgICAgIDE5OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuPT0xID8gMCA6IG49PT0wIHx8ICggbiUxMDA+MSAmJiBuJTEwMDwxMSkgPyAxIDogKG4lMTAwPjEwICYmIG4lMTAwPDIwICkgPyAyIDogMyk7fSxcbiAgICAgICAgMjA6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG49PTEgPyAwIDogKG49PT0wIHx8IChuJTEwMCA+IDAgJiYgbiUxMDAgPCAyMCkpID8gMSA6IDIpO30sXG4gICAgICAgIDIxOiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuJTEwMD09MSA/IDEgOiBuJTEwMD09MiA/IDIgOiBuJTEwMD09MyB8fCBuJTEwMD09NCA/IDMgOiAwKTsgfVxuICAgIH07XG4gICAgXG4gICAgdmFyIHBsdXJhbEV4dGVuc2lvbnMgPSB7XG4gICAgXG4gICAgICAgIHJ1bGVzOiAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGwsIHJ1bGVzID0ge307XG4gICAgICAgICAgICBmb3IgKGw9X3J1bGVzLmxlbmd0aDsgbC0tIDspIHtcbiAgICAgICAgICAgICAgICBydWxlc1tfcnVsZXNbbF1bMF1dID0ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiBfcnVsZXNbbF1bMV0sXG4gICAgICAgICAgICAgICAgICAgIG51bWJlcnM6IF9ydWxlc1tsXVsyXSxcbiAgICAgICAgICAgICAgICAgICAgcGx1cmFsczogX3J1bGVzUGx1cmFsc1R5cGVzW19ydWxlc1tsXVszXV1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gcnVsZXM7XG4gICAgICAgIH0oKSksXG4gICAgXG4gICAgICAgIC8vIHlvdSBjYW4gYWRkIHlvdXIgb3duIHBsdXJhbEV4dGVuc2lvbnNcbiAgICAgICAgYWRkUnVsZTogZnVuY3Rpb24obG5nLCBvYmopIHtcbiAgICAgICAgICAgIHBsdXJhbEV4dGVuc2lvbnMucnVsZXNbbG5nXSA9IG9iajtcbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgc2V0Q3VycmVudExuZzogZnVuY3Rpb24obG5nKSB7XG4gICAgICAgICAgICBpZiAoIXBsdXJhbEV4dGVuc2lvbnMuY3VycmVudFJ1bGUgfHwgcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZS5sbmcgIT09IGxuZykge1xuICAgICAgICAgICAgICAgIHZhciBwYXJ0cyA9IGxuZy5zcGxpdCgnLScpO1xuICAgIFxuICAgICAgICAgICAgICAgIHBsdXJhbEV4dGVuc2lvbnMuY3VycmVudFJ1bGUgPSB7XG4gICAgICAgICAgICAgICAgICAgIGxuZzogbG5nLFxuICAgICAgICAgICAgICAgICAgICBydWxlOiBwbHVyYWxFeHRlbnNpb25zLnJ1bGVzW3BhcnRzWzBdXVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIG5lZWRzUGx1cmFsOiBmdW5jdGlvbihsbmcsIGNvdW50KSB7XG4gICAgICAgICAgICB2YXIgcGFydHMgPSBsbmcuc3BsaXQoJy0nKTtcbiAgICBcbiAgICAgICAgICAgIHZhciBleHQ7XG4gICAgICAgICAgICBpZiAocGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZSAmJiBwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlLmxuZyA9PT0gbG5nKSB7XG4gICAgICAgICAgICAgICAgZXh0ID0gcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZS5ydWxlOyBcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZXh0ID0gcGx1cmFsRXh0ZW5zaW9ucy5ydWxlc1twYXJ0c1tmLmdldENvdW50eUluZGV4T2ZMbmcobG5nKV1dO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgaWYgKGV4dCAmJiBleHQubnVtYmVycy5sZW5ndGggPD0gMSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0KGxuZywgY291bnQpICE9PSAxO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgIFxuICAgICAgICBnZXQ6IGZ1bmN0aW9uKGxuZywgY291bnQpIHtcbiAgICAgICAgICAgIHZhciBwYXJ0cyA9IGxuZy5zcGxpdCgnLScpO1xuICAgIFxuICAgICAgICAgICAgZnVuY3Rpb24gZ2V0UmVzdWx0KGwsIGMpIHtcbiAgICAgICAgICAgICAgICB2YXIgZXh0O1xuICAgICAgICAgICAgICAgIGlmIChwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlICYmIHBsdXJhbEV4dGVuc2lvbnMuY3VycmVudFJ1bGUubG5nID09PSBsbmcpIHtcbiAgICAgICAgICAgICAgICAgICAgZXh0ID0gcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZS5ydWxlOyBcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBleHQgPSBwbHVyYWxFeHRlbnNpb25zLnJ1bGVzW2xdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoZXh0KSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZXh0Lm5vQWJzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpID0gZXh0LnBsdXJhbHMoYyk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpID0gZXh0LnBsdXJhbHMoTWF0aC5hYnMoYykpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICB2YXIgbnVtYmVyID0gZXh0Lm51bWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIGlmIChleHQubnVtYmVycy5sZW5ndGggPT09IDIgJiYgZXh0Lm51bWJlcnNbMF0gPT09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChudW1iZXIgPT09IDIpIHsgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gLTE7IC8vIHJlZ3VsYXIgcGx1cmFsXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG51bWJlciA9PT0gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDE7IC8vIHNpbmd1bGFyXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0vL2NvbnNvbGUubG9nKGNvdW50ICsgJy0nICsgbnVtYmVyKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG51bWJlcjtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gYyA9PT0gMSA/ICcxJyA6ICctMSc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICByZXR1cm4gZ2V0UmVzdWx0KHBhcnRzW2YuZ2V0Q291bnR5SW5kZXhPZkxuZyhsbmcpXSwgY291bnQpO1xuICAgICAgICB9XG4gICAgXG4gICAgfTtcbiAgICB2YXIgcG9zdFByb2Nlc3NvcnMgPSB7fTtcbiAgICB2YXIgYWRkUG9zdFByb2Nlc3NvciA9IGZ1bmN0aW9uKG5hbWUsIGZjKSB7XG4gICAgICAgIHBvc3RQcm9jZXNzb3JzW25hbWVdID0gZmM7XG4gICAgfTtcbiAgICAvLyBzcHJpbnRmIHN1cHBvcnRcbiAgICB2YXIgc3ByaW50ZiA9IChmdW5jdGlvbigpIHtcbiAgICAgICAgZnVuY3Rpb24gZ2V0X3R5cGUodmFyaWFibGUpIHtcbiAgICAgICAgICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodmFyaWFibGUpLnNsaWNlKDgsIC0xKS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICB9XG4gICAgICAgIGZ1bmN0aW9uIHN0cl9yZXBlYXQoaW5wdXQsIG11bHRpcGxpZXIpIHtcbiAgICAgICAgICAgIGZvciAodmFyIG91dHB1dCA9IFtdOyBtdWx0aXBsaWVyID4gMDsgb3V0cHV0Wy0tbXVsdGlwbGllcl0gPSBpbnB1dCkgey8qIGRvIG5vdGhpbmcgKi99XG4gICAgICAgICAgICByZXR1cm4gb3V0cHV0LmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBzdHJfZm9ybWF0ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIXN0cl9mb3JtYXQuY2FjaGUuaGFzT3duUHJvcGVydHkoYXJndW1lbnRzWzBdKSkge1xuICAgICAgICAgICAgICAgIHN0cl9mb3JtYXQuY2FjaGVbYXJndW1lbnRzWzBdXSA9IHN0cl9mb3JtYXQucGFyc2UoYXJndW1lbnRzWzBdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBzdHJfZm9ybWF0LmZvcm1hdC5jYWxsKG51bGwsIHN0cl9mb3JtYXQuY2FjaGVbYXJndW1lbnRzWzBdXSwgYXJndW1lbnRzKTtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgc3RyX2Zvcm1hdC5mb3JtYXQgPSBmdW5jdGlvbihwYXJzZV90cmVlLCBhcmd2KSB7XG4gICAgICAgICAgICB2YXIgY3Vyc29yID0gMSwgdHJlZV9sZW5ndGggPSBwYXJzZV90cmVlLmxlbmd0aCwgbm9kZV90eXBlID0gJycsIGFyZywgb3V0cHV0ID0gW10sIGksIGssIG1hdGNoLCBwYWQsIHBhZF9jaGFyYWN0ZXIsIHBhZF9sZW5ndGg7XG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdHJlZV9sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIG5vZGVfdHlwZSA9IGdldF90eXBlKHBhcnNlX3RyZWVbaV0pO1xuICAgICAgICAgICAgICAgIGlmIChub2RlX3R5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgICAgIG91dHB1dC5wdXNoKHBhcnNlX3RyZWVbaV0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChub2RlX3R5cGUgPT09ICdhcnJheScpIHtcbiAgICAgICAgICAgICAgICAgICAgbWF0Y2ggPSBwYXJzZV90cmVlW2ldOyAvLyBjb252ZW5pZW5jZSBwdXJwb3NlcyBvbmx5XG4gICAgICAgICAgICAgICAgICAgIGlmIChtYXRjaFsyXSkgeyAvLyBrZXl3b3JkIGFyZ3VtZW50XG4gICAgICAgICAgICAgICAgICAgICAgICBhcmcgPSBhcmd2W2N1cnNvcl07XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKGsgPSAwOyBrIDwgbWF0Y2hbMl0ubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWFyZy5oYXNPd25Qcm9wZXJ0eShtYXRjaFsyXVtrXSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3coc3ByaW50ZignW3NwcmludGZdIHByb3BlcnR5IFwiJXNcIiBkb2VzIG5vdCBleGlzdCcsIG1hdGNoWzJdW2tdKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyZyA9IGFyZ1ttYXRjaFsyXVtrXV07XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSBpZiAobWF0Y2hbMV0pIHsgLy8gcG9zaXRpb25hbCBhcmd1bWVudCAoZXhwbGljaXQpXG4gICAgICAgICAgICAgICAgICAgICAgICBhcmcgPSBhcmd2W21hdGNoWzFdXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlIHsgLy8gcG9zaXRpb25hbCBhcmd1bWVudCAoaW1wbGljaXQpXG4gICAgICAgICAgICAgICAgICAgICAgICBhcmcgPSBhcmd2W2N1cnNvcisrXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgICAgICBpZiAoL1tec10vLnRlc3QobWF0Y2hbOF0pICYmIChnZXRfdHlwZShhcmcpICE9ICdudW1iZXInKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3coc3ByaW50ZignW3NwcmludGZdIGV4cGVjdGluZyBudW1iZXIgYnV0IGZvdW5kICVzJywgZ2V0X3R5cGUoYXJnKSkpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHN3aXRjaCAobWF0Y2hbOF0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ2InOiBhcmcgPSBhcmcudG9TdHJpbmcoMik7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnYyc6IGFyZyA9IFN0cmluZy5mcm9tQ2hhckNvZGUoYXJnKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdkJzogYXJnID0gcGFyc2VJbnQoYXJnLCAxMCk7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnZSc6IGFyZyA9IG1hdGNoWzddID8gYXJnLnRvRXhwb25lbnRpYWwobWF0Y2hbN10pIDogYXJnLnRvRXhwb25lbnRpYWwoKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdmJzogYXJnID0gbWF0Y2hbN10gPyBwYXJzZUZsb2F0KGFyZykudG9GaXhlZChtYXRjaFs3XSkgOiBwYXJzZUZsb2F0KGFyZyk7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnbyc6IGFyZyA9IGFyZy50b1N0cmluZyg4KTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzJzogYXJnID0gKChhcmcgPSBTdHJpbmcoYXJnKSkgJiYgbWF0Y2hbN10gPyBhcmcuc3Vic3RyaW5nKDAsIG1hdGNoWzddKSA6IGFyZyk7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAndSc6IGFyZyA9IE1hdGguYWJzKGFyZyk7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAneCc6IGFyZyA9IGFyZy50b1N0cmluZygxNik7IGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnWCc6IGFyZyA9IGFyZy50b1N0cmluZygxNikudG9VcHBlckNhc2UoKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYXJnID0gKC9bZGVmXS8udGVzdChtYXRjaFs4XSkgJiYgbWF0Y2hbM10gJiYgYXJnID49IDAgPyAnKycrIGFyZyA6IGFyZyk7XG4gICAgICAgICAgICAgICAgICAgIHBhZF9jaGFyYWN0ZXIgPSBtYXRjaFs0XSA/IG1hdGNoWzRdID09ICcwJyA/ICcwJyA6IG1hdGNoWzRdLmNoYXJBdCgxKSA6ICcgJztcbiAgICAgICAgICAgICAgICAgICAgcGFkX2xlbmd0aCA9IG1hdGNoWzZdIC0gU3RyaW5nKGFyZykubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICBwYWQgPSBtYXRjaFs2XSA/IHN0cl9yZXBlYXQocGFkX2NoYXJhY3RlciwgcGFkX2xlbmd0aCkgOiAnJztcbiAgICAgICAgICAgICAgICAgICAgb3V0cHV0LnB1c2gobWF0Y2hbNV0gPyBhcmcgKyBwYWQgOiBwYWQgKyBhcmcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBvdXRwdXQuam9pbignJyk7XG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIHN0cl9mb3JtYXQuY2FjaGUgPSB7fTtcbiAgICBcbiAgICAgICAgc3RyX2Zvcm1hdC5wYXJzZSA9IGZ1bmN0aW9uKGZtdCkge1xuICAgICAgICAgICAgdmFyIF9mbXQgPSBmbXQsIG1hdGNoID0gW10sIHBhcnNlX3RyZWUgPSBbXSwgYXJnX25hbWVzID0gMDtcbiAgICAgICAgICAgIHdoaWxlIChfZm10KSB7XG4gICAgICAgICAgICAgICAgaWYgKChtYXRjaCA9IC9eW15cXHgyNV0rLy5leGVjKF9mbXQpKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBwYXJzZV90cmVlLnB1c2gobWF0Y2hbMF0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmICgobWF0Y2ggPSAvXlxceDI1ezJ9Ly5leGVjKF9mbXQpKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBwYXJzZV90cmVlLnB1c2goJyUnKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoKG1hdGNoID0gL15cXHgyNSg/OihbMS05XVxcZCopXFwkfFxcKChbXlxcKV0rKVxcKSk/KFxcKyk/KDB8J1teJF0pPygtKT8oXFxkKyk/KD86XFwuKFxcZCspKT8oW2ItZm9zdXhYXSkvLmV4ZWMoX2ZtdCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChtYXRjaFsyXSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYXJnX25hbWVzIHw9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZmllbGRfbGlzdCA9IFtdLCByZXBsYWNlbWVudF9maWVsZCA9IG1hdGNoWzJdLCBmaWVsZF9tYXRjaCA9IFtdO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKChmaWVsZF9tYXRjaCA9IC9eKFthLXpfXVthLXpfXFxkXSopL2kuZXhlYyhyZXBsYWNlbWVudF9maWVsZCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRfbGlzdC5wdXNoKGZpZWxkX21hdGNoWzFdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGlsZSAoKHJlcGxhY2VtZW50X2ZpZWxkID0gcmVwbGFjZW1lbnRfZmllbGQuc3Vic3RyaW5nKGZpZWxkX21hdGNoWzBdLmxlbmd0aCkpICE9PSAnJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoKGZpZWxkX21hdGNoID0gL15cXC4oW2Etel9dW2Etel9cXGRdKikvaS5leGVjKHJlcGxhY2VtZW50X2ZpZWxkKSkgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpZWxkX2xpc3QucHVzaChmaWVsZF9tYXRjaFsxXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBpZiAoKGZpZWxkX21hdGNoID0gL15cXFsoXFxkKylcXF0vLmV4ZWMocmVwbGFjZW1lbnRfZmllbGQpKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRfbGlzdC5wdXNoKGZpZWxkX21hdGNoWzFdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93KCdbc3ByaW50Zl0gaHVoPycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3coJ1tzcHJpbnRmXSBodWg/Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBtYXRjaFsyXSA9IGZpZWxkX2xpc3Q7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhcmdfbmFtZXMgfD0gMjtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoYXJnX25hbWVzID09PSAzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdygnW3NwcmludGZdIG1peGluZyBwb3NpdGlvbmFsIGFuZCBuYW1lZCBwbGFjZWhvbGRlcnMgaXMgbm90ICh5ZXQpIHN1cHBvcnRlZCcpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHBhcnNlX3RyZWUucHVzaChtYXRjaCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdygnW3NwcmludGZdIGh1aD8nKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgX2ZtdCA9IF9mbXQuc3Vic3RyaW5nKG1hdGNoWzBdLmxlbmd0aCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VfdHJlZTtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgcmV0dXJuIHN0cl9mb3JtYXQ7XG4gICAgfSkoKTtcbiAgICBcbiAgICB2YXIgdnNwcmludGYgPSBmdW5jdGlvbihmbXQsIGFyZ3YpIHtcbiAgICAgICAgYXJndi51bnNoaWZ0KGZtdCk7XG4gICAgICAgIHJldHVybiBzcHJpbnRmLmFwcGx5KG51bGwsIGFyZ3YpO1xuICAgIH07XG4gICAgXG4gICAgYWRkUG9zdFByb2Nlc3NvcihcInNwcmludGZcIiwgZnVuY3Rpb24odmFsLCBrZXksIG9wdHMpIHtcbiAgICAgICAgaWYgKCFvcHRzLnNwcmludGYpIHJldHVybiB2YWw7XG4gICAgXG4gICAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmFwcGx5KG9wdHMuc3ByaW50ZikgPT09ICdbb2JqZWN0IEFycmF5XScpIHtcbiAgICAgICAgICAgIHJldHVybiB2c3ByaW50Zih2YWwsIG9wdHMuc3ByaW50Zik7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIG9wdHMuc3ByaW50ZiA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybiBzcHJpbnRmKHZhbCwgb3B0cy5zcHJpbnRmKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH0pO1xuICAgIC8vIHB1YmxpYyBhcGkgaW50ZXJmYWNlXG4gICAgaTE4bi5pbml0ID0gaW5pdDtcbiAgICBpMThuLnNldExuZyA9IHNldExuZztcbiAgICBpMThuLnByZWxvYWQgPSBwcmVsb2FkO1xuICAgIGkxOG4uYWRkUmVzb3VyY2VCdW5kbGUgPSBhZGRSZXNvdXJjZUJ1bmRsZTtcbiAgICBpMThuLmhhc1Jlc291cmNlQnVuZGxlID0gaGFzUmVzb3VyY2VCdW5kbGU7XG4gICAgaTE4bi5hZGRSZXNvdXJjZSA9IGFkZFJlc291cmNlO1xuICAgIGkxOG4uYWRkUmVzb3VyY2VzID0gYWRkUmVzb3VyY2VzO1xuICAgIGkxOG4ucmVtb3ZlUmVzb3VyY2VCdW5kbGUgPSByZW1vdmVSZXNvdXJjZUJ1bmRsZTtcbiAgICBpMThuLmxvYWROYW1lc3BhY2UgPSBsb2FkTmFtZXNwYWNlO1xuICAgIGkxOG4ubG9hZE5hbWVzcGFjZXMgPSBsb2FkTmFtZXNwYWNlcztcbiAgICBpMThuLnNldERlZmF1bHROYW1lc3BhY2UgPSBzZXREZWZhdWx0TmFtZXNwYWNlO1xuICAgIGkxOG4udCA9IHRyYW5zbGF0ZTtcbiAgICBpMThuLnRyYW5zbGF0ZSA9IHRyYW5zbGF0ZTtcbiAgICBpMThuLmV4aXN0cyA9IGV4aXN0cztcbiAgICBpMThuLmRldGVjdExhbmd1YWdlID0gZi5kZXRlY3RMYW5ndWFnZTtcbiAgICBpMThuLnBsdXJhbEV4dGVuc2lvbnMgPSBwbHVyYWxFeHRlbnNpb25zO1xuICAgIGkxOG4uc3luYyA9IHN5bmM7XG4gICAgaTE4bi5mdW5jdGlvbnMgPSBmO1xuICAgIGkxOG4ubG5nID0gbG5nO1xuICAgIGkxOG4uYWRkUG9zdFByb2Nlc3NvciA9IGFkZFBvc3RQcm9jZXNzb3I7XG4gICAgaTE4bi5vcHRpb25zID0gbztcblxufSkoKTsiLCIvLyBUb3AgbGV2ZWwgZmlsZSBpcyBqdXN0IGEgbWl4aW4gb2Ygc3VibW9kdWxlcyAmIGNvbnN0YW50c1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgYXNzaWduICAgID0gcmVxdWlyZSgnLi9saWIvdXRpbHMvY29tbW9uJykuYXNzaWduO1xuXG52YXIgZGVmbGF0ZSAgID0gcmVxdWlyZSgnLi9saWIvZGVmbGF0ZScpO1xudmFyIGluZmxhdGUgICA9IHJlcXVpcmUoJy4vbGliL2luZmxhdGUnKTtcbnZhciBjb25zdGFudHMgPSByZXF1aXJlKCcuL2xpYi96bGliL2NvbnN0YW50cycpO1xuXG52YXIgcGFrbyA9IHt9O1xuXG5hc3NpZ24ocGFrbywgZGVmbGF0ZSwgaW5mbGF0ZSwgY29uc3RhbnRzKTtcblxubW9kdWxlLmV4cG9ydHMgPSBwYWtvOyIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgemxpYl9kZWZsYXRlID0gcmVxdWlyZSgnLi96bGliL2RlZmxhdGUuanMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMvY29tbW9uJyk7XG52YXIgc3RyaW5ncyA9IHJlcXVpcmUoJy4vdXRpbHMvc3RyaW5ncycpO1xudmFyIG1zZyA9IHJlcXVpcmUoJy4vemxpYi9tZXNzYWdlcycpO1xudmFyIHpzdHJlYW0gPSByZXF1aXJlKCcuL3psaWIvenN0cmVhbScpO1xuXG52YXIgdG9TdHJpbmcgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO1xuXG4vKiBQdWJsaWMgY29uc3RhbnRzID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxudmFyIFpfTk9fRkxVU0ggICAgICA9IDA7XG52YXIgWl9GSU5JU0ggICAgICAgID0gNDtcblxudmFyIFpfT0sgICAgICAgICAgICA9IDA7XG52YXIgWl9TVFJFQU1fRU5EICAgID0gMTtcblxudmFyIFpfREVGQVVMVF9DT01QUkVTU0lPTiA9IC0xO1xuXG52YXIgWl9ERUZBVUxUX1NUUkFURUdZICAgID0gMDtcblxudmFyIFpfREVGTEFURUQgID0gODtcblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuXG4vKipcbiAqIGNsYXNzIERlZmxhdGVcbiAqXG4gKiBHZW5lcmljIEpTLXN0eWxlIHdyYXBwZXIgZm9yIHpsaWIgY2FsbHMuIElmIHlvdSBkb24ndCBuZWVkXG4gKiBzdHJlYW1pbmcgYmVoYXZpb3VyIC0gdXNlIG1vcmUgc2ltcGxlIGZ1bmN0aW9uczogW1tkZWZsYXRlXV0sXG4gKiBbW2RlZmxhdGVSYXddXSBhbmQgW1tnemlwXV0uXG4gKiovXG5cbi8qIGludGVybmFsXG4gKiBEZWZsYXRlLmNodW5rcyAtPiBBcnJheVxuICpcbiAqIENodW5rcyBvZiBvdXRwdXQgZGF0YSwgaWYgW1tEZWZsYXRlI29uRGF0YV1dIG5vdCBvdmVycmlkZW4uXG4gKiovXG5cbi8qKlxuICogRGVmbGF0ZS5yZXN1bHQgLT4gVWludDhBcnJheXxBcnJheVxuICpcbiAqIENvbXByZXNzZWQgcmVzdWx0LCBnZW5lcmF0ZWQgYnkgZGVmYXVsdCBbW0RlZmxhdGUjb25EYXRhXV1cbiAqIGFuZCBbW0RlZmxhdGUjb25FbmRdXSBoYW5kbGVycy4gRmlsbGVkIGFmdGVyIHlvdSBwdXNoIGxhc3QgY2h1bmtcbiAqIChjYWxsIFtbRGVmbGF0ZSNwdXNoXV0gd2l0aCBgWl9GSU5JU0hgIC8gYHRydWVgIHBhcmFtKS5cbiAqKi9cblxuLyoqXG4gKiBEZWZsYXRlLmVyciAtPiBOdW1iZXJcbiAqXG4gKiBFcnJvciBjb2RlIGFmdGVyIGRlZmxhdGUgZmluaXNoZWQuIDAgKFpfT0spIG9uIHN1Y2Nlc3MuXG4gKiBZb3Ugd2lsbCBub3QgbmVlZCBpdCBpbiByZWFsIGxpZmUsIGJlY2F1c2UgZGVmbGF0ZSBlcnJvcnNcbiAqIGFyZSBwb3NzaWJsZSBvbmx5IG9uIHdyb25nIG9wdGlvbnMgb3IgYmFkIGBvbkRhdGFgIC8gYG9uRW5kYFxuICogY3VzdG9tIGhhbmRsZXJzLlxuICoqL1xuXG4vKipcbiAqIERlZmxhdGUubXNnIC0+IFN0cmluZ1xuICpcbiAqIEVycm9yIG1lc3NhZ2UsIGlmIFtbRGVmbGF0ZS5lcnJdXSAhPSAwXG4gKiovXG5cblxuLyoqXG4gKiBuZXcgRGVmbGF0ZShvcHRpb25zKVxuICogLSBvcHRpb25zIChPYmplY3QpOiB6bGliIGRlZmxhdGUgb3B0aW9ucy5cbiAqXG4gKiBDcmVhdGVzIG5ldyBkZWZsYXRvciBpbnN0YW5jZSB3aXRoIHNwZWNpZmllZCBwYXJhbXMuIFRocm93cyBleGNlcHRpb25cbiAqIG9uIGJhZCBwYXJhbXMuIFN1cHBvcnRlZCBvcHRpb25zOlxuICpcbiAqIC0gYGxldmVsYFxuICogLSBgd2luZG93Qml0c2BcbiAqIC0gYG1lbUxldmVsYFxuICogLSBgc3RyYXRlZ3lgXG4gKlxuICogW2h0dHA6Ly96bGliLm5ldC9tYW51YWwuaHRtbCNBZHZhbmNlZF0oaHR0cDovL3psaWIubmV0L21hbnVhbC5odG1sI0FkdmFuY2VkKVxuICogZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhlc2UuXG4gKlxuICogQWRkaXRpb25hbCBvcHRpb25zLCBmb3IgaW50ZXJuYWwgbmVlZHM6XG4gKlxuICogLSBgY2h1bmtTaXplYCAtIHNpemUgb2YgZ2VuZXJhdGVkIGRhdGEgY2h1bmtzICgxNksgYnkgZGVmYXVsdClcbiAqIC0gYHJhd2AgKEJvb2xlYW4pIC0gZG8gcmF3IGRlZmxhdGVcbiAqIC0gYGd6aXBgIChCb29sZWFuKSAtIGNyZWF0ZSBnemlwIHdyYXBwZXJcbiAqIC0gYHRvYCAoU3RyaW5nKSAtIGlmIGVxdWFsIHRvICdzdHJpbmcnLCB0aGVuIHJlc3VsdCB3aWxsIGJlIFwiYmluYXJ5IHN0cmluZ1wiXG4gKiAgICAoZWFjaCBjaGFyIGNvZGUgWzAuLjI1NV0pXG4gKiAtIGBoZWFkZXJgIChPYmplY3QpIC0gY3VzdG9tIGhlYWRlciBmb3IgZ3ppcFxuICogICAtIGB0ZXh0YCAoQm9vbGVhbikgLSB0cnVlIGlmIGNvbXByZXNzZWQgZGF0YSBiZWxpZXZlZCB0byBiZSB0ZXh0XG4gKiAgIC0gYHRpbWVgIChOdW1iZXIpIC0gbW9kaWZpY2F0aW9uIHRpbWUsIHVuaXggdGltZXN0YW1wXG4gKiAgIC0gYG9zYCAoTnVtYmVyKSAtIG9wZXJhdGlvbiBzeXN0ZW0gY29kZVxuICogICAtIGBleHRyYWAgKEFycmF5KSAtIGFycmF5IG9mIGJ5dGVzIHdpdGggZXh0cmEgZGF0YSAobWF4IDY1NTM2KVxuICogICAtIGBuYW1lYCAoU3RyaW5nKSAtIGZpbGUgbmFtZSAoYmluYXJ5IHN0cmluZylcbiAqICAgLSBgY29tbWVudGAgKFN0cmluZykgLSBjb21tZW50IChiaW5hcnkgc3RyaW5nKVxuICogICAtIGBoY3JjYCAoQm9vbGVhbikgLSB0cnVlIGlmIGhlYWRlciBjcmMgc2hvdWxkIGJlIGFkZGVkXG4gKlxuICogIyMjIyMgRXhhbXBsZTpcbiAqXG4gKiBgYGBqYXZhc2NyaXB0XG4gKiB2YXIgcGFrbyA9IHJlcXVpcmUoJ3Bha28nKVxuICogICAsIGNodW5rMSA9IFVpbnQ4QXJyYXkoWzEsMiwzLDQsNSw2LDcsOCw5XSlcbiAqICAgLCBjaHVuazIgPSBVaW50OEFycmF5KFsxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwxOV0pO1xuICpcbiAqIHZhciBkZWZsYXRlID0gbmV3IHBha28uRGVmbGF0ZSh7IGxldmVsOiAzfSk7XG4gKlxuICogZGVmbGF0ZS5wdXNoKGNodW5rMSwgZmFsc2UpO1xuICogZGVmbGF0ZS5wdXNoKGNodW5rMiwgdHJ1ZSk7ICAvLyB0cnVlIC0+IGxhc3QgY2h1bmtcbiAqXG4gKiBpZiAoZGVmbGF0ZS5lcnIpIHsgdGhyb3cgbmV3IEVycm9yKGRlZmxhdGUuZXJyKTsgfVxuICpcbiAqIGNvbnNvbGUubG9nKGRlZmxhdGUucmVzdWx0KTtcbiAqIGBgYFxuICoqL1xudmFyIERlZmxhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG5cbiAgdGhpcy5vcHRpb25zID0gdXRpbHMuYXNzaWduKHtcbiAgICBsZXZlbDogWl9ERUZBVUxUX0NPTVBSRVNTSU9OLFxuICAgIG1ldGhvZDogWl9ERUZMQVRFRCxcbiAgICBjaHVua1NpemU6IDE2Mzg0LFxuICAgIHdpbmRvd0JpdHM6IDE1LFxuICAgIG1lbUxldmVsOiA4LFxuICAgIHN0cmF0ZWd5OiBaX0RFRkFVTFRfU1RSQVRFR1ksXG4gICAgdG86ICcnXG4gIH0sIG9wdGlvbnMgfHwge30pO1xuXG4gIHZhciBvcHQgPSB0aGlzLm9wdGlvbnM7XG5cbiAgaWYgKG9wdC5yYXcgJiYgKG9wdC53aW5kb3dCaXRzID4gMCkpIHtcbiAgICBvcHQud2luZG93Qml0cyA9IC1vcHQud2luZG93Qml0cztcbiAgfVxuXG4gIGVsc2UgaWYgKG9wdC5nemlwICYmIChvcHQud2luZG93Qml0cyA+IDApICYmIChvcHQud2luZG93Qml0cyA8IDE2KSkge1xuICAgIG9wdC53aW5kb3dCaXRzICs9IDE2O1xuICB9XG5cbiAgdGhpcy5lcnIgICAgPSAwOyAgICAgIC8vIGVycm9yIGNvZGUsIGlmIGhhcHBlbnMgKDAgPSBaX09LKVxuICB0aGlzLm1zZyAgICA9ICcnOyAgICAgLy8gZXJyb3IgbWVzc2FnZVxuICB0aGlzLmVuZGVkICA9IGZhbHNlOyAgLy8gdXNlZCB0byBhdm9pZCBtdWx0aXBsZSBvbkVuZCgpIGNhbGxzXG4gIHRoaXMuY2h1bmtzID0gW107ICAgICAvLyBjaHVua3Mgb2YgY29tcHJlc3NlZCBkYXRhXG5cbiAgdGhpcy5zdHJtID0gbmV3IHpzdHJlYW0oKTtcbiAgdGhpcy5zdHJtLmF2YWlsX291dCA9IDA7XG5cbiAgdmFyIHN0YXR1cyA9IHpsaWJfZGVmbGF0ZS5kZWZsYXRlSW5pdDIoXG4gICAgdGhpcy5zdHJtLFxuICAgIG9wdC5sZXZlbCxcbiAgICBvcHQubWV0aG9kLFxuICAgIG9wdC53aW5kb3dCaXRzLFxuICAgIG9wdC5tZW1MZXZlbCxcbiAgICBvcHQuc3RyYXRlZ3lcbiAgKTtcblxuICBpZiAoc3RhdHVzICE9PSBaX09LKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1zZ1tzdGF0dXNdKTtcbiAgfVxuXG4gIGlmIChvcHQuaGVhZGVyKSB7XG4gICAgemxpYl9kZWZsYXRlLmRlZmxhdGVTZXRIZWFkZXIodGhpcy5zdHJtLCBvcHQuaGVhZGVyKTtcbiAgfVxufTtcblxuLyoqXG4gKiBEZWZsYXRlI3B1c2goZGF0YVssIG1vZGVdKSAtPiBCb29sZWFuXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8QXJyYXlCdWZmZXJ8U3RyaW5nKTogaW5wdXQgZGF0YS4gU3RyaW5ncyB3aWxsIGJlXG4gKiAgIGNvbnZlcnRlZCB0byB1dGY4IGJ5dGUgc2VxdWVuY2UuXG4gKiAtIG1vZGUgKE51bWJlcnxCb29sZWFuKTogMC4uNiBmb3IgY29ycmVzcG9uZGluZyBaX05PX0ZMVVNILi5aX1RSRUUgbW9kZXMuXG4gKiAgIFNlZSBjb25zdGFudHMuIFNraXBwZWQgb3IgYGZhbHNlYCBtZWFucyBaX05PX0ZMVVNILCBgdHJ1ZWAgbWVhbnNoIFpfRklOSVNILlxuICpcbiAqIFNlbmRzIGlucHV0IGRhdGEgdG8gZGVmbGF0ZSBwaXBlLCBnZW5lcmF0aW5nIFtbRGVmbGF0ZSNvbkRhdGFdXSBjYWxscyB3aXRoXG4gKiBuZXcgY29tcHJlc3NlZCBjaHVua3MuIFJldHVybnMgYHRydWVgIG9uIHN1Y2Nlc3MuIFRoZSBsYXN0IGRhdGEgYmxvY2sgbXVzdCBoYXZlXG4gKiBtb2RlIFpfRklOSVNIIChvciBgdHJ1ZWApLiBUaGF0IGZsdXNoIGludGVybmFsIHBlbmRpbmcgYnVmZmVycyBhbmQgY2FsbFxuICogW1tEZWZsYXRlI29uRW5kXV0uXG4gKlxuICogT24gZmFpbCBjYWxsIFtbRGVmbGF0ZSNvbkVuZF1dIHdpdGggZXJyb3IgY29kZSBhbmQgcmV0dXJuIGZhbHNlLlxuICpcbiAqIFdlIHN0cm9uZ2x5IHJlY29tbWVuZCB0byB1c2UgYFVpbnQ4QXJyYXlgIG9uIGlucHV0IGZvciBiZXN0IHNwZWVkIChvdXRwdXRcbiAqIGFycmF5IGZvcm1hdCBpcyBkZXRlY3RlZCBhdXRvbWF0aWNhbGx5KS4gQWxzbywgZG9uJ3Qgc2tpcCBsYXN0IHBhcmFtIGFuZCBhbHdheXNcbiAqIHVzZSB0aGUgc2FtZSB0eXBlIGluIHlvdXIgY29kZSAoYm9vbGVhbiBvciBudW1iZXIpLiBUaGF0IHdpbGwgaW1wcm92ZSBKUyBzcGVlZC5cbiAqXG4gKiBGb3IgcmVndWxhciBgQXJyYXlgLXMgbWFrZSBzdXJlIGFsbCBlbGVtZW50cyBhcmUgWzAuLjI1NV0uXG4gKlxuICogIyMjIyMgRXhhbXBsZVxuICpcbiAqIGBgYGphdmFzY3JpcHRcbiAqIHB1c2goY2h1bmssIGZhbHNlKTsgLy8gcHVzaCBvbmUgb2YgZGF0YSBjaHVua3NcbiAqIC4uLlxuICogcHVzaChjaHVuaywgdHJ1ZSk7ICAvLyBwdXNoIGxhc3QgY2h1bmtcbiAqIGBgYFxuICoqL1xuRGVmbGF0ZS5wcm90b3R5cGUucHVzaCA9IGZ1bmN0aW9uKGRhdGEsIG1vZGUpIHtcbiAgdmFyIHN0cm0gPSB0aGlzLnN0cm07XG4gIHZhciBjaHVua1NpemUgPSB0aGlzLm9wdGlvbnMuY2h1bmtTaXplO1xuICB2YXIgc3RhdHVzLCBfbW9kZTtcblxuICBpZiAodGhpcy5lbmRlZCkgeyByZXR1cm4gZmFsc2U7IH1cblxuICBfbW9kZSA9IChtb2RlID09PSB+fm1vZGUpID8gbW9kZSA6ICgobW9kZSA9PT0gdHJ1ZSkgPyBaX0ZJTklTSCA6IFpfTk9fRkxVU0gpO1xuXG4gIC8vIENvbnZlcnQgZGF0YSBpZiBuZWVkZWRcbiAgaWYgKHR5cGVvZiBkYXRhID09PSAnc3RyaW5nJykge1xuICAgIC8vIElmIHdlIG5lZWQgdG8gY29tcHJlc3MgdGV4dCwgY2hhbmdlIGVuY29kaW5nIHRvIHV0ZjguXG4gICAgc3RybS5pbnB1dCA9IHN0cmluZ3Muc3RyaW5nMmJ1ZihkYXRhKTtcbiAgfSBlbHNlIGlmICh0b1N0cmluZy5jYWxsKGRhdGEpID09PSAnW29iamVjdCBBcnJheUJ1ZmZlcl0nKSB7XG4gICAgc3RybS5pbnB1dCA9IG5ldyBVaW50OEFycmF5KGRhdGEpO1xuICB9IGVsc2Uge1xuICAgIHN0cm0uaW5wdXQgPSBkYXRhO1xuICB9XG5cbiAgc3RybS5uZXh0X2luID0gMDtcbiAgc3RybS5hdmFpbF9pbiA9IHN0cm0uaW5wdXQubGVuZ3RoO1xuXG4gIGRvIHtcbiAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHN0cm0ub3V0cHV0ID0gbmV3IHV0aWxzLkJ1ZjgoY2h1bmtTaXplKTtcbiAgICAgIHN0cm0ubmV4dF9vdXQgPSAwO1xuICAgICAgc3RybS5hdmFpbF9vdXQgPSBjaHVua1NpemU7XG4gICAgfVxuICAgIHN0YXR1cyA9IHpsaWJfZGVmbGF0ZS5kZWZsYXRlKHN0cm0sIF9tb2RlKTsgICAgLyogbm8gYmFkIHJldHVybiB2YWx1ZSAqL1xuXG4gICAgaWYgKHN0YXR1cyAhPT0gWl9TVFJFQU1fRU5EICYmIHN0YXR1cyAhPT0gWl9PSykge1xuICAgICAgdGhpcy5vbkVuZChzdGF0dXMpO1xuICAgICAgdGhpcy5lbmRlZCA9IHRydWU7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCB8fCAoc3RybS5hdmFpbF9pbiA9PT0gMCAmJiBfbW9kZSA9PT0gWl9GSU5JU0gpKSB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLnRvID09PSAnc3RyaW5nJykge1xuICAgICAgICB0aGlzLm9uRGF0YShzdHJpbmdzLmJ1ZjJiaW5zdHJpbmcodXRpbHMuc2hyaW5rQnVmKHN0cm0ub3V0cHV0LCBzdHJtLm5leHRfb3V0KSkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5vbkRhdGEodXRpbHMuc2hyaW5rQnVmKHN0cm0ub3V0cHV0LCBzdHJtLm5leHRfb3V0KSk7XG4gICAgICB9XG4gICAgfVxuICB9IHdoaWxlICgoc3RybS5hdmFpbF9pbiA+IDAgfHwgc3RybS5hdmFpbF9vdXQgPT09IDApICYmIHN0YXR1cyAhPT0gWl9TVFJFQU1fRU5EKTtcblxuICAvLyBGaW5hbGl6ZSBvbiB0aGUgbGFzdCBjaHVuay5cbiAgaWYgKF9tb2RlID09PSBaX0ZJTklTSCkge1xuICAgIHN0YXR1cyA9IHpsaWJfZGVmbGF0ZS5kZWZsYXRlRW5kKHRoaXMuc3RybSk7XG4gICAgdGhpcy5vbkVuZChzdGF0dXMpO1xuICAgIHRoaXMuZW5kZWQgPSB0cnVlO1xuICAgIHJldHVybiBzdGF0dXMgPT09IFpfT0s7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cblxuLyoqXG4gKiBEZWZsYXRlI29uRGF0YShjaHVuaykgLT4gVm9pZFxuICogLSBjaHVuayAoVWludDhBcnJheXxBcnJheXxTdHJpbmcpOiBvdXB1dCBkYXRhLiBUeXBlIG9mIGFycmF5IGRlcGVuZHNcbiAqICAgb24ganMgZW5naW5lIHN1cHBvcnQuIFdoZW4gc3RyaW5nIG91dHB1dCByZXF1ZXN0ZWQsIGVhY2ggY2h1bmtcbiAqICAgd2lsbCBiZSBzdHJpbmcuXG4gKlxuICogQnkgZGVmYXVsdCwgc3RvcmVzIGRhdGEgYmxvY2tzIGluIGBjaHVua3NbXWAgcHJvcGVydHkgYW5kIGdsdWVcbiAqIHRob3NlIGluIGBvbkVuZGAuIE92ZXJyaWRlIHRoaXMgaGFuZGxlciwgaWYgeW91IG5lZWQgYW5vdGhlciBiZWhhdmlvdXIuXG4gKiovXG5EZWZsYXRlLnByb3RvdHlwZS5vbkRhdGEgPSBmdW5jdGlvbihjaHVuaykge1xuICB0aGlzLmNodW5rcy5wdXNoKGNodW5rKTtcbn07XG5cblxuLyoqXG4gKiBEZWZsYXRlI29uRW5kKHN0YXR1cykgLT4gVm9pZFxuICogLSBzdGF0dXMgKE51bWJlcik6IGRlZmxhdGUgc3RhdHVzLiAwIChaX09LKSBvbiBzdWNjZXNzLFxuICogICBvdGhlciBpZiBub3QuXG4gKlxuICogQ2FsbGVkIG9uY2UgYWZ0ZXIgeW91IHRlbGwgZGVmbGF0ZSB0aGF0IGlucHV0IHN0cmVhbSBjb21wbGV0ZVxuICogb3IgZXJyb3IgaGFwcGVubmVkLiBCeSBkZWZhdWx0IC0gam9pbiBjb2xsZWN0ZWQgY2h1bmtzLFxuICogZnJlZSBtZW1vcnkgYW5kIGZpbGwgYHJlc3VsdHNgIC8gYGVycmAgcHJvcGVydGllcy5cbiAqKi9cbkRlZmxhdGUucHJvdG90eXBlLm9uRW5kID0gZnVuY3Rpb24oc3RhdHVzKSB7XG4gIC8vIE9uIHN1Y2Nlc3MgLSBqb2luXG4gIGlmIChzdGF0dXMgPT09IFpfT0spIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLnRvID09PSAnc3RyaW5nJykge1xuICAgICAgdGhpcy5yZXN1bHQgPSB0aGlzLmNodW5rcy5qb2luKCcnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZXN1bHQgPSB1dGlscy5mbGF0dGVuQ2h1bmtzKHRoaXMuY2h1bmtzKTtcbiAgICB9XG4gIH1cbiAgdGhpcy5jaHVua3MgPSBbXTtcbiAgdGhpcy5lcnIgPSBzdGF0dXM7XG4gIHRoaXMubXNnID0gdGhpcy5zdHJtLm1zZztcbn07XG5cblxuLyoqXG4gKiBkZWZsYXRlKGRhdGFbLCBvcHRpb25zXSkgLT4gVWludDhBcnJheXxBcnJheXxTdHJpbmdcbiAqIC0gZGF0YSAoVWludDhBcnJheXxBcnJheXxTdHJpbmcpOiBpbnB1dCBkYXRhIHRvIGNvbXByZXNzLlxuICogLSBvcHRpb25zIChPYmplY3QpOiB6bGliIGRlZmxhdGUgb3B0aW9ucy5cbiAqXG4gKiBDb21wcmVzcyBgZGF0YWAgd2l0aCBkZWZsYXRlIGFscm9yeXRobSBhbmQgYG9wdGlvbnNgLlxuICpcbiAqIFN1cHBvcnRlZCBvcHRpb25zIGFyZTpcbiAqXG4gKiAtIGxldmVsXG4gKiAtIHdpbmRvd0JpdHNcbiAqIC0gbWVtTGV2ZWxcbiAqIC0gc3RyYXRlZ3lcbiAqXG4gKiBbaHR0cDovL3psaWIubmV0L21hbnVhbC5odG1sI0FkdmFuY2VkXShodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWQpXG4gKiBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGVzZS5cbiAqXG4gKiBTdWdhciAob3B0aW9ucyk6XG4gKlxuICogLSBgcmF3YCAoQm9vbGVhbikgLSBzYXkgdGhhdCB3ZSB3b3JrIHdpdGggcmF3IHN0cmVhbSwgaWYgeW91IGRvbid0IHdpc2ggdG8gc3BlY2lmeVxuICogICBuZWdhdGl2ZSB3aW5kb3dCaXRzIGltcGxpY2l0bHkuXG4gKiAtIGB0b2AgKFN0cmluZykgLSBpZiBlcXVhbCB0byAnc3RyaW5nJywgdGhlbiByZXN1bHQgd2lsbCBiZSBcImJpbmFyeSBzdHJpbmdcIlxuICogICAgKGVhY2ggY2hhciBjb2RlIFswLi4yNTVdKVxuICpcbiAqICMjIyMjIEV4YW1wbGU6XG4gKlxuICogYGBgamF2YXNjcmlwdFxuICogdmFyIHBha28gPSByZXF1aXJlKCdwYWtvJylcbiAqICAgLCBkYXRhID0gVWludDhBcnJheShbMSwyLDMsNCw1LDYsNyw4LDldKTtcbiAqXG4gKiBjb25zb2xlLmxvZyhwYWtvLmRlZmxhdGUoZGF0YSkpO1xuICogYGBgXG4gKiovXG5mdW5jdGlvbiBkZWZsYXRlKGlucHV0LCBvcHRpb25zKSB7XG4gIHZhciBkZWZsYXRvciA9IG5ldyBEZWZsYXRlKG9wdGlvbnMpO1xuXG4gIGRlZmxhdG9yLnB1c2goaW5wdXQsIHRydWUpO1xuXG4gIC8vIFRoYXQgd2lsbCBuZXZlciBoYXBwZW5zLCBpZiB5b3UgZG9uJ3QgY2hlYXQgd2l0aCBvcHRpb25zIDopXG4gIGlmIChkZWZsYXRvci5lcnIpIHsgdGhyb3cgZGVmbGF0b3IubXNnOyB9XG5cbiAgcmV0dXJuIGRlZmxhdG9yLnJlc3VsdDtcbn1cblxuXG4vKipcbiAqIGRlZmxhdGVSYXcoZGF0YVssIG9wdGlvbnNdKSAtPiBVaW50OEFycmF5fEFycmF5fFN0cmluZ1xuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEgdG8gY29tcHJlc3MuXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgZGVmbGF0ZSBvcHRpb25zLlxuICpcbiAqIFRoZSBzYW1lIGFzIFtbZGVmbGF0ZV1dLCBidXQgY3JlYXRlcyByYXcgZGF0YSwgd2l0aG91dCB3cmFwcGVyXG4gKiAoaGVhZGVyIGFuZCBhZGxlcjMyIGNyYykuXG4gKiovXG5mdW5jdGlvbiBkZWZsYXRlUmF3KGlucHV0LCBvcHRpb25zKSB7XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICBvcHRpb25zLnJhdyA9IHRydWU7XG4gIHJldHVybiBkZWZsYXRlKGlucHV0LCBvcHRpb25zKTtcbn1cblxuXG4vKipcbiAqIGd6aXAoZGF0YVssIG9wdGlvbnNdKSAtPiBVaW50OEFycmF5fEFycmF5fFN0cmluZ1xuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEgdG8gY29tcHJlc3MuXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgZGVmbGF0ZSBvcHRpb25zLlxuICpcbiAqIFRoZSBzYW1lIGFzIFtbZGVmbGF0ZV1dLCBidXQgY3JlYXRlIGd6aXAgd3JhcHBlciBpbnN0ZWFkIG9mXG4gKiBkZWZsYXRlIG9uZS5cbiAqKi9cbmZ1bmN0aW9uIGd6aXAoaW5wdXQsIG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gIG9wdGlvbnMuZ3ppcCA9IHRydWU7XG4gIHJldHVybiBkZWZsYXRlKGlucHV0LCBvcHRpb25zKTtcbn1cblxuXG5leHBvcnRzLkRlZmxhdGUgPSBEZWZsYXRlO1xuZXhwb3J0cy5kZWZsYXRlID0gZGVmbGF0ZTtcbmV4cG9ydHMuZGVmbGF0ZVJhdyA9IGRlZmxhdGVSYXc7XG5leHBvcnRzLmd6aXAgPSBnemlwOyIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgemxpYl9pbmZsYXRlID0gcmVxdWlyZSgnLi96bGliL2luZmxhdGUuanMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMvY29tbW9uJyk7XG52YXIgc3RyaW5ncyA9IHJlcXVpcmUoJy4vdXRpbHMvc3RyaW5ncycpO1xudmFyIGMgPSByZXF1aXJlKCcuL3psaWIvY29uc3RhbnRzJyk7XG52YXIgbXNnID0gcmVxdWlyZSgnLi96bGliL21lc3NhZ2VzJyk7XG52YXIgenN0cmVhbSA9IHJlcXVpcmUoJy4vemxpYi96c3RyZWFtJyk7XG52YXIgZ3poZWFkZXIgPSByZXF1aXJlKCcuL3psaWIvZ3poZWFkZXInKTtcblxudmFyIHRvU3RyaW5nID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztcblxuLyoqXG4gKiBjbGFzcyBJbmZsYXRlXG4gKlxuICogR2VuZXJpYyBKUy1zdHlsZSB3cmFwcGVyIGZvciB6bGliIGNhbGxzLiBJZiB5b3UgZG9uJ3QgbmVlZFxuICogc3RyZWFtaW5nIGJlaGF2aW91ciAtIHVzZSBtb3JlIHNpbXBsZSBmdW5jdGlvbnM6IFtbaW5mbGF0ZV1dXG4gKiBhbmQgW1tpbmZsYXRlUmF3XV0uXG4gKiovXG5cbi8qIGludGVybmFsXG4gKiBpbmZsYXRlLmNodW5rcyAtPiBBcnJheVxuICpcbiAqIENodW5rcyBvZiBvdXRwdXQgZGF0YSwgaWYgW1tJbmZsYXRlI29uRGF0YV1dIG5vdCBvdmVycmlkZW4uXG4gKiovXG5cbi8qKlxuICogSW5mbGF0ZS5yZXN1bHQgLT4gVWludDhBcnJheXxBcnJheXxTdHJpbmdcbiAqXG4gKiBVbmNvbXByZXNzZWQgcmVzdWx0LCBnZW5lcmF0ZWQgYnkgZGVmYXVsdCBbW0luZmxhdGUjb25EYXRhXV1cbiAqIGFuZCBbW0luZmxhdGUjb25FbmRdXSBoYW5kbGVycy4gRmlsbGVkIGFmdGVyIHlvdSBwdXNoIGxhc3QgY2h1bmtcbiAqIChjYWxsIFtbSW5mbGF0ZSNwdXNoXV0gd2l0aCBgWl9GSU5JU0hgIC8gYHRydWVgIHBhcmFtKS5cbiAqKi9cblxuLyoqXG4gKiBJbmZsYXRlLmVyciAtPiBOdW1iZXJcbiAqXG4gKiBFcnJvciBjb2RlIGFmdGVyIGluZmxhdGUgZmluaXNoZWQuIDAgKFpfT0spIG9uIHN1Y2Nlc3MuXG4gKiBTaG91bGQgYmUgY2hlY2tlZCBpZiBicm9rZW4gZGF0YSBwb3NzaWJsZS5cbiAqKi9cblxuLyoqXG4gKiBJbmZsYXRlLm1zZyAtPiBTdHJpbmdcbiAqXG4gKiBFcnJvciBtZXNzYWdlLCBpZiBbW0luZmxhdGUuZXJyXV0gIT0gMFxuICoqL1xuXG5cbi8qKlxuICogbmV3IEluZmxhdGUob3B0aW9ucylcbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBpbmZsYXRlIG9wdGlvbnMuXG4gKlxuICogQ3JlYXRlcyBuZXcgaW5mbGF0b3IgaW5zdGFuY2Ugd2l0aCBzcGVjaWZpZWQgcGFyYW1zLiBUaHJvd3MgZXhjZXB0aW9uXG4gKiBvbiBiYWQgcGFyYW1zLiBTdXBwb3J0ZWQgb3B0aW9uczpcbiAqXG4gKiAtIGB3aW5kb3dCaXRzYFxuICpcbiAqIFtodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWRdKGh0dHA6Ly96bGliLm5ldC9tYW51YWwuaHRtbCNBZHZhbmNlZClcbiAqIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZXNlLlxuICpcbiAqIEFkZGl0aW9uYWwgb3B0aW9ucywgZm9yIGludGVybmFsIG5lZWRzOlxuICpcbiAqIC0gYGNodW5rU2l6ZWAgLSBzaXplIG9mIGdlbmVyYXRlZCBkYXRhIGNodW5rcyAoMTZLIGJ5IGRlZmF1bHQpXG4gKiAtIGByYXdgIChCb29sZWFuKSAtIGRvIHJhdyBpbmZsYXRlXG4gKiAtIGB0b2AgKFN0cmluZykgLSBpZiBlcXVhbCB0byAnc3RyaW5nJywgdGhlbiByZXN1bHQgd2lsbCBiZSBjb252ZXJ0ZWRcbiAqICAgZnJvbSB1dGY4IHRvIHV0ZjE2IChqYXZhc2NyaXB0KSBzdHJpbmcuIFdoZW4gc3RyaW5nIG91dHB1dCByZXF1ZXN0ZWQsXG4gKiAgIGNodW5rIGxlbmd0aCBjYW4gZGlmZmVyIGZyb20gYGNodW5rU2l6ZWAsIGRlcGVuZGluZyBvbiBjb250ZW50LlxuICpcbiAqIEJ5IGRlZmF1bHQsIHdoZW4gbm8gb3B0aW9ucyBzZXQsIGF1dG9kZXRlY3QgZGVmbGF0ZS9nemlwIGRhdGEgZm9ybWF0IHZpYVxuICogd3JhcHBlciBoZWFkZXIuXG4gKlxuICogIyMjIyMgRXhhbXBsZTpcbiAqXG4gKiBgYGBqYXZhc2NyaXB0XG4gKiB2YXIgcGFrbyA9IHJlcXVpcmUoJ3Bha28nKVxuICogICAsIGNodW5rMSA9IFVpbnQ4QXJyYXkoWzEsMiwzLDQsNSw2LDcsOCw5XSlcbiAqICAgLCBjaHVuazIgPSBVaW50OEFycmF5KFsxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwxOV0pO1xuICpcbiAqIHZhciBpbmZsYXRlID0gbmV3IHBha28uSW5mbGF0ZSh7IGxldmVsOiAzfSk7XG4gKlxuICogaW5mbGF0ZS5wdXNoKGNodW5rMSwgZmFsc2UpO1xuICogaW5mbGF0ZS5wdXNoKGNodW5rMiwgdHJ1ZSk7ICAvLyB0cnVlIC0+IGxhc3QgY2h1bmtcbiAqXG4gKiBpZiAoaW5mbGF0ZS5lcnIpIHsgdGhyb3cgbmV3IEVycm9yKGluZmxhdGUuZXJyKTsgfVxuICpcbiAqIGNvbnNvbGUubG9nKGluZmxhdGUucmVzdWx0KTtcbiAqIGBgYFxuICoqL1xudmFyIEluZmxhdGUgPSBmdW5jdGlvbihvcHRpb25zKSB7XG5cbiAgdGhpcy5vcHRpb25zID0gdXRpbHMuYXNzaWduKHtcbiAgICBjaHVua1NpemU6IDE2Mzg0LFxuICAgIHdpbmRvd0JpdHM6IDAsXG4gICAgdG86ICcnXG4gIH0sIG9wdGlvbnMgfHwge30pO1xuXG4gIHZhciBvcHQgPSB0aGlzLm9wdGlvbnM7XG5cbiAgLy8gRm9yY2Ugd2luZG93IHNpemUgZm9yIGByYXdgIGRhdGEsIGlmIG5vdCBzZXQgZGlyZWN0bHksXG4gIC8vIGJlY2F1c2Ugd2UgaGF2ZSBubyBoZWFkZXIgZm9yIGF1dG9kZXRlY3QuXG4gIGlmIChvcHQucmF3ICYmIChvcHQud2luZG93Qml0cyA+PSAwKSAmJiAob3B0LndpbmRvd0JpdHMgPCAxNikpIHtcbiAgICBvcHQud2luZG93Qml0cyA9IC1vcHQud2luZG93Qml0cztcbiAgICBpZiAob3B0LndpbmRvd0JpdHMgPT09IDApIHsgb3B0LndpbmRvd0JpdHMgPSAtMTU7IH1cbiAgfVxuXG4gIC8vIElmIGB3aW5kb3dCaXRzYCBub3QgZGVmaW5lZCAoYW5kIG1vZGUgbm90IHJhdykgLSBzZXQgYXV0b2RldGVjdCBmbGFnIGZvciBnemlwL2RlZmxhdGVcbiAgaWYgKChvcHQud2luZG93Qml0cyA+PSAwKSAmJiAob3B0LndpbmRvd0JpdHMgPCAxNikgJiZcbiAgICAgICEob3B0aW9ucyAmJiBvcHRpb25zLndpbmRvd0JpdHMpKSB7XG4gICAgb3B0LndpbmRvd0JpdHMgKz0gMzI7XG4gIH1cblxuICAvLyBHemlwIGhlYWRlciBoYXMgbm8gaW5mbyBhYm91dCB3aW5kb3dzIHNpemUsIHdlIGNhbiBkbyBhdXRvZGV0ZWN0IG9ubHlcbiAgLy8gZm9yIGRlZmxhdGUuIFNvLCBpZiB3aW5kb3cgc2l6ZSBub3Qgc2V0LCBmb3JjZSBpdCB0byBtYXggd2hlbiBnemlwIHBvc3NpYmxlXG4gIGlmICgob3B0LndpbmRvd0JpdHMgPiAxNSkgJiYgKG9wdC53aW5kb3dCaXRzIDwgNDgpKSB7XG4gICAgLy8gYml0IDMgKDE2KSAtPiBnemlwcGVkIGRhdGFcbiAgICAvLyBiaXQgNCAoMzIpIC0+IGF1dG9kZXRlY3QgZ3ppcC9kZWZsYXRlXG4gICAgaWYgKChvcHQud2luZG93Qml0cyAmIDE1KSA9PT0gMCkge1xuICAgICAgb3B0LndpbmRvd0JpdHMgfD0gMTU7XG4gICAgfVxuICB9XG5cbiAgdGhpcy5lcnIgICAgPSAwOyAgICAgIC8vIGVycm9yIGNvZGUsIGlmIGhhcHBlbnMgKDAgPSBaX09LKVxuICB0aGlzLm1zZyAgICA9ICcnOyAgICAgLy8gZXJyb3IgbWVzc2FnZVxuICB0aGlzLmVuZGVkICA9IGZhbHNlOyAgLy8gdXNlZCB0byBhdm9pZCBtdWx0aXBsZSBvbkVuZCgpIGNhbGxzXG4gIHRoaXMuY2h1bmtzID0gW107ICAgICAvLyBjaHVua3Mgb2YgY29tcHJlc3NlZCBkYXRhXG5cbiAgdGhpcy5zdHJtICAgPSBuZXcgenN0cmVhbSgpO1xuICB0aGlzLnN0cm0uYXZhaWxfb3V0ID0gMDtcblxuICB2YXIgc3RhdHVzICA9IHpsaWJfaW5mbGF0ZS5pbmZsYXRlSW5pdDIoXG4gICAgdGhpcy5zdHJtLFxuICAgIG9wdC53aW5kb3dCaXRzXG4gICk7XG5cbiAgaWYgKHN0YXR1cyAhPT0gYy5aX09LKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1zZ1tzdGF0dXNdKTtcbiAgfVxuXG4gIHRoaXMuaGVhZGVyID0gbmV3IGd6aGVhZGVyKCk7XG5cbiAgemxpYl9pbmZsYXRlLmluZmxhdGVHZXRIZWFkZXIodGhpcy5zdHJtLCB0aGlzLmhlYWRlcik7XG59O1xuXG4vKipcbiAqIEluZmxhdGUjcHVzaChkYXRhWywgbW9kZV0pIC0+IEJvb2xlYW5cbiAqIC0gZGF0YSAoVWludDhBcnJheXxBcnJheXxBcnJheUJ1ZmZlcnxTdHJpbmcpOiBpbnB1dCBkYXRhXG4gKiAtIG1vZGUgKE51bWJlcnxCb29sZWFuKTogMC4uNiBmb3IgY29ycmVzcG9uZGluZyBaX05PX0ZMVVNILi5aX1RSRUUgbW9kZXMuXG4gKiAgIFNlZSBjb25zdGFudHMuIFNraXBwZWQgb3IgYGZhbHNlYCBtZWFucyBaX05PX0ZMVVNILCBgdHJ1ZWAgbWVhbnNoIFpfRklOSVNILlxuICpcbiAqIFNlbmRzIGlucHV0IGRhdGEgdG8gaW5mbGF0ZSBwaXBlLCBnZW5lcmF0aW5nIFtbSW5mbGF0ZSNvbkRhdGFdXSBjYWxscyB3aXRoXG4gKiBuZXcgb3V0cHV0IGNodW5rcy4gUmV0dXJucyBgdHJ1ZWAgb24gc3VjY2Vzcy4gVGhlIGxhc3QgZGF0YSBibG9jayBtdXN0IGhhdmVcbiAqIG1vZGUgWl9GSU5JU0ggKG9yIGB0cnVlYCkuIFRoYXQgZmx1c2ggaW50ZXJuYWwgcGVuZGluZyBidWZmZXJzIGFuZCBjYWxsXG4gKiBbW0luZmxhdGUjb25FbmRdXS5cbiAqXG4gKiBPbiBmYWlsIGNhbGwgW1tJbmZsYXRlI29uRW5kXV0gd2l0aCBlcnJvciBjb2RlIGFuZCByZXR1cm4gZmFsc2UuXG4gKlxuICogV2Ugc3Ryb25nbHkgcmVjb21tZW5kIHRvIHVzZSBgVWludDhBcnJheWAgb24gaW5wdXQgZm9yIGJlc3Qgc3BlZWQgKG91dHB1dFxuICogZm9ybWF0IGlzIGRldGVjdGVkIGF1dG9tYXRpY2FsbHkpLiBBbHNvLCBkb24ndCBza2lwIGxhc3QgcGFyYW0gYW5kIGFsd2F5c1xuICogdXNlIHRoZSBzYW1lIHR5cGUgaW4geW91ciBjb2RlIChib29sZWFuIG9yIG51bWJlcikuIFRoYXQgd2lsbCBpbXByb3ZlIEpTIHNwZWVkLlxuICpcbiAqIEZvciByZWd1bGFyIGBBcnJheWAtcyBtYWtlIHN1cmUgYWxsIGVsZW1lbnRzIGFyZSBbMC4uMjU1XS5cbiAqXG4gKiAjIyMjIyBFeGFtcGxlXG4gKlxuICogYGBgamF2YXNjcmlwdFxuICogcHVzaChjaHVuaywgZmFsc2UpOyAvLyBwdXNoIG9uZSBvZiBkYXRhIGNodW5rc1xuICogLi4uXG4gKiBwdXNoKGNodW5rLCB0cnVlKTsgIC8vIHB1c2ggbGFzdCBjaHVua1xuICogYGBgXG4gKiovXG5JbmZsYXRlLnByb3RvdHlwZS5wdXNoID0gZnVuY3Rpb24oZGF0YSwgbW9kZSkge1xuICB2YXIgc3RybSA9IHRoaXMuc3RybTtcbiAgdmFyIGNodW5rU2l6ZSA9IHRoaXMub3B0aW9ucy5jaHVua1NpemU7XG4gIHZhciBzdGF0dXMsIF9tb2RlO1xuICB2YXIgbmV4dF9vdXRfdXRmOCwgdGFpbCwgdXRmOHN0cjtcblxuICBpZiAodGhpcy5lbmRlZCkgeyByZXR1cm4gZmFsc2U7IH1cbiAgX21vZGUgPSAobW9kZSA9PT0gfn5tb2RlKSA/IG1vZGUgOiAoKG1vZGUgPT09IHRydWUpID8gYy5aX0ZJTklTSCA6IGMuWl9OT19GTFVTSCk7XG5cbiAgLy8gQ29udmVydCBkYXRhIGlmIG5lZWRlZFxuICBpZiAodHlwZW9mIGRhdGEgPT09ICdzdHJpbmcnKSB7XG4gICAgLy8gT25seSBiaW5hcnkgc3RyaW5ncyBjYW4gYmUgZGVjb21wcmVzc2VkIG9uIHByYWN0aWNlXG4gICAgc3RybS5pbnB1dCA9IHN0cmluZ3MuYmluc3RyaW5nMmJ1ZihkYXRhKTtcbiAgfSBlbHNlIGlmICh0b1N0cmluZy5jYWxsKGRhdGEpID09PSAnW29iamVjdCBBcnJheUJ1ZmZlcl0nKSB7XG4gICAgc3RybS5pbnB1dCA9IG5ldyBVaW50OEFycmF5KGRhdGEpO1xuICB9IGVsc2Uge1xuICAgIHN0cm0uaW5wdXQgPSBkYXRhO1xuICB9XG5cbiAgc3RybS5uZXh0X2luID0gMDtcbiAgc3RybS5hdmFpbF9pbiA9IHN0cm0uaW5wdXQubGVuZ3RoO1xuXG4gIGRvIHtcbiAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHN0cm0ub3V0cHV0ID0gbmV3IHV0aWxzLkJ1ZjgoY2h1bmtTaXplKTtcbiAgICAgIHN0cm0ubmV4dF9vdXQgPSAwO1xuICAgICAgc3RybS5hdmFpbF9vdXQgPSBjaHVua1NpemU7XG4gICAgfVxuXG4gICAgc3RhdHVzID0gemxpYl9pbmZsYXRlLmluZmxhdGUoc3RybSwgYy5aX05PX0ZMVVNIKTsgICAgLyogbm8gYmFkIHJldHVybiB2YWx1ZSAqL1xuXG4gICAgaWYgKHN0YXR1cyAhPT0gYy5aX1NUUkVBTV9FTkQgJiYgc3RhdHVzICE9PSBjLlpfT0spIHtcbiAgICAgIHRoaXMub25FbmQoc3RhdHVzKTtcbiAgICAgIHRoaXMuZW5kZWQgPSB0cnVlO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGlmIChzdHJtLm5leHRfb3V0KSB7XG4gICAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDAgfHwgc3RhdHVzID09PSBjLlpfU1RSRUFNX0VORCB8fCAoc3RybS5hdmFpbF9pbiA9PT0gMCAmJiBfbW9kZSA9PT0gYy5aX0ZJTklTSCkpIHtcblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLnRvID09PSAnc3RyaW5nJykge1xuXG4gICAgICAgICAgbmV4dF9vdXRfdXRmOCA9IHN0cmluZ3MudXRmOGJvcmRlcihzdHJtLm91dHB1dCwgc3RybS5uZXh0X291dCk7XG5cbiAgICAgICAgICB0YWlsID0gc3RybS5uZXh0X291dCAtIG5leHRfb3V0X3V0Zjg7XG4gICAgICAgICAgdXRmOHN0ciA9IHN0cmluZ3MuYnVmMnN0cmluZyhzdHJtLm91dHB1dCwgbmV4dF9vdXRfdXRmOCk7XG5cbiAgICAgICAgICAvLyBtb3ZlIHRhaWxcbiAgICAgICAgICBzdHJtLm5leHRfb3V0ID0gdGFpbDtcbiAgICAgICAgICBzdHJtLmF2YWlsX291dCA9IGNodW5rU2l6ZSAtIHRhaWw7XG4gICAgICAgICAgaWYgKHRhaWwpIHsgdXRpbHMuYXJyYXlTZXQoc3RybS5vdXRwdXQsIHN0cm0ub3V0cHV0LCBuZXh0X291dF91dGY4LCB0YWlsLCAwKTsgfVxuXG4gICAgICAgICAgdGhpcy5vbkRhdGEodXRmOHN0cik7XG5cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLm9uRGF0YSh1dGlscy5zaHJpbmtCdWYoc3RybS5vdXRwdXQsIHN0cm0ubmV4dF9vdXQpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSB3aGlsZSAoKHN0cm0uYXZhaWxfaW4gPiAwKSAmJiBzdGF0dXMgIT09IGMuWl9TVFJFQU1fRU5EKTtcblxuICBpZiAoc3RhdHVzID09PSBjLlpfU1RSRUFNX0VORCkge1xuICAgIF9tb2RlID0gYy5aX0ZJTklTSDtcbiAgfVxuICAvLyBGaW5hbGl6ZSBvbiB0aGUgbGFzdCBjaHVuay5cbiAgaWYgKF9tb2RlID09PSBjLlpfRklOSVNIKSB7XG4gICAgc3RhdHVzID0gemxpYl9pbmZsYXRlLmluZmxhdGVFbmQodGhpcy5zdHJtKTtcbiAgICB0aGlzLm9uRW5kKHN0YXR1cyk7XG4gICAgdGhpcy5lbmRlZCA9IHRydWU7XG4gICAgcmV0dXJuIHN0YXR1cyA9PT0gYy5aX09LO1xuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59O1xuXG5cbi8qKlxuICogSW5mbGF0ZSNvbkRhdGEoY2h1bmspIC0+IFZvaWRcbiAqIC0gY2h1bmsgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogb3VwdXQgZGF0YS4gVHlwZSBvZiBhcnJheSBkZXBlbmRzXG4gKiAgIG9uIGpzIGVuZ2luZSBzdXBwb3J0LiBXaGVuIHN0cmluZyBvdXRwdXQgcmVxdWVzdGVkLCBlYWNoIGNodW5rXG4gKiAgIHdpbGwgYmUgc3RyaW5nLlxuICpcbiAqIEJ5IGRlZmF1bHQsIHN0b3JlcyBkYXRhIGJsb2NrcyBpbiBgY2h1bmtzW11gIHByb3BlcnR5IGFuZCBnbHVlXG4gKiB0aG9zZSBpbiBgb25FbmRgLiBPdmVycmlkZSB0aGlzIGhhbmRsZXIsIGlmIHlvdSBuZWVkIGFub3RoZXIgYmVoYXZpb3VyLlxuICoqL1xuSW5mbGF0ZS5wcm90b3R5cGUub25EYXRhID0gZnVuY3Rpb24oY2h1bmspIHtcbiAgdGhpcy5jaHVua3MucHVzaChjaHVuayk7XG59O1xuXG5cbi8qKlxuICogSW5mbGF0ZSNvbkVuZChzdGF0dXMpIC0+IFZvaWRcbiAqIC0gc3RhdHVzIChOdW1iZXIpOiBpbmZsYXRlIHN0YXR1cy4gMCAoWl9PSykgb24gc3VjY2VzcyxcbiAqICAgb3RoZXIgaWYgbm90LlxuICpcbiAqIENhbGxlZCBvbmNlIGFmdGVyIHlvdSB0ZWxsIGluZmxhdGUgdGhhdCBpbnB1dCBzdHJlYW0gY29tcGxldGVcbiAqIG9yIGVycm9yIGhhcHBlbm5lZC4gQnkgZGVmYXVsdCAtIGpvaW4gY29sbGVjdGVkIGNodW5rcyxcbiAqIGZyZWUgbWVtb3J5IGFuZCBmaWxsIGByZXN1bHRzYCAvIGBlcnJgIHByb3BlcnRpZXMuXG4gKiovXG5JbmZsYXRlLnByb3RvdHlwZS5vbkVuZCA9IGZ1bmN0aW9uKHN0YXR1cykge1xuICAvLyBPbiBzdWNjZXNzIC0gam9pblxuICBpZiAoc3RhdHVzID09PSBjLlpfT0spIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLnRvID09PSAnc3RyaW5nJykge1xuICAgICAgLy8gR2x1ZSAmIGNvbnZlcnQgaGVyZSwgdW50aWwgd2UgdGVhY2ggcGFrbyB0byBzZW5kXG4gICAgICAvLyB1dGY4IGFsbGlnbmVkIHN0cmluZ3MgdG8gb25EYXRhXG4gICAgICB0aGlzLnJlc3VsdCA9IHRoaXMuY2h1bmtzLmpvaW4oJycpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlc3VsdCA9IHV0aWxzLmZsYXR0ZW5DaHVua3ModGhpcy5jaHVua3MpO1xuICAgIH1cbiAgfVxuICB0aGlzLmNodW5rcyA9IFtdO1xuICB0aGlzLmVyciA9IHN0YXR1cztcbiAgdGhpcy5tc2cgPSB0aGlzLnN0cm0ubXNnO1xufTtcblxuXG4vKipcbiAqIGluZmxhdGUoZGF0YVssIG9wdGlvbnNdKSAtPiBVaW50OEFycmF5fEFycmF5fFN0cmluZ1xuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEgdG8gZGVjb21wcmVzcy5cbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBpbmZsYXRlIG9wdGlvbnMuXG4gKlxuICogRGVjb21wcmVzcyBgZGF0YWAgd2l0aCBpbmZsYXRlL3VuZ3ppcCBhbmQgYG9wdGlvbnNgLiBBdXRvZGV0ZWN0XG4gKiBmb3JtYXQgdmlhIHdyYXBwZXIgaGVhZGVyIGJ5IGRlZmF1bHQuIFRoYXQncyB3aHkgd2UgZG9uJ3QgcHJvdmlkZVxuICogc2VwYXJhdGUgYHVuZ3ppcGAgbWV0aG9kLlxuICpcbiAqIFN1cHBvcnRlZCBvcHRpb25zIGFyZTpcbiAqXG4gKiAtIHdpbmRvd0JpdHNcbiAqXG4gKiBbaHR0cDovL3psaWIubmV0L21hbnVhbC5odG1sI0FkdmFuY2VkXShodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWQpXG4gKiBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAqXG4gKiBTdWdhciAob3B0aW9ucyk6XG4gKlxuICogLSBgcmF3YCAoQm9vbGVhbikgLSBzYXkgdGhhdCB3ZSB3b3JrIHdpdGggcmF3IHN0cmVhbSwgaWYgeW91IGRvbid0IHdpc2ggdG8gc3BlY2lmeVxuICogICBuZWdhdGl2ZSB3aW5kb3dCaXRzIGltcGxpY2l0bHkuXG4gKiAtIGB0b2AgKFN0cmluZykgLSBpZiBlcXVhbCB0byAnc3RyaW5nJywgdGhlbiByZXN1bHQgd2lsbCBiZSBjb252ZXJ0ZWRcbiAqICAgZnJvbSB1dGY4IHRvIHV0ZjE2IChqYXZhc2NyaXB0KSBzdHJpbmcuIFdoZW4gc3RyaW5nIG91dHB1dCByZXF1ZXN0ZWQsXG4gKiAgIGNodW5rIGxlbmd0aCBjYW4gZGlmZmVyIGZyb20gYGNodW5rU2l6ZWAsIGRlcGVuZGluZyBvbiBjb250ZW50LlxuICpcbiAqXG4gKiAjIyMjIyBFeGFtcGxlOlxuICpcbiAqIGBgYGphdmFzY3JpcHRcbiAqIHZhciBwYWtvID0gcmVxdWlyZSgncGFrbycpXG4gKiAgICwgaW5wdXQgPSBwYWtvLmRlZmxhdGUoWzEsMiwzLDQsNSw2LDcsOCw5XSlcbiAqICAgLCBvdXRwdXQ7XG4gKlxuICogdHJ5IHtcbiAqICAgb3V0cHV0ID0gcGFrby5pbmZsYXRlKGlucHV0KTtcbiAqIH0gY2F0Y2ggKGVycilcbiAqICAgY29uc29sZS5sb2coZXJyKTtcbiAqIH1cbiAqIGBgYFxuICoqL1xuZnVuY3Rpb24gaW5mbGF0ZShpbnB1dCwgb3B0aW9ucykge1xuICB2YXIgaW5mbGF0b3IgPSBuZXcgSW5mbGF0ZShvcHRpb25zKTtcblxuICBpbmZsYXRvci5wdXNoKGlucHV0LCB0cnVlKTtcblxuICAvLyBUaGF0IHdpbGwgbmV2ZXIgaGFwcGVucywgaWYgeW91IGRvbid0IGNoZWF0IHdpdGggb3B0aW9ucyA6KVxuICBpZiAoaW5mbGF0b3IuZXJyKSB7IHRocm93IGluZmxhdG9yLm1zZzsgfVxuXG4gIHJldHVybiBpbmZsYXRvci5yZXN1bHQ7XG59XG5cblxuLyoqXG4gKiBpbmZsYXRlUmF3KGRhdGFbLCBvcHRpb25zXSkgLT4gVWludDhBcnJheXxBcnJheXxTdHJpbmdcbiAqIC0gZGF0YSAoVWludDhBcnJheXxBcnJheXxTdHJpbmcpOiBpbnB1dCBkYXRhIHRvIGRlY29tcHJlc3MuXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgaW5mbGF0ZSBvcHRpb25zLlxuICpcbiAqIFRoZSBzYW1lIGFzIFtbaW5mbGF0ZV1dLCBidXQgY3JlYXRlcyByYXcgZGF0YSwgd2l0aG91dCB3cmFwcGVyXG4gKiAoaGVhZGVyIGFuZCBhZGxlcjMyIGNyYykuXG4gKiovXG5mdW5jdGlvbiBpbmZsYXRlUmF3KGlucHV0LCBvcHRpb25zKSB7XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICBvcHRpb25zLnJhdyA9IHRydWU7XG4gIHJldHVybiBpbmZsYXRlKGlucHV0LCBvcHRpb25zKTtcbn1cblxuXG4vKipcbiAqIHVuZ3ppcChkYXRhWywgb3B0aW9uc10pIC0+IFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogaW5wdXQgZGF0YSB0byBkZWNvbXByZXNzLlxuICogLSBvcHRpb25zIChPYmplY3QpOiB6bGliIGluZmxhdGUgb3B0aW9ucy5cbiAqXG4gKiBKdXN0IHNob3J0Y3V0IHRvIFtbaW5mbGF0ZV1dLCBiZWNhdXNlIGl0IGF1dG9kZXRlY3RzIGZvcm1hdFxuICogYnkgaGVhZGVyLmNvbnRlbnQuIERvbmUgZm9yIGNvbnZlbmllbmNlLlxuICoqL1xuXG5cbmV4cG9ydHMuSW5mbGF0ZSA9IEluZmxhdGU7XG5leHBvcnRzLmluZmxhdGUgPSBpbmZsYXRlO1xuZXhwb3J0cy5pbmZsYXRlUmF3ID0gaW5mbGF0ZVJhdztcbmV4cG9ydHMudW5nemlwICA9IGluZmxhdGU7XG4iLCIndXNlIHN0cmljdCc7XG5cblxudmFyIFRZUEVEX09LID0gICh0eXBlb2YgVWludDhBcnJheSAhPT0gJ3VuZGVmaW5lZCcpICYmXG4gICAgICAgICAgICAgICAgKHR5cGVvZiBVaW50MTZBcnJheSAhPT0gJ3VuZGVmaW5lZCcpICYmXG4gICAgICAgICAgICAgICAgKHR5cGVvZiBJbnQzMkFycmF5ICE9PSAndW5kZWZpbmVkJyk7XG5cblxuZXhwb3J0cy5hc3NpZ24gPSBmdW5jdGlvbiAob2JqIC8qZnJvbTEsIGZyb20yLCBmcm9tMywgLi4uKi8pIHtcbiAgdmFyIHNvdXJjZXMgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xuICB3aGlsZSAoc291cmNlcy5sZW5ndGgpIHtcbiAgICB2YXIgc291cmNlID0gc291cmNlcy5zaGlmdCgpO1xuICAgIGlmICghc291cmNlKSB7IGNvbnRpbnVlOyB9XG5cbiAgICBpZiAodHlwZW9mKHNvdXJjZSkgIT09ICdvYmplY3QnKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKHNvdXJjZSArICdtdXN0IGJlIG5vbi1vYmplY3QnKTtcbiAgICB9XG5cbiAgICBmb3IgKHZhciBwIGluIHNvdXJjZSkge1xuICAgICAgaWYgKHNvdXJjZS5oYXNPd25Qcm9wZXJ0eShwKSkge1xuICAgICAgICBvYmpbcF0gPSBzb3VyY2VbcF07XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIG9iajtcbn07XG5cblxuLy8gcmVkdWNlIGJ1ZmZlciBzaXplLCBhdm9pZGluZyBtZW0gY29weVxuZXhwb3J0cy5zaHJpbmtCdWYgPSBmdW5jdGlvbiAoYnVmLCBzaXplKSB7XG4gIGlmIChidWYubGVuZ3RoID09PSBzaXplKSB7IHJldHVybiBidWY7IH1cbiAgaWYgKGJ1Zi5zdWJhcnJheSkgeyByZXR1cm4gYnVmLnN1YmFycmF5KDAsIHNpemUpOyB9XG4gIGJ1Zi5sZW5ndGggPSBzaXplO1xuICByZXR1cm4gYnVmO1xufTtcblxuXG52YXIgZm5UeXBlZCA9IHtcbiAgYXJyYXlTZXQ6IGZ1bmN0aW9uIChkZXN0LCBzcmMsIHNyY19vZmZzLCBsZW4sIGRlc3Rfb2Zmcykge1xuICAgIGlmIChzcmMuc3ViYXJyYXkgJiYgZGVzdC5zdWJhcnJheSkge1xuICAgICAgZGVzdC5zZXQoc3JjLnN1YmFycmF5KHNyY19vZmZzLCBzcmNfb2ZmcytsZW4pLCBkZXN0X29mZnMpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBGYWxsYmFjayB0byBvcmRpbmFyeSBhcnJheVxuICAgIGZvcih2YXIgaT0wOyBpPGxlbjsgaSsrKSB7XG4gICAgICBkZXN0W2Rlc3Rfb2ZmcyArIGldID0gc3JjW3NyY19vZmZzICsgaV07XG4gICAgfVxuICB9LFxuICAvLyBKb2luIGFycmF5IG9mIGNodW5rcyB0byBzaW5nbGUgYXJyYXkuXG4gIGZsYXR0ZW5DaHVua3M6IGZ1bmN0aW9uKGNodW5rcykge1xuICAgIHZhciBpLCBsLCBsZW4sIHBvcywgY2h1bmssIHJlc3VsdDtcblxuICAgIC8vIGNhbGN1bGF0ZSBkYXRhIGxlbmd0aFxuICAgIGxlbiA9IDA7XG4gICAgZm9yIChpPTAsIGw9Y2h1bmtzLmxlbmd0aDsgaTxsOyBpKyspIHtcbiAgICAgIGxlbiArPSBjaHVua3NbaV0ubGVuZ3RoO1xuICAgIH1cblxuICAgIC8vIGpvaW4gY2h1bmtzXG4gICAgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkobGVuKTtcbiAgICBwb3MgPSAwO1xuICAgIGZvciAoaT0wLCBsPWNodW5rcy5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICBjaHVuayA9IGNodW5rc1tpXTtcbiAgICAgIHJlc3VsdC5zZXQoY2h1bmssIHBvcyk7XG4gICAgICBwb3MgKz0gY2h1bmsubGVuZ3RoO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn07XG5cbnZhciBmblVudHlwZWQgPSB7XG4gIGFycmF5U2V0OiBmdW5jdGlvbiAoZGVzdCwgc3JjLCBzcmNfb2ZmcywgbGVuLCBkZXN0X29mZnMpIHtcbiAgICBmb3IodmFyIGk9MDsgaTxsZW47IGkrKykge1xuICAgICAgZGVzdFtkZXN0X29mZnMgKyBpXSA9IHNyY1tzcmNfb2ZmcyArIGldO1xuICAgIH1cbiAgfSxcbiAgLy8gSm9pbiBhcnJheSBvZiBjaHVua3MgdG8gc2luZ2xlIGFycmF5LlxuICBmbGF0dGVuQ2h1bmtzOiBmdW5jdGlvbihjaHVua3MpIHtcbiAgICByZXR1cm4gW10uY29uY2F0LmFwcGx5KFtdLCBjaHVua3MpO1xuICB9XG59O1xuXG5cbi8vIEVuYWJsZS9EaXNhYmxlIHR5cGVkIGFycmF5cyB1c2UsIGZvciB0ZXN0aW5nXG4vL1xuZXhwb3J0cy5zZXRUeXBlZCA9IGZ1bmN0aW9uIChvbikge1xuICBpZiAob24pIHtcbiAgICBleHBvcnRzLkJ1ZjggID0gVWludDhBcnJheTtcbiAgICBleHBvcnRzLkJ1ZjE2ID0gVWludDE2QXJyYXk7XG4gICAgZXhwb3J0cy5CdWYzMiA9IEludDMyQXJyYXk7XG4gICAgZXhwb3J0cy5hc3NpZ24oZXhwb3J0cywgZm5UeXBlZCk7XG4gIH0gZWxzZSB7XG4gICAgZXhwb3J0cy5CdWY4ICA9IEFycmF5O1xuICAgIGV4cG9ydHMuQnVmMTYgPSBBcnJheTtcbiAgICBleHBvcnRzLkJ1ZjMyID0gQXJyYXk7XG4gICAgZXhwb3J0cy5hc3NpZ24oZXhwb3J0cywgZm5VbnR5cGVkKTtcbiAgfVxufTtcblxuZXhwb3J0cy5zZXRUeXBlZChUWVBFRF9PSyk7IiwiLy8gU3RyaW5nIGVuY29kZS9kZWNvZGUgaGVscGVyc1xuJ3VzZSBzdHJpY3QnO1xuXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vY29tbW9uJyk7XG5cblxuLy8gUXVpY2sgY2hlY2sgaWYgd2UgY2FuIHVzZSBmYXN0IGFycmF5IHRvIGJpbiBzdHJpbmcgY29udmVyc2lvblxuLy9cbi8vIC0gYXBwbHkoQXJyYXkpIGNhbiBmYWlsIG9uIEFuZHJvaWQgMi4yXG4vLyAtIGFwcGx5KFVpbnQ4QXJyYXkpIGNhbiBmYWlsIG9uIGlPUyA1LjEgU2FmYXJ5XG4vL1xudmFyIFNUUl9BUFBMWV9PSyA9IHRydWU7XG52YXIgU1RSX0FQUExZX1VJQV9PSyA9IHRydWU7XG5cbnRyeSB7IFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCwgWzBdKTsgfSBjYXRjaChfXykgeyBTVFJfQVBQTFlfT0sgPSBmYWxzZTsgfVxudHJ5IHsgU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBuZXcgVWludDhBcnJheSgxKSk7IH0gY2F0Y2goX18pIHsgU1RSX0FQUExZX1VJQV9PSyA9IGZhbHNlOyB9XG5cblxuLy8gVGFibGUgd2l0aCB1dGY4IGxlbmd0aHMgKGNhbGN1bGF0ZWQgYnkgZmlyc3QgYnl0ZSBvZiBzZXF1ZW5jZSlcbi8vIE5vdGUsIHRoYXQgNSAmIDYtYnl0ZSB2YWx1ZXMgYW5kIHNvbWUgNC1ieXRlIHZhbHVlcyBjYW4gbm90IGJlIHJlcHJlc2VudGVkIGluIEpTLFxuLy8gYmVjYXVzZSBtYXggcG9zc2libGUgY29kZXBvaW50IGlzIDB4MTBmZmZmXG52YXIgX3V0ZjhsZW4gPSBuZXcgdXRpbHMuQnVmOCgyNTYpO1xuZm9yICh2YXIgaT0wOyBpPDI1NjsgaSsrKSB7XG4gIF91dGY4bGVuW2ldID0gKGkgPj0gMjUyID8gNiA6IGkgPj0gMjQ4ID8gNSA6IGkgPj0gMjQwID8gNCA6IGkgPj0gMjI0ID8gMyA6IGkgPj0gMTkyID8gMiA6IDEpO1xufVxuX3V0ZjhsZW5bMjU0XT1fdXRmOGxlblsyNTRdPTE7IC8vIEludmFsaWQgc2VxdWVuY2Ugc3RhcnRcblxuXG4vLyBjb252ZXJ0IHN0cmluZyB0byBhcnJheSAodHlwZWQsIHdoZW4gcG9zc2libGUpXG5leHBvcnRzLnN0cmluZzJidWYgPSBmdW5jdGlvbiAoc3RyKSB7XG4gIHZhciBidWYsIGMsIGMyLCBtX3BvcywgaSwgc3RyX2xlbiA9IHN0ci5sZW5ndGgsIGJ1Zl9sZW4gPSAwO1xuXG4gIC8vIGNvdW50IGJpbmFyeSBzaXplXG4gIGZvciAobV9wb3MgPSAwOyBtX3BvcyA8IHN0cl9sZW47IG1fcG9zKyspIHtcbiAgICBjID0gc3RyLmNoYXJDb2RlQXQobV9wb3MpO1xuICAgIGlmICgoYyAmIDB4ZmMwMCkgPT09IDB4ZDgwMCAmJiAobV9wb3MrMSA8IHN0cl9sZW4pKSB7XG4gICAgICBjMiA9IHN0ci5jaGFyQ29kZUF0KG1fcG9zKzEpO1xuICAgICAgaWYgKChjMiAmIDB4ZmMwMCkgPT09IDB4ZGMwMCkge1xuICAgICAgICBjID0gMHgxMDAwMCArICgoYyAtIDB4ZDgwMCkgPDwgMTApICsgKGMyIC0gMHhkYzAwKTtcbiAgICAgICAgbV9wb3MrKztcbiAgICAgIH1cbiAgICB9XG4gICAgYnVmX2xlbiArPSBjIDwgMHg4MCA/IDEgOiBjIDwgMHg4MDAgPyAyIDogYyA8IDB4MTAwMDAgPyAzIDogNDtcbiAgfVxuXG4gIC8vIGFsbG9jYXRlIGJ1ZmZlclxuICBidWYgPSBuZXcgdXRpbHMuQnVmOChidWZfbGVuKTtcblxuICAvLyBjb252ZXJ0XG4gIGZvciAoaT0wLCBtX3BvcyA9IDA7IGkgPCBidWZfbGVuOyBtX3BvcysrKSB7XG4gICAgYyA9IHN0ci5jaGFyQ29kZUF0KG1fcG9zKTtcbiAgICBpZiAoKGMgJiAweGZjMDApID09PSAweGQ4MDAgJiYgKG1fcG9zKzEgPCBzdHJfbGVuKSkge1xuICAgICAgYzIgPSBzdHIuY2hhckNvZGVBdChtX3BvcysxKTtcbiAgICAgIGlmICgoYzIgJiAweGZjMDApID09PSAweGRjMDApIHtcbiAgICAgICAgYyA9IDB4MTAwMDAgKyAoKGMgLSAweGQ4MDApIDw8IDEwKSArIChjMiAtIDB4ZGMwMCk7XG4gICAgICAgIG1fcG9zKys7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChjIDwgMHg4MCkge1xuICAgICAgLyogb25lIGJ5dGUgKi9cbiAgICAgIGJ1ZltpKytdID0gYztcbiAgICB9IGVsc2UgaWYgKGMgPCAweDgwMCkge1xuICAgICAgLyogdHdvIGJ5dGVzICovXG4gICAgICBidWZbaSsrXSA9IDB4QzAgfCAoYyA+Pj4gNik7XG4gICAgICBidWZbaSsrXSA9IDB4ODAgfCAoYyAmIDB4M2YpO1xuICAgIH0gZWxzZSBpZiAoYyA8IDB4MTAwMDApIHtcbiAgICAgIC8qIHRocmVlIGJ5dGVzICovXG4gICAgICBidWZbaSsrXSA9IDB4RTAgfCAoYyA+Pj4gMTIpO1xuICAgICAgYnVmW2krK10gPSAweDgwIHwgKGMgPj4+IDYgJiAweDNmKTtcbiAgICAgIGJ1ZltpKytdID0gMHg4MCB8IChjICYgMHgzZik7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qIGZvdXIgYnl0ZXMgKi9cbiAgICAgIGJ1ZltpKytdID0gMHhmMCB8IChjID4+PiAxOCk7XG4gICAgICBidWZbaSsrXSA9IDB4ODAgfCAoYyA+Pj4gMTIgJiAweDNmKTtcbiAgICAgIGJ1ZltpKytdID0gMHg4MCB8IChjID4+PiA2ICYgMHgzZik7XG4gICAgICBidWZbaSsrXSA9IDB4ODAgfCAoYyAmIDB4M2YpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBidWY7XG59O1xuXG4vLyBIZWxwZXIgKHVzZWQgaW4gMiBwbGFjZXMpXG5mdW5jdGlvbiBidWYyYmluc3RyaW5nKGJ1ZiwgbGVuKSB7XG4gIC8vIHVzZSBmYWxsYmFjayBmb3IgYmlnIGFycmF5cyB0byBhdm9pZCBzdGFjayBvdmVyZmxvd1xuICBpZiAobGVuIDwgNjU1MzcpIHtcbiAgICBpZiAoKGJ1Zi5zdWJhcnJheSAmJiBTVFJfQVBQTFlfVUlBX09LKSB8fCAoIWJ1Zi5zdWJhcnJheSAmJiBTVFJfQVBQTFlfT0spKSB7XG4gICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCB1dGlscy5zaHJpbmtCdWYoYnVmLCBsZW4pKTtcbiAgICB9XG4gIH1cblxuICB2YXIgcmVzdWx0ID0gJyc7XG4gIGZvcih2YXIgaT0wOyBpIDwgbGVuOyBpKyspIHtcbiAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShidWZbaV0pO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cblxuLy8gQ29udmVydCBieXRlIGFycmF5IHRvIGJpbmFyeSBzdHJpbmdcbmV4cG9ydHMuYnVmMmJpbnN0cmluZyA9IGZ1bmN0aW9uKGJ1Zikge1xuICByZXR1cm4gYnVmMmJpbnN0cmluZyhidWYsIGJ1Zi5sZW5ndGgpO1xufTtcblxuXG4vLyBDb252ZXJ0IGJpbmFyeSBzdHJpbmcgKHR5cGVkLCB3aGVuIHBvc3NpYmxlKVxuZXhwb3J0cy5iaW5zdHJpbmcyYnVmID0gZnVuY3Rpb24oc3RyKSB7XG4gIHZhciBidWYgPSBuZXcgdXRpbHMuQnVmOChzdHIubGVuZ3RoKTtcbiAgZm9yKHZhciBpPTAsIGxlbj1idWYubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICBidWZbaV0gPSBzdHIuY2hhckNvZGVBdChpKTtcbiAgfVxuICByZXR1cm4gYnVmO1xufTtcblxuXG4vLyBjb252ZXJ0IGFycmF5IHRvIHN0cmluZ1xuZXhwb3J0cy5idWYyc3RyaW5nID0gZnVuY3Rpb24gKGJ1ZiwgbWF4KSB7XG4gIHZhciBpLCBvdXQsIGMsIGNfbGVuO1xuICB2YXIgbGVuID0gbWF4IHx8IGJ1Zi5sZW5ndGg7XG5cbiAgLy8gUmVzZXJ2ZSBtYXggcG9zc2libGUgbGVuZ3RoICgyIHdvcmRzIHBlciBjaGFyKVxuICAvLyBOQjogYnkgdW5rbm93biByZWFzb25zLCBBcnJheSBpcyBzaWduaWZpY2FudGx5IGZhc3RlciBmb3JcbiAgLy8gICAgIFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkgdGhhbiBVaW50MTZBcnJheS5cbiAgdmFyIHV0ZjE2YnVmID0gbmV3IEFycmF5KGxlbioyKTtcblxuICBmb3IgKG91dD0wLCBpPTA7IGk8bGVuOykge1xuICAgIGMgPSBidWZbaSsrXTtcbiAgICAvLyBxdWljayBwcm9jZXNzIGFzY2lpXG4gICAgaWYgKGMgPCAweDgwKSB7IHV0ZjE2YnVmW291dCsrXSA9IGM7IGNvbnRpbnVlOyB9XG5cbiAgICBjX2xlbiA9IF91dGY4bGVuW2NdO1xuICAgIC8vIHNraXAgNSAmIDYgYnl0ZSBjb2Rlc1xuICAgIGlmIChjX2xlbiA+IDQpIHsgdXRmMTZidWZbb3V0KytdID0gMHhmZmZkOyBpICs9IGNfbGVuLTE7IGNvbnRpbnVlOyB9XG5cbiAgICAvLyBhcHBseSBtYXNrIG9uIGZpcnN0IGJ5dGVcbiAgICBjICY9IGNfbGVuID09PSAyID8gMHgxZiA6IGNfbGVuID09PSAzID8gMHgwZiA6IDB4MDc7XG4gICAgLy8gam9pbiB0aGUgcmVzdFxuICAgIHdoaWxlIChjX2xlbiA+IDEgJiYgaSA8IGxlbikge1xuICAgICAgYyA9IChjIDw8IDYpIHwgKGJ1ZltpKytdICYgMHgzZik7XG4gICAgICBjX2xlbi0tO1xuICAgIH1cblxuICAgIC8vIHRlcm1pbmF0ZWQgYnkgZW5kIG9mIHN0cmluZz9cbiAgICBpZiAoY19sZW4gPiAxKSB7IHV0ZjE2YnVmW291dCsrXSA9IDB4ZmZmZDsgY29udGludWU7IH1cblxuICAgIGlmIChjIDwgMHgxMDAwMCkge1xuICAgICAgdXRmMTZidWZbb3V0KytdID0gYztcbiAgICB9IGVsc2Uge1xuICAgICAgYyAtPSAweDEwMDAwO1xuICAgICAgdXRmMTZidWZbb3V0KytdID0gMHhkODAwIHwgKChjID4+IDEwKSAmIDB4M2ZmKTtcbiAgICAgIHV0ZjE2YnVmW291dCsrXSA9IDB4ZGMwMCB8IChjICYgMHgzZmYpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBidWYyYmluc3RyaW5nKHV0ZjE2YnVmLCBvdXQpO1xufTtcblxuXG4vLyBDYWxjdWxhdGUgbWF4IHBvc3NpYmxlIHBvc2l0aW9uIGluIHV0ZjggYnVmZmVyLFxuLy8gdGhhdCB3aWxsIG5vdCBicmVhayBzZXF1ZW5jZS4gSWYgdGhhdCdzIG5vdCBwb3NzaWJsZVxuLy8gLSAodmVyeSBzbWFsbCBsaW1pdHMpIHJldHVybiBtYXggc2l6ZSBhcyBpcy5cbi8vXG4vLyBidWZbXSAtIHV0ZjggYnl0ZXMgYXJyYXlcbi8vIG1heCAgIC0gbGVuZ3RoIGxpbWl0IChtYW5kYXRvcnkpO1xuZXhwb3J0cy51dGY4Ym9yZGVyID0gZnVuY3Rpb24oYnVmLCBtYXgpIHtcbiAgdmFyIHBvcztcblxuICBtYXggPSBtYXggfHwgYnVmLmxlbmd0aDtcbiAgaWYgKG1heCA+IGJ1Zi5sZW5ndGgpIHsgbWF4ID0gYnVmLmxlbmd0aDsgfVxuXG4gIC8vIGdvIGJhY2sgZnJvbSBsYXN0IHBvc2l0aW9uLCB1bnRpbCBzdGFydCBvZiBzZXF1ZW5jZSBmb3VuZFxuICBwb3MgPSBtYXgtMTtcbiAgd2hpbGUgKHBvcyA+PSAwICYmIChidWZbcG9zXSAmIDB4QzApID09PSAweDgwKSB7IHBvcy0tOyB9XG5cbiAgLy8gRnVja3VwIC0gdmVyeSBzbWFsbCBhbmQgYnJva2VuIHNlcXVlbmNlLFxuICAvLyByZXR1cm4gbWF4LCBiZWNhdXNlIHdlIHNob3VsZCByZXR1cm4gc29tZXRoaW5nIGFueXdheS5cbiAgaWYgKHBvcyA8IDApIHsgcmV0dXJuIG1heDsgfVxuXG4gIC8vIElmIHdlIGNhbWUgdG8gc3RhcnQgb2YgYnVmZmVyIC0gdGhhdCBtZWFucyB2dWZmZXIgaXMgdG9vIHNtYWxsLFxuICAvLyByZXR1cm4gbWF4IHRvby5cbiAgaWYgKHBvcyA9PT0gMCkgeyByZXR1cm4gbWF4OyB9XG5cbiAgcmV0dXJuIChwb3MgKyBfdXRmOGxlbltidWZbcG9zXV0gPiBtYXgpID8gcG9zIDogbWF4O1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gTm90ZTogYWRsZXIzMiB0YWtlcyAxMiUgZm9yIGxldmVsIDAgYW5kIDIlIGZvciBsZXZlbCA2LlxuLy8gSXQgZG9lc24ndCB3b3J0aCB0byBtYWtlIGFkZGl0aW9uYWwgb3B0aW1pemF0aW9uYSBhcyBpbiBvcmlnaW5hbC5cbi8vIFNtYWxsIHNpemUgaXMgcHJlZmVyYWJsZS5cblxuZnVuY3Rpb24gYWRsZXIzMihhZGxlciwgYnVmLCBsZW4sIHBvcykge1xuICB2YXIgczEgPSAoYWRsZXIgJiAweGZmZmYpIHwwXG4gICAgLCBzMiA9ICgoYWRsZXIgPj4+IDE2KSAmIDB4ZmZmZikgfDBcbiAgICAsIG4gPSAwO1xuXG4gIHdoaWxlIChsZW4gIT09IDApIHtcbiAgICAvLyBTZXQgbGltaXQgfiB0d2ljZSBsZXNzIHRoYW4gNTU1MiwgdG8ga2VlcFxuICAgIC8vIHMyIGluIDMxLWJpdHMsIGJlY2F1c2Ugd2UgZm9yY2Ugc2lnbmVkIGludHMuXG4gICAgLy8gaW4gb3RoZXIgY2FzZSAlPSB3aWxsIGZhaWwuXG4gICAgbiA9IGxlbiA+IDIwMDAgPyAyMDAwIDogbGVuO1xuICAgIGxlbiAtPSBuO1xuXG4gICAgZG8ge1xuICAgICAgczEgPSAoczEgKyBidWZbcG9zKytdKSB8MDtcbiAgICAgIHMyID0gKHMyICsgczEpIHwwO1xuICAgIH0gd2hpbGUgKC0tbik7XG5cbiAgICBzMSAlPSA2NTUyMTtcbiAgICBzMiAlPSA2NTUyMTtcbiAgfVxuXG4gIHJldHVybiAoczEgfCAoczIgPDwgMTYpKSB8MDtcbn1cblxuXG5tb2R1bGUuZXhwb3J0cyA9IGFkbGVyMzI7IiwibW9kdWxlLmV4cG9ydHMgPSB7XG5cbiAgLyogQWxsb3dlZCBmbHVzaCB2YWx1ZXM7IHNlZSBkZWZsYXRlKCkgYW5kIGluZmxhdGUoKSBiZWxvdyBmb3IgZGV0YWlscyAqL1xuICBaX05PX0ZMVVNIOiAgICAgICAgIDAsXG4gIFpfUEFSVElBTF9GTFVTSDogICAgMSxcbiAgWl9TWU5DX0ZMVVNIOiAgICAgICAyLFxuICBaX0ZVTExfRkxVU0g6ICAgICAgIDMsXG4gIFpfRklOSVNIOiAgICAgICAgICAgNCxcbiAgWl9CTE9DSzogICAgICAgICAgICA1LFxuICBaX1RSRUVTOiAgICAgICAgICAgIDYsXG5cbiAgLyogUmV0dXJuIGNvZGVzIGZvciB0aGUgY29tcHJlc3Npb24vZGVjb21wcmVzc2lvbiBmdW5jdGlvbnMuIE5lZ2F0aXZlIHZhbHVlc1xuICAqIGFyZSBlcnJvcnMsIHBvc2l0aXZlIHZhbHVlcyBhcmUgdXNlZCBmb3Igc3BlY2lhbCBidXQgbm9ybWFsIGV2ZW50cy5cbiAgKi9cbiAgWl9PSzogICAgICAgICAgICAgICAwLFxuICBaX1NUUkVBTV9FTkQ6ICAgICAgIDEsXG4gIFpfTkVFRF9ESUNUOiAgICAgICAgMixcbiAgWl9FUlJOTzogICAgICAgICAgIC0xLFxuICBaX1NUUkVBTV9FUlJPUjogICAgLTIsXG4gIFpfREFUQV9FUlJPUjogICAgICAtMyxcbiAgLy9aX01FTV9FUlJPUjogICAgIC00LFxuICBaX0JVRl9FUlJPUjogICAgICAgLTUsXG4gIC8vWl9WRVJTSU9OX0VSUk9SOiAtNixcblxuICAvKiBjb21wcmVzc2lvbiBsZXZlbHMgKi9cbiAgWl9OT19DT01QUkVTU0lPTjogICAgICAgICAwLFxuICBaX0JFU1RfU1BFRUQ6ICAgICAgICAgICAgIDEsXG4gIFpfQkVTVF9DT01QUkVTU0lPTjogICAgICAgOSxcbiAgWl9ERUZBVUxUX0NPTVBSRVNTSU9OOiAgIC0xLFxuXG5cbiAgWl9GSUxURVJFRDogICAgICAgICAgICAgICAxLFxuICBaX0hVRkZNQU5fT05MWTogICAgICAgICAgIDIsXG4gIFpfUkxFOiAgICAgICAgICAgICAgICAgICAgMyxcbiAgWl9GSVhFRDogICAgICAgICAgICAgICAgICA0LFxuICBaX0RFRkFVTFRfU1RSQVRFR1k6ICAgICAgIDAsXG5cbiAgLyogUG9zc2libGUgdmFsdWVzIG9mIHRoZSBkYXRhX3R5cGUgZmllbGQgKHRob3VnaCBzZWUgaW5mbGF0ZSgpKSAqL1xuICBaX0JJTkFSWTogICAgICAgICAgICAgICAgIDAsXG4gIFpfVEVYVDogICAgICAgICAgICAgICAgICAgMSxcbiAgLy9aX0FTQ0lJOiAgICAgICAgICAgICAgICAxLCAvLyA9IFpfVEVYVCAoZGVwcmVjYXRlZClcbiAgWl9VTktOT1dOOiAgICAgICAgICAgICAgICAyLFxuXG4gIC8qIFRoZSBkZWZsYXRlIGNvbXByZXNzaW9uIG1ldGhvZCAqL1xuICBaX0RFRkxBVEVEOiAgICAgICAgICAgICAgIDhcbiAgLy9aX05VTEw6ICAgICAgICAgICAgICAgICBudWxsIC8vIFVzZSAtMSBvciBudWxsIGlubGluZSwgZGVwZW5kaW5nIG9uIHZhciB0eXBlXG59OyIsIid1c2Ugc3RyaWN0JztcblxuLy8gTm90ZTogd2UgY2FuJ3QgZ2V0IHNpZ25pZmljYW50IHNwZWVkIGJvb3N0IGhlcmUuXG4vLyBTbyB3cml0ZSBjb2RlIHRvIG1pbmltaXplIHNpemUgLSBubyBwcmVnZW5lcmF0ZWQgdGFibGVzXG4vLyBhbmQgYXJyYXkgdG9vbHMgZGVwZW5kZW5jaWVzLlxuXG5cbi8vIFVzZSBvcmRpbmFyeSBhcnJheSwgc2luY2UgdW50eXBlZCBtYWtlcyBubyBib29zdCBoZXJlXG5mdW5jdGlvbiBtYWtlVGFibGUoKSB7XG4gIHZhciBjLCB0YWJsZSA9IFtdO1xuXG4gIGZvcih2YXIgbiA9MDsgbiA8IDI1NjsgbisrKXtcbiAgICBjID0gbjtcbiAgICBmb3IodmFyIGsgPTA7IGsgPCA4OyBrKyspe1xuICAgICAgYyA9ICgoYyYxKSA/ICgweEVEQjg4MzIwIF4gKGMgPj4+IDEpKSA6IChjID4+PiAxKSk7XG4gICAgfVxuICAgIHRhYmxlW25dID0gYztcbiAgfVxuXG4gIHJldHVybiB0YWJsZTtcbn1cblxuLy8gQ3JlYXRlIHRhYmxlIG9uIGxvYWQuIEp1c3QgMjU1IHNpZ25lZCBsb25ncy4gTm90IGEgcHJvYmxlbS5cbnZhciBjcmNUYWJsZSA9IG1ha2VUYWJsZSgpO1xuXG5cbmZ1bmN0aW9uIGNyYzMyKGNyYywgYnVmLCBsZW4sIHBvcykge1xuICB2YXIgdCA9IGNyY1RhYmxlXG4gICAgLCBlbmQgPSBwb3MgKyBsZW47XG5cbiAgY3JjID0gY3JjIF4gKC0xKTtcblxuICBmb3IgKHZhciBpID0gcG9zOyBpIDwgZW5kOyBpKysgKSB7XG4gICAgY3JjID0gKGNyYyA+Pj4gOCkgXiB0WyhjcmMgXiBidWZbaV0pICYgMHhGRl07XG4gIH1cblxuICByZXR1cm4gKGNyYyBeICgtMSkpOyAvLyA+Pj4gMDtcbn1cblxuXG5tb2R1bGUuZXhwb3J0cyA9IGNyYzMyOyIsIid1c2Ugc3RyaWN0JztcblxudmFyIHV0aWxzICAgPSByZXF1aXJlKCcuLi91dGlscy9jb21tb24nKTtcbnZhciB0cmVlcyAgID0gcmVxdWlyZSgnLi90cmVlcycpO1xudmFyIGFkbGVyMzIgPSByZXF1aXJlKCcuL2FkbGVyMzInKTtcbnZhciBjcmMzMiAgID0gcmVxdWlyZSgnLi9jcmMzMicpO1xudmFyIG1zZyAgID0gcmVxdWlyZSgnLi9tZXNzYWdlcycpO1xuXG4vKiBQdWJsaWMgY29uc3RhbnRzID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuXG4vKiBBbGxvd2VkIGZsdXNoIHZhbHVlczsgc2VlIGRlZmxhdGUoKSBhbmQgaW5mbGF0ZSgpIGJlbG93IGZvciBkZXRhaWxzICovXG52YXIgWl9OT19GTFVTSCAgICAgID0gMDtcbnZhciBaX1BBUlRJQUxfRkxVU0ggPSAxO1xuLy92YXIgWl9TWU5DX0ZMVVNIICAgID0gMjtcbnZhciBaX0ZVTExfRkxVU0ggICAgPSAzO1xudmFyIFpfRklOSVNIICAgICAgICA9IDQ7XG52YXIgWl9CTE9DSyAgICAgICAgID0gNTtcbi8vdmFyIFpfVFJFRVMgICAgICAgICA9IDY7XG5cblxuLyogUmV0dXJuIGNvZGVzIGZvciB0aGUgY29tcHJlc3Npb24vZGVjb21wcmVzc2lvbiBmdW5jdGlvbnMuIE5lZ2F0aXZlIHZhbHVlc1xuICogYXJlIGVycm9ycywgcG9zaXRpdmUgdmFsdWVzIGFyZSB1c2VkIGZvciBzcGVjaWFsIGJ1dCBub3JtYWwgZXZlbnRzLlxuICovXG52YXIgWl9PSyAgICAgICAgICAgID0gMDtcbnZhciBaX1NUUkVBTV9FTkQgICAgPSAxO1xuLy92YXIgWl9ORUVEX0RJQ1QgICAgID0gMjtcbi8vdmFyIFpfRVJSTk8gICAgICAgICA9IC0xO1xudmFyIFpfU1RSRUFNX0VSUk9SICA9IC0yO1xudmFyIFpfREFUQV9FUlJPUiAgICA9IC0zO1xuLy92YXIgWl9NRU1fRVJST1IgICAgID0gLTQ7XG52YXIgWl9CVUZfRVJST1IgICAgID0gLTU7XG4vL3ZhciBaX1ZFUlNJT05fRVJST1IgPSAtNjtcblxuXG4vKiBjb21wcmVzc2lvbiBsZXZlbHMgKi9cbi8vdmFyIFpfTk9fQ09NUFJFU1NJT04gICAgICA9IDA7XG4vL3ZhciBaX0JFU1RfU1BFRUQgICAgICAgICAgPSAxO1xuLy92YXIgWl9CRVNUX0NPTVBSRVNTSU9OICAgID0gOTtcbnZhciBaX0RFRkFVTFRfQ09NUFJFU1NJT04gPSAtMTtcblxuXG52YXIgWl9GSUxURVJFRCAgICAgICAgICAgID0gMTtcbnZhciBaX0hVRkZNQU5fT05MWSAgICAgICAgPSAyO1xudmFyIFpfUkxFICAgICAgICAgICAgICAgICA9IDM7XG52YXIgWl9GSVhFRCAgICAgICAgICAgICAgID0gNDtcbnZhciBaX0RFRkFVTFRfU1RSQVRFR1kgICAgPSAwO1xuXG4vKiBQb3NzaWJsZSB2YWx1ZXMgb2YgdGhlIGRhdGFfdHlwZSBmaWVsZCAodGhvdWdoIHNlZSBpbmZsYXRlKCkpICovXG4vL3ZhciBaX0JJTkFSWSAgICAgICAgICAgICAgPSAwO1xuLy92YXIgWl9URVhUICAgICAgICAgICAgICAgID0gMTtcbi8vdmFyIFpfQVNDSUkgICAgICAgICAgICAgICA9IDE7IC8vID0gWl9URVhUXG52YXIgWl9VTktOT1dOICAgICAgICAgICAgID0gMjtcblxuXG4vKiBUaGUgZGVmbGF0ZSBjb21wcmVzc2lvbiBtZXRob2QgKi9cbnZhciBaX0RFRkxBVEVEICA9IDg7XG5cbi8qPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxudmFyIE1BWF9NRU1fTEVWRUwgPSA5O1xuLyogTWF4aW11bSB2YWx1ZSBmb3IgbWVtTGV2ZWwgaW4gZGVmbGF0ZUluaXQyICovXG52YXIgTUFYX1dCSVRTID0gMTU7XG4vKiAzMksgTFo3NyB3aW5kb3cgKi9cbnZhciBERUZfTUVNX0xFVkVMID0gODtcblxuXG52YXIgTEVOR1RIX0NPREVTICA9IDI5O1xuLyogbnVtYmVyIG9mIGxlbmd0aCBjb2Rlcywgbm90IGNvdW50aW5nIHRoZSBzcGVjaWFsIEVORF9CTE9DSyBjb2RlICovXG52YXIgTElURVJBTFMgICAgICA9IDI1Njtcbi8qIG51bWJlciBvZiBsaXRlcmFsIGJ5dGVzIDAuLjI1NSAqL1xudmFyIExfQ09ERVMgICAgICAgPSBMSVRFUkFMUyArIDEgKyBMRU5HVEhfQ09ERVM7XG4vKiBudW1iZXIgb2YgTGl0ZXJhbCBvciBMZW5ndGggY29kZXMsIGluY2x1ZGluZyB0aGUgRU5EX0JMT0NLIGNvZGUgKi9cbnZhciBEX0NPREVTICAgICAgID0gMzA7XG4vKiBudW1iZXIgb2YgZGlzdGFuY2UgY29kZXMgKi9cbnZhciBCTF9DT0RFUyAgICAgID0gMTk7XG4vKiBudW1iZXIgb2YgY29kZXMgdXNlZCB0byB0cmFuc2ZlciB0aGUgYml0IGxlbmd0aHMgKi9cbnZhciBIRUFQX1NJWkUgICAgID0gMipMX0NPREVTICsgMTtcbi8qIG1heGltdW0gaGVhcCBzaXplICovXG52YXIgTUFYX0JJVFMgID0gMTU7XG4vKiBBbGwgY29kZXMgbXVzdCBub3QgZXhjZWVkIE1BWF9CSVRTIGJpdHMgKi9cblxudmFyIE1JTl9NQVRDSCA9IDM7XG52YXIgTUFYX01BVENIID0gMjU4O1xudmFyIE1JTl9MT09LQUhFQUQgPSAoTUFYX01BVENIICsgTUlOX01BVENIICsgMSk7XG5cbnZhciBQUkVTRVRfRElDVCA9IDB4MjA7XG5cbnZhciBJTklUX1NUQVRFID0gNDI7XG52YXIgRVhUUkFfU1RBVEUgPSA2OTtcbnZhciBOQU1FX1NUQVRFID0gNzM7XG52YXIgQ09NTUVOVF9TVEFURSA9IDkxO1xudmFyIEhDUkNfU1RBVEUgPSAxMDM7XG52YXIgQlVTWV9TVEFURSA9IDExMztcbnZhciBGSU5JU0hfU1RBVEUgPSA2NjY7XG5cbnZhciBCU19ORUVEX01PUkUgICAgICA9IDE7IC8qIGJsb2NrIG5vdCBjb21wbGV0ZWQsIG5lZWQgbW9yZSBpbnB1dCBvciBtb3JlIG91dHB1dCAqL1xudmFyIEJTX0JMT0NLX0RPTkUgICAgID0gMjsgLyogYmxvY2sgZmx1c2ggcGVyZm9ybWVkICovXG52YXIgQlNfRklOSVNIX1NUQVJURUQgPSAzOyAvKiBmaW5pc2ggc3RhcnRlZCwgbmVlZCBvbmx5IG1vcmUgb3V0cHV0IGF0IG5leHQgZGVmbGF0ZSAqL1xudmFyIEJTX0ZJTklTSF9ET05FICAgID0gNDsgLyogZmluaXNoIGRvbmUsIGFjY2VwdCBubyBtb3JlIGlucHV0IG9yIG91dHB1dCAqL1xuXG52YXIgT1NfQ09ERSA9IDB4MDM7IC8vIFVuaXggOikgLiBEb24ndCBkZXRlY3QsIHVzZSB0aGlzIGRlZmF1bHQuXG5cbmZ1bmN0aW9uIGVycihzdHJtLCBlcnJvckNvZGUpIHtcbiAgc3RybS5tc2cgPSBtc2dbZXJyb3JDb2RlXTtcbiAgcmV0dXJuIGVycm9yQ29kZTtcbn1cblxuZnVuY3Rpb24gcmFuayhmKSB7XG4gIHJldHVybiAoKGYpIDw8IDEpIC0gKChmKSA+IDQgPyA5IDogMCk7XG59XG5cbmZ1bmN0aW9uIHplcm8oYnVmKSB7IHZhciBsZW4gPSBidWYubGVuZ3RoOyB3aGlsZSAoLS1sZW4gPj0gMCkgeyBidWZbbGVuXSA9IDA7IH0gfVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEZsdXNoIGFzIG11Y2ggcGVuZGluZyBvdXRwdXQgYXMgcG9zc2libGUuIEFsbCBkZWZsYXRlKCkgb3V0cHV0IGdvZXNcbiAqIHRocm91Z2ggdGhpcyBmdW5jdGlvbiBzbyBzb21lIGFwcGxpY2F0aW9ucyBtYXkgd2lzaCB0byBtb2RpZnkgaXRcbiAqIHRvIGF2b2lkIGFsbG9jYXRpbmcgYSBsYXJnZSBzdHJtLT5vdXRwdXQgYnVmZmVyIGFuZCBjb3B5aW5nIGludG8gaXQuXG4gKiAoU2VlIGFsc28gcmVhZF9idWYoKSkuXG4gKi9cbmZ1bmN0aW9uIGZsdXNoX3BlbmRpbmcoc3RybSkge1xuICB2YXIgcyA9IHN0cm0uc3RhdGU7XG5cbiAgLy9fdHJfZmx1c2hfYml0cyhzKTtcbiAgdmFyIGxlbiA9IHMucGVuZGluZztcbiAgaWYgKGxlbiA+IHN0cm0uYXZhaWxfb3V0KSB7XG4gICAgbGVuID0gc3RybS5hdmFpbF9vdXQ7XG4gIH1cbiAgaWYgKGxlbiA9PT0gMCkgeyByZXR1cm47IH1cblxuICB1dGlscy5hcnJheVNldChzdHJtLm91dHB1dCwgcy5wZW5kaW5nX2J1Ziwgcy5wZW5kaW5nX291dCwgbGVuLCBzdHJtLm5leHRfb3V0KTtcbiAgc3RybS5uZXh0X291dCArPSBsZW47XG4gIHMucGVuZGluZ19vdXQgKz0gbGVuO1xuICBzdHJtLnRvdGFsX291dCArPSBsZW47XG4gIHN0cm0uYXZhaWxfb3V0IC09IGxlbjtcbiAgcy5wZW5kaW5nIC09IGxlbjtcbiAgaWYgKHMucGVuZGluZyA9PT0gMCkge1xuICAgIHMucGVuZGluZ19vdXQgPSAwO1xuICB9XG59XG5cblxuZnVuY3Rpb24gZmx1c2hfYmxvY2tfb25seSAocywgbGFzdCkge1xuICB0cmVlcy5fdHJfZmx1c2hfYmxvY2socywgKHMuYmxvY2tfc3RhcnQgPj0gMCA/IHMuYmxvY2tfc3RhcnQgOiAtMSksIHMuc3Ryc3RhcnQgLSBzLmJsb2NrX3N0YXJ0LCBsYXN0KTtcbiAgcy5ibG9ja19zdGFydCA9IHMuc3Ryc3RhcnQ7XG4gIGZsdXNoX3BlbmRpbmcocy5zdHJtKTtcbn1cblxuXG5mdW5jdGlvbiBwdXRfYnl0ZShzLCBiKSB7XG4gIHMucGVuZGluZ19idWZbcy5wZW5kaW5nKytdID0gYjtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBQdXQgYSBzaG9ydCBpbiB0aGUgcGVuZGluZyBidWZmZXIuIFRoZSAxNi1iaXQgdmFsdWUgaXMgcHV0IGluIE1TQiBvcmRlci5cbiAqIElOIGFzc2VydGlvbjogdGhlIHN0cmVhbSBzdGF0ZSBpcyBjb3JyZWN0IGFuZCB0aGVyZSBpcyBlbm91Z2ggcm9vbSBpblxuICogcGVuZGluZ19idWYuXG4gKi9cbmZ1bmN0aW9uIHB1dFNob3J0TVNCKHMsIGIpIHtcbi8vICBwdXRfYnl0ZShzLCAoQnl0ZSkoYiA+PiA4KSk7XG4vLyAgcHV0X2J5dGUocywgKEJ5dGUpKGIgJiAweGZmKSk7XG4gIHMucGVuZGluZ19idWZbcy5wZW5kaW5nKytdID0gKGIgPj4+IDgpICYgMHhmZjtcbiAgcy5wZW5kaW5nX2J1ZltzLnBlbmRpbmcrK10gPSBiICYgMHhmZjtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFJlYWQgYSBuZXcgYnVmZmVyIGZyb20gdGhlIGN1cnJlbnQgaW5wdXQgc3RyZWFtLCB1cGRhdGUgdGhlIGFkbGVyMzJcbiAqIGFuZCB0b3RhbCBudW1iZXIgb2YgYnl0ZXMgcmVhZC4gIEFsbCBkZWZsYXRlKCkgaW5wdXQgZ29lcyB0aHJvdWdoXG4gKiB0aGlzIGZ1bmN0aW9uIHNvIHNvbWUgYXBwbGljYXRpb25zIG1heSB3aXNoIHRvIG1vZGlmeSBpdCB0byBhdm9pZFxuICogYWxsb2NhdGluZyBhIGxhcmdlIHN0cm0tPmlucHV0IGJ1ZmZlciBhbmQgY29weWluZyBmcm9tIGl0LlxuICogKFNlZSBhbHNvIGZsdXNoX3BlbmRpbmcoKSkuXG4gKi9cbmZ1bmN0aW9uIHJlYWRfYnVmKHN0cm0sIGJ1Ziwgc3RhcnQsIHNpemUpIHtcbiAgdmFyIGxlbiA9IHN0cm0uYXZhaWxfaW47XG5cbiAgaWYgKGxlbiA+IHNpemUpIHsgbGVuID0gc2l6ZTsgfVxuICBpZiAobGVuID09PSAwKSB7IHJldHVybiAwOyB9XG5cbiAgc3RybS5hdmFpbF9pbiAtPSBsZW47XG5cbiAgdXRpbHMuYXJyYXlTZXQoYnVmLCBzdHJtLmlucHV0LCBzdHJtLm5leHRfaW4sIGxlbiwgc3RhcnQpO1xuICBpZiAoc3RybS5zdGF0ZS53cmFwID09PSAxKSB7XG4gICAgc3RybS5hZGxlciA9IGFkbGVyMzIoc3RybS5hZGxlciwgYnVmLCBsZW4sIHN0YXJ0KTtcbiAgfVxuXG4gIGVsc2UgaWYgKHN0cm0uc3RhdGUud3JhcCA9PT0gMikge1xuICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBidWYsIGxlbiwgc3RhcnQpO1xuICB9XG5cbiAgc3RybS5uZXh0X2luICs9IGxlbjtcbiAgc3RybS50b3RhbF9pbiArPSBsZW47XG5cbiAgcmV0dXJuIGxlbjtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNldCBtYXRjaF9zdGFydCB0byB0aGUgbG9uZ2VzdCBtYXRjaCBzdGFydGluZyBhdCB0aGUgZ2l2ZW4gc3RyaW5nIGFuZFxuICogcmV0dXJuIGl0cyBsZW5ndGguIE1hdGNoZXMgc2hvcnRlciBvciBlcXVhbCB0byBwcmV2X2xlbmd0aCBhcmUgZGlzY2FyZGVkLFxuICogaW4gd2hpY2ggY2FzZSB0aGUgcmVzdWx0IGlzIGVxdWFsIHRvIHByZXZfbGVuZ3RoIGFuZCBtYXRjaF9zdGFydCBpc1xuICogZ2FyYmFnZS5cbiAqIElOIGFzc2VydGlvbnM6IGN1cl9tYXRjaCBpcyB0aGUgaGVhZCBvZiB0aGUgaGFzaCBjaGFpbiBmb3IgdGhlIGN1cnJlbnRcbiAqICAgc3RyaW5nIChzdHJzdGFydCkgYW5kIGl0cyBkaXN0YW5jZSBpcyA8PSBNQVhfRElTVCwgYW5kIHByZXZfbGVuZ3RoID49IDFcbiAqIE9VVCBhc3NlcnRpb246IHRoZSBtYXRjaCBsZW5ndGggaXMgbm90IGdyZWF0ZXIgdGhhbiBzLT5sb29rYWhlYWQuXG4gKi9cbmZ1bmN0aW9uIGxvbmdlc3RfbWF0Y2gocywgY3VyX21hdGNoKSB7XG4gIHZhciBjaGFpbl9sZW5ndGggPSBzLm1heF9jaGFpbl9sZW5ndGg7ICAgICAgLyogbWF4IGhhc2ggY2hhaW4gbGVuZ3RoICovXG4gIHZhciBzY2FuID0gcy5zdHJzdGFydDsgLyogY3VycmVudCBzdHJpbmcgKi9cbiAgdmFyIG1hdGNoOyAgICAgICAgICAgICAgICAgICAgICAgLyogbWF0Y2hlZCBzdHJpbmcgKi9cbiAgdmFyIGxlbjsgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBsZW5ndGggb2YgY3VycmVudCBtYXRjaCAqL1xuICB2YXIgYmVzdF9sZW4gPSBzLnByZXZfbGVuZ3RoOyAgICAgICAgICAgICAgLyogYmVzdCBtYXRjaCBsZW5ndGggc28gZmFyICovXG4gIHZhciBuaWNlX21hdGNoID0gcy5uaWNlX21hdGNoOyAgICAgICAgICAgICAvKiBzdG9wIGlmIG1hdGNoIGxvbmcgZW5vdWdoICovXG4gIHZhciBsaW1pdCA9IChzLnN0cnN0YXJ0ID4gKHMud19zaXplIC0gTUlOX0xPT0tBSEVBRCkpID9cbiAgICAgIHMuc3Ryc3RhcnQgLSAocy53X3NpemUgLSBNSU5fTE9PS0FIRUFEKSA6IDAvKk5JTCovO1xuXG4gIHZhciBfd2luID0gcy53aW5kb3c7IC8vIHNob3J0Y3V0XG5cbiAgdmFyIHdtYXNrID0gcy53X21hc2s7XG4gIHZhciBwcmV2ICA9IHMucHJldjtcblxuICAvKiBTdG9wIHdoZW4gY3VyX21hdGNoIGJlY29tZXMgPD0gbGltaXQuIFRvIHNpbXBsaWZ5IHRoZSBjb2RlLFxuICAgKiB3ZSBwcmV2ZW50IG1hdGNoZXMgd2l0aCB0aGUgc3RyaW5nIG9mIHdpbmRvdyBpbmRleCAwLlxuICAgKi9cblxuICB2YXIgc3RyZW5kID0gcy5zdHJzdGFydCArIE1BWF9NQVRDSDtcbiAgdmFyIHNjYW5fZW5kMSAgPSBfd2luW3NjYW4gKyBiZXN0X2xlbiAtIDFdO1xuICB2YXIgc2Nhbl9lbmQgICA9IF93aW5bc2NhbiArIGJlc3RfbGVuXTtcblxuICAvKiBUaGUgY29kZSBpcyBvcHRpbWl6ZWQgZm9yIEhBU0hfQklUUyA+PSA4IGFuZCBNQVhfTUFUQ0gtMiBtdWx0aXBsZSBvZiAxNi5cbiAgICogSXQgaXMgZWFzeSB0byBnZXQgcmlkIG9mIHRoaXMgb3B0aW1pemF0aW9uIGlmIG5lY2Vzc2FyeS5cbiAgICovXG4gIC8vIEFzc2VydChzLT5oYXNoX2JpdHMgPj0gOCAmJiBNQVhfTUFUQ0ggPT0gMjU4LCBcIkNvZGUgdG9vIGNsZXZlclwiKTtcblxuICAvKiBEbyBub3Qgd2FzdGUgdG9vIG11Y2ggdGltZSBpZiB3ZSBhbHJlYWR5IGhhdmUgYSBnb29kIG1hdGNoOiAqL1xuICBpZiAocy5wcmV2X2xlbmd0aCA+PSBzLmdvb2RfbWF0Y2gpIHtcbiAgICBjaGFpbl9sZW5ndGggPj49IDI7XG4gIH1cbiAgLyogRG8gbm90IGxvb2sgZm9yIG1hdGNoZXMgYmV5b25kIHRoZSBlbmQgb2YgdGhlIGlucHV0LiBUaGlzIGlzIG5lY2Vzc2FyeVxuICAgKiB0byBtYWtlIGRlZmxhdGUgZGV0ZXJtaW5pc3RpYy5cbiAgICovXG4gIGlmIChuaWNlX21hdGNoID4gcy5sb29rYWhlYWQpIHsgbmljZV9tYXRjaCA9IHMubG9va2FoZWFkOyB9XG5cbiAgLy8gQXNzZXJ0KCh1bGcpcy0+c3Ryc3RhcnQgPD0gcy0+d2luZG93X3NpemUtTUlOX0xPT0tBSEVBRCwgXCJuZWVkIGxvb2thaGVhZFwiKTtcblxuICBkbyB7XG4gICAgLy8gQXNzZXJ0KGN1cl9tYXRjaCA8IHMtPnN0cnN0YXJ0LCBcIm5vIGZ1dHVyZVwiKTtcbiAgICBtYXRjaCA9IGN1cl9tYXRjaDtcblxuICAgIC8qIFNraXAgdG8gbmV4dCBtYXRjaCBpZiB0aGUgbWF0Y2ggbGVuZ3RoIGNhbm5vdCBpbmNyZWFzZVxuICAgICAqIG9yIGlmIHRoZSBtYXRjaCBsZW5ndGggaXMgbGVzcyB0aGFuIDIuICBOb3RlIHRoYXQgdGhlIGNoZWNrcyBiZWxvd1xuICAgICAqIGZvciBpbnN1ZmZpY2llbnQgbG9va2FoZWFkIG9ubHkgb2NjdXIgb2NjYXNpb25hbGx5IGZvciBwZXJmb3JtYW5jZVxuICAgICAqIHJlYXNvbnMuICBUaGVyZWZvcmUgdW5pbml0aWFsaXplZCBtZW1vcnkgd2lsbCBiZSBhY2Nlc3NlZCwgYW5kXG4gICAgICogY29uZGl0aW9uYWwganVtcHMgd2lsbCBiZSBtYWRlIHRoYXQgZGVwZW5kIG9uIHRob3NlIHZhbHVlcy5cbiAgICAgKiBIb3dldmVyIHRoZSBsZW5ndGggb2YgdGhlIG1hdGNoIGlzIGxpbWl0ZWQgdG8gdGhlIGxvb2thaGVhZCwgc29cbiAgICAgKiB0aGUgb3V0cHV0IG9mIGRlZmxhdGUgaXMgbm90IGFmZmVjdGVkIGJ5IHRoZSB1bmluaXRpYWxpemVkIHZhbHVlcy5cbiAgICAgKi9cblxuICAgIGlmIChfd2luW21hdGNoICsgYmVzdF9sZW5dICAgICAhPT0gc2Nhbl9lbmQgIHx8XG4gICAgICAgIF93aW5bbWF0Y2ggKyBiZXN0X2xlbiAtIDFdICE9PSBzY2FuX2VuZDEgfHxcbiAgICAgICAgX3dpblttYXRjaF0gICAgICAgICAgICAgICAgIT09IF93aW5bc2Nhbl0gfHxcbiAgICAgICAgX3dpblsrK21hdGNoXSAgICAgICAgICAgICAgIT09IF93aW5bc2NhbiArIDFdKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvKiBUaGUgY2hlY2sgYXQgYmVzdF9sZW4tMSBjYW4gYmUgcmVtb3ZlZCBiZWNhdXNlIGl0IHdpbGwgYmUgbWFkZVxuICAgICAqIGFnYWluIGxhdGVyLiAoVGhpcyBoZXVyaXN0aWMgaXMgbm90IGFsd2F5cyBhIHdpbi4pXG4gICAgICogSXQgaXMgbm90IG5lY2Vzc2FyeSB0byBjb21wYXJlIHNjYW5bMl0gYW5kIG1hdGNoWzJdIHNpbmNlIHRoZXlcbiAgICAgKiBhcmUgYWx3YXlzIGVxdWFsIHdoZW4gdGhlIG90aGVyIGJ5dGVzIG1hdGNoLCBnaXZlbiB0aGF0XG4gICAgICogdGhlIGhhc2gga2V5cyBhcmUgZXF1YWwgYW5kIHRoYXQgSEFTSF9CSVRTID49IDguXG4gICAgICovXG4gICAgc2NhbiArPSAyO1xuICAgIG1hdGNoKys7XG4gICAgLy8gQXNzZXJ0KCpzY2FuID09ICptYXRjaCwgXCJtYXRjaFsyXT9cIik7XG5cbiAgICAvKiBXZSBjaGVjayBmb3IgaW5zdWZmaWNpZW50IGxvb2thaGVhZCBvbmx5IGV2ZXJ5IDh0aCBjb21wYXJpc29uO1xuICAgICAqIHRoZSAyNTZ0aCBjaGVjayB3aWxsIGJlIG1hZGUgYXQgc3Ryc3RhcnQrMjU4LlxuICAgICAqL1xuICAgIGRvIHtcbiAgICAgIC8qanNoaW50IG5vZW1wdHk6ZmFsc2UqL1xuICAgIH0gd2hpbGUgKF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJiBfd2luWysrc2Nhbl0gPT09IF93aW5bKyttYXRjaF0gJiZcbiAgICAgICAgICAgICBfd2luWysrc2Nhbl0gPT09IF93aW5bKyttYXRjaF0gJiYgX3dpblsrK3NjYW5dID09PSBfd2luWysrbWF0Y2hdICYmXG4gICAgICAgICAgICAgX3dpblsrK3NjYW5dID09PSBfd2luWysrbWF0Y2hdICYmIF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJlxuICAgICAgICAgICAgIF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJiBfd2luWysrc2Nhbl0gPT09IF93aW5bKyttYXRjaF0gJiZcbiAgICAgICAgICAgICBzY2FuIDwgc3RyZW5kKTtcblxuICAgIC8vIEFzc2VydChzY2FuIDw9IHMtPndpbmRvdysodW5zaWduZWQpKHMtPndpbmRvd19zaXplLTEpLCBcIndpbGQgc2NhblwiKTtcblxuICAgIGxlbiA9IE1BWF9NQVRDSCAtIChzdHJlbmQgLSBzY2FuKTtcbiAgICBzY2FuID0gc3RyZW5kIC0gTUFYX01BVENIO1xuXG4gICAgaWYgKGxlbiA+IGJlc3RfbGVuKSB7XG4gICAgICBzLm1hdGNoX3N0YXJ0ID0gY3VyX21hdGNoO1xuICAgICAgYmVzdF9sZW4gPSBsZW47XG4gICAgICBpZiAobGVuID49IG5pY2VfbWF0Y2gpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBzY2FuX2VuZDEgID0gX3dpbltzY2FuICsgYmVzdF9sZW4gLSAxXTtcbiAgICAgIHNjYW5fZW5kICAgPSBfd2luW3NjYW4gKyBiZXN0X2xlbl07XG4gICAgfVxuICB9IHdoaWxlICgoY3VyX21hdGNoID0gcHJldltjdXJfbWF0Y2ggJiB3bWFza10pID4gbGltaXQgJiYgLS1jaGFpbl9sZW5ndGggIT09IDApO1xuXG4gIGlmIChiZXN0X2xlbiA8PSBzLmxvb2thaGVhZCkge1xuICAgIHJldHVybiBiZXN0X2xlbjtcbiAgfVxuICByZXR1cm4gcy5sb29rYWhlYWQ7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBGaWxsIHRoZSB3aW5kb3cgd2hlbiB0aGUgbG9va2FoZWFkIGJlY29tZXMgaW5zdWZmaWNpZW50LlxuICogVXBkYXRlcyBzdHJzdGFydCBhbmQgbG9va2FoZWFkLlxuICpcbiAqIElOIGFzc2VydGlvbjogbG9va2FoZWFkIDwgTUlOX0xPT0tBSEVBRFxuICogT1VUIGFzc2VydGlvbnM6IHN0cnN0YXJ0IDw9IHdpbmRvd19zaXplLU1JTl9MT09LQUhFQURcbiAqICAgIEF0IGxlYXN0IG9uZSBieXRlIGhhcyBiZWVuIHJlYWQsIG9yIGF2YWlsX2luID09IDA7IHJlYWRzIGFyZVxuICogICAgcGVyZm9ybWVkIGZvciBhdCBsZWFzdCB0d28gYnl0ZXMgKHJlcXVpcmVkIGZvciB0aGUgemlwIHRyYW5zbGF0ZV9lb2xcbiAqICAgIG9wdGlvbiAtLSBub3Qgc3VwcG9ydGVkIGhlcmUpLlxuICovXG5mdW5jdGlvbiBmaWxsX3dpbmRvdyhzKSB7XG4gIHZhciBfd19zaXplID0gcy53X3NpemU7XG4gIHZhciBwLCBuLCBtLCBtb3JlLCBzdHI7XG5cbiAgLy9Bc3NlcnQocy0+bG9va2FoZWFkIDwgTUlOX0xPT0tBSEVBRCwgXCJhbHJlYWR5IGVub3VnaCBsb29rYWhlYWRcIik7XG5cbiAgZG8ge1xuICAgIG1vcmUgPSBzLndpbmRvd19zaXplIC0gcy5sb29rYWhlYWQgLSBzLnN0cnN0YXJ0O1xuXG4gICAgLy8gSlMgaW50cyBoYXZlIDMyIGJpdCwgYmxvY2sgYmVsb3cgbm90IG5lZWRlZFxuICAgIC8qIERlYWwgd2l0aCAhQCMkJSA2NEsgbGltaXQ6ICovXG4gICAgLy9pZiAoc2l6ZW9mKGludCkgPD0gMikge1xuICAgIC8vICAgIGlmIChtb3JlID09IDAgJiYgcy0+c3Ryc3RhcnQgPT0gMCAmJiBzLT5sb29rYWhlYWQgPT0gMCkge1xuICAgIC8vICAgICAgICBtb3JlID0gd3NpemU7XG4gICAgLy9cbiAgICAvLyAgfSBlbHNlIGlmIChtb3JlID09ICh1bnNpZ25lZCkoLTEpKSB7XG4gICAgLy8gICAgICAgIC8qIFZlcnkgdW5saWtlbHksIGJ1dCBwb3NzaWJsZSBvbiAxNiBiaXQgbWFjaGluZSBpZlxuICAgIC8vICAgICAgICAgKiBzdHJzdGFydCA9PSAwICYmIGxvb2thaGVhZCA9PSAxIChpbnB1dCBkb25lIGEgYnl0ZSBhdCB0aW1lKVxuICAgIC8vICAgICAgICAgKi9cbiAgICAvLyAgICAgICAgbW9yZS0tO1xuICAgIC8vICAgIH1cbiAgICAvL31cblxuXG4gICAgLyogSWYgdGhlIHdpbmRvdyBpcyBhbG1vc3QgZnVsbCBhbmQgdGhlcmUgaXMgaW5zdWZmaWNpZW50IGxvb2thaGVhZCxcbiAgICAgKiBtb3ZlIHRoZSB1cHBlciBoYWxmIHRvIHRoZSBsb3dlciBvbmUgdG8gbWFrZSByb29tIGluIHRoZSB1cHBlciBoYWxmLlxuICAgICAqL1xuICAgIGlmIChzLnN0cnN0YXJ0ID49IF93X3NpemUgKyAoX3dfc2l6ZSAtIE1JTl9MT09LQUhFQUQpKSB7XG5cbiAgICAgIHV0aWxzLmFycmF5U2V0KHMud2luZG93LCBzLndpbmRvdywgX3dfc2l6ZSwgX3dfc2l6ZSwgMCk7XG4gICAgICBzLm1hdGNoX3N0YXJ0IC09IF93X3NpemU7XG4gICAgICBzLnN0cnN0YXJ0IC09IF93X3NpemU7XG4gICAgICAvKiB3ZSBub3cgaGF2ZSBzdHJzdGFydCA+PSBNQVhfRElTVCAqL1xuICAgICAgcy5ibG9ja19zdGFydCAtPSBfd19zaXplO1xuXG4gICAgICAvKiBTbGlkZSB0aGUgaGFzaCB0YWJsZSAoY291bGQgYmUgYXZvaWRlZCB3aXRoIDMyIGJpdCB2YWx1ZXNcbiAgICAgICBhdCB0aGUgZXhwZW5zZSBvZiBtZW1vcnkgdXNhZ2UpLiBXZSBzbGlkZSBldmVuIHdoZW4gbGV2ZWwgPT0gMFxuICAgICAgIHRvIGtlZXAgdGhlIGhhc2ggdGFibGUgY29uc2lzdGVudCBpZiB3ZSBzd2l0Y2ggYmFjayB0byBsZXZlbCA+IDBcbiAgICAgICBsYXRlci4gKFVzaW5nIGxldmVsIDAgcGVybWFuZW50bHkgaXMgbm90IGFuIG9wdGltYWwgdXNhZ2Ugb2ZcbiAgICAgICB6bGliLCBzbyB3ZSBkb24ndCBjYXJlIGFib3V0IHRoaXMgcGF0aG9sb2dpY2FsIGNhc2UuKVxuICAgICAgICovXG5cbiAgICAgIG4gPSBzLmhhc2hfc2l6ZTtcbiAgICAgIHAgPSBuO1xuICAgICAgZG8ge1xuICAgICAgICBtID0gcy5oZWFkWy0tcF07XG4gICAgICAgIHMuaGVhZFtwXSA9IChtID49IF93X3NpemUgPyBtIC0gX3dfc2l6ZSA6IDApO1xuICAgICAgfSB3aGlsZSAoLS1uKTtcblxuICAgICAgbiA9IF93X3NpemU7XG4gICAgICBwID0gbjtcbiAgICAgIGRvIHtcbiAgICAgICAgbSA9IHMucHJldlstLXBdO1xuICAgICAgICBzLnByZXZbcF0gPSAobSA+PSBfd19zaXplID8gbSAtIF93X3NpemUgOiAwKTtcbiAgICAgICAgLyogSWYgbiBpcyBub3Qgb24gYW55IGhhc2ggY2hhaW4sIHByZXZbbl0gaXMgZ2FyYmFnZSBidXRcbiAgICAgICAgICogaXRzIHZhbHVlIHdpbGwgbmV2ZXIgYmUgdXNlZC5cbiAgICAgICAgICovXG4gICAgICB9IHdoaWxlICgtLW4pO1xuXG4gICAgICBtb3JlICs9IF93X3NpemU7XG4gICAgfVxuICAgIGlmIChzLnN0cm0uYXZhaWxfaW4gPT09IDApIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIC8qIElmIHRoZXJlIHdhcyBubyBzbGlkaW5nOlxuICAgICAqICAgIHN0cnN0YXJ0IDw9IFdTSVpFK01BWF9ESVNULTEgJiYgbG9va2FoZWFkIDw9IE1JTl9MT09LQUhFQUQgLSAxICYmXG4gICAgICogICAgbW9yZSA9PSB3aW5kb3dfc2l6ZSAtIGxvb2thaGVhZCAtIHN0cnN0YXJ0XG4gICAgICogPT4gbW9yZSA+PSB3aW5kb3dfc2l6ZSAtIChNSU5fTE9PS0FIRUFELTEgKyBXU0laRSArIE1BWF9ESVNULTEpXG4gICAgICogPT4gbW9yZSA+PSB3aW5kb3dfc2l6ZSAtIDIqV1NJWkUgKyAyXG4gICAgICogSW4gdGhlIEJJR19NRU0gb3IgTU1BUCBjYXNlIChub3QgeWV0IHN1cHBvcnRlZCksXG4gICAgICogICB3aW5kb3dfc2l6ZSA9PSBpbnB1dF9zaXplICsgTUlOX0xPT0tBSEVBRCAgJiZcbiAgICAgKiAgIHN0cnN0YXJ0ICsgcy0+bG9va2FoZWFkIDw9IGlucHV0X3NpemUgPT4gbW9yZSA+PSBNSU5fTE9PS0FIRUFELlxuICAgICAqIE90aGVyd2lzZSwgd2luZG93X3NpemUgPT0gMipXU0laRSBzbyBtb3JlID49IDIuXG4gICAgICogSWYgdGhlcmUgd2FzIHNsaWRpbmcsIG1vcmUgPj0gV1NJWkUuIFNvIGluIGFsbCBjYXNlcywgbW9yZSA+PSAyLlxuICAgICAqL1xuICAgIC8vQXNzZXJ0KG1vcmUgPj0gMiwgXCJtb3JlIDwgMlwiKTtcbiAgICBuID0gcmVhZF9idWYocy5zdHJtLCBzLndpbmRvdywgcy5zdHJzdGFydCArIHMubG9va2FoZWFkLCBtb3JlKTtcbiAgICBzLmxvb2thaGVhZCArPSBuO1xuXG4gICAgLyogSW5pdGlhbGl6ZSB0aGUgaGFzaCB2YWx1ZSBub3cgdGhhdCB3ZSBoYXZlIHNvbWUgaW5wdXQ6ICovXG4gICAgaWYgKHMubG9va2FoZWFkICsgcy5pbnNlcnQgPj0gTUlOX01BVENIKSB7XG4gICAgICBzdHIgPSBzLnN0cnN0YXJ0IC0gcy5pbnNlcnQ7XG4gICAgICBzLmluc19oID0gcy53aW5kb3dbc3RyXTtcblxuICAgICAgLyogVVBEQVRFX0hBU0gocywgcy0+aW5zX2gsIHMtPndpbmRvd1tzdHIgKyAxXSk7ICovXG4gICAgICBzLmluc19oID0gKChzLmluc19oIDw8IHMuaGFzaF9zaGlmdCkgXiBzLndpbmRvd1tzdHIgKyAxXSkgJiBzLmhhc2hfbWFzaztcbi8vI2lmIE1JTl9NQVRDSCAhPSAzXG4vLyAgICAgICAgQ2FsbCB1cGRhdGVfaGFzaCgpIE1JTl9NQVRDSC0zIG1vcmUgdGltZXNcbi8vI2VuZGlmXG4gICAgICB3aGlsZSAocy5pbnNlcnQpIHtcbiAgICAgICAgLyogVVBEQVRFX0hBU0gocywgcy0+aW5zX2gsIHMtPndpbmRvd1tzdHIgKyBNSU5fTUFUQ0gtMV0pOyAqL1xuICAgICAgICBzLmluc19oID0gKChzLmluc19oIDw8IHMuaGFzaF9zaGlmdCkgXiBzLndpbmRvd1tzdHIgKyBNSU5fTUFUQ0gtMV0pICYgcy5oYXNoX21hc2s7XG5cbiAgICAgICAgcy5wcmV2W3N0ciAmIHMud19tYXNrXSA9IHMuaGVhZFtzLmluc19oXTtcbiAgICAgICAgcy5oZWFkW3MuaW5zX2hdID0gc3RyO1xuICAgICAgICBzdHIrKztcbiAgICAgICAgcy5pbnNlcnQtLTtcbiAgICAgICAgaWYgKHMubG9va2FoZWFkICsgcy5pbnNlcnQgPCBNSU5fTUFUQ0gpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICAvKiBJZiB0aGUgd2hvbGUgaW5wdXQgaGFzIGxlc3MgdGhhbiBNSU5fTUFUQ0ggYnl0ZXMsIGluc19oIGlzIGdhcmJhZ2UsXG4gICAgICogYnV0IHRoaXMgaXMgbm90IGltcG9ydGFudCBzaW5jZSBvbmx5IGxpdGVyYWwgYnl0ZXMgd2lsbCBiZSBlbWl0dGVkLlxuICAgICAqL1xuXG4gIH0gd2hpbGUgKHMubG9va2FoZWFkIDwgTUlOX0xPT0tBSEVBRCAmJiBzLnN0cm0uYXZhaWxfaW4gIT09IDApO1xuXG4gIC8qIElmIHRoZSBXSU5fSU5JVCBieXRlcyBhZnRlciB0aGUgZW5kIG9mIHRoZSBjdXJyZW50IGRhdGEgaGF2ZSBuZXZlciBiZWVuXG4gICAqIHdyaXR0ZW4sIHRoZW4gemVybyB0aG9zZSBieXRlcyBpbiBvcmRlciB0byBhdm9pZCBtZW1vcnkgY2hlY2sgcmVwb3J0cyBvZlxuICAgKiB0aGUgdXNlIG9mIHVuaW5pdGlhbGl6ZWQgKG9yIHVuaW5pdGlhbGlzZWQgYXMgSnVsaWFuIHdyaXRlcykgYnl0ZXMgYnlcbiAgICogdGhlIGxvbmdlc3QgbWF0Y2ggcm91dGluZXMuICBVcGRhdGUgdGhlIGhpZ2ggd2F0ZXIgbWFyayBmb3IgdGhlIG5leHRcbiAgICogdGltZSB0aHJvdWdoIGhlcmUuICBXSU5fSU5JVCBpcyBzZXQgdG8gTUFYX01BVENIIHNpbmNlIHRoZSBsb25nZXN0IG1hdGNoXG4gICAqIHJvdXRpbmVzIGFsbG93IHNjYW5uaW5nIHRvIHN0cnN0YXJ0ICsgTUFYX01BVENILCBpZ25vcmluZyBsb29rYWhlYWQuXG4gICAqL1xuLy8gIGlmIChzLmhpZ2hfd2F0ZXIgPCBzLndpbmRvd19zaXplKSB7XG4vLyAgICB2YXIgY3VyciA9IHMuc3Ryc3RhcnQgKyBzLmxvb2thaGVhZDtcbi8vICAgIHZhciBpbml0ID0gMDtcbi8vXG4vLyAgICBpZiAocy5oaWdoX3dhdGVyIDwgY3Vycikge1xuLy8gICAgICAvKiBQcmV2aW91cyBoaWdoIHdhdGVyIG1hcmsgYmVsb3cgY3VycmVudCBkYXRhIC0tIHplcm8gV0lOX0lOSVRcbi8vICAgICAgICogYnl0ZXMgb3IgdXAgdG8gZW5kIG9mIHdpbmRvdywgd2hpY2hldmVyIGlzIGxlc3MuXG4vLyAgICAgICAqL1xuLy8gICAgICBpbml0ID0gcy53aW5kb3dfc2l6ZSAtIGN1cnI7XG4vLyAgICAgIGlmIChpbml0ID4gV0lOX0lOSVQpXG4vLyAgICAgICAgaW5pdCA9IFdJTl9JTklUO1xuLy8gICAgICB6bWVtemVybyhzLT53aW5kb3cgKyBjdXJyLCAodW5zaWduZWQpaW5pdCk7XG4vLyAgICAgIHMtPmhpZ2hfd2F0ZXIgPSBjdXJyICsgaW5pdDtcbi8vICAgIH1cbi8vICAgIGVsc2UgaWYgKHMtPmhpZ2hfd2F0ZXIgPCAodWxnKWN1cnIgKyBXSU5fSU5JVCkge1xuLy8gICAgICAvKiBIaWdoIHdhdGVyIG1hcmsgYXQgb3IgYWJvdmUgY3VycmVudCBkYXRhLCBidXQgYmVsb3cgY3VycmVudCBkYXRhXG4vLyAgICAgICAqIHBsdXMgV0lOX0lOSVQgLS0gemVybyBvdXQgdG8gY3VycmVudCBkYXRhIHBsdXMgV0lOX0lOSVQsIG9yIHVwXG4vLyAgICAgICAqIHRvIGVuZCBvZiB3aW5kb3csIHdoaWNoZXZlciBpcyBsZXNzLlxuLy8gICAgICAgKi9cbi8vICAgICAgaW5pdCA9ICh1bGcpY3VyciArIFdJTl9JTklUIC0gcy0+aGlnaF93YXRlcjtcbi8vICAgICAgaWYgKGluaXQgPiBzLT53aW5kb3dfc2l6ZSAtIHMtPmhpZ2hfd2F0ZXIpXG4vLyAgICAgICAgaW5pdCA9IHMtPndpbmRvd19zaXplIC0gcy0+aGlnaF93YXRlcjtcbi8vICAgICAgem1lbXplcm8ocy0+d2luZG93ICsgcy0+aGlnaF93YXRlciwgKHVuc2lnbmVkKWluaXQpO1xuLy8gICAgICBzLT5oaWdoX3dhdGVyICs9IGluaXQ7XG4vLyAgICB9XG4vLyAgfVxuLy9cbi8vICBBc3NlcnQoKHVsZylzLT5zdHJzdGFydCA8PSBzLT53aW5kb3dfc2l6ZSAtIE1JTl9MT09LQUhFQUQsXG4vLyAgICBcIm5vdCBlbm91Z2ggcm9vbSBmb3Igc2VhcmNoXCIpO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvcHkgd2l0aG91dCBjb21wcmVzc2lvbiBhcyBtdWNoIGFzIHBvc3NpYmxlIGZyb20gdGhlIGlucHV0IHN0cmVhbSwgcmV0dXJuXG4gKiB0aGUgY3VycmVudCBibG9jayBzdGF0ZS5cbiAqIFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgaW5zZXJ0IG5ldyBzdHJpbmdzIGluIHRoZSBkaWN0aW9uYXJ5IHNpbmNlXG4gKiB1bmNvbXByZXNzaWJsZSBkYXRhIGlzIHByb2JhYmx5IG5vdCB1c2VmdWwuIFRoaXMgZnVuY3Rpb24gaXMgdXNlZFxuICogb25seSBmb3IgdGhlIGxldmVsPTAgY29tcHJlc3Npb24gb3B0aW9uLlxuICogTk9URTogdGhpcyBmdW5jdGlvbiBzaG91bGQgYmUgb3B0aW1pemVkIHRvIGF2b2lkIGV4dHJhIGNvcHlpbmcgZnJvbVxuICogd2luZG93IHRvIHBlbmRpbmdfYnVmLlxuICovXG5mdW5jdGlvbiBkZWZsYXRlX3N0b3JlZChzLCBmbHVzaCkge1xuICAvKiBTdG9yZWQgYmxvY2tzIGFyZSBsaW1pdGVkIHRvIDB4ZmZmZiBieXRlcywgcGVuZGluZ19idWYgaXMgbGltaXRlZFxuICAgKiB0byBwZW5kaW5nX2J1Zl9zaXplLCBhbmQgZWFjaCBzdG9yZWQgYmxvY2sgaGFzIGEgNSBieXRlIGhlYWRlcjpcbiAgICovXG4gIHZhciBtYXhfYmxvY2tfc2l6ZSA9IDB4ZmZmZjtcblxuICBpZiAobWF4X2Jsb2NrX3NpemUgPiBzLnBlbmRpbmdfYnVmX3NpemUgLSA1KSB7XG4gICAgbWF4X2Jsb2NrX3NpemUgPSBzLnBlbmRpbmdfYnVmX3NpemUgLSA1O1xuICB9XG5cbiAgLyogQ29weSBhcyBtdWNoIGFzIHBvc3NpYmxlIGZyb20gaW5wdXQgdG8gb3V0cHV0OiAqL1xuICBmb3IgKDs7KSB7XG4gICAgLyogRmlsbCB0aGUgd2luZG93IGFzIG11Y2ggYXMgcG9zc2libGU6ICovXG4gICAgaWYgKHMubG9va2FoZWFkIDw9IDEpIHtcblxuICAgICAgLy9Bc3NlcnQocy0+c3Ryc3RhcnQgPCBzLT53X3NpemUrTUFYX0RJU1QocykgfHxcbiAgICAgIC8vICBzLT5ibG9ja19zdGFydCA+PSAobG9uZylzLT53X3NpemUsIFwic2xpZGUgdG9vIGxhdGVcIik7XG4vLyAgICAgIGlmICghKHMuc3Ryc3RhcnQgPCBzLndfc2l6ZSArIChzLndfc2l6ZSAtIE1JTl9MT09LQUhFQUQpIHx8XG4vLyAgICAgICAgcy5ibG9ja19zdGFydCA+PSBzLndfc2l6ZSkpIHtcbi8vICAgICAgICB0aHJvdyAgbmV3IEVycm9yKFwic2xpZGUgdG9vIGxhdGVcIik7XG4vLyAgICAgIH1cblxuICAgICAgZmlsbF93aW5kb3cocyk7XG4gICAgICBpZiAocy5sb29rYWhlYWQgPT09IDAgJiYgZmx1c2ggPT09IFpfTk9fRkxVU0gpIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cblxuICAgICAgaWYgKHMubG9va2FoZWFkID09PSAwKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgLyogZmx1c2ggdGhlIGN1cnJlbnQgYmxvY2sgKi9cbiAgICB9XG4gICAgLy9Bc3NlcnQocy0+YmxvY2tfc3RhcnQgPj0gMEwsIFwiYmxvY2sgZ29uZVwiKTtcbi8vICAgIGlmIChzLmJsb2NrX3N0YXJ0IDwgMCkgdGhyb3cgbmV3IEVycm9yKFwiYmxvY2sgZ29uZVwiKTtcblxuICAgIHMuc3Ryc3RhcnQgKz0gcy5sb29rYWhlYWQ7XG4gICAgcy5sb29rYWhlYWQgPSAwO1xuXG4gICAgLyogRW1pdCBhIHN0b3JlZCBibG9jayBpZiBwZW5kaW5nX2J1ZiB3aWxsIGJlIGZ1bGw6ICovXG4gICAgdmFyIG1heF9zdGFydCA9IHMuYmxvY2tfc3RhcnQgKyBtYXhfYmxvY2tfc2l6ZTtcblxuICAgIGlmIChzLnN0cnN0YXJ0ID09PSAwIHx8IHMuc3Ryc3RhcnQgPj0gbWF4X3N0YXJ0KSB7XG4gICAgICAvKiBzdHJzdGFydCA9PSAwIGlzIHBvc3NpYmxlIHdoZW4gd3JhcGFyb3VuZCBvbiAxNi1iaXQgbWFjaGluZSAqL1xuICAgICAgcy5sb29rYWhlYWQgPSBzLnN0cnN0YXJ0IC0gbWF4X3N0YXJ0O1xuICAgICAgcy5zdHJzdGFydCA9IG1heF9zdGFydDtcbiAgICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIC8qKiovXG5cblxuICAgIH1cbiAgICAvKiBGbHVzaCBpZiB3ZSBtYXkgaGF2ZSB0byBzbGlkZSwgb3RoZXJ3aXNlIGJsb2NrX3N0YXJ0IG1heSBiZWNvbWVcbiAgICAgKiBuZWdhdGl2ZSBhbmQgdGhlIGRhdGEgd2lsbCBiZSBnb25lOlxuICAgICAqL1xuICAgIGlmIChzLnN0cnN0YXJ0IC0gcy5ibG9ja19zdGFydCA+PSAocy53X3NpemUgLSBNSU5fTE9PS0FIRUFEKSkge1xuICAgICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgLyoqKi9cbiAgICB9XG4gIH1cblxuICBzLmluc2VydCA9IDA7XG5cbiAgaWYgKGZsdXNoID09PSBaX0ZJTklTSCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMSk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIHRydWUpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfRklOSVNIX1NUQVJURUQ7XG4gICAgfVxuICAgIC8qKiovXG4gICAgcmV0dXJuIEJTX0ZJTklTSF9ET05FO1xuICB9XG5cbiAgaWYgKHMuc3Ryc3RhcnQgPiBzLmJsb2NrX3N0YXJ0KSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgIH1cbiAgICAvKioqL1xuICB9XG5cbiAgcmV0dXJuIEJTX05FRURfTU9SRTtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb21wcmVzcyBhcyBtdWNoIGFzIHBvc3NpYmxlIGZyb20gdGhlIGlucHV0IHN0cmVhbSwgcmV0dXJuIHRoZSBjdXJyZW50XG4gKiBibG9jayBzdGF0ZS5cbiAqIFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcGVyZm9ybSBsYXp5IGV2YWx1YXRpb24gb2YgbWF0Y2hlcyBhbmQgaW5zZXJ0c1xuICogbmV3IHN0cmluZ3MgaW4gdGhlIGRpY3Rpb25hcnkgb25seSBmb3IgdW5tYXRjaGVkIHN0cmluZ3Mgb3IgZm9yIHNob3J0XG4gKiBtYXRjaGVzLiBJdCBpcyB1c2VkIG9ubHkgZm9yIHRoZSBmYXN0IGNvbXByZXNzaW9uIG9wdGlvbnMuXG4gKi9cbmZ1bmN0aW9uIGRlZmxhdGVfZmFzdChzLCBmbHVzaCkge1xuICB2YXIgaGFzaF9oZWFkOyAgICAgICAgLyogaGVhZCBvZiB0aGUgaGFzaCBjaGFpbiAqL1xuICB2YXIgYmZsdXNoOyAgICAgICAgICAgLyogc2V0IGlmIGN1cnJlbnQgYmxvY2sgbXVzdCBiZSBmbHVzaGVkICovXG5cbiAgZm9yICg7Oykge1xuICAgIC8qIE1ha2Ugc3VyZSB0aGF0IHdlIGFsd2F5cyBoYXZlIGVub3VnaCBsb29rYWhlYWQsIGV4Y2VwdFxuICAgICAqIGF0IHRoZSBlbmQgb2YgdGhlIGlucHV0IGZpbGUuIFdlIG5lZWQgTUFYX01BVENIIGJ5dGVzXG4gICAgICogZm9yIHRoZSBuZXh0IG1hdGNoLCBwbHVzIE1JTl9NQVRDSCBieXRlcyB0byBpbnNlcnQgdGhlXG4gICAgICogc3RyaW5nIGZvbGxvd2luZyB0aGUgbmV4dCBtYXRjaC5cbiAgICAgKi9cbiAgICBpZiAocy5sb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFEKSB7XG4gICAgICBmaWxsX3dpbmRvdyhzKTtcbiAgICAgIGlmIChzLmxvb2thaGVhZCA8IE1JTl9MT09LQUhFQUQgJiYgZmx1c2ggPT09IFpfTk9fRkxVU0gpIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkge1xuICAgICAgICBicmVhazsgLyogZmx1c2ggdGhlIGN1cnJlbnQgYmxvY2sgKi9cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKiBJbnNlcnQgdGhlIHN0cmluZyB3aW5kb3dbc3Ryc3RhcnQgLi4gc3Ryc3RhcnQrMl0gaW4gdGhlXG4gICAgICogZGljdGlvbmFyeSwgYW5kIHNldCBoYXNoX2hlYWQgdG8gdGhlIGhlYWQgb2YgdGhlIGhhc2ggY2hhaW46XG4gICAgICovXG4gICAgaGFzaF9oZWFkID0gMC8qTklMKi87XG4gICAgaWYgKHMubG9va2FoZWFkID49IE1JTl9NQVRDSCkge1xuICAgICAgLyoqKiBJTlNFUlRfU1RSSU5HKHMsIHMuc3Ryc3RhcnQsIGhhc2hfaGVhZCk7ICoqKi9cbiAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3Muc3Ryc3RhcnQgKyBNSU5fTUFUQ0ggLSAxXSkgJiBzLmhhc2hfbWFzaztcbiAgICAgIGhhc2hfaGVhZCA9IHMucHJldltzLnN0cnN0YXJ0ICYgcy53X21hc2tdID0gcy5oZWFkW3MuaW5zX2hdO1xuICAgICAgcy5oZWFkW3MuaW5zX2hdID0gcy5zdHJzdGFydDtcbiAgICAgIC8qKiovXG4gICAgfVxuXG4gICAgLyogRmluZCB0aGUgbG9uZ2VzdCBtYXRjaCwgZGlzY2FyZGluZyB0aG9zZSA8PSBwcmV2X2xlbmd0aC5cbiAgICAgKiBBdCB0aGlzIHBvaW50IHdlIGhhdmUgYWx3YXlzIG1hdGNoX2xlbmd0aCA8IE1JTl9NQVRDSFxuICAgICAqL1xuICAgIGlmIChoYXNoX2hlYWQgIT09IDAvKk5JTCovICYmICgocy5zdHJzdGFydCAtIGhhc2hfaGVhZCkgPD0gKHMud19zaXplIC0gTUlOX0xPT0tBSEVBRCkpKSB7XG4gICAgICAvKiBUbyBzaW1wbGlmeSB0aGUgY29kZSwgd2UgcHJldmVudCBtYXRjaGVzIHdpdGggdGhlIHN0cmluZ1xuICAgICAgICogb2Ygd2luZG93IGluZGV4IDAgKGluIHBhcnRpY3VsYXIgd2UgaGF2ZSB0byBhdm9pZCBhIG1hdGNoXG4gICAgICAgKiBvZiB0aGUgc3RyaW5nIHdpdGggaXRzZWxmIGF0IHRoZSBzdGFydCBvZiB0aGUgaW5wdXQgZmlsZSkuXG4gICAgICAgKi9cbiAgICAgIHMubWF0Y2hfbGVuZ3RoID0gbG9uZ2VzdF9tYXRjaChzLCBoYXNoX2hlYWQpO1xuICAgICAgLyogbG9uZ2VzdF9tYXRjaCgpIHNldHMgbWF0Y2hfc3RhcnQgKi9cbiAgICB9XG4gICAgaWYgKHMubWF0Y2hfbGVuZ3RoID49IE1JTl9NQVRDSCkge1xuICAgICAgLy8gY2hlY2tfbWF0Y2gocywgcy5zdHJzdGFydCwgcy5tYXRjaF9zdGFydCwgcy5tYXRjaF9sZW5ndGgpOyAvLyBmb3IgZGVidWcgb25seVxuXG4gICAgICAvKioqIF90cl90YWxseV9kaXN0KHMsIHMuc3Ryc3RhcnQgLSBzLm1hdGNoX3N0YXJ0LFxuICAgICAgICAgICAgICAgICAgICAgcy5tYXRjaF9sZW5ndGggLSBNSU5fTUFUQ0gsIGJmbHVzaCk7ICoqKi9cbiAgICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCBzLnN0cnN0YXJ0IC0gcy5tYXRjaF9zdGFydCwgcy5tYXRjaF9sZW5ndGggLSBNSU5fTUFUQ0gpO1xuXG4gICAgICBzLmxvb2thaGVhZCAtPSBzLm1hdGNoX2xlbmd0aDtcblxuICAgICAgLyogSW5zZXJ0IG5ldyBzdHJpbmdzIGluIHRoZSBoYXNoIHRhYmxlIG9ubHkgaWYgdGhlIG1hdGNoIGxlbmd0aFxuICAgICAgICogaXMgbm90IHRvbyBsYXJnZS4gVGhpcyBzYXZlcyB0aW1lIGJ1dCBkZWdyYWRlcyBjb21wcmVzc2lvbi5cbiAgICAgICAqL1xuICAgICAgaWYgKHMubWF0Y2hfbGVuZ3RoIDw9IHMubWF4X2xhenlfbWF0Y2gvKm1heF9pbnNlcnRfbGVuZ3RoKi8gJiYgcy5sb29rYWhlYWQgPj0gTUlOX01BVENIKSB7XG4gICAgICAgIHMubWF0Y2hfbGVuZ3RoLS07IC8qIHN0cmluZyBhdCBzdHJzdGFydCBhbHJlYWR5IGluIHRhYmxlICovXG4gICAgICAgIGRvIHtcbiAgICAgICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgICAgICAgLyoqKiBJTlNFUlRfU1RSSU5HKHMsIHMuc3Ryc3RhcnQsIGhhc2hfaGVhZCk7ICoqKi9cbiAgICAgICAgICBzLmluc19oID0gKChzLmluc19oIDw8IHMuaGFzaF9zaGlmdCkgXiBzLndpbmRvd1tzLnN0cnN0YXJ0ICsgTUlOX01BVENIIC0gMV0pICYgcy5oYXNoX21hc2s7XG4gICAgICAgICAgaGFzaF9oZWFkID0gcy5wcmV2W3Muc3Ryc3RhcnQgJiBzLndfbWFza10gPSBzLmhlYWRbcy5pbnNfaF07XG4gICAgICAgICAgcy5oZWFkW3MuaW5zX2hdID0gcy5zdHJzdGFydDtcbiAgICAgICAgICAvKioqL1xuICAgICAgICAgIC8qIHN0cnN0YXJ0IG5ldmVyIGV4Y2VlZHMgV1NJWkUtTUFYX01BVENILCBzbyB0aGVyZSBhcmVcbiAgICAgICAgICAgKiBhbHdheXMgTUlOX01BVENIIGJ5dGVzIGFoZWFkLlxuICAgICAgICAgICAqL1xuICAgICAgICB9IHdoaWxlICgtLXMubWF0Y2hfbGVuZ3RoICE9PSAwKTtcbiAgICAgICAgcy5zdHJzdGFydCsrO1xuICAgICAgfSBlbHNlXG4gICAgICB7XG4gICAgICAgIHMuc3Ryc3RhcnQgKz0gcy5tYXRjaF9sZW5ndGg7XG4gICAgICAgIHMubWF0Y2hfbGVuZ3RoID0gMDtcbiAgICAgICAgcy5pbnNfaCA9IHMud2luZG93W3Muc3Ryc3RhcnRdO1xuICAgICAgICAvKiBVUERBVEVfSEFTSChzLCBzLmluc19oLCBzLndpbmRvd1tzLnN0cnN0YXJ0KzFdKTsgKi9cbiAgICAgICAgcy5pbnNfaCA9ICgocy5pbnNfaCA8PCBzLmhhc2hfc2hpZnQpIF4gcy53aW5kb3dbcy5zdHJzdGFydCArIDFdKSAmIHMuaGFzaF9tYXNrO1xuXG4vLyNpZiBNSU5fTUFUQ0ggIT0gM1xuLy8gICAgICAgICAgICAgICAgQ2FsbCBVUERBVEVfSEFTSCgpIE1JTl9NQVRDSC0zIG1vcmUgdGltZXNcbi8vI2VuZGlmXG4gICAgICAgIC8qIElmIGxvb2thaGVhZCA8IE1JTl9NQVRDSCwgaW5zX2ggaXMgZ2FyYmFnZSwgYnV0IGl0IGRvZXMgbm90XG4gICAgICAgICAqIG1hdHRlciBzaW5jZSBpdCB3aWxsIGJlIHJlY29tcHV0ZWQgYXQgbmV4dCBkZWZsYXRlIGNhbGwuXG4gICAgICAgICAqL1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvKiBObyBtYXRjaCwgb3V0cHV0IGEgbGl0ZXJhbCBieXRlICovXG4gICAgICAvL1RyYWNldnYoKHN0ZGVycixcIiVjXCIsIHMud2luZG93W3Muc3Ryc3RhcnRdKSk7XG4gICAgICAvKioqIF90cl90YWxseV9saXQocywgcy53aW5kb3dbcy5zdHJzdGFydF0sIGJmbHVzaCk7ICoqKi9cbiAgICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCAwLCBzLndpbmRvd1tzLnN0cnN0YXJ0XSk7XG5cbiAgICAgIHMubG9va2FoZWFkLS07XG4gICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgfVxuICAgIGlmIChiZmx1c2gpIHtcbiAgICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIC8qKiovXG4gICAgfVxuICB9XG4gIHMuaW5zZXJ0ID0gKChzLnN0cnN0YXJ0IDwgKE1JTl9NQVRDSC0xKSkgPyBzLnN0cnN0YXJ0IDogTUlOX01BVENILTEpO1xuICBpZiAoZmx1c2ggPT09IFpfRklOSVNIKSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAxKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgdHJ1ZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19GSU5JU0hfU1RBUlRFRDtcbiAgICB9XG4gICAgLyoqKi9cbiAgICByZXR1cm4gQlNfRklOSVNIX0RPTkU7XG4gIH1cbiAgaWYgKHMubGFzdF9saXQpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDApOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgfVxuICAgIC8qKiovXG4gIH1cbiAgcmV0dXJuIEJTX0JMT0NLX0RPTkU7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2FtZSBhcyBhYm92ZSwgYnV0IGFjaGlldmVzIGJldHRlciBjb21wcmVzc2lvbi4gV2UgdXNlIGEgbGF6eVxuICogZXZhbHVhdGlvbiBmb3IgbWF0Y2hlczogYSBtYXRjaCBpcyBmaW5hbGx5IGFkb3B0ZWQgb25seSBpZiB0aGVyZSBpc1xuICogbm8gYmV0dGVyIG1hdGNoIGF0IHRoZSBuZXh0IHdpbmRvdyBwb3NpdGlvbi5cbiAqL1xuZnVuY3Rpb24gZGVmbGF0ZV9zbG93KHMsIGZsdXNoKSB7XG4gIHZhciBoYXNoX2hlYWQ7ICAgICAgICAgIC8qIGhlYWQgb2YgaGFzaCBjaGFpbiAqL1xuICB2YXIgYmZsdXNoOyAgICAgICAgICAgICAgLyogc2V0IGlmIGN1cnJlbnQgYmxvY2sgbXVzdCBiZSBmbHVzaGVkICovXG5cbiAgdmFyIG1heF9pbnNlcnQ7XG5cbiAgLyogUHJvY2VzcyB0aGUgaW5wdXQgYmxvY2suICovXG4gIGZvciAoOzspIHtcbiAgICAvKiBNYWtlIHN1cmUgdGhhdCB3ZSBhbHdheXMgaGF2ZSBlbm91Z2ggbG9va2FoZWFkLCBleGNlcHRcbiAgICAgKiBhdCB0aGUgZW5kIG9mIHRoZSBpbnB1dCBmaWxlLiBXZSBuZWVkIE1BWF9NQVRDSCBieXRlc1xuICAgICAqIGZvciB0aGUgbmV4dCBtYXRjaCwgcGx1cyBNSU5fTUFUQ0ggYnl0ZXMgdG8gaW5zZXJ0IHRoZVxuICAgICAqIHN0cmluZyBmb2xsb3dpbmcgdGhlIG5leHQgbWF0Y2guXG4gICAgICovXG4gICAgaWYgKHMubG9va2FoZWFkIDwgTUlOX0xPT0tBSEVBRCkge1xuICAgICAgZmlsbF93aW5kb3cocyk7XG4gICAgICBpZiAocy5sb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFEICYmIGZsdXNoID09PSBaX05PX0ZMVVNIKSB7XG4gICAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgICB9XG4gICAgICBpZiAocy5sb29rYWhlYWQgPT09IDApIHsgYnJlYWs7IH0gLyogZmx1c2ggdGhlIGN1cnJlbnQgYmxvY2sgKi9cbiAgICB9XG5cbiAgICAvKiBJbnNlcnQgdGhlIHN0cmluZyB3aW5kb3dbc3Ryc3RhcnQgLi4gc3Ryc3RhcnQrMl0gaW4gdGhlXG4gICAgICogZGljdGlvbmFyeSwgYW5kIHNldCBoYXNoX2hlYWQgdG8gdGhlIGhlYWQgb2YgdGhlIGhhc2ggY2hhaW46XG4gICAgICovXG4gICAgaGFzaF9oZWFkID0gMC8qTklMKi87XG4gICAgaWYgKHMubG9va2FoZWFkID49IE1JTl9NQVRDSCkge1xuICAgICAgLyoqKiBJTlNFUlRfU1RSSU5HKHMsIHMuc3Ryc3RhcnQsIGhhc2hfaGVhZCk7ICoqKi9cbiAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3Muc3Ryc3RhcnQgKyBNSU5fTUFUQ0ggLSAxXSkgJiBzLmhhc2hfbWFzaztcbiAgICAgIGhhc2hfaGVhZCA9IHMucHJldltzLnN0cnN0YXJ0ICYgcy53X21hc2tdID0gcy5oZWFkW3MuaW5zX2hdO1xuICAgICAgcy5oZWFkW3MuaW5zX2hdID0gcy5zdHJzdGFydDtcbiAgICAgIC8qKiovXG4gICAgfVxuXG4gICAgLyogRmluZCB0aGUgbG9uZ2VzdCBtYXRjaCwgZGlzY2FyZGluZyB0aG9zZSA8PSBwcmV2X2xlbmd0aC5cbiAgICAgKi9cbiAgICBzLnByZXZfbGVuZ3RoID0gcy5tYXRjaF9sZW5ndGg7XG4gICAgcy5wcmV2X21hdGNoID0gcy5tYXRjaF9zdGFydDtcbiAgICBzLm1hdGNoX2xlbmd0aCA9IE1JTl9NQVRDSC0xO1xuXG4gICAgaWYgKGhhc2hfaGVhZCAhPT0gMC8qTklMKi8gJiYgcy5wcmV2X2xlbmd0aCA8IHMubWF4X2xhenlfbWF0Y2ggJiZcbiAgICAgICAgcy5zdHJzdGFydCAtIGhhc2hfaGVhZCA8PSAocy53X3NpemUtTUlOX0xPT0tBSEVBRCkvKk1BWF9ESVNUKHMpKi8pIHtcbiAgICAgIC8qIFRvIHNpbXBsaWZ5IHRoZSBjb2RlLCB3ZSBwcmV2ZW50IG1hdGNoZXMgd2l0aCB0aGUgc3RyaW5nXG4gICAgICAgKiBvZiB3aW5kb3cgaW5kZXggMCAoaW4gcGFydGljdWxhciB3ZSBoYXZlIHRvIGF2b2lkIGEgbWF0Y2hcbiAgICAgICAqIG9mIHRoZSBzdHJpbmcgd2l0aCBpdHNlbGYgYXQgdGhlIHN0YXJ0IG9mIHRoZSBpbnB1dCBmaWxlKS5cbiAgICAgICAqL1xuICAgICAgcy5tYXRjaF9sZW5ndGggPSBsb25nZXN0X21hdGNoKHMsIGhhc2hfaGVhZCk7XG4gICAgICAvKiBsb25nZXN0X21hdGNoKCkgc2V0cyBtYXRjaF9zdGFydCAqL1xuXG4gICAgICBpZiAocy5tYXRjaF9sZW5ndGggPD0gNSAmJlxuICAgICAgICAgKHMuc3RyYXRlZ3kgPT09IFpfRklMVEVSRUQgfHwgKHMubWF0Y2hfbGVuZ3RoID09PSBNSU5fTUFUQ0ggJiYgcy5zdHJzdGFydCAtIHMubWF0Y2hfc3RhcnQgPiA0MDk2LypUT09fRkFSKi8pKSkge1xuXG4gICAgICAgIC8qIElmIHByZXZfbWF0Y2ggaXMgYWxzbyBNSU5fTUFUQ0gsIG1hdGNoX3N0YXJ0IGlzIGdhcmJhZ2VcbiAgICAgICAgICogYnV0IHdlIHdpbGwgaWdub3JlIHRoZSBjdXJyZW50IG1hdGNoIGFueXdheS5cbiAgICAgICAgICovXG4gICAgICAgIHMubWF0Y2hfbGVuZ3RoID0gTUlOX01BVENILTE7XG4gICAgICB9XG4gICAgfVxuICAgIC8qIElmIHRoZXJlIHdhcyBhIG1hdGNoIGF0IHRoZSBwcmV2aW91cyBzdGVwIGFuZCB0aGUgY3VycmVudFxuICAgICAqIG1hdGNoIGlzIG5vdCBiZXR0ZXIsIG91dHB1dCB0aGUgcHJldmlvdXMgbWF0Y2g6XG4gICAgICovXG4gICAgaWYgKHMucHJldl9sZW5ndGggPj0gTUlOX01BVENIICYmIHMubWF0Y2hfbGVuZ3RoIDw9IHMucHJldl9sZW5ndGgpIHtcbiAgICAgIG1heF9pbnNlcnQgPSBzLnN0cnN0YXJ0ICsgcy5sb29rYWhlYWQgLSBNSU5fTUFUQ0g7XG4gICAgICAvKiBEbyBub3QgaW5zZXJ0IHN0cmluZ3MgaW4gaGFzaCB0YWJsZSBiZXlvbmQgdGhpcy4gKi9cblxuICAgICAgLy9jaGVja19tYXRjaChzLCBzLnN0cnN0YXJ0LTEsIHMucHJldl9tYXRjaCwgcy5wcmV2X2xlbmd0aCk7XG5cbiAgICAgIC8qKipfdHJfdGFsbHlfZGlzdChzLCBzLnN0cnN0YXJ0IC0gMSAtIHMucHJldl9tYXRjaCxcbiAgICAgICAgICAgICAgICAgICAgIHMucHJldl9sZW5ndGggLSBNSU5fTUFUQ0gsIGJmbHVzaCk7KioqL1xuICAgICAgYmZsdXNoID0gdHJlZXMuX3RyX3RhbGx5KHMsIHMuc3Ryc3RhcnQgLSAxLSBzLnByZXZfbWF0Y2gsIHMucHJldl9sZW5ndGggLSBNSU5fTUFUQ0gpO1xuICAgICAgLyogSW5zZXJ0IGluIGhhc2ggdGFibGUgYWxsIHN0cmluZ3MgdXAgdG8gdGhlIGVuZCBvZiB0aGUgbWF0Y2guXG4gICAgICAgKiBzdHJzdGFydC0xIGFuZCBzdHJzdGFydCBhcmUgYWxyZWFkeSBpbnNlcnRlZC4gSWYgdGhlcmUgaXMgbm90XG4gICAgICAgKiBlbm91Z2ggbG9va2FoZWFkLCB0aGUgbGFzdCB0d28gc3RyaW5ncyBhcmUgbm90IGluc2VydGVkIGluXG4gICAgICAgKiB0aGUgaGFzaCB0YWJsZS5cbiAgICAgICAqL1xuICAgICAgcy5sb29rYWhlYWQgLT0gcy5wcmV2X2xlbmd0aC0xO1xuICAgICAgcy5wcmV2X2xlbmd0aCAtPSAyO1xuICAgICAgZG8ge1xuICAgICAgICBpZiAoKytzLnN0cnN0YXJ0IDw9IG1heF9pbnNlcnQpIHtcbiAgICAgICAgICAvKioqIElOU0VSVF9TVFJJTkcocywgcy5zdHJzdGFydCwgaGFzaF9oZWFkKTsgKioqL1xuICAgICAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3Muc3Ryc3RhcnQgKyBNSU5fTUFUQ0ggLSAxXSkgJiBzLmhhc2hfbWFzaztcbiAgICAgICAgICBoYXNoX2hlYWQgPSBzLnByZXZbcy5zdHJzdGFydCAmIHMud19tYXNrXSA9IHMuaGVhZFtzLmluc19oXTtcbiAgICAgICAgICBzLmhlYWRbcy5pbnNfaF0gPSBzLnN0cnN0YXJ0O1xuICAgICAgICAgIC8qKiovXG4gICAgICAgIH1cbiAgICAgIH0gd2hpbGUgKC0tcy5wcmV2X2xlbmd0aCAhPT0gMCk7XG4gICAgICBzLm1hdGNoX2F2YWlsYWJsZSA9IDA7XG4gICAgICBzLm1hdGNoX2xlbmd0aCA9IE1JTl9NQVRDSC0xO1xuICAgICAgcy5zdHJzdGFydCsrO1xuXG4gICAgICBpZiAoYmZsdXNoKSB7XG4gICAgICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgICAgfVxuICAgICAgICAvKioqL1xuICAgICAgfVxuXG4gICAgfSBlbHNlIGlmIChzLm1hdGNoX2F2YWlsYWJsZSkge1xuICAgICAgLyogSWYgdGhlcmUgd2FzIG5vIG1hdGNoIGF0IHRoZSBwcmV2aW91cyBwb3NpdGlvbiwgb3V0cHV0IGFcbiAgICAgICAqIHNpbmdsZSBsaXRlcmFsLiBJZiB0aGVyZSB3YXMgYSBtYXRjaCBidXQgdGhlIGN1cnJlbnQgbWF0Y2hcbiAgICAgICAqIGlzIGxvbmdlciwgdHJ1bmNhdGUgdGhlIHByZXZpb3VzIG1hdGNoIHRvIGEgc2luZ2xlIGxpdGVyYWwuXG4gICAgICAgKi9cbiAgICAgIC8vVHJhY2V2digoc3RkZXJyLFwiJWNcIiwgcy0+d2luZG93W3MtPnN0cnN0YXJ0LTFdKSk7XG4gICAgICAvKioqIF90cl90YWxseV9saXQocywgcy53aW5kb3dbcy5zdHJzdGFydC0xXSwgYmZsdXNoKTsgKioqL1xuICAgICAgYmZsdXNoID0gdHJlZXMuX3RyX3RhbGx5KHMsIDAsIHMud2luZG93W3Muc3Ryc3RhcnQtMV0pO1xuXG4gICAgICBpZiAoYmZsdXNoKSB7XG4gICAgICAgIC8qKiogRkxVU0hfQkxPQ0tfT05MWShzLCAwKSAqKiovXG4gICAgICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgICAgICAvKioqL1xuICAgICAgfVxuICAgICAgcy5zdHJzdGFydCsrO1xuICAgICAgcy5sb29rYWhlYWQtLTtcbiAgICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qIFRoZXJlIGlzIG5vIHByZXZpb3VzIG1hdGNoIHRvIGNvbXBhcmUgd2l0aCwgd2FpdCBmb3JcbiAgICAgICAqIHRoZSBuZXh0IHN0ZXAgdG8gZGVjaWRlLlxuICAgICAgICovXG4gICAgICBzLm1hdGNoX2F2YWlsYWJsZSA9IDE7XG4gICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgICBzLmxvb2thaGVhZC0tO1xuICAgIH1cbiAgfVxuICAvL0Fzc2VydCAoZmx1c2ggIT0gWl9OT19GTFVTSCwgXCJubyBmbHVzaD9cIik7XG4gIGlmIChzLm1hdGNoX2F2YWlsYWJsZSkge1xuICAgIC8vVHJhY2V2digoc3RkZXJyLFwiJWNcIiwgcy0+d2luZG93W3MtPnN0cnN0YXJ0LTFdKSk7XG4gICAgLyoqKiBfdHJfdGFsbHlfbGl0KHMsIHMud2luZG93W3Muc3Ryc3RhcnQtMV0sIGJmbHVzaCk7ICoqKi9cbiAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgMCwgcy53aW5kb3dbcy5zdHJzdGFydC0xXSk7XG5cbiAgICBzLm1hdGNoX2F2YWlsYWJsZSA9IDA7XG4gIH1cbiAgcy5pbnNlcnQgPSBzLnN0cnN0YXJ0IDwgTUlOX01BVENILTEgPyBzLnN0cnN0YXJ0IDogTUlOX01BVENILTE7XG4gIGlmIChmbHVzaCA9PT0gWl9GSU5JU0gpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDEpOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCB0cnVlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX0ZJTklTSF9TVEFSVEVEO1xuICAgIH1cbiAgICAvKioqL1xuICAgIHJldHVybiBCU19GSU5JU0hfRE9ORTtcbiAgfVxuICBpZiAocy5sYXN0X2xpdCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICB9XG4gICAgLyoqKi9cbiAgfVxuXG4gIHJldHVybiBCU19CTE9DS19ET05FO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogRm9yIFpfUkxFLCBzaW1wbHkgbG9vayBmb3IgcnVucyBvZiBieXRlcywgZ2VuZXJhdGUgbWF0Y2hlcyBvbmx5IG9mIGRpc3RhbmNlXG4gKiBvbmUuICBEbyBub3QgbWFpbnRhaW4gYSBoYXNoIHRhYmxlLiAgKEl0IHdpbGwgYmUgcmVnZW5lcmF0ZWQgaWYgdGhpcyBydW4gb2ZcbiAqIGRlZmxhdGUgc3dpdGNoZXMgYXdheSBmcm9tIFpfUkxFLilcbiAqL1xuZnVuY3Rpb24gZGVmbGF0ZV9ybGUocywgZmx1c2gpIHtcbiAgdmFyIGJmbHVzaDsgICAgICAgICAgICAvKiBzZXQgaWYgY3VycmVudCBibG9jayBtdXN0IGJlIGZsdXNoZWQgKi9cbiAgdmFyIHByZXY7ICAgICAgICAgICAgICAvKiBieXRlIGF0IGRpc3RhbmNlIG9uZSB0byBtYXRjaCAqL1xuICB2YXIgc2Nhbiwgc3RyZW5kOyAgICAgIC8qIHNjYW4gZ29lcyB1cCB0byBzdHJlbmQgZm9yIGxlbmd0aCBvZiBydW4gKi9cblxuICB2YXIgX3dpbiA9IHMud2luZG93O1xuXG4gIGZvciAoOzspIHtcbiAgICAvKiBNYWtlIHN1cmUgdGhhdCB3ZSBhbHdheXMgaGF2ZSBlbm91Z2ggbG9va2FoZWFkLCBleGNlcHRcbiAgICAgKiBhdCB0aGUgZW5kIG9mIHRoZSBpbnB1dCBmaWxlLiBXZSBuZWVkIE1BWF9NQVRDSCBieXRlc1xuICAgICAqIGZvciB0aGUgbG9uZ2VzdCBydW4sIHBsdXMgb25lIGZvciB0aGUgdW5yb2xsZWQgbG9vcC5cbiAgICAgKi9cbiAgICBpZiAocy5sb29rYWhlYWQgPD0gTUFYX01BVENIKSB7XG4gICAgICBmaWxsX3dpbmRvdyhzKTtcbiAgICAgIGlmIChzLmxvb2thaGVhZCA8PSBNQVhfTUFUQ0ggJiYgZmx1c2ggPT09IFpfTk9fRkxVU0gpIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkgeyBicmVhazsgfSAvKiBmbHVzaCB0aGUgY3VycmVudCBibG9jayAqL1xuICAgIH1cblxuICAgIC8qIFNlZSBob3cgbWFueSB0aW1lcyB0aGUgcHJldmlvdXMgYnl0ZSByZXBlYXRzICovXG4gICAgcy5tYXRjaF9sZW5ndGggPSAwO1xuICAgIGlmIChzLmxvb2thaGVhZCA+PSBNSU5fTUFUQ0ggJiYgcy5zdHJzdGFydCA+IDApIHtcbiAgICAgIHNjYW4gPSBzLnN0cnN0YXJ0IC0gMTtcbiAgICAgIHByZXYgPSBfd2luW3NjYW5dO1xuICAgICAgaWYgKHByZXYgPT09IF93aW5bKytzY2FuXSAmJiBwcmV2ID09PSBfd2luWysrc2Nhbl0gJiYgcHJldiA9PT0gX3dpblsrK3NjYW5dKSB7XG4gICAgICAgIHN0cmVuZCA9IHMuc3Ryc3RhcnQgKyBNQVhfTUFUQ0g7XG4gICAgICAgIGRvIHtcbiAgICAgICAgICAvKmpzaGludCBub2VtcHR5OmZhbHNlKi9cbiAgICAgICAgfSB3aGlsZSAocHJldiA9PT0gX3dpblsrK3NjYW5dICYmIHByZXYgPT09IF93aW5bKytzY2FuXSAmJlxuICAgICAgICAgICAgICAgICBwcmV2ID09PSBfd2luWysrc2Nhbl0gJiYgcHJldiA9PT0gX3dpblsrK3NjYW5dICYmXG4gICAgICAgICAgICAgICAgIHByZXYgPT09IF93aW5bKytzY2FuXSAmJiBwcmV2ID09PSBfd2luWysrc2Nhbl0gJiZcbiAgICAgICAgICAgICAgICAgcHJldiA9PT0gX3dpblsrK3NjYW5dICYmIHByZXYgPT09IF93aW5bKytzY2FuXSAmJlxuICAgICAgICAgICAgICAgICBzY2FuIDwgc3RyZW5kKTtcbiAgICAgICAgcy5tYXRjaF9sZW5ndGggPSBNQVhfTUFUQ0ggLSAoc3RyZW5kIC0gc2Nhbik7XG4gICAgICAgIGlmIChzLm1hdGNoX2xlbmd0aCA+IHMubG9va2FoZWFkKSB7XG4gICAgICAgICAgcy5tYXRjaF9sZW5ndGggPSBzLmxvb2thaGVhZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy9Bc3NlcnQoc2NhbiA8PSBzLT53aW5kb3crKHVJbnQpKHMtPndpbmRvd19zaXplLTEpLCBcIndpbGQgc2NhblwiKTtcbiAgICB9XG5cbiAgICAvKiBFbWl0IG1hdGNoIGlmIGhhdmUgcnVuIG9mIE1JTl9NQVRDSCBvciBsb25nZXIsIGVsc2UgZW1pdCBsaXRlcmFsICovXG4gICAgaWYgKHMubWF0Y2hfbGVuZ3RoID49IE1JTl9NQVRDSCkge1xuICAgICAgLy9jaGVja19tYXRjaChzLCBzLnN0cnN0YXJ0LCBzLnN0cnN0YXJ0IC0gMSwgcy5tYXRjaF9sZW5ndGgpO1xuXG4gICAgICAvKioqIF90cl90YWxseV9kaXN0KHMsIDEsIHMubWF0Y2hfbGVuZ3RoIC0gTUlOX01BVENILCBiZmx1c2gpOyAqKiovXG4gICAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgMSwgcy5tYXRjaF9sZW5ndGggLSBNSU5fTUFUQ0gpO1xuXG4gICAgICBzLmxvb2thaGVhZCAtPSBzLm1hdGNoX2xlbmd0aDtcbiAgICAgIHMuc3Ryc3RhcnQgKz0gcy5tYXRjaF9sZW5ndGg7XG4gICAgICBzLm1hdGNoX2xlbmd0aCA9IDA7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qIE5vIG1hdGNoLCBvdXRwdXQgYSBsaXRlcmFsIGJ5dGUgKi9cbiAgICAgIC8vVHJhY2V2digoc3RkZXJyLFwiJWNcIiwgcy0+d2luZG93W3MtPnN0cnN0YXJ0XSkpO1xuICAgICAgLyoqKiBfdHJfdGFsbHlfbGl0KHMsIHMud2luZG93W3Muc3Ryc3RhcnRdLCBiZmx1c2gpOyAqKiovXG4gICAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgMCwgcy53aW5kb3dbcy5zdHJzdGFydF0pO1xuXG4gICAgICBzLmxvb2thaGVhZC0tO1xuICAgICAgcy5zdHJzdGFydCsrO1xuICAgIH1cbiAgICBpZiAoYmZsdXNoKSB7XG4gICAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDApOyAqKiovXG4gICAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgICB9XG4gICAgICAvKioqL1xuICAgIH1cbiAgfVxuICBzLmluc2VydCA9IDA7XG4gIGlmIChmbHVzaCA9PT0gWl9GSU5JU0gpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDEpOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCB0cnVlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX0ZJTklTSF9TVEFSVEVEO1xuICAgIH1cbiAgICAvKioqL1xuICAgIHJldHVybiBCU19GSU5JU0hfRE9ORTtcbiAgfVxuICBpZiAocy5sYXN0X2xpdCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICB9XG4gICAgLyoqKi9cbiAgfVxuICByZXR1cm4gQlNfQkxPQ0tfRE9ORTtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBGb3IgWl9IVUZGTUFOX09OTFksIGRvIG5vdCBsb29rIGZvciBtYXRjaGVzLiAgRG8gbm90IG1haW50YWluIGEgaGFzaCB0YWJsZS5cbiAqIChJdCB3aWxsIGJlIHJlZ2VuZXJhdGVkIGlmIHRoaXMgcnVuIG9mIGRlZmxhdGUgc3dpdGNoZXMgYXdheSBmcm9tIEh1ZmZtYW4uKVxuICovXG5mdW5jdGlvbiBkZWZsYXRlX2h1ZmYocywgZmx1c2gpIHtcbiAgdmFyIGJmbHVzaDsgICAgICAgICAgICAgLyogc2V0IGlmIGN1cnJlbnQgYmxvY2sgbXVzdCBiZSBmbHVzaGVkICovXG5cbiAgZm9yICg7Oykge1xuICAgIC8qIE1ha2Ugc3VyZSB0aGF0IHdlIGhhdmUgYSBsaXRlcmFsIHRvIHdyaXRlLiAqL1xuICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkge1xuICAgICAgZmlsbF93aW5kb3cocyk7XG4gICAgICBpZiAocy5sb29rYWhlYWQgPT09IDApIHtcbiAgICAgICAgaWYgKGZsdXNoID09PSBaX05PX0ZMVVNIKSB7XG4gICAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgICAgfVxuICAgICAgICBicmVhazsgICAgICAvKiBmbHVzaCB0aGUgY3VycmVudCBibG9jayAqL1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qIE91dHB1dCBhIGxpdGVyYWwgYnl0ZSAqL1xuICAgIHMubWF0Y2hfbGVuZ3RoID0gMDtcbiAgICAvL1RyYWNldnYoKHN0ZGVycixcIiVjXCIsIHMtPndpbmRvd1tzLT5zdHJzdGFydF0pKTtcbiAgICAvKioqIF90cl90YWxseV9saXQocywgcy53aW5kb3dbcy5zdHJzdGFydF0sIGJmbHVzaCk7ICoqKi9cbiAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgMCwgcy53aW5kb3dbcy5zdHJzdGFydF0pO1xuICAgIHMubG9va2FoZWFkLS07XG4gICAgcy5zdHJzdGFydCsrO1xuICAgIGlmIChiZmx1c2gpIHtcbiAgICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIC8qKiovXG4gICAgfVxuICB9XG4gIHMuaW5zZXJ0ID0gMDtcbiAgaWYgKGZsdXNoID09PSBaX0ZJTklTSCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMSk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIHRydWUpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfRklOSVNIX1NUQVJURUQ7XG4gICAgfVxuICAgIC8qKiovXG4gICAgcmV0dXJuIEJTX0ZJTklTSF9ET05FO1xuICB9XG4gIGlmIChzLmxhc3RfbGl0KSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgIH1cbiAgICAvKioqL1xuICB9XG4gIHJldHVybiBCU19CTE9DS19ET05FO1xufVxuXG4vKiBWYWx1ZXMgZm9yIG1heF9sYXp5X21hdGNoLCBnb29kX21hdGNoIGFuZCBtYXhfY2hhaW5fbGVuZ3RoLCBkZXBlbmRpbmcgb25cbiAqIHRoZSBkZXNpcmVkIHBhY2sgbGV2ZWwgKDAuLjkpLiBUaGUgdmFsdWVzIGdpdmVuIGJlbG93IGhhdmUgYmVlbiB0dW5lZCB0b1xuICogZXhjbHVkZSB3b3JzdCBjYXNlIHBlcmZvcm1hbmNlIGZvciBwYXRob2xvZ2ljYWwgZmlsZXMuIEJldHRlciB2YWx1ZXMgbWF5IGJlXG4gKiBmb3VuZCBmb3Igc3BlY2lmaWMgZmlsZXMuXG4gKi9cbnZhciBDb25maWcgPSBmdW5jdGlvbiAoZ29vZF9sZW5ndGgsIG1heF9sYXp5LCBuaWNlX2xlbmd0aCwgbWF4X2NoYWluLCBmdW5jKSB7XG4gIHRoaXMuZ29vZF9sZW5ndGggPSBnb29kX2xlbmd0aDtcbiAgdGhpcy5tYXhfbGF6eSA9IG1heF9sYXp5O1xuICB0aGlzLm5pY2VfbGVuZ3RoID0gbmljZV9sZW5ndGg7XG4gIHRoaXMubWF4X2NoYWluID0gbWF4X2NoYWluO1xuICB0aGlzLmZ1bmMgPSBmdW5jO1xufTtcblxudmFyIGNvbmZpZ3VyYXRpb25fdGFibGU7XG5cbmNvbmZpZ3VyYXRpb25fdGFibGUgPSBbXG4gIC8qICAgICAgZ29vZCBsYXp5IG5pY2UgY2hhaW4gKi9cbiAgbmV3IENvbmZpZygwLCAwLCAwLCAwLCBkZWZsYXRlX3N0b3JlZCksICAgICAgICAgIC8qIDAgc3RvcmUgb25seSAqL1xuICBuZXcgQ29uZmlnKDQsIDQsIDgsIDQsIGRlZmxhdGVfZmFzdCksICAgICAgICAgICAgLyogMSBtYXggc3BlZWQsIG5vIGxhenkgbWF0Y2hlcyAqL1xuICBuZXcgQ29uZmlnKDQsIDUsIDE2LCA4LCBkZWZsYXRlX2Zhc3QpLCAgICAgICAgICAgLyogMiAqL1xuICBuZXcgQ29uZmlnKDQsIDYsIDMyLCAzMiwgZGVmbGF0ZV9mYXN0KSwgICAgICAgICAgLyogMyAqL1xuXG4gIG5ldyBDb25maWcoNCwgNCwgMTYsIDE2LCBkZWZsYXRlX3Nsb3cpLCAgICAgICAgICAvKiA0IGxhenkgbWF0Y2hlcyAqL1xuICBuZXcgQ29uZmlnKDgsIDE2LCAzMiwgMzIsIGRlZmxhdGVfc2xvdyksICAgICAgICAgLyogNSAqL1xuICBuZXcgQ29uZmlnKDgsIDE2LCAxMjgsIDEyOCwgZGVmbGF0ZV9zbG93KSwgICAgICAgLyogNiAqL1xuICBuZXcgQ29uZmlnKDgsIDMyLCAxMjgsIDI1NiwgZGVmbGF0ZV9zbG93KSwgICAgICAgLyogNyAqL1xuICBuZXcgQ29uZmlnKDMyLCAxMjgsIDI1OCwgMTAyNCwgZGVmbGF0ZV9zbG93KSwgICAgLyogOCAqL1xuICBuZXcgQ29uZmlnKDMyLCAyNTgsIDI1OCwgNDA5NiwgZGVmbGF0ZV9zbG93KSAgICAgLyogOSBtYXggY29tcHJlc3Npb24gKi9cbl07XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBJbml0aWFsaXplIHRoZSBcImxvbmdlc3QgbWF0Y2hcIiByb3V0aW5lcyBmb3IgYSBuZXcgemxpYiBzdHJlYW1cbiAqL1xuZnVuY3Rpb24gbG1faW5pdChzKSB7XG4gIHMud2luZG93X3NpemUgPSAyICogcy53X3NpemU7XG5cbiAgLyoqKiBDTEVBUl9IQVNIKHMpOyAqKiovXG4gIHplcm8ocy5oZWFkKTsgLy8gRmlsbCB3aXRoIE5JTCAoPSAwKTtcblxuICAvKiBTZXQgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBwYXJhbWV0ZXJzOlxuICAgKi9cbiAgcy5tYXhfbGF6eV9tYXRjaCA9IGNvbmZpZ3VyYXRpb25fdGFibGVbcy5sZXZlbF0ubWF4X2xhenk7XG4gIHMuZ29vZF9tYXRjaCA9IGNvbmZpZ3VyYXRpb25fdGFibGVbcy5sZXZlbF0uZ29vZF9sZW5ndGg7XG4gIHMubmljZV9tYXRjaCA9IGNvbmZpZ3VyYXRpb25fdGFibGVbcy5sZXZlbF0ubmljZV9sZW5ndGg7XG4gIHMubWF4X2NoYWluX2xlbmd0aCA9IGNvbmZpZ3VyYXRpb25fdGFibGVbcy5sZXZlbF0ubWF4X2NoYWluO1xuXG4gIHMuc3Ryc3RhcnQgPSAwO1xuICBzLmJsb2NrX3N0YXJ0ID0gMDtcbiAgcy5sb29rYWhlYWQgPSAwO1xuICBzLmluc2VydCA9IDA7XG4gIHMubWF0Y2hfbGVuZ3RoID0gcy5wcmV2X2xlbmd0aCA9IE1JTl9NQVRDSCAtIDE7XG4gIHMubWF0Y2hfYXZhaWxhYmxlID0gMDtcbiAgcy5pbnNfaCA9IDA7XG59XG5cblxuZnVuY3Rpb24gRGVmbGF0ZVN0YXRlKCkge1xuICB0aGlzLnN0cm0gPSBudWxsOyAgICAgICAgICAgIC8qIHBvaW50ZXIgYmFjayB0byB0aGlzIHpsaWIgc3RyZWFtICovXG4gIHRoaXMuc3RhdHVzID0gMDsgICAgICAgICAgICAvKiBhcyB0aGUgbmFtZSBpbXBsaWVzICovXG4gIHRoaXMucGVuZGluZ19idWYgPSBudWxsOyAgICAgIC8qIG91dHB1dCBzdGlsbCBwZW5kaW5nICovXG4gIHRoaXMucGVuZGluZ19idWZfc2l6ZSA9IDA7ICAvKiBzaXplIG9mIHBlbmRpbmdfYnVmICovXG4gIHRoaXMucGVuZGluZ19vdXQgPSAwOyAgICAgICAvKiBuZXh0IHBlbmRpbmcgYnl0ZSB0byBvdXRwdXQgdG8gdGhlIHN0cmVhbSAqL1xuICB0aGlzLnBlbmRpbmcgPSAwOyAgICAgICAgICAgLyogbmIgb2YgYnl0ZXMgaW4gdGhlIHBlbmRpbmcgYnVmZmVyICovXG4gIHRoaXMud3JhcCA9IDA7ICAgICAgICAgICAgICAvKiBiaXQgMCB0cnVlIGZvciB6bGliLCBiaXQgMSB0cnVlIGZvciBnemlwICovXG4gIHRoaXMuZ3poZWFkID0gbnVsbDsgICAgICAgICAvKiBnemlwIGhlYWRlciBpbmZvcm1hdGlvbiB0byB3cml0ZSAqL1xuICB0aGlzLmd6aW5kZXggPSAwOyAgICAgICAgICAgLyogd2hlcmUgaW4gZXh0cmEsIG5hbWUsIG9yIGNvbW1lbnQgKi9cbiAgdGhpcy5tZXRob2QgPSBaX0RFRkxBVEVEOyAvKiBjYW4gb25seSBiZSBERUZMQVRFRCAqL1xuICB0aGlzLmxhc3RfZmx1c2ggPSAtMTsgICAvKiB2YWx1ZSBvZiBmbHVzaCBwYXJhbSBmb3IgcHJldmlvdXMgZGVmbGF0ZSBjYWxsICovXG5cbiAgdGhpcy53X3NpemUgPSAwOyAgLyogTFo3NyB3aW5kb3cgc2l6ZSAoMzJLIGJ5IGRlZmF1bHQpICovXG4gIHRoaXMud19iaXRzID0gMDsgIC8qIGxvZzIod19zaXplKSAgKDguLjE2KSAqL1xuICB0aGlzLndfbWFzayA9IDA7ICAvKiB3X3NpemUgLSAxICovXG5cbiAgdGhpcy53aW5kb3cgPSBudWxsO1xuICAvKiBTbGlkaW5nIHdpbmRvdy4gSW5wdXQgYnl0ZXMgYXJlIHJlYWQgaW50byB0aGUgc2Vjb25kIGhhbGYgb2YgdGhlIHdpbmRvdyxcbiAgICogYW5kIG1vdmUgdG8gdGhlIGZpcnN0IGhhbGYgbGF0ZXIgdG8ga2VlcCBhIGRpY3Rpb25hcnkgb2YgYXQgbGVhc3Qgd1NpemVcbiAgICogYnl0ZXMuIFdpdGggdGhpcyBvcmdhbml6YXRpb24sIG1hdGNoZXMgYXJlIGxpbWl0ZWQgdG8gYSBkaXN0YW5jZSBvZlxuICAgKiB3U2l6ZS1NQVhfTUFUQ0ggYnl0ZXMsIGJ1dCB0aGlzIGVuc3VyZXMgdGhhdCBJTyBpcyBhbHdheXNcbiAgICogcGVyZm9ybWVkIHdpdGggYSBsZW5ndGggbXVsdGlwbGUgb2YgdGhlIGJsb2NrIHNpemUuXG4gICAqL1xuXG4gIHRoaXMud2luZG93X3NpemUgPSAwO1xuICAvKiBBY3R1YWwgc2l6ZSBvZiB3aW5kb3c6IDIqd1NpemUsIGV4Y2VwdCB3aGVuIHRoZSB1c2VyIGlucHV0IGJ1ZmZlclxuICAgKiBpcyBkaXJlY3RseSB1c2VkIGFzIHNsaWRpbmcgd2luZG93LlxuICAgKi9cblxuICB0aGlzLnByZXYgPSBudWxsO1xuICAvKiBMaW5rIHRvIG9sZGVyIHN0cmluZyB3aXRoIHNhbWUgaGFzaCBpbmRleC4gVG8gbGltaXQgdGhlIHNpemUgb2YgdGhpc1xuICAgKiBhcnJheSB0byA2NEssIHRoaXMgbGluayBpcyBtYWludGFpbmVkIG9ubHkgZm9yIHRoZSBsYXN0IDMySyBzdHJpbmdzLlxuICAgKiBBbiBpbmRleCBpbiB0aGlzIGFycmF5IGlzIHRodXMgYSB3aW5kb3cgaW5kZXggbW9kdWxvIDMySy5cbiAgICovXG5cbiAgdGhpcy5oZWFkID0gbnVsbDsgICAvKiBIZWFkcyBvZiB0aGUgaGFzaCBjaGFpbnMgb3IgTklMLiAqL1xuXG4gIHRoaXMuaW5zX2ggPSAwOyAgICAgICAvKiBoYXNoIGluZGV4IG9mIHN0cmluZyB0byBiZSBpbnNlcnRlZCAqL1xuICB0aGlzLmhhc2hfc2l6ZSA9IDA7ICAgLyogbnVtYmVyIG9mIGVsZW1lbnRzIGluIGhhc2ggdGFibGUgKi9cbiAgdGhpcy5oYXNoX2JpdHMgPSAwOyAgIC8qIGxvZzIoaGFzaF9zaXplKSAqL1xuICB0aGlzLmhhc2hfbWFzayA9IDA7ICAgLyogaGFzaF9zaXplLTEgKi9cblxuICB0aGlzLmhhc2hfc2hpZnQgPSAwO1xuICAvKiBOdW1iZXIgb2YgYml0cyBieSB3aGljaCBpbnNfaCBtdXN0IGJlIHNoaWZ0ZWQgYXQgZWFjaCBpbnB1dFxuICAgKiBzdGVwLiBJdCBtdXN0IGJlIHN1Y2ggdGhhdCBhZnRlciBNSU5fTUFUQ0ggc3RlcHMsIHRoZSBvbGRlc3RcbiAgICogYnl0ZSBubyBsb25nZXIgdGFrZXMgcGFydCBpbiB0aGUgaGFzaCBrZXksIHRoYXQgaXM6XG4gICAqICAgaGFzaF9zaGlmdCAqIE1JTl9NQVRDSCA+PSBoYXNoX2JpdHNcbiAgICovXG5cbiAgdGhpcy5ibG9ja19zdGFydCA9IDA7XG4gIC8qIFdpbmRvdyBwb3NpdGlvbiBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBjdXJyZW50IG91dHB1dCBibG9jay4gR2V0c1xuICAgKiBuZWdhdGl2ZSB3aGVuIHRoZSB3aW5kb3cgaXMgbW92ZWQgYmFja3dhcmRzLlxuICAgKi9cblxuICB0aGlzLm1hdGNoX2xlbmd0aCA9IDA7ICAgICAgLyogbGVuZ3RoIG9mIGJlc3QgbWF0Y2ggKi9cbiAgdGhpcy5wcmV2X21hdGNoID0gMDsgICAgICAgIC8qIHByZXZpb3VzIG1hdGNoICovXG4gIHRoaXMubWF0Y2hfYXZhaWxhYmxlID0gMDsgICAvKiBzZXQgaWYgcHJldmlvdXMgbWF0Y2ggZXhpc3RzICovXG4gIHRoaXMuc3Ryc3RhcnQgPSAwOyAgICAgICAgICAvKiBzdGFydCBvZiBzdHJpbmcgdG8gaW5zZXJ0ICovXG4gIHRoaXMubWF0Y2hfc3RhcnQgPSAwOyAgICAgICAvKiBzdGFydCBvZiBtYXRjaGluZyBzdHJpbmcgKi9cbiAgdGhpcy5sb29rYWhlYWQgPSAwOyAgICAgICAgIC8qIG51bWJlciBvZiB2YWxpZCBieXRlcyBhaGVhZCBpbiB3aW5kb3cgKi9cblxuICB0aGlzLnByZXZfbGVuZ3RoID0gMDtcbiAgLyogTGVuZ3RoIG9mIHRoZSBiZXN0IG1hdGNoIGF0IHByZXZpb3VzIHN0ZXAuIE1hdGNoZXMgbm90IGdyZWF0ZXIgdGhhbiB0aGlzXG4gICAqIGFyZSBkaXNjYXJkZWQuIFRoaXMgaXMgdXNlZCBpbiB0aGUgbGF6eSBtYXRjaCBldmFsdWF0aW9uLlxuICAgKi9cblxuICB0aGlzLm1heF9jaGFpbl9sZW5ndGggPSAwO1xuICAvKiBUbyBzcGVlZCB1cCBkZWZsYXRpb24sIGhhc2ggY2hhaW5zIGFyZSBuZXZlciBzZWFyY2hlZCBiZXlvbmQgdGhpc1xuICAgKiBsZW5ndGguICBBIGhpZ2hlciBsaW1pdCBpbXByb3ZlcyBjb21wcmVzc2lvbiByYXRpbyBidXQgZGVncmFkZXMgdGhlXG4gICAqIHNwZWVkLlxuICAgKi9cblxuICB0aGlzLm1heF9sYXp5X21hdGNoID0gMDtcbiAgLyogQXR0ZW1wdCB0byBmaW5kIGEgYmV0dGVyIG1hdGNoIG9ubHkgd2hlbiB0aGUgY3VycmVudCBtYXRjaCBpcyBzdHJpY3RseVxuICAgKiBzbWFsbGVyIHRoYW4gdGhpcyB2YWx1ZS4gVGhpcyBtZWNoYW5pc20gaXMgdXNlZCBvbmx5IGZvciBjb21wcmVzc2lvblxuICAgKiBsZXZlbHMgPj0gNC5cbiAgICovXG4gIC8vIFRoYXQncyBhbGlhcyB0byBtYXhfbGF6eV9tYXRjaCwgZG9uJ3QgdXNlIGRpcmVjdGx5XG4gIC8vdGhpcy5tYXhfaW5zZXJ0X2xlbmd0aCA9IDA7XG4gIC8qIEluc2VydCBuZXcgc3RyaW5ncyBpbiB0aGUgaGFzaCB0YWJsZSBvbmx5IGlmIHRoZSBtYXRjaCBsZW5ndGggaXMgbm90XG4gICAqIGdyZWF0ZXIgdGhhbiB0aGlzIGxlbmd0aC4gVGhpcyBzYXZlcyB0aW1lIGJ1dCBkZWdyYWRlcyBjb21wcmVzc2lvbi5cbiAgICogbWF4X2luc2VydF9sZW5ndGggaXMgdXNlZCBvbmx5IGZvciBjb21wcmVzc2lvbiBsZXZlbHMgPD0gMy5cbiAgICovXG5cbiAgdGhpcy5sZXZlbCA9IDA7ICAgICAvKiBjb21wcmVzc2lvbiBsZXZlbCAoMS4uOSkgKi9cbiAgdGhpcy5zdHJhdGVneSA9IDA7ICAvKiBmYXZvciBvciBmb3JjZSBIdWZmbWFuIGNvZGluZyovXG5cbiAgdGhpcy5nb29kX21hdGNoID0gMDtcbiAgLyogVXNlIGEgZmFzdGVyIHNlYXJjaCB3aGVuIHRoZSBwcmV2aW91cyBtYXRjaCBpcyBsb25nZXIgdGhhbiB0aGlzICovXG5cbiAgdGhpcy5uaWNlX21hdGNoID0gMDsgLyogU3RvcCBzZWFyY2hpbmcgd2hlbiBjdXJyZW50IG1hdGNoIGV4Y2VlZHMgdGhpcyAqL1xuXG4gICAgICAgICAgICAgIC8qIHVzZWQgYnkgdHJlZXMuYzogKi9cblxuICAvKiBEaWRuJ3QgdXNlIGN0X2RhdGEgdHlwZWRlZiBiZWxvdyB0byBzdXBwcmVzcyBjb21waWxlciB3YXJuaW5nICovXG5cbiAgLy8gc3RydWN0IGN0X2RhdGFfcyBkeW5fbHRyZWVbSEVBUF9TSVpFXTsgICAvKiBsaXRlcmFsIGFuZCBsZW5ndGggdHJlZSAqL1xuICAvLyBzdHJ1Y3QgY3RfZGF0YV9zIGR5bl9kdHJlZVsyKkRfQ09ERVMrMV07IC8qIGRpc3RhbmNlIHRyZWUgKi9cbiAgLy8gc3RydWN0IGN0X2RhdGFfcyBibF90cmVlWzIqQkxfQ09ERVMrMV07ICAvKiBIdWZmbWFuIHRyZWUgZm9yIGJpdCBsZW5ndGhzICovXG5cbiAgLy8gVXNlIGZsYXQgYXJyYXkgb2YgRE9VQkxFIHNpemUsIHdpdGggaW50ZXJsZWF2ZWQgZmF0YSxcbiAgLy8gYmVjYXVzZSBKUyBkb2VzIG5vdCBzdXBwb3J0IGVmZmVjdGl2ZVxuICB0aGlzLmR5bl9sdHJlZSAgPSBuZXcgdXRpbHMuQnVmMTYoSEVBUF9TSVpFICogMik7XG4gIHRoaXMuZHluX2R0cmVlICA9IG5ldyB1dGlscy5CdWYxNigoMipEX0NPREVTKzEpICogMik7XG4gIHRoaXMuYmxfdHJlZSAgICA9IG5ldyB1dGlscy5CdWYxNigoMipCTF9DT0RFUysxKSAqIDIpO1xuICB6ZXJvKHRoaXMuZHluX2x0cmVlKTtcbiAgemVybyh0aGlzLmR5bl9kdHJlZSk7XG4gIHplcm8odGhpcy5ibF90cmVlKTtcblxuICB0aGlzLmxfZGVzYyAgID0gbnVsbDsgICAgICAgICAvKiBkZXNjLiBmb3IgbGl0ZXJhbCB0cmVlICovXG4gIHRoaXMuZF9kZXNjICAgPSBudWxsOyAgICAgICAgIC8qIGRlc2MuIGZvciBkaXN0YW5jZSB0cmVlICovXG4gIHRoaXMuYmxfZGVzYyAgPSBudWxsOyAgICAgICAgIC8qIGRlc2MuIGZvciBiaXQgbGVuZ3RoIHRyZWUgKi9cblxuICAvL3VzaCBibF9jb3VudFtNQVhfQklUUysxXTtcbiAgdGhpcy5ibF9jb3VudCA9IG5ldyB1dGlscy5CdWYxNihNQVhfQklUUysxKTtcbiAgLyogbnVtYmVyIG9mIGNvZGVzIGF0IGVhY2ggYml0IGxlbmd0aCBmb3IgYW4gb3B0aW1hbCB0cmVlICovXG5cbiAgLy9pbnQgaGVhcFsyKkxfQ09ERVMrMV07ICAgICAgLyogaGVhcCB1c2VkIHRvIGJ1aWxkIHRoZSBIdWZmbWFuIHRyZWVzICovXG4gIHRoaXMuaGVhcCA9IG5ldyB1dGlscy5CdWYxNigyKkxfQ09ERVMrMSk7ICAvKiBoZWFwIHVzZWQgdG8gYnVpbGQgdGhlIEh1ZmZtYW4gdHJlZXMgKi9cbiAgemVybyh0aGlzLmhlYXApO1xuXG4gIHRoaXMuaGVhcF9sZW4gPSAwOyAgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBlbGVtZW50cyBpbiB0aGUgaGVhcCAqL1xuICB0aGlzLmhlYXBfbWF4ID0gMDsgICAgICAgICAgICAgICAvKiBlbGVtZW50IG9mIGxhcmdlc3QgZnJlcXVlbmN5ICovXG4gIC8qIFRoZSBzb25zIG9mIGhlYXBbbl0gYXJlIGhlYXBbMipuXSBhbmQgaGVhcFsyKm4rMV0uIGhlYXBbMF0gaXMgbm90IHVzZWQuXG4gICAqIFRoZSBzYW1lIGhlYXAgYXJyYXkgaXMgdXNlZCB0byBidWlsZCBhbGwgdHJlZXMuXG4gICAqL1xuXG4gIHRoaXMuZGVwdGggPSBuZXcgdXRpbHMuQnVmMTYoMipMX0NPREVTKzEpOyAvL3VjaCBkZXB0aFsyKkxfQ09ERVMrMV07XG4gIHplcm8odGhpcy5kZXB0aCk7XG4gIC8qIERlcHRoIG9mIGVhY2ggc3VidHJlZSB1c2VkIGFzIHRpZSBicmVha2VyIGZvciB0cmVlcyBvZiBlcXVhbCBmcmVxdWVuY3lcbiAgICovXG5cbiAgdGhpcy5sX2J1ZiA9IDA7ICAgICAgICAgIC8qIGJ1ZmZlciBpbmRleCBmb3IgbGl0ZXJhbHMgb3IgbGVuZ3RocyAqL1xuXG4gIHRoaXMubGl0X2J1ZnNpemUgPSAwO1xuICAvKiBTaXplIG9mIG1hdGNoIGJ1ZmZlciBmb3IgbGl0ZXJhbHMvbGVuZ3Rocy4gIFRoZXJlIGFyZSA0IHJlYXNvbnMgZm9yXG4gICAqIGxpbWl0aW5nIGxpdF9idWZzaXplIHRvIDY0SzpcbiAgICogICAtIGZyZXF1ZW5jaWVzIGNhbiBiZSBrZXB0IGluIDE2IGJpdCBjb3VudGVyc1xuICAgKiAgIC0gaWYgY29tcHJlc3Npb24gaXMgbm90IHN1Y2Nlc3NmdWwgZm9yIHRoZSBmaXJzdCBibG9jaywgYWxsIGlucHV0XG4gICAqICAgICBkYXRhIGlzIHN0aWxsIGluIHRoZSB3aW5kb3cgc28gd2UgY2FuIHN0aWxsIGVtaXQgYSBzdG9yZWQgYmxvY2sgZXZlblxuICAgKiAgICAgd2hlbiBpbnB1dCBjb21lcyBmcm9tIHN0YW5kYXJkIGlucHV0LiAgKFRoaXMgY2FuIGFsc28gYmUgZG9uZSBmb3JcbiAgICogICAgIGFsbCBibG9ja3MgaWYgbGl0X2J1ZnNpemUgaXMgbm90IGdyZWF0ZXIgdGhhbiAzMksuKVxuICAgKiAgIC0gaWYgY29tcHJlc3Npb24gaXMgbm90IHN1Y2Nlc3NmdWwgZm9yIGEgZmlsZSBzbWFsbGVyIHRoYW4gNjRLLCB3ZSBjYW5cbiAgICogICAgIGV2ZW4gZW1pdCBhIHN0b3JlZCBmaWxlIGluc3RlYWQgb2YgYSBzdG9yZWQgYmxvY2sgKHNhdmluZyA1IGJ5dGVzKS5cbiAgICogICAgIFRoaXMgaXMgYXBwbGljYWJsZSBvbmx5IGZvciB6aXAgKG5vdCBnemlwIG9yIHpsaWIpLlxuICAgKiAgIC0gY3JlYXRpbmcgbmV3IEh1ZmZtYW4gdHJlZXMgbGVzcyBmcmVxdWVudGx5IG1heSBub3QgcHJvdmlkZSBmYXN0XG4gICAqICAgICBhZGFwdGF0aW9uIHRvIGNoYW5nZXMgaW4gdGhlIGlucHV0IGRhdGEgc3RhdGlzdGljcy4gKFRha2UgZm9yXG4gICAqICAgICBleGFtcGxlIGEgYmluYXJ5IGZpbGUgd2l0aCBwb29ybHkgY29tcHJlc3NpYmxlIGNvZGUgZm9sbG93ZWQgYnlcbiAgICogICAgIGEgaGlnaGx5IGNvbXByZXNzaWJsZSBzdHJpbmcgdGFibGUuKSBTbWFsbGVyIGJ1ZmZlciBzaXplcyBnaXZlXG4gICAqICAgICBmYXN0IGFkYXB0YXRpb24gYnV0IGhhdmUgb2YgY291cnNlIHRoZSBvdmVyaGVhZCBvZiB0cmFuc21pdHRpbmdcbiAgICogICAgIHRyZWVzIG1vcmUgZnJlcXVlbnRseS5cbiAgICogICAtIEkgY2FuJ3QgY291bnQgYWJvdmUgNFxuICAgKi9cblxuICB0aGlzLmxhc3RfbGl0ID0gMDsgICAgICAvKiBydW5uaW5nIGluZGV4IGluIGxfYnVmICovXG5cbiAgdGhpcy5kX2J1ZiA9IDA7XG4gIC8qIEJ1ZmZlciBpbmRleCBmb3IgZGlzdGFuY2VzLiBUbyBzaW1wbGlmeSB0aGUgY29kZSwgZF9idWYgYW5kIGxfYnVmIGhhdmVcbiAgICogdGhlIHNhbWUgbnVtYmVyIG9mIGVsZW1lbnRzLiBUbyB1c2UgZGlmZmVyZW50IGxlbmd0aHMsIGFuIGV4dHJhIGZsYWdcbiAgICogYXJyYXkgd291bGQgYmUgbmVjZXNzYXJ5LlxuICAgKi9cblxuICB0aGlzLm9wdF9sZW4gPSAwOyAgICAgICAvKiBiaXQgbGVuZ3RoIG9mIGN1cnJlbnQgYmxvY2sgd2l0aCBvcHRpbWFsIHRyZWVzICovXG4gIHRoaXMuc3RhdGljX2xlbiA9IDA7ICAgIC8qIGJpdCBsZW5ndGggb2YgY3VycmVudCBibG9jayB3aXRoIHN0YXRpYyB0cmVlcyAqL1xuICB0aGlzLm1hdGNoZXMgPSAwOyAgICAgICAvKiBudW1iZXIgb2Ygc3RyaW5nIG1hdGNoZXMgaW4gY3VycmVudCBibG9jayAqL1xuICB0aGlzLmluc2VydCA9IDA7ICAgICAgICAvKiBieXRlcyBhdCBlbmQgb2Ygd2luZG93IGxlZnQgdG8gaW5zZXJ0ICovXG5cblxuICB0aGlzLmJpX2J1ZiA9IDA7XG4gIC8qIE91dHB1dCBidWZmZXIuIGJpdHMgYXJlIGluc2VydGVkIHN0YXJ0aW5nIGF0IHRoZSBib3R0b20gKGxlYXN0XG4gICAqIHNpZ25pZmljYW50IGJpdHMpLlxuICAgKi9cbiAgdGhpcy5iaV92YWxpZCA9IDA7XG4gIC8qIE51bWJlciBvZiB2YWxpZCBiaXRzIGluIGJpX2J1Zi4gIEFsbCBiaXRzIGFib3ZlIHRoZSBsYXN0IHZhbGlkIGJpdFxuICAgKiBhcmUgYWx3YXlzIHplcm8uXG4gICAqL1xuXG4gIC8vIFVzZWQgZm9yIHdpbmRvdyBtZW1vcnkgaW5pdC4gV2Ugc2FmZWx5IGlnbm9yZSBpdCBmb3IgSlMuIFRoYXQgbWFrZXNcbiAgLy8gc2Vuc2Ugb25seSBmb3IgcG9pbnRlcnMgYW5kIG1lbW9yeSBjaGVjayB0b29scy5cbiAgLy90aGlzLmhpZ2hfd2F0ZXIgPSAwO1xuICAvKiBIaWdoIHdhdGVyIG1hcmsgb2Zmc2V0IGluIHdpbmRvdyBmb3IgaW5pdGlhbGl6ZWQgYnl0ZXMgLS0gYnl0ZXMgYWJvdmVcbiAgICogdGhpcyBhcmUgc2V0IHRvIHplcm8gaW4gb3JkZXIgdG8gYXZvaWQgbWVtb3J5IGNoZWNrIHdhcm5pbmdzIHdoZW5cbiAgICogbG9uZ2VzdCBtYXRjaCByb3V0aW5lcyBhY2Nlc3MgYnl0ZXMgcGFzdCB0aGUgaW5wdXQuICBUaGlzIGlzIHRoZW5cbiAgICogdXBkYXRlZCB0byB0aGUgbmV3IGhpZ2ggd2F0ZXIgbWFyay5cbiAgICovXG59XG5cblxuZnVuY3Rpb24gZGVmbGF0ZVJlc2V0S2VlcChzdHJtKSB7XG4gIHZhciBzO1xuXG4gIGlmICghc3RybSB8fCAhc3RybS5zdGF0ZSkge1xuICAgIHJldHVybiBlcnIoc3RybSwgWl9TVFJFQU1fRVJST1IpO1xuICB9XG5cbiAgc3RybS50b3RhbF9pbiA9IHN0cm0udG90YWxfb3V0ID0gMDtcbiAgc3RybS5kYXRhX3R5cGUgPSBaX1VOS05PV047XG5cbiAgcyA9IHN0cm0uc3RhdGU7XG4gIHMucGVuZGluZyA9IDA7XG4gIHMucGVuZGluZ19vdXQgPSAwO1xuXG4gIGlmIChzLndyYXAgPCAwKSB7XG4gICAgcy53cmFwID0gLXMud3JhcDtcbiAgICAvKiB3YXMgbWFkZSBuZWdhdGl2ZSBieSBkZWZsYXRlKC4uLiwgWl9GSU5JU0gpOyAqL1xuICB9XG4gIHMuc3RhdHVzID0gKHMud3JhcCA/IElOSVRfU1RBVEUgOiBCVVNZX1NUQVRFKTtcbiAgc3RybS5hZGxlciA9IChzLndyYXAgPT09IDIpID9cbiAgICAwICAvLyBjcmMzMigwLCBaX05VTEwsIDApXG4gIDpcbiAgICAxOyAvLyBhZGxlcjMyKDAsIFpfTlVMTCwgMClcbiAgcy5sYXN0X2ZsdXNoID0gWl9OT19GTFVTSDtcbiAgdHJlZXMuX3RyX2luaXQocyk7XG4gIHJldHVybiBaX09LO1xufVxuXG5cbmZ1bmN0aW9uIGRlZmxhdGVSZXNldChzdHJtKSB7XG4gIHZhciByZXQgPSBkZWZsYXRlUmVzZXRLZWVwKHN0cm0pO1xuICBpZiAocmV0ID09PSBaX09LKSB7XG4gICAgbG1faW5pdChzdHJtLnN0YXRlKTtcbiAgfVxuICByZXR1cm4gcmV0O1xufVxuXG5cbmZ1bmN0aW9uIGRlZmxhdGVTZXRIZWFkZXIoc3RybSwgaGVhZCkge1xuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUpIHsgcmV0dXJuIFpfU1RSRUFNX0VSUk9SOyB9XG4gIGlmIChzdHJtLnN0YXRlLndyYXAgIT09IDIpIHsgcmV0dXJuIFpfU1RSRUFNX0VSUk9SOyB9XG4gIHN0cm0uc3RhdGUuZ3poZWFkID0gaGVhZDtcbiAgcmV0dXJuIFpfT0s7XG59XG5cblxuZnVuY3Rpb24gZGVmbGF0ZUluaXQyKHN0cm0sIGxldmVsLCBtZXRob2QsIHdpbmRvd0JpdHMsIG1lbUxldmVsLCBzdHJhdGVneSkge1xuICBpZiAoIXN0cm0pIHsgLy8gPT09IFpfTlVMTFxuICAgIHJldHVybiBaX1NUUkVBTV9FUlJPUjtcbiAgfVxuICB2YXIgd3JhcCA9IDE7XG5cbiAgaWYgKGxldmVsID09PSBaX0RFRkFVTFRfQ09NUFJFU1NJT04pIHtcbiAgICBsZXZlbCA9IDY7XG4gIH1cblxuICBpZiAod2luZG93Qml0cyA8IDApIHsgLyogc3VwcHJlc3MgemxpYiB3cmFwcGVyICovXG4gICAgd3JhcCA9IDA7XG4gICAgd2luZG93Qml0cyA9IC13aW5kb3dCaXRzO1xuICB9XG5cbiAgZWxzZSBpZiAod2luZG93Qml0cyA+IDE1KSB7XG4gICAgd3JhcCA9IDI7ICAgICAgICAgICAvKiB3cml0ZSBnemlwIHdyYXBwZXIgaW5zdGVhZCAqL1xuICAgIHdpbmRvd0JpdHMgLT0gMTY7XG4gIH1cblxuXG4gIGlmIChtZW1MZXZlbCA8IDEgfHwgbWVtTGV2ZWwgPiBNQVhfTUVNX0xFVkVMIHx8IG1ldGhvZCAhPT0gWl9ERUZMQVRFRCB8fFxuICAgIHdpbmRvd0JpdHMgPCA4IHx8IHdpbmRvd0JpdHMgPiAxNSB8fCBsZXZlbCA8IDAgfHwgbGV2ZWwgPiA5IHx8XG4gICAgc3RyYXRlZ3kgPCAwIHx8IHN0cmF0ZWd5ID4gWl9GSVhFRCkge1xuICAgIHJldHVybiBlcnIoc3RybSwgWl9TVFJFQU1fRVJST1IpO1xuICB9XG5cblxuICBpZiAod2luZG93Qml0cyA9PT0gOCkge1xuICAgIHdpbmRvd0JpdHMgPSA5O1xuICB9XG4gIC8qIHVudGlsIDI1Ni1ieXRlIHdpbmRvdyBidWcgZml4ZWQgKi9cblxuICB2YXIgcyA9IG5ldyBEZWZsYXRlU3RhdGUoKTtcblxuICBzdHJtLnN0YXRlID0gcztcbiAgcy5zdHJtID0gc3RybTtcblxuICBzLndyYXAgPSB3cmFwO1xuICBzLmd6aGVhZCA9IG51bGw7XG4gIHMud19iaXRzID0gd2luZG93Qml0cztcbiAgcy53X3NpemUgPSAxIDw8IHMud19iaXRzO1xuICBzLndfbWFzayA9IHMud19zaXplIC0gMTtcblxuICBzLmhhc2hfYml0cyA9IG1lbUxldmVsICsgNztcbiAgcy5oYXNoX3NpemUgPSAxIDw8IHMuaGFzaF9iaXRzO1xuICBzLmhhc2hfbWFzayA9IHMuaGFzaF9zaXplIC0gMTtcbiAgcy5oYXNoX3NoaWZ0ID0gfn4oKHMuaGFzaF9iaXRzICsgTUlOX01BVENIIC0gMSkgLyBNSU5fTUFUQ0gpO1xuXG4gIHMud2luZG93ID0gbmV3IHV0aWxzLkJ1Zjgocy53X3NpemUgKiAyKTtcbiAgcy5oZWFkID0gbmV3IHV0aWxzLkJ1ZjE2KHMuaGFzaF9zaXplKTtcbiAgcy5wcmV2ID0gbmV3IHV0aWxzLkJ1ZjE2KHMud19zaXplKTtcblxuICAvLyBEb24ndCBuZWVkIG1lbSBpbml0IG1hZ2ljIGZvciBKUy5cbiAgLy9zLmhpZ2hfd2F0ZXIgPSAwOyAgLyogbm90aGluZyB3cml0dGVuIHRvIHMtPndpbmRvdyB5ZXQgKi9cblxuICBzLmxpdF9idWZzaXplID0gMSA8PCAobWVtTGV2ZWwgKyA2KTsgLyogMTZLIGVsZW1lbnRzIGJ5IGRlZmF1bHQgKi9cblxuICBzLnBlbmRpbmdfYnVmX3NpemUgPSBzLmxpdF9idWZzaXplICogNDtcbiAgcy5wZW5kaW5nX2J1ZiA9IG5ldyB1dGlscy5CdWY4KHMucGVuZGluZ19idWZfc2l6ZSk7XG5cbiAgcy5kX2J1ZiA9IHMubGl0X2J1ZnNpemUgPj4gMTtcbiAgcy5sX2J1ZiA9ICgxICsgMikgKiBzLmxpdF9idWZzaXplO1xuXG4gIHMubGV2ZWwgPSBsZXZlbDtcbiAgcy5zdHJhdGVneSA9IHN0cmF0ZWd5O1xuICBzLm1ldGhvZCA9IG1ldGhvZDtcblxuICByZXR1cm4gZGVmbGF0ZVJlc2V0KHN0cm0pO1xufVxuXG5mdW5jdGlvbiBkZWZsYXRlSW5pdChzdHJtLCBsZXZlbCkge1xuICByZXR1cm4gZGVmbGF0ZUluaXQyKHN0cm0sIGxldmVsLCBaX0RFRkxBVEVELCBNQVhfV0JJVFMsIERFRl9NRU1fTEVWRUwsIFpfREVGQVVMVF9TVFJBVEVHWSk7XG59XG5cblxuZnVuY3Rpb24gZGVmbGF0ZShzdHJtLCBmbHVzaCkge1xuICB2YXIgb2xkX2ZsdXNoLCBzO1xuICB2YXIgYmVnLCB2YWw7IC8vIGZvciBnemlwIGhlYWRlciB3cml0ZSBvbmx5XG5cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlIHx8XG4gICAgZmx1c2ggPiBaX0JMT0NLIHx8IGZsdXNoIDwgMCkge1xuICAgIHJldHVybiBzdHJtID8gZXJyKHN0cm0sIFpfU1RSRUFNX0VSUk9SKSA6IFpfU1RSRUFNX0VSUk9SO1xuICB9XG5cbiAgcyA9IHN0cm0uc3RhdGU7XG5cbiAgaWYgKCFzdHJtLm91dHB1dCB8fFxuICAgICAgKCFzdHJtLmlucHV0ICYmIHN0cm0uYXZhaWxfaW4gIT09IDApIHx8XG4gICAgICAocy5zdGF0dXMgPT09IEZJTklTSF9TVEFURSAmJiBmbHVzaCAhPT0gWl9GSU5JU0gpKSB7XG4gICAgcmV0dXJuIGVycihzdHJtLCAoc3RybS5hdmFpbF9vdXQgPT09IDApID8gWl9CVUZfRVJST1IgOiBaX1NUUkVBTV9FUlJPUik7XG4gIH1cblxuICBzLnN0cm0gPSBzdHJtOyAvKiBqdXN0IGluIGNhc2UgKi9cbiAgb2xkX2ZsdXNoID0gcy5sYXN0X2ZsdXNoO1xuICBzLmxhc3RfZmx1c2ggPSBmbHVzaDtcblxuICAvKiBXcml0ZSB0aGUgaGVhZGVyICovXG4gIGlmIChzLnN0YXR1cyA9PT0gSU5JVF9TVEFURSkge1xuXG4gICAgaWYgKHMud3JhcCA9PT0gMikgeyAvLyBHWklQIGhlYWRlclxuICAgICAgc3RybS5hZGxlciA9IDA7ICAvL2NyYzMyKDBMLCBaX05VTEwsIDApO1xuICAgICAgcHV0X2J5dGUocywgMzEpO1xuICAgICAgcHV0X2J5dGUocywgMTM5KTtcbiAgICAgIHB1dF9ieXRlKHMsIDgpO1xuICAgICAgaWYgKCFzLmd6aGVhZCkgeyAvLyBzLT5nemhlYWQgPT0gWl9OVUxMXG4gICAgICAgIHB1dF9ieXRlKHMsIDApO1xuICAgICAgICBwdXRfYnl0ZShzLCAwKTtcbiAgICAgICAgcHV0X2J5dGUocywgMCk7XG4gICAgICAgIHB1dF9ieXRlKHMsIDApO1xuICAgICAgICBwdXRfYnl0ZShzLCAwKTtcbiAgICAgICAgcHV0X2J5dGUocywgcy5sZXZlbCA9PT0gOSA/IDIgOlxuICAgICAgICAgICAgICAgICAgICAocy5zdHJhdGVneSA+PSBaX0hVRkZNQU5fT05MWSB8fCBzLmxldmVsIDwgMiA/XG4gICAgICAgICAgICAgICAgICAgICA0IDogMCkpO1xuICAgICAgICBwdXRfYnl0ZShzLCBPU19DT0RFKTtcbiAgICAgICAgcy5zdGF0dXMgPSBCVVNZX1NUQVRFO1xuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIHB1dF9ieXRlKHMsIChzLmd6aGVhZC50ZXh0ID8gMSA6IDApICtcbiAgICAgICAgICAgICAgICAgICAgKHMuZ3poZWFkLmhjcmMgPyAyIDogMCkgK1xuICAgICAgICAgICAgICAgICAgICAoIXMuZ3poZWFkLmV4dHJhID8gMCA6IDQpICtcbiAgICAgICAgICAgICAgICAgICAgKCFzLmd6aGVhZC5uYW1lID8gMCA6IDgpICtcbiAgICAgICAgICAgICAgICAgICAgKCFzLmd6aGVhZC5jb21tZW50ID8gMCA6IDE2KVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgIHB1dF9ieXRlKHMsIHMuZ3poZWFkLnRpbWUgJiAweGZmKTtcbiAgICAgICAgcHV0X2J5dGUocywgKHMuZ3poZWFkLnRpbWUgPj4gOCkgJiAweGZmKTtcbiAgICAgICAgcHV0X2J5dGUocywgKHMuZ3poZWFkLnRpbWUgPj4gMTYpICYgMHhmZik7XG4gICAgICAgIHB1dF9ieXRlKHMsIChzLmd6aGVhZC50aW1lID4+IDI0KSAmIDB4ZmYpO1xuICAgICAgICBwdXRfYnl0ZShzLCBzLmxldmVsID09PSA5ID8gMiA6XG4gICAgICAgICAgICAgICAgICAgIChzLnN0cmF0ZWd5ID49IFpfSFVGRk1BTl9PTkxZIHx8IHMubGV2ZWwgPCAyID9cbiAgICAgICAgICAgICAgICAgICAgIDQgOiAwKSk7XG4gICAgICAgIHB1dF9ieXRlKHMsIHMuZ3poZWFkLm9zICYgMHhmZik7XG4gICAgICAgIGlmIChzLmd6aGVhZC5leHRyYSAmJiBzLmd6aGVhZC5leHRyYS5sZW5ndGgpIHtcbiAgICAgICAgICBwdXRfYnl0ZShzLCBzLmd6aGVhZC5leHRyYS5sZW5ndGggJiAweGZmKTtcbiAgICAgICAgICBwdXRfYnl0ZShzLCAocy5nemhlYWQuZXh0cmEubGVuZ3RoID4+IDgpICYgMHhmZik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHMuZ3poZWFkLmhjcmMpIHtcbiAgICAgICAgICBzdHJtLmFkbGVyID0gY3JjMzIoc3RybS5hZGxlciwgcy5wZW5kaW5nX2J1Ziwgcy5wZW5kaW5nLCAwKTtcbiAgICAgICAgfVxuICAgICAgICBzLmd6aW5kZXggPSAwO1xuICAgICAgICBzLnN0YXR1cyA9IEVYVFJBX1NUQVRFO1xuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIC8vIERFRkxBVEUgaGVhZGVyXG4gICAge1xuICAgICAgdmFyIGhlYWRlciA9IChaX0RFRkxBVEVEICsgKChzLndfYml0cyAtIDgpIDw8IDQpKSA8PCA4O1xuICAgICAgdmFyIGxldmVsX2ZsYWdzID0gLTE7XG5cbiAgICAgIGlmIChzLnN0cmF0ZWd5ID49IFpfSFVGRk1BTl9PTkxZIHx8IHMubGV2ZWwgPCAyKSB7XG4gICAgICAgIGxldmVsX2ZsYWdzID0gMDtcbiAgICAgIH0gZWxzZSBpZiAocy5sZXZlbCA8IDYpIHtcbiAgICAgICAgbGV2ZWxfZmxhZ3MgPSAxO1xuICAgICAgfSBlbHNlIGlmIChzLmxldmVsID09PSA2KSB7XG4gICAgICAgIGxldmVsX2ZsYWdzID0gMjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldmVsX2ZsYWdzID0gMztcbiAgICAgIH1cbiAgICAgIGhlYWRlciB8PSAobGV2ZWxfZmxhZ3MgPDwgNik7XG4gICAgICBpZiAocy5zdHJzdGFydCAhPT0gMCkgeyBoZWFkZXIgfD0gUFJFU0VUX0RJQ1Q7IH1cbiAgICAgIGhlYWRlciArPSAzMSAtIChoZWFkZXIgJSAzMSk7XG5cbiAgICAgIHMuc3RhdHVzID0gQlVTWV9TVEFURTtcbiAgICAgIHB1dFNob3J0TVNCKHMsIGhlYWRlcik7XG5cbiAgICAgIC8qIFNhdmUgdGhlIGFkbGVyMzIgb2YgdGhlIHByZXNldCBkaWN0aW9uYXJ5OiAqL1xuICAgICAgaWYgKHMuc3Ryc3RhcnQgIT09IDApIHtcbiAgICAgICAgcHV0U2hvcnRNU0Iocywgc3RybS5hZGxlciA+Pj4gMTYpO1xuICAgICAgICBwdXRTaG9ydE1TQihzLCBzdHJtLmFkbGVyICYgMHhmZmZmKTtcbiAgICAgIH1cbiAgICAgIHN0cm0uYWRsZXIgPSAxOyAvLyBhZGxlcjMyKDBMLCBaX05VTEwsIDApO1xuICAgIH1cbiAgfVxuXG4vLyNpZmRlZiBHWklQXG4gIGlmIChzLnN0YXR1cyA9PT0gRVhUUkFfU1RBVEUpIHtcbiAgICBpZiAocy5nemhlYWQuZXh0cmEvKiAhPSBaX05VTEwqLykge1xuICAgICAgYmVnID0gcy5wZW5kaW5nOyAgLyogc3RhcnQgb2YgYnl0ZXMgdG8gdXBkYXRlIGNyYyAqL1xuXG4gICAgICB3aGlsZSAocy5nemluZGV4IDwgKHMuZ3poZWFkLmV4dHJhLmxlbmd0aCAmIDB4ZmZmZikpIHtcbiAgICAgICAgaWYgKHMucGVuZGluZyA9PT0gcy5wZW5kaW5nX2J1Zl9zaXplKSB7XG4gICAgICAgICAgaWYgKHMuZ3poZWFkLmhjcmMgJiYgcy5wZW5kaW5nID4gYmVnKSB7XG4gICAgICAgICAgICBzdHJtLmFkbGVyID0gY3JjMzIoc3RybS5hZGxlciwgcy5wZW5kaW5nX2J1Ziwgcy5wZW5kaW5nIC0gYmVnLCBiZWcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBmbHVzaF9wZW5kaW5nKHN0cm0pO1xuICAgICAgICAgIGJlZyA9IHMucGVuZGluZztcbiAgICAgICAgICBpZiAocy5wZW5kaW5nID09PSBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBwdXRfYnl0ZShzLCBzLmd6aGVhZC5leHRyYVtzLmd6aW5kZXhdICYgMHhmZik7XG4gICAgICAgIHMuZ3ppbmRleCsrO1xuICAgICAgfVxuICAgICAgaWYgKHMuZ3poZWFkLmhjcmMgJiYgcy5wZW5kaW5nID4gYmVnKSB7XG4gICAgICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmcgLSBiZWcsIGJlZyk7XG4gICAgICB9XG4gICAgICBpZiAocy5nemluZGV4ID09PSBzLmd6aGVhZC5leHRyYS5sZW5ndGgpIHtcbiAgICAgICAgcy5nemluZGV4ID0gMDtcbiAgICAgICAgcy5zdGF0dXMgPSBOQU1FX1NUQVRFO1xuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHMuc3RhdHVzID0gTkFNRV9TVEFURTtcbiAgICB9XG4gIH1cbiAgaWYgKHMuc3RhdHVzID09PSBOQU1FX1NUQVRFKSB7XG4gICAgaWYgKHMuZ3poZWFkLm5hbWUvKiAhPSBaX05VTEwqLykge1xuICAgICAgYmVnID0gcy5wZW5kaW5nOyAgLyogc3RhcnQgb2YgYnl0ZXMgdG8gdXBkYXRlIGNyYyAqL1xuICAgICAgLy9pbnQgdmFsO1xuXG4gICAgICBkbyB7XG4gICAgICAgIGlmIChzLnBlbmRpbmcgPT09IHMucGVuZGluZ19idWZfc2l6ZSkge1xuICAgICAgICAgIGlmIChzLmd6aGVhZC5oY3JjICYmIHMucGVuZGluZyA+IGJlZykge1xuICAgICAgICAgICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIHMucGVuZGluZ19idWYsIHMucGVuZGluZyAtIGJlZywgYmVnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgICAgICAgICBiZWcgPSBzLnBlbmRpbmc7XG4gICAgICAgICAgaWYgKHMucGVuZGluZyA9PT0gcy5wZW5kaW5nX2J1Zl9zaXplKSB7XG4gICAgICAgICAgICB2YWwgPSAxO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEpTIHNwZWNpZmljOiBsaXR0bGUgbWFnaWMgdG8gYWRkIHplcm8gdGVybWluYXRvciB0byBlbmQgb2Ygc3RyaW5nXG4gICAgICAgIGlmIChzLmd6aW5kZXggPCBzLmd6aGVhZC5uYW1lLmxlbmd0aCkge1xuICAgICAgICAgIHZhbCA9IHMuZ3poZWFkLm5hbWUuY2hhckNvZGVBdChzLmd6aW5kZXgrKykgJiAweGZmO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhbCA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgcHV0X2J5dGUocywgdmFsKTtcbiAgICAgIH0gd2hpbGUgKHZhbCAhPT0gMCk7XG5cbiAgICAgIGlmIChzLmd6aGVhZC5oY3JjICYmIHMucGVuZGluZyA+IGJlZyl7XG4gICAgICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmcgLSBiZWcsIGJlZyk7XG4gICAgICB9XG4gICAgICBpZiAodmFsID09PSAwKSB7XG4gICAgICAgIHMuZ3ppbmRleCA9IDA7XG4gICAgICAgIHMuc3RhdHVzID0gQ09NTUVOVF9TVEFURTtcbiAgICAgIH1cbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBzLnN0YXR1cyA9IENPTU1FTlRfU1RBVEU7XG4gICAgfVxuICB9XG4gIGlmIChzLnN0YXR1cyA9PT0gQ09NTUVOVF9TVEFURSkge1xuICAgIGlmIChzLmd6aGVhZC5jb21tZW50LyogIT0gWl9OVUxMKi8pIHtcbiAgICAgIGJlZyA9IHMucGVuZGluZzsgIC8qIHN0YXJ0IG9mIGJ5dGVzIHRvIHVwZGF0ZSBjcmMgKi9cbiAgICAgIC8vaW50IHZhbDtcblxuICAgICAgZG8ge1xuICAgICAgICBpZiAocy5wZW5kaW5nID09PSBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgICBpZiAocy5nemhlYWQuaGNyYyAmJiBzLnBlbmRpbmcgPiBiZWcpIHtcbiAgICAgICAgICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmcgLSBiZWcsIGJlZyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZsdXNoX3BlbmRpbmcoc3RybSk7XG4gICAgICAgICAgYmVnID0gcy5wZW5kaW5nO1xuICAgICAgICAgIGlmIChzLnBlbmRpbmcgPT09IHMucGVuZGluZ19idWZfc2l6ZSkge1xuICAgICAgICAgICAgdmFsID0gMTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBKUyBzcGVjaWZpYzogbGl0dGxlIG1hZ2ljIHRvIGFkZCB6ZXJvIHRlcm1pbmF0b3IgdG8gZW5kIG9mIHN0cmluZ1xuICAgICAgICBpZiAocy5nemluZGV4IDwgcy5nemhlYWQuY29tbWVudC5sZW5ndGgpIHtcbiAgICAgICAgICB2YWwgPSBzLmd6aGVhZC5jb21tZW50LmNoYXJDb2RlQXQocy5nemluZGV4KyspICYgMHhmZjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB2YWwgPSAwO1xuICAgICAgICB9XG4gICAgICAgIHB1dF9ieXRlKHMsIHZhbCk7XG4gICAgICB9IHdoaWxlICh2YWwgIT09IDApO1xuXG4gICAgICBpZiAocy5nemhlYWQuaGNyYyAmJiBzLnBlbmRpbmcgPiBiZWcpIHtcbiAgICAgICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIHMucGVuZGluZ19idWYsIHMucGVuZGluZyAtIGJlZywgYmVnKTtcbiAgICAgIH1cbiAgICAgIGlmICh2YWwgPT09IDApIHtcbiAgICAgICAgcy5zdGF0dXMgPSBIQ1JDX1NUQVRFO1xuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHMuc3RhdHVzID0gSENSQ19TVEFURTtcbiAgICB9XG4gIH1cbiAgaWYgKHMuc3RhdHVzID09PSBIQ1JDX1NUQVRFKSB7XG4gICAgaWYgKHMuZ3poZWFkLmhjcmMpIHtcbiAgICAgIGlmIChzLnBlbmRpbmcgKyAyID4gcy5wZW5kaW5nX2J1Zl9zaXplKSB7XG4gICAgICAgIGZsdXNoX3BlbmRpbmcoc3RybSk7XG4gICAgICB9XG4gICAgICBpZiAocy5wZW5kaW5nICsgMiA8PSBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgcHV0X2J5dGUocywgc3RybS5hZGxlciAmIDB4ZmYpO1xuICAgICAgICBwdXRfYnl0ZShzLCAoc3RybS5hZGxlciA+PiA4KSAmIDB4ZmYpO1xuICAgICAgICBzdHJtLmFkbGVyID0gMDsgLy9jcmMzMigwTCwgWl9OVUxMLCAwKTtcbiAgICAgICAgcy5zdGF0dXMgPSBCVVNZX1NUQVRFO1xuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHMuc3RhdHVzID0gQlVTWV9TVEFURTtcbiAgICB9XG4gIH1cbi8vI2VuZGlmXG5cbiAgLyogRmx1c2ggYXMgbXVjaCBwZW5kaW5nIG91dHB1dCBhcyBwb3NzaWJsZSAqL1xuICBpZiAocy5wZW5kaW5nICE9PSAwKSB7XG4gICAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIC8qIFNpbmNlIGF2YWlsX291dCBpcyAwLCBkZWZsYXRlIHdpbGwgYmUgY2FsbGVkIGFnYWluIHdpdGhcbiAgICAgICAqIG1vcmUgb3V0cHV0IHNwYWNlLCBidXQgcG9zc2libHkgd2l0aCBib3RoIHBlbmRpbmcgYW5kXG4gICAgICAgKiBhdmFpbF9pbiBlcXVhbCB0byB6ZXJvLiBUaGVyZSB3b24ndCBiZSBhbnl0aGluZyB0byBkbyxcbiAgICAgICAqIGJ1dCB0aGlzIGlzIG5vdCBhbiBlcnJvciBzaXR1YXRpb24gc28gbWFrZSBzdXJlIHdlXG4gICAgICAgKiByZXR1cm4gT0sgaW5zdGVhZCBvZiBCVUZfRVJST1IgYXQgbmV4dCBjYWxsIG9mIGRlZmxhdGU6XG4gICAgICAgKi9cbiAgICAgIHMubGFzdF9mbHVzaCA9IC0xO1xuICAgICAgcmV0dXJuIFpfT0s7XG4gICAgfVxuXG4gICAgLyogTWFrZSBzdXJlIHRoZXJlIGlzIHNvbWV0aGluZyB0byBkbyBhbmQgYXZvaWQgZHVwbGljYXRlIGNvbnNlY3V0aXZlXG4gICAgICogZmx1c2hlcy4gRm9yIHJlcGVhdGVkIGFuZCB1c2VsZXNzIGNhbGxzIHdpdGggWl9GSU5JU0gsIHdlIGtlZXBcbiAgICAgKiByZXR1cm5pbmcgWl9TVFJFQU1fRU5EIGluc3RlYWQgb2YgWl9CVUZfRVJST1IuXG4gICAgICovXG4gIH0gZWxzZSBpZiAoc3RybS5hdmFpbF9pbiA9PT0gMCAmJiByYW5rKGZsdXNoKSA8PSByYW5rKG9sZF9mbHVzaCkgJiZcbiAgICBmbHVzaCAhPT0gWl9GSU5JU0gpIHtcbiAgICByZXR1cm4gZXJyKHN0cm0sIFpfQlVGX0VSUk9SKTtcbiAgfVxuXG4gIC8qIFVzZXIgbXVzdCBub3QgcHJvdmlkZSBtb3JlIGlucHV0IGFmdGVyIHRoZSBmaXJzdCBGSU5JU0g6ICovXG4gIGlmIChzLnN0YXR1cyA9PT0gRklOSVNIX1NUQVRFICYmIHN0cm0uYXZhaWxfaW4gIT09IDApIHtcbiAgICByZXR1cm4gZXJyKHN0cm0sIFpfQlVGX0VSUk9SKTtcbiAgfVxuXG4gIC8qIFN0YXJ0IGEgbmV3IGJsb2NrIG9yIGNvbnRpbnVlIHRoZSBjdXJyZW50IG9uZS5cbiAgICovXG4gIGlmIChzdHJtLmF2YWlsX2luICE9PSAwIHx8IHMubG9va2FoZWFkICE9PSAwIHx8XG4gICAgKGZsdXNoICE9PSBaX05PX0ZMVVNIICYmIHMuc3RhdHVzICE9PSBGSU5JU0hfU1RBVEUpKSB7XG4gICAgdmFyIGJzdGF0ZSA9IChzLnN0cmF0ZWd5ID09PSBaX0hVRkZNQU5fT05MWSkgPyBkZWZsYXRlX2h1ZmYocywgZmx1c2gpIDpcbiAgICAgIChzLnN0cmF0ZWd5ID09PSBaX1JMRSA/IGRlZmxhdGVfcmxlKHMsIGZsdXNoKSA6XG4gICAgICAgIGNvbmZpZ3VyYXRpb25fdGFibGVbcy5sZXZlbF0uZnVuYyhzLCBmbHVzaCkpO1xuXG4gICAgaWYgKGJzdGF0ZSA9PT0gQlNfRklOSVNIX1NUQVJURUQgfHwgYnN0YXRlID09PSBCU19GSU5JU0hfRE9ORSkge1xuICAgICAgcy5zdGF0dXMgPSBGSU5JU0hfU1RBVEU7XG4gICAgfVxuICAgIGlmIChic3RhdGUgPT09IEJTX05FRURfTU9SRSB8fCBic3RhdGUgPT09IEJTX0ZJTklTSF9TVEFSVEVEKSB7XG4gICAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcy5sYXN0X2ZsdXNoID0gLTE7XG4gICAgICAgIC8qIGF2b2lkIEJVRl9FUlJPUiBuZXh0IGNhbGwsIHNlZSBhYm92ZSAqL1xuICAgICAgfVxuICAgICAgcmV0dXJuIFpfT0s7XG4gICAgICAvKiBJZiBmbHVzaCAhPSBaX05PX0ZMVVNIICYmIGF2YWlsX291dCA9PSAwLCB0aGUgbmV4dCBjYWxsXG4gICAgICAgKiBvZiBkZWZsYXRlIHNob3VsZCB1c2UgdGhlIHNhbWUgZmx1c2ggcGFyYW1ldGVyIHRvIG1ha2Ugc3VyZVxuICAgICAgICogdGhhdCB0aGUgZmx1c2ggaXMgY29tcGxldGUuIFNvIHdlIGRvbid0IGhhdmUgdG8gb3V0cHV0IGFuXG4gICAgICAgKiBlbXB0eSBibG9jayBoZXJlLCB0aGlzIHdpbGwgYmUgZG9uZSBhdCBuZXh0IGNhbGwuIFRoaXMgYWxzb1xuICAgICAgICogZW5zdXJlcyB0aGF0IGZvciBhIHZlcnkgc21hbGwgb3V0cHV0IGJ1ZmZlciwgd2UgZW1pdCBhdCBtb3N0XG4gICAgICAgKiBvbmUgZW1wdHkgYmxvY2suXG4gICAgICAgKi9cbiAgICB9XG4gICAgaWYgKGJzdGF0ZSA9PT0gQlNfQkxPQ0tfRE9ORSkge1xuICAgICAgaWYgKGZsdXNoID09PSBaX1BBUlRJQUxfRkxVU0gpIHtcbiAgICAgICAgdHJlZXMuX3RyX2FsaWduKHMpO1xuICAgICAgfVxuICAgICAgZWxzZSBpZiAoZmx1c2ggIT09IFpfQkxPQ0spIHsgLyogRlVMTF9GTFVTSCBvciBTWU5DX0ZMVVNIICovXG5cbiAgICAgICAgdHJlZXMuX3RyX3N0b3JlZF9ibG9jayhzLCAwLCAwLCBmYWxzZSk7XG4gICAgICAgIC8qIEZvciBhIGZ1bGwgZmx1c2gsIHRoaXMgZW1wdHkgYmxvY2sgd2lsbCBiZSByZWNvZ25pemVkXG4gICAgICAgICAqIGFzIGEgc3BlY2lhbCBtYXJrZXIgYnkgaW5mbGF0ZV9zeW5jKCkuXG4gICAgICAgICAqL1xuICAgICAgICBpZiAoZmx1c2ggPT09IFpfRlVMTF9GTFVTSCkge1xuICAgICAgICAgIC8qKiogQ0xFQVJfSEFTSChzKTsgKioqLyAgICAgICAgICAgICAvKiBmb3JnZXQgaGlzdG9yeSAqL1xuICAgICAgICAgIHplcm8ocy5oZWFkKTsgLy8gRmlsbCB3aXRoIE5JTCAoPSAwKTtcblxuICAgICAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkge1xuICAgICAgICAgICAgcy5zdHJzdGFydCA9IDA7XG4gICAgICAgICAgICBzLmJsb2NrX3N0YXJ0ID0gMDtcbiAgICAgICAgICAgIHMuaW5zZXJ0ID0gMDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGZsdXNoX3BlbmRpbmcoc3RybSk7XG4gICAgICBpZiAoc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcy5sYXN0X2ZsdXNoID0gLTE7IC8qIGF2b2lkIEJVRl9FUlJPUiBhdCBuZXh0IGNhbGwsIHNlZSBhYm92ZSAqL1xuICAgICAgICByZXR1cm4gWl9PSztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgLy9Bc3NlcnQoc3RybS0+YXZhaWxfb3V0ID4gMCwgXCJidWcyXCIpO1xuICAvL2lmIChzdHJtLmF2YWlsX291dCA8PSAwKSB7IHRocm93IG5ldyBFcnJvcihcImJ1ZzJcIik7fVxuXG4gIGlmIChmbHVzaCAhPT0gWl9GSU5JU0gpIHsgcmV0dXJuIFpfT0s7IH1cbiAgaWYgKHMud3JhcCA8PSAwKSB7IHJldHVybiBaX1NUUkVBTV9FTkQ7IH1cblxuICAvKiBXcml0ZSB0aGUgdHJhaWxlciAqL1xuICBpZiAocy53cmFwID09PSAyKSB7XG4gICAgcHV0X2J5dGUocywgc3RybS5hZGxlciAmIDB4ZmYpO1xuICAgIHB1dF9ieXRlKHMsIChzdHJtLmFkbGVyID4+IDgpICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0uYWRsZXIgPj4gMTYpICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0uYWRsZXIgPj4gMjQpICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgc3RybS50b3RhbF9pbiAmIDB4ZmYpO1xuICAgIHB1dF9ieXRlKHMsIChzdHJtLnRvdGFsX2luID4+IDgpICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0udG90YWxfaW4gPj4gMTYpICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0udG90YWxfaW4gPj4gMjQpICYgMHhmZik7XG4gIH1cbiAgZWxzZVxuICB7XG4gICAgcHV0U2hvcnRNU0Iocywgc3RybS5hZGxlciA+Pj4gMTYpO1xuICAgIHB1dFNob3J0TVNCKHMsIHN0cm0uYWRsZXIgJiAweGZmZmYpO1xuICB9XG5cbiAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgLyogSWYgYXZhaWxfb3V0IGlzIHplcm8sIHRoZSBhcHBsaWNhdGlvbiB3aWxsIGNhbGwgZGVmbGF0ZSBhZ2FpblxuICAgKiB0byBmbHVzaCB0aGUgcmVzdC5cbiAgICovXG4gIGlmIChzLndyYXAgPiAwKSB7IHMud3JhcCA9IC1zLndyYXA7IH1cbiAgLyogd3JpdGUgdGhlIHRyYWlsZXIgb25seSBvbmNlISAqL1xuICByZXR1cm4gcy5wZW5kaW5nICE9PSAwID8gWl9PSyA6IFpfU1RSRUFNX0VORDtcbn1cblxuZnVuY3Rpb24gZGVmbGF0ZUVuZChzdHJtKSB7XG4gIHZhciBzdGF0dXM7XG5cbiAgaWYgKCFzdHJtLyo9PSBaX05VTEwqLyB8fCAhc3RybS5zdGF0ZS8qPT0gWl9OVUxMKi8pIHtcbiAgICByZXR1cm4gWl9TVFJFQU1fRVJST1I7XG4gIH1cblxuICBzdGF0dXMgPSBzdHJtLnN0YXRlLnN0YXR1cztcbiAgaWYgKHN0YXR1cyAhPT0gSU5JVF9TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gRVhUUkFfU1RBVEUgJiZcbiAgICBzdGF0dXMgIT09IE5BTUVfU1RBVEUgJiZcbiAgICBzdGF0dXMgIT09IENPTU1FTlRfU1RBVEUgJiZcbiAgICBzdGF0dXMgIT09IEhDUkNfU1RBVEUgJiZcbiAgICBzdGF0dXMgIT09IEJVU1lfU1RBVEUgJiZcbiAgICBzdGF0dXMgIT09IEZJTklTSF9TVEFURVxuICApIHtcbiAgICByZXR1cm4gZXJyKHN0cm0sIFpfU1RSRUFNX0VSUk9SKTtcbiAgfVxuXG4gIHN0cm0uc3RhdGUgPSBudWxsO1xuXG4gIHJldHVybiBzdGF0dXMgPT09IEJVU1lfU1RBVEUgPyBlcnIoc3RybSwgWl9EQVRBX0VSUk9SKSA6IFpfT0s7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvcHkgdGhlIHNvdXJjZSBzdGF0ZSB0byB0aGUgZGVzdGluYXRpb24gc3RhdGVcbiAqL1xuLy9mdW5jdGlvbiBkZWZsYXRlQ29weShkZXN0LCBzb3VyY2UpIHtcbi8vXG4vL31cblxuZXhwb3J0cy5kZWZsYXRlSW5pdCA9IGRlZmxhdGVJbml0O1xuZXhwb3J0cy5kZWZsYXRlSW5pdDIgPSBkZWZsYXRlSW5pdDI7XG5leHBvcnRzLmRlZmxhdGVSZXNldCA9IGRlZmxhdGVSZXNldDtcbmV4cG9ydHMuZGVmbGF0ZVJlc2V0S2VlcCA9IGRlZmxhdGVSZXNldEtlZXA7XG5leHBvcnRzLmRlZmxhdGVTZXRIZWFkZXIgPSBkZWZsYXRlU2V0SGVhZGVyO1xuZXhwb3J0cy5kZWZsYXRlID0gZGVmbGF0ZTtcbmV4cG9ydHMuZGVmbGF0ZUVuZCA9IGRlZmxhdGVFbmQ7XG5leHBvcnRzLmRlZmxhdGVJbmZvID0gJ3Bha28gZGVmbGF0ZSAoZnJvbSBOb2RlY2EgcHJvamVjdCknO1xuXG4vKiBOb3QgaW1wbGVtZW50ZWRcbmV4cG9ydHMuZGVmbGF0ZUJvdW5kID0gZGVmbGF0ZUJvdW5kO1xuZXhwb3J0cy5kZWZsYXRlQ29weSA9IGRlZmxhdGVDb3B5O1xuZXhwb3J0cy5kZWZsYXRlU2V0RGljdGlvbmFyeSA9IGRlZmxhdGVTZXREaWN0aW9uYXJ5O1xuZXhwb3J0cy5kZWZsYXRlUGFyYW1zID0gZGVmbGF0ZVBhcmFtcztcbmV4cG9ydHMuZGVmbGF0ZVBlbmRpbmcgPSBkZWZsYXRlUGVuZGluZztcbmV4cG9ydHMuZGVmbGF0ZVByaW1lID0gZGVmbGF0ZVByaW1lO1xuZXhwb3J0cy5kZWZsYXRlVHVuZSA9IGRlZmxhdGVUdW5lO1xuKi8iLCIndXNlIHN0cmljdCc7XG5cblxuZnVuY3Rpb24gR1poZWFkZXIoKSB7XG4gIC8qIHRydWUgaWYgY29tcHJlc3NlZCBkYXRhIGJlbGlldmVkIHRvIGJlIHRleHQgKi9cbiAgdGhpcy50ZXh0ICAgICAgID0gMDtcbiAgLyogbW9kaWZpY2F0aW9uIHRpbWUgKi9cbiAgdGhpcy50aW1lICAgICAgID0gMDtcbiAgLyogZXh0cmEgZmxhZ3MgKG5vdCB1c2VkIHdoZW4gd3JpdGluZyBhIGd6aXAgZmlsZSkgKi9cbiAgdGhpcy54ZmxhZ3MgICAgID0gMDtcbiAgLyogb3BlcmF0aW5nIHN5c3RlbSAqL1xuICB0aGlzLm9zICAgICAgICAgPSAwO1xuICAvKiBwb2ludGVyIHRvIGV4dHJhIGZpZWxkIG9yIFpfTlVMTCBpZiBub25lICovXG4gIHRoaXMuZXh0cmEgICAgICA9IG51bGw7XG4gIC8qIGV4dHJhIGZpZWxkIGxlbmd0aCAodmFsaWQgaWYgZXh0cmEgIT0gWl9OVUxMKSAqL1xuICB0aGlzLmV4dHJhX2xlbiAgPSAwOyAvLyBBY3R1YWxseSwgd2UgZG9uJ3QgbmVlZCBpdCBpbiBKUyxcbiAgICAgICAgICAgICAgICAgICAgICAgLy8gYnV0IGxlYXZlIGZvciBmZXcgY29kZSBtb2RpZmljYXRpb25zXG5cbiAgLy9cbiAgLy8gU2V0dXAgbGltaXRzIGlzIG5vdCBuZWNlc3NhcnkgYmVjYXVzZSBpbiBqcyB3ZSBzaG91bGQgbm90IHByZWFsbG9jYXRlIG1lbW9yeSBcbiAgLy8gZm9yIGluZmxhdGUgdXNlIGNvbnN0YW50IGxpbWl0IGluIDY1NTM2IGJ5dGVzXG4gIC8vXG5cbiAgLyogc3BhY2UgYXQgZXh0cmEgKG9ubHkgd2hlbiByZWFkaW5nIGhlYWRlcikgKi9cbiAgLy8gdGhpcy5leHRyYV9tYXggID0gMDtcbiAgLyogcG9pbnRlciB0byB6ZXJvLXRlcm1pbmF0ZWQgZmlsZSBuYW1lIG9yIFpfTlVMTCAqL1xuICB0aGlzLm5hbWUgICAgICAgPSAnJztcbiAgLyogc3BhY2UgYXQgbmFtZSAob25seSB3aGVuIHJlYWRpbmcgaGVhZGVyKSAqL1xuICAvLyB0aGlzLm5hbWVfbWF4ICAgPSAwO1xuICAvKiBwb2ludGVyIHRvIHplcm8tdGVybWluYXRlZCBjb21tZW50IG9yIFpfTlVMTCAqL1xuICB0aGlzLmNvbW1lbnQgICAgPSAnJztcbiAgLyogc3BhY2UgYXQgY29tbWVudCAob25seSB3aGVuIHJlYWRpbmcgaGVhZGVyKSAqL1xuICAvLyB0aGlzLmNvbW1fbWF4ICAgPSAwO1xuICAvKiB0cnVlIGlmIHRoZXJlIHdhcyBvciB3aWxsIGJlIGEgaGVhZGVyIGNyYyAqL1xuICB0aGlzLmhjcmMgICAgICAgPSAwO1xuICAvKiB0cnVlIHdoZW4gZG9uZSByZWFkaW5nIGd6aXAgaGVhZGVyIChub3QgdXNlZCB3aGVuIHdyaXRpbmcgYSBnemlwIGZpbGUpICovXG4gIHRoaXMuZG9uZSAgICAgICA9IGZhbHNlO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IEdaaGVhZGVyOyIsIid1c2Ugc3RyaWN0JztcblxuLy8gU2VlIHN0YXRlIGRlZnMgZnJvbSBpbmZsYXRlLmpzXG52YXIgQkFEID0gMzA7ICAgICAgIC8qIGdvdCBhIGRhdGEgZXJyb3IgLS0gcmVtYWluIGhlcmUgdW50aWwgcmVzZXQgKi9cbnZhciBUWVBFID0gMTI7ICAgICAgLyogaTogd2FpdGluZyBmb3IgdHlwZSBiaXRzLCBpbmNsdWRpbmcgbGFzdC1mbGFnIGJpdCAqL1xuXG4vKlxuICAgRGVjb2RlIGxpdGVyYWwsIGxlbmd0aCwgYW5kIGRpc3RhbmNlIGNvZGVzIGFuZCB3cml0ZSBvdXQgdGhlIHJlc3VsdGluZ1xuICAgbGl0ZXJhbCBhbmQgbWF0Y2ggYnl0ZXMgdW50aWwgZWl0aGVyIG5vdCBlbm91Z2ggaW5wdXQgb3Igb3V0cHV0IGlzXG4gICBhdmFpbGFibGUsIGFuIGVuZC1vZi1ibG9jayBpcyBlbmNvdW50ZXJlZCwgb3IgYSBkYXRhIGVycm9yIGlzIGVuY291bnRlcmVkLlxuICAgV2hlbiBsYXJnZSBlbm91Z2ggaW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIGFyZSBzdXBwbGllZCB0byBpbmZsYXRlKCksIGZvclxuICAgZXhhbXBsZSwgYSAxNksgaW5wdXQgYnVmZmVyIGFuZCBhIDY0SyBvdXRwdXQgYnVmZmVyLCBtb3JlIHRoYW4gOTUlIG9mIHRoZVxuICAgaW5mbGF0ZSBleGVjdXRpb24gdGltZSBpcyBzcGVudCBpbiB0aGlzIHJvdXRpbmUuXG5cbiAgIEVudHJ5IGFzc3VtcHRpb25zOlxuXG4gICAgICAgIHN0YXRlLm1vZGUgPT09IExFTlxuICAgICAgICBzdHJtLmF2YWlsX2luID49IDZcbiAgICAgICAgc3RybS5hdmFpbF9vdXQgPj0gMjU4XG4gICAgICAgIHN0YXJ0ID49IHN0cm0uYXZhaWxfb3V0XG4gICAgICAgIHN0YXRlLmJpdHMgPCA4XG5cbiAgIE9uIHJldHVybiwgc3RhdGUubW9kZSBpcyBvbmUgb2Y6XG5cbiAgICAgICAgTEVOIC0tIHJhbiBvdXQgb2YgZW5vdWdoIG91dHB1dCBzcGFjZSBvciBlbm91Z2ggYXZhaWxhYmxlIGlucHV0XG4gICAgICAgIFRZUEUgLS0gcmVhY2hlZCBlbmQgb2YgYmxvY2sgY29kZSwgaW5mbGF0ZSgpIHRvIGludGVycHJldCBuZXh0IGJsb2NrXG4gICAgICAgIEJBRCAtLSBlcnJvciBpbiBibG9jayBkYXRhXG5cbiAgIE5vdGVzOlxuXG4gICAgLSBUaGUgbWF4aW11bSBpbnB1dCBiaXRzIHVzZWQgYnkgYSBsZW5ndGgvZGlzdGFuY2UgcGFpciBpcyAxNSBiaXRzIGZvciB0aGVcbiAgICAgIGxlbmd0aCBjb2RlLCA1IGJpdHMgZm9yIHRoZSBsZW5ndGggZXh0cmEsIDE1IGJpdHMgZm9yIHRoZSBkaXN0YW5jZSBjb2RlLFxuICAgICAgYW5kIDEzIGJpdHMgZm9yIHRoZSBkaXN0YW5jZSBleHRyYS4gIFRoaXMgdG90YWxzIDQ4IGJpdHMsIG9yIHNpeCBieXRlcy5cbiAgICAgIFRoZXJlZm9yZSBpZiBzdHJtLmF2YWlsX2luID49IDYsIHRoZW4gdGhlcmUgaXMgZW5vdWdoIGlucHV0IHRvIGF2b2lkXG4gICAgICBjaGVja2luZyBmb3IgYXZhaWxhYmxlIGlucHV0IHdoaWxlIGRlY29kaW5nLlxuXG4gICAgLSBUaGUgbWF4aW11bSBieXRlcyB0aGF0IGEgc2luZ2xlIGxlbmd0aC9kaXN0YW5jZSBwYWlyIGNhbiBvdXRwdXQgaXMgMjU4XG4gICAgICBieXRlcywgd2hpY2ggaXMgdGhlIG1heGltdW0gbGVuZ3RoIHRoYXQgY2FuIGJlIGNvZGVkLiAgaW5mbGF0ZV9mYXN0KClcbiAgICAgIHJlcXVpcmVzIHN0cm0uYXZhaWxfb3V0ID49IDI1OCBmb3IgZWFjaCBsb29wIHRvIGF2b2lkIGNoZWNraW5nIGZvclxuICAgICAgb3V0cHV0IHNwYWNlLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGluZmxhdGVfZmFzdChzdHJtLCBzdGFydCkge1xuICB2YXIgc3RhdGU7XG4gIHZhciBfaW47ICAgICAgICAgICAgICAgICAgICAvKiBsb2NhbCBzdHJtLmlucHV0ICovXG4gIHZhciBsYXN0OyAgICAgICAgICAgICAgICAgICAvKiBoYXZlIGVub3VnaCBpbnB1dCB3aGlsZSBpbiA8IGxhc3QgKi9cbiAgdmFyIF9vdXQ7ICAgICAgICAgICAgICAgICAgIC8qIGxvY2FsIHN0cm0ub3V0cHV0ICovXG4gIHZhciBiZWc7ICAgICAgICAgICAgICAgICAgICAvKiBpbmZsYXRlKCkncyBpbml0aWFsIHN0cm0ub3V0cHV0ICovXG4gIHZhciBlbmQ7ICAgICAgICAgICAgICAgICAgICAvKiB3aGlsZSBvdXQgPCBlbmQsIGVub3VnaCBzcGFjZSBhdmFpbGFibGUgKi9cbi8vI2lmZGVmIElORkxBVEVfU1RSSUNUXG4gIHZhciBkbWF4OyAgICAgICAgICAgICAgICAgICAvKiBtYXhpbXVtIGRpc3RhbmNlIGZyb20gemxpYiBoZWFkZXIgKi9cbi8vI2VuZGlmXG4gIHZhciB3c2l6ZTsgICAgICAgICAgICAgICAgICAvKiB3aW5kb3cgc2l6ZSBvciB6ZXJvIGlmIG5vdCB1c2luZyB3aW5kb3cgKi9cbiAgdmFyIHdoYXZlOyAgICAgICAgICAgICAgICAgIC8qIHZhbGlkIGJ5dGVzIGluIHRoZSB3aW5kb3cgKi9cbiAgdmFyIHduZXh0OyAgICAgICAgICAgICAgICAgIC8qIHdpbmRvdyB3cml0ZSBpbmRleCAqL1xuICB2YXIgd2luZG93OyAgICAgICAgICAgICAgICAgLyogYWxsb2NhdGVkIHNsaWRpbmcgd2luZG93LCBpZiB3c2l6ZSAhPSAwICovXG4gIHZhciBob2xkOyAgICAgICAgICAgICAgICAgICAvKiBsb2NhbCBzdHJtLmhvbGQgKi9cbiAgdmFyIGJpdHM7ICAgICAgICAgICAgICAgICAgIC8qIGxvY2FsIHN0cm0uYml0cyAqL1xuICB2YXIgbGNvZGU7ICAgICAgICAgICAgICAgICAgLyogbG9jYWwgc3RybS5sZW5jb2RlICovXG4gIHZhciBkY29kZTsgICAgICAgICAgICAgICAgICAvKiBsb2NhbCBzdHJtLmRpc3Rjb2RlICovXG4gIHZhciBsbWFzazsgICAgICAgICAgICAgICAgICAvKiBtYXNrIGZvciBmaXJzdCBsZXZlbCBvZiBsZW5ndGggY29kZXMgKi9cbiAgdmFyIGRtYXNrOyAgICAgICAgICAgICAgICAgIC8qIG1hc2sgZm9yIGZpcnN0IGxldmVsIG9mIGRpc3RhbmNlIGNvZGVzICovXG4gIHZhciBoZXJlOyAgICAgICAgICAgICAgICAgICAvKiByZXRyaWV2ZWQgdGFibGUgZW50cnkgKi9cbiAgdmFyIG9wOyAgICAgICAgICAgICAgICAgICAgIC8qIGNvZGUgYml0cywgb3BlcmF0aW9uLCBleHRyYSBiaXRzLCBvciAqL1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLyogIHdpbmRvdyBwb3NpdGlvbiwgd2luZG93IGJ5dGVzIHRvIGNvcHkgKi9cbiAgdmFyIGxlbjsgICAgICAgICAgICAgICAgICAgIC8qIG1hdGNoIGxlbmd0aCwgdW51c2VkIGJ5dGVzICovXG4gIHZhciBkaXN0OyAgICAgICAgICAgICAgICAgICAvKiBtYXRjaCBkaXN0YW5jZSAqL1xuICB2YXIgZnJvbTsgICAgICAgICAgICAgICAgICAgLyogd2hlcmUgdG8gY29weSBtYXRjaCBmcm9tICovXG4gIHZhciBmcm9tX3NvdXJjZTtcblxuXG4gIHZhciBpbnB1dCwgb3V0cHV0OyAvLyBKUyBzcGVjaWZpYywgYmVjYXVzZSB3ZSBoYXZlIG5vIHBvaW50ZXJzXG5cbiAgLyogY29weSBzdGF0ZSB0byBsb2NhbCB2YXJpYWJsZXMgKi9cbiAgc3RhdGUgPSBzdHJtLnN0YXRlO1xuICAvL2hlcmUgPSBzdGF0ZS5oZXJlO1xuICBfaW4gPSBzdHJtLm5leHRfaW47XG4gIGlucHV0ID0gc3RybS5pbnB1dDtcbiAgbGFzdCA9IF9pbiArIChzdHJtLmF2YWlsX2luIC0gNSk7XG4gIF9vdXQgPSBzdHJtLm5leHRfb3V0O1xuICBvdXRwdXQgPSBzdHJtLm91dHB1dDtcbiAgYmVnID0gX291dCAtIChzdGFydCAtIHN0cm0uYXZhaWxfb3V0KTtcbiAgZW5kID0gX291dCArIChzdHJtLmF2YWlsX291dCAtIDI1Nyk7XG4vLyNpZmRlZiBJTkZMQVRFX1NUUklDVFxuICBkbWF4ID0gc3RhdGUuZG1heDtcbi8vI2VuZGlmXG4gIHdzaXplID0gc3RhdGUud3NpemU7XG4gIHdoYXZlID0gc3RhdGUud2hhdmU7XG4gIHduZXh0ID0gc3RhdGUud25leHQ7XG4gIHdpbmRvdyA9IHN0YXRlLndpbmRvdztcbiAgaG9sZCA9IHN0YXRlLmhvbGQ7XG4gIGJpdHMgPSBzdGF0ZS5iaXRzO1xuICBsY29kZSA9IHN0YXRlLmxlbmNvZGU7XG4gIGRjb2RlID0gc3RhdGUuZGlzdGNvZGU7XG4gIGxtYXNrID0gKDEgPDwgc3RhdGUubGVuYml0cykgLSAxO1xuICBkbWFzayA9ICgxIDw8IHN0YXRlLmRpc3RiaXRzKSAtIDE7XG5cblxuICAvKiBkZWNvZGUgbGl0ZXJhbHMgYW5kIGxlbmd0aC9kaXN0YW5jZXMgdW50aWwgZW5kLW9mLWJsb2NrIG9yIG5vdCBlbm91Z2hcbiAgICAgaW5wdXQgZGF0YSBvciBvdXRwdXQgc3BhY2UgKi9cblxuICB0b3A6XG4gIGRvIHtcbiAgICBpZiAoYml0cyA8IDE1KSB7XG4gICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgYml0cyArPSA4O1xuICAgICAgaG9sZCArPSBpbnB1dFtfaW4rK10gPDwgYml0cztcbiAgICAgIGJpdHMgKz0gODtcbiAgICB9XG5cbiAgICBoZXJlID0gbGNvZGVbaG9sZCAmIGxtYXNrXTtcblxuICAgIGRvbGVuOlxuICAgIGZvciAoOzspIHsgLy8gR290byBlbXVsYXRpb25cbiAgICAgIG9wID0gaGVyZSA+Pj4gMjQvKmhlcmUuYml0cyovO1xuICAgICAgaG9sZCA+Pj49IG9wO1xuICAgICAgYml0cyAtPSBvcDtcbiAgICAgIG9wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmYvKmhlcmUub3AqLztcbiAgICAgIGlmIChvcCA9PT0gMCkgeyAgICAgICAgICAgICAgICAgICAgICAgICAgLyogbGl0ZXJhbCAqL1xuICAgICAgICAvL1RyYWNldnYoKHN0ZGVyciwgaGVyZS52YWwgPj0gMHgyMCAmJiBoZXJlLnZhbCA8IDB4N2YgP1xuICAgICAgICAvLyAgICAgICAgXCJpbmZsYXRlOiAgICAgICAgIGxpdGVyYWwgJyVjJ1xcblwiIDpcbiAgICAgICAgLy8gICAgICAgIFwiaW5mbGF0ZTogICAgICAgICBsaXRlcmFsIDB4JTAyeFxcblwiLCBoZXJlLnZhbCkpO1xuICAgICAgICBvdXRwdXRbX291dCsrXSA9IGhlcmUgJiAweGZmZmYvKmhlcmUudmFsKi87XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChvcCAmIDE2KSB7ICAgICAgICAgICAgICAgICAgICAgLyogbGVuZ3RoIGJhc2UgKi9cbiAgICAgICAgbGVuID0gaGVyZSAmIDB4ZmZmZi8qaGVyZS52YWwqLztcbiAgICAgICAgb3AgJj0gMTU7ICAgICAgICAgICAgICAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGV4dHJhIGJpdHMgKi9cbiAgICAgICAgaWYgKG9wKSB7XG4gICAgICAgICAgaWYgKGJpdHMgPCBvcCkge1xuICAgICAgICAgICAgaG9sZCArPSBpbnB1dFtfaW4rK10gPDwgYml0cztcbiAgICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICB9XG4gICAgICAgICAgbGVuICs9IGhvbGQgJiAoKDEgPDwgb3ApIC0gMSk7XG4gICAgICAgICAgaG9sZCA+Pj49IG9wO1xuICAgICAgICAgIGJpdHMgLT0gb3A7XG4gICAgICAgIH1cbiAgICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgICBsZW5ndGggJXVcXG5cIiwgbGVuKSk7XG4gICAgICAgIGlmIChiaXRzIDwgMTUpIHtcbiAgICAgICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgfVxuICAgICAgICBoZXJlID0gZGNvZGVbaG9sZCAmIGRtYXNrXTtcblxuICAgICAgICBkb2Rpc3Q6XG4gICAgICAgIGZvciAoOzspIHsgLy8gZ290byBlbXVsYXRpb25cbiAgICAgICAgICBvcCA9IGhlcmUgPj4+IDI0LypoZXJlLmJpdHMqLztcbiAgICAgICAgICBob2xkID4+Pj0gb3A7XG4gICAgICAgICAgYml0cyAtPSBvcDtcbiAgICAgICAgICBvcCA9IChoZXJlID4+PiAxNikgJiAweGZmLypoZXJlLm9wKi87XG5cbiAgICAgICAgICBpZiAob3AgJiAxNikgeyAgICAgICAgICAgICAgICAgICAgICAvKiBkaXN0YW5jZSBiYXNlICovXG4gICAgICAgICAgICBkaXN0ID0gaGVyZSAmIDB4ZmZmZi8qaGVyZS52YWwqLztcbiAgICAgICAgICAgIG9wICY9IDE1OyAgICAgICAgICAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGV4dHJhIGJpdHMgKi9cbiAgICAgICAgICAgIGlmIChiaXRzIDwgb3ApIHtcbiAgICAgICAgICAgICAgaG9sZCArPSBpbnB1dFtfaW4rK10gPDwgYml0cztcbiAgICAgICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgICAgICBpZiAoYml0cyA8IG9wKSB7XG4gICAgICAgICAgICAgICAgaG9sZCArPSBpbnB1dFtfaW4rK10gPDwgYml0cztcbiAgICAgICAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGRpc3QgKz0gaG9sZCAmICgoMSA8PCBvcCkgLSAxKTtcbi8vI2lmZGVmIElORkxBVEVfU1RSSUNUXG4gICAgICAgICAgICBpZiAoZGlzdCA+IGRtYXgpIHtcbiAgICAgICAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBkaXN0YW5jZSB0b28gZmFyIGJhY2snO1xuICAgICAgICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICAgICAgICBicmVhayB0b3A7XG4gICAgICAgICAgICB9XG4vLyNlbmRpZlxuICAgICAgICAgICAgaG9sZCA+Pj49IG9wO1xuICAgICAgICAgICAgYml0cyAtPSBvcDtcbiAgICAgICAgICAgIC8vVHJhY2V2digoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgICAgZGlzdGFuY2UgJXVcXG5cIiwgZGlzdCkpO1xuICAgICAgICAgICAgb3AgPSBfb3V0IC0gYmVnOyAgICAgICAgICAgICAgICAvKiBtYXggZGlzdGFuY2UgaW4gb3V0cHV0ICovXG4gICAgICAgICAgICBpZiAoZGlzdCA+IG9wKSB7ICAgICAgICAgICAgICAgIC8qIHNlZSBpZiBjb3B5IGZyb20gd2luZG93ICovXG4gICAgICAgICAgICAgIG9wID0gZGlzdCAtIG9wOyAgICAgICAgICAgICAgIC8qIGRpc3RhbmNlIGJhY2sgaW4gd2luZG93ICovXG4gICAgICAgICAgICAgIGlmIChvcCA+IHdoYXZlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHN0YXRlLnNhbmUpIHtcbiAgICAgICAgICAgICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgZGlzdGFuY2UgdG9vIGZhciBiYWNrJztcbiAgICAgICAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICAgICAgICBicmVhayB0b3A7XG4gICAgICAgICAgICAgICAgfVxuXG4vLyAoISkgVGhpcyBibG9jayBpcyBkaXNhYmxlZCBpbiB6bGliIGRlZmFpbHRzLFxuLy8gZG9uJ3QgZW5hYmxlIGl0IGZvciBiaW5hcnkgY29tcGF0aWJpbGl0eVxuLy8jaWZkZWYgSU5GTEFURV9BTExPV19JTlZBTElEX0RJU1RBTkNFX1RPT0ZBUl9BUlJSXG4vLyAgICAgICAgICAgICAgICBpZiAobGVuIDw9IG9wIC0gd2hhdmUpIHtcbi8vICAgICAgICAgICAgICAgICAgZG8ge1xuLy8gICAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gMDtcbi8vICAgICAgICAgICAgICAgICAgfSB3aGlsZSAoLS1sZW4pO1xuLy8gICAgICAgICAgICAgICAgICBjb250aW51ZSB0b3A7XG4vLyAgICAgICAgICAgICAgICB9XG4vLyAgICAgICAgICAgICAgICBsZW4gLT0gb3AgLSB3aGF2ZTtcbi8vICAgICAgICAgICAgICAgIGRvIHtcbi8vICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSAwO1xuLy8gICAgICAgICAgICAgICAgfSB3aGlsZSAoLS1vcCA+IHdoYXZlKTtcbi8vICAgICAgICAgICAgICAgIGlmIChvcCA9PT0gMCkge1xuLy8gICAgICAgICAgICAgICAgICBmcm9tID0gX291dCAtIGRpc3Q7XG4vLyAgICAgICAgICAgICAgICAgIGRvIHtcbi8vICAgICAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IG91dHB1dFtmcm9tKytdO1xuLy8gICAgICAgICAgICAgICAgICB9IHdoaWxlICgtLWxlbik7XG4vLyAgICAgICAgICAgICAgICAgIGNvbnRpbnVlIHRvcDtcbi8vICAgICAgICAgICAgICAgIH1cbi8vI2VuZGlmXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZnJvbSA9IDA7IC8vIHdpbmRvdyBpbmRleFxuICAgICAgICAgICAgICBmcm9tX3NvdXJjZSA9IHdpbmRvdztcbiAgICAgICAgICAgICAgaWYgKHduZXh0ID09PSAwKSB7ICAgICAgICAgICAvKiB2ZXJ5IGNvbW1vbiBjYXNlICovXG4gICAgICAgICAgICAgICAgZnJvbSArPSB3c2l6ZSAtIG9wO1xuICAgICAgICAgICAgICAgIGlmIChvcCA8IGxlbikgeyAgICAgICAgIC8qIHNvbWUgZnJvbSB3aW5kb3cgKi9cbiAgICAgICAgICAgICAgICAgIGxlbiAtPSBvcDtcbiAgICAgICAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSB3aW5kb3dbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICAgIH0gd2hpbGUgKC0tb3ApO1xuICAgICAgICAgICAgICAgICAgZnJvbSA9IF9vdXQgLSBkaXN0OyAgLyogcmVzdCBmcm9tIG91dHB1dCAqL1xuICAgICAgICAgICAgICAgICAgZnJvbV9zb3VyY2UgPSBvdXRwdXQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGVsc2UgaWYgKHduZXh0IDwgb3ApIHsgICAgICAvKiB3cmFwIGFyb3VuZCB3aW5kb3cgKi9cbiAgICAgICAgICAgICAgICBmcm9tICs9IHdzaXplICsgd25leHQgLSBvcDtcbiAgICAgICAgICAgICAgICBvcCAtPSB3bmV4dDtcbiAgICAgICAgICAgICAgICBpZiAob3AgPCBsZW4pIHsgICAgICAgICAvKiBzb21lIGZyb20gZW5kIG9mIHdpbmRvdyAqL1xuICAgICAgICAgICAgICAgICAgbGVuIC09IG9wO1xuICAgICAgICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IHdpbmRvd1tmcm9tKytdO1xuICAgICAgICAgICAgICAgICAgfSB3aGlsZSAoLS1vcCk7XG4gICAgICAgICAgICAgICAgICBmcm9tID0gMDtcbiAgICAgICAgICAgICAgICAgIGlmICh3bmV4dCA8IGxlbikgeyAgLyogc29tZSBmcm9tIHN0YXJ0IG9mIHdpbmRvdyAqL1xuICAgICAgICAgICAgICAgICAgICBvcCA9IHduZXh0O1xuICAgICAgICAgICAgICAgICAgICBsZW4gLT0gb3A7XG4gICAgICAgICAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IHdpbmRvd1tmcm9tKytdO1xuICAgICAgICAgICAgICAgICAgICB9IHdoaWxlICgtLW9wKTtcbiAgICAgICAgICAgICAgICAgICAgZnJvbSA9IF9vdXQgLSBkaXN0OyAgICAgIC8qIHJlc3QgZnJvbSBvdXRwdXQgKi9cbiAgICAgICAgICAgICAgICAgICAgZnJvbV9zb3VyY2UgPSBvdXRwdXQ7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGVsc2UgeyAgICAgICAgICAgICAgICAgICAgICAvKiBjb250aWd1b3VzIGluIHdpbmRvdyAqL1xuICAgICAgICAgICAgICAgIGZyb20gKz0gd25leHQgLSBvcDtcbiAgICAgICAgICAgICAgICBpZiAob3AgPCBsZW4pIHsgICAgICAgICAvKiBzb21lIGZyb20gd2luZG93ICovXG4gICAgICAgICAgICAgICAgICBsZW4gLT0gb3A7XG4gICAgICAgICAgICAgICAgICBkbyB7XG4gICAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gd2luZG93W2Zyb20rK107XG4gICAgICAgICAgICAgICAgICB9IHdoaWxlICgtLW9wKTtcbiAgICAgICAgICAgICAgICAgIGZyb20gPSBfb3V0IC0gZGlzdDsgIC8qIHJlc3QgZnJvbSBvdXRwdXQgKi9cbiAgICAgICAgICAgICAgICAgIGZyb21fc291cmNlID0gb3V0cHV0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB3aGlsZSAobGVuID4gMikge1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gZnJvbV9zb3VyY2VbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IGZyb21fc291cmNlW2Zyb20rK107XG4gICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBmcm9tX3NvdXJjZVtmcm9tKytdO1xuICAgICAgICAgICAgICAgIGxlbiAtPSAzO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChsZW4pIHtcbiAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IGZyb21fc291cmNlW2Zyb20rK107XG4gICAgICAgICAgICAgICAgaWYgKGxlbiA+IDEpIHtcbiAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gZnJvbV9zb3VyY2VbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICBmcm9tID0gX291dCAtIGRpc3Q7ICAgICAgICAgIC8qIGNvcHkgZGlyZWN0IGZyb20gb3V0cHV0ICovXG4gICAgICAgICAgICAgIGRvIHsgICAgICAgICAgICAgICAgICAgICAgICAvKiBtaW5pbXVtIGxlbmd0aCBpcyB0aHJlZSAqL1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gb3V0cHV0W2Zyb20rK107XG4gICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBvdXRwdXRbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IG91dHB1dFtmcm9tKytdO1xuICAgICAgICAgICAgICAgIGxlbiAtPSAzO1xuICAgICAgICAgICAgICB9IHdoaWxlIChsZW4gPiAyKTtcbiAgICAgICAgICAgICAgaWYgKGxlbikge1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gb3V0cHV0W2Zyb20rK107XG4gICAgICAgICAgICAgICAgaWYgKGxlbiA+IDEpIHtcbiAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gb3V0cHV0W2Zyb20rK107XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGVsc2UgaWYgKChvcCAmIDY0KSA9PT0gMCkgeyAgICAgICAgICAvKiAybmQgbGV2ZWwgZGlzdGFuY2UgY29kZSAqL1xuICAgICAgICAgICAgaGVyZSA9IGRjb2RlWyhoZXJlICYgMHhmZmZmKS8qaGVyZS52YWwqLyArIChob2xkICYgKCgxIDw8IG9wKSAtIDEpKV07XG4gICAgICAgICAgICBjb250aW51ZSBkb2Rpc3Q7XG4gICAgICAgICAgfVxuICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBkaXN0YW5jZSBjb2RlJztcbiAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICBicmVhayB0b3A7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7IC8vIG5lZWQgdG8gZW11bGF0ZSBnb3RvIHZpYSBcImNvbnRpbnVlXCJcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZWxzZSBpZiAoKG9wICYgNjQpID09PSAwKSB7ICAgICAgICAgICAgICAvKiAybmQgbGV2ZWwgbGVuZ3RoIGNvZGUgKi9cbiAgICAgICAgaGVyZSA9IGxjb2RlWyhoZXJlICYgMHhmZmZmKS8qaGVyZS52YWwqLyArIChob2xkICYgKCgxIDw8IG9wKSAtIDEpKV07XG4gICAgICAgIGNvbnRpbnVlIGRvbGVuO1xuICAgICAgfVxuICAgICAgZWxzZSBpZiAob3AgJiAzMikgeyAgICAgICAgICAgICAgICAgICAgIC8qIGVuZC1vZi1ibG9jayAqL1xuICAgICAgICAvL1RyYWNldnYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgICAgIGVuZCBvZiBibG9ja1xcblwiKSk7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBUWVBFO1xuICAgICAgICBicmVhayB0b3A7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBsaXRlcmFsL2xlbmd0aCBjb2RlJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWsgdG9wO1xuICAgICAgfVxuXG4gICAgICBicmVhazsgLy8gbmVlZCB0byBlbXVsYXRlIGdvdG8gdmlhIFwiY29udGludWVcIlxuICAgIH1cbiAgfSB3aGlsZSAoX2luIDwgbGFzdCAmJiBfb3V0IDwgZW5kKTtcblxuICAvKiByZXR1cm4gdW51c2VkIGJ5dGVzIChvbiBlbnRyeSwgYml0cyA8IDgsIHNvIGluIHdvbid0IGdvIHRvbyBmYXIgYmFjaykgKi9cbiAgbGVuID0gYml0cyA+PiAzO1xuICBfaW4gLT0gbGVuO1xuICBiaXRzIC09IGxlbiA8PCAzO1xuICBob2xkICY9ICgxIDw8IGJpdHMpIC0gMTtcblxuICAvKiB1cGRhdGUgc3RhdGUgYW5kIHJldHVybiAqL1xuICBzdHJtLm5leHRfaW4gPSBfaW47XG4gIHN0cm0ubmV4dF9vdXQgPSBfb3V0O1xuICBzdHJtLmF2YWlsX2luID0gKF9pbiA8IGxhc3QgPyA1ICsgKGxhc3QgLSBfaW4pIDogNSAtIChfaW4gLSBsYXN0KSk7XG4gIHN0cm0uYXZhaWxfb3V0ID0gKF9vdXQgPCBlbmQgPyAyNTcgKyAoZW5kIC0gX291dCkgOiAyNTcgLSAoX291dCAtIGVuZCkpO1xuICBzdGF0ZS5ob2xkID0gaG9sZDtcbiAgc3RhdGUuYml0cyA9IGJpdHM7XG4gIHJldHVybjtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvY29tbW9uJyk7XG52YXIgYWRsZXIzMiA9IHJlcXVpcmUoJy4vYWRsZXIzMicpO1xudmFyIGNyYzMyICAgPSByZXF1aXJlKCcuL2NyYzMyJyk7XG52YXIgaW5mbGF0ZV9mYXN0ID0gcmVxdWlyZSgnLi9pbmZmYXN0Jyk7XG52YXIgaW5mbGF0ZV90YWJsZSA9IHJlcXVpcmUoJy4vaW5mdHJlZXMnKTtcblxudmFyIENPREVTID0gMDtcbnZhciBMRU5TID0gMTtcbnZhciBESVNUUyA9IDI7XG5cbi8qIFB1YmxpYyBjb25zdGFudHMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5cbi8qIEFsbG93ZWQgZmx1c2ggdmFsdWVzOyBzZWUgZGVmbGF0ZSgpIGFuZCBpbmZsYXRlKCkgYmVsb3cgZm9yIGRldGFpbHMgKi9cbi8vdmFyIFpfTk9fRkxVU0ggICAgICA9IDA7XG4vL3ZhciBaX1BBUlRJQUxfRkxVU0ggPSAxO1xuLy92YXIgWl9TWU5DX0ZMVVNIICAgID0gMjtcbi8vdmFyIFpfRlVMTF9GTFVTSCAgICA9IDM7XG52YXIgWl9GSU5JU0ggICAgICAgID0gNDtcbnZhciBaX0JMT0NLICAgICAgICAgPSA1O1xudmFyIFpfVFJFRVMgICAgICAgICA9IDY7XG5cblxuLyogUmV0dXJuIGNvZGVzIGZvciB0aGUgY29tcHJlc3Npb24vZGVjb21wcmVzc2lvbiBmdW5jdGlvbnMuIE5lZ2F0aXZlIHZhbHVlc1xuICogYXJlIGVycm9ycywgcG9zaXRpdmUgdmFsdWVzIGFyZSB1c2VkIGZvciBzcGVjaWFsIGJ1dCBub3JtYWwgZXZlbnRzLlxuICovXG52YXIgWl9PSyAgICAgICAgICAgID0gMDtcbnZhciBaX1NUUkVBTV9FTkQgICAgPSAxO1xudmFyIFpfTkVFRF9ESUNUICAgICA9IDI7XG4vL3ZhciBaX0VSUk5PICAgICAgICAgPSAtMTtcbnZhciBaX1NUUkVBTV9FUlJPUiAgPSAtMjtcbnZhciBaX0RBVEFfRVJST1IgICAgPSAtMztcbnZhciBaX01FTV9FUlJPUiAgICAgPSAtNDtcbnZhciBaX0JVRl9FUlJPUiAgICAgPSAtNTtcbi8vdmFyIFpfVkVSU0lPTl9FUlJPUiA9IC02O1xuXG4vKiBUaGUgZGVmbGF0ZSBjb21wcmVzc2lvbiBtZXRob2QgKi9cbnZhciBaX0RFRkxBVEVEICA9IDg7XG5cblxuLyogU1RBVEVTID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxudmFyICAgIEhFQUQgPSAxOyAgICAgICAvKiBpOiB3YWl0aW5nIGZvciBtYWdpYyBoZWFkZXIgKi9cbnZhciAgICBGTEFHUyA9IDI7ICAgICAgLyogaTogd2FpdGluZyBmb3IgbWV0aG9kIGFuZCBmbGFncyAoZ3ppcCkgKi9cbnZhciAgICBUSU1FID0gMzsgICAgICAgLyogaTogd2FpdGluZyBmb3IgbW9kaWZpY2F0aW9uIHRpbWUgKGd6aXApICovXG52YXIgICAgT1MgPSA0OyAgICAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGV4dHJhIGZsYWdzIGFuZCBvcGVyYXRpbmcgc3lzdGVtIChnemlwKSAqL1xudmFyICAgIEVYTEVOID0gNTsgICAgICAvKiBpOiB3YWl0aW5nIGZvciBleHRyYSBsZW5ndGggKGd6aXApICovXG52YXIgICAgRVhUUkEgPSA2OyAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGV4dHJhIGJ5dGVzIChnemlwKSAqL1xudmFyICAgIE5BTUUgPSA3OyAgICAgICAvKiBpOiB3YWl0aW5nIGZvciBlbmQgb2YgZmlsZSBuYW1lIChnemlwKSAqL1xudmFyICAgIENPTU1FTlQgPSA4OyAgICAvKiBpOiB3YWl0aW5nIGZvciBlbmQgb2YgY29tbWVudCAoZ3ppcCkgKi9cbnZhciAgICBIQ1JDID0gOTsgICAgICAgLyogaTogd2FpdGluZyBmb3IgaGVhZGVyIGNyYyAoZ3ppcCkgKi9cbnZhciAgICBESUNUSUQgPSAxMDsgICAgLyogaTogd2FpdGluZyBmb3IgZGljdGlvbmFyeSBjaGVjayB2YWx1ZSAqL1xudmFyICAgIERJQ1QgPSAxMTsgICAgICAvKiB3YWl0aW5nIGZvciBpbmZsYXRlU2V0RGljdGlvbmFyeSgpIGNhbGwgKi9cbnZhciAgICAgICAgVFlQRSA9IDEyOyAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIHR5cGUgYml0cywgaW5jbHVkaW5nIGxhc3QtZmxhZyBiaXQgKi9cbnZhciAgICAgICAgVFlQRURPID0gMTM7ICAgIC8qIGk6IHNhbWUsIGJ1dCBza2lwIGNoZWNrIHRvIGV4aXQgaW5mbGF0ZSBvbiBuZXcgYmxvY2sgKi9cbnZhciAgICAgICAgU1RPUkVEID0gMTQ7ICAgIC8qIGk6IHdhaXRpbmcgZm9yIHN0b3JlZCBzaXplIChsZW5ndGggYW5kIGNvbXBsZW1lbnQpICovXG52YXIgICAgICAgIENPUFlfID0gMTU7ICAgICAvKiBpL286IHNhbWUgYXMgQ09QWSBiZWxvdywgYnV0IG9ubHkgZmlyc3QgdGltZSBpbiAqL1xudmFyICAgICAgICBDT1BZID0gMTY7ICAgICAgLyogaS9vOiB3YWl0aW5nIGZvciBpbnB1dCBvciBvdXRwdXQgdG8gY29weSBzdG9yZWQgYmxvY2sgKi9cbnZhciAgICAgICAgVEFCTEUgPSAxNzsgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGR5bmFtaWMgYmxvY2sgdGFibGUgbGVuZ3RocyAqL1xudmFyICAgICAgICBMRU5MRU5TID0gMTg7ICAgLyogaTogd2FpdGluZyBmb3IgY29kZSBsZW5ndGggY29kZSBsZW5ndGhzICovXG52YXIgICAgICAgIENPREVMRU5TID0gMTk7ICAvKiBpOiB3YWl0aW5nIGZvciBsZW5ndGgvbGl0IGFuZCBkaXN0YW5jZSBjb2RlIGxlbmd0aHMgKi9cbnZhciAgICAgICAgICAgIExFTl8gPSAyMDsgICAgICAvKiBpOiBzYW1lIGFzIExFTiBiZWxvdywgYnV0IG9ubHkgZmlyc3QgdGltZSBpbiAqL1xudmFyICAgICAgICAgICAgTEVOID0gMjE7ICAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGxlbmd0aC9saXQvZW9iIGNvZGUgKi9cbnZhciAgICAgICAgICAgIExFTkVYVCA9IDIyOyAgICAvKiBpOiB3YWl0aW5nIGZvciBsZW5ndGggZXh0cmEgYml0cyAqL1xudmFyICAgICAgICAgICAgRElTVCA9IDIzOyAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGRpc3RhbmNlIGNvZGUgKi9cbnZhciAgICAgICAgICAgIERJU1RFWFQgPSAyNDsgICAvKiBpOiB3YWl0aW5nIGZvciBkaXN0YW5jZSBleHRyYSBiaXRzICovXG52YXIgICAgICAgICAgICBNQVRDSCA9IDI1OyAgICAgLyogbzogd2FpdGluZyBmb3Igb3V0cHV0IHNwYWNlIHRvIGNvcHkgc3RyaW5nICovXG52YXIgICAgICAgICAgICBMSVQgPSAyNjsgICAgICAgLyogbzogd2FpdGluZyBmb3Igb3V0cHV0IHNwYWNlIHRvIHdyaXRlIGxpdGVyYWwgKi9cbnZhciAgICBDSEVDSyA9IDI3OyAgICAgLyogaTogd2FpdGluZyBmb3IgMzItYml0IGNoZWNrIHZhbHVlICovXG52YXIgICAgTEVOR1RIID0gMjg7ICAgIC8qIGk6IHdhaXRpbmcgZm9yIDMyLWJpdCBsZW5ndGggKGd6aXApICovXG52YXIgICAgRE9ORSA9IDI5OyAgICAgIC8qIGZpbmlzaGVkIGNoZWNrLCBkb25lIC0tIHJlbWFpbiBoZXJlIHVudGlsIHJlc2V0ICovXG52YXIgICAgQkFEID0gMzA7ICAgICAgIC8qIGdvdCBhIGRhdGEgZXJyb3IgLS0gcmVtYWluIGhlcmUgdW50aWwgcmVzZXQgKi9cbnZhciAgICBNRU0gPSAzMTsgICAgICAgLyogZ290IGFuIGluZmxhdGUoKSBtZW1vcnkgZXJyb3IgLS0gcmVtYWluIGhlcmUgdW50aWwgcmVzZXQgKi9cbnZhciAgICBTWU5DID0gMzI7ICAgICAgLyogbG9va2luZyBmb3Igc3luY2hyb25pemF0aW9uIGJ5dGVzIHRvIHJlc3RhcnQgaW5mbGF0ZSgpICovXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxuXG52YXIgRU5PVUdIX0xFTlMgPSA4NTI7XG52YXIgRU5PVUdIX0RJU1RTID0gNTkyO1xuLy92YXIgRU5PVUdIID0gIChFTk9VR0hfTEVOUytFTk9VR0hfRElTVFMpO1xuXG52YXIgTUFYX1dCSVRTID0gMTU7XG4vKiAzMksgTFo3NyB3aW5kb3cgKi9cbnZhciBERUZfV0JJVFMgPSBNQVhfV0JJVFM7XG5cblxuZnVuY3Rpb24gWlNXQVAzMihxKSB7XG4gIHJldHVybiAgKCgocSA+Pj4gMjQpICYgMHhmZikgK1xuICAgICAgICAgICgocSA+Pj4gOCkgJiAweGZmMDApICtcbiAgICAgICAgICAoKHEgJiAweGZmMDApIDw8IDgpICtcbiAgICAgICAgICAoKHEgJiAweGZmKSA8PCAyNCkpO1xufVxuXG5cbmZ1bmN0aW9uIEluZmxhdGVTdGF0ZSgpIHtcbiAgdGhpcy5tb2RlID0gMDsgICAgICAgICAgICAgLyogY3VycmVudCBpbmZsYXRlIG1vZGUgKi9cbiAgdGhpcy5sYXN0ID0gZmFsc2U7ICAgICAgICAgIC8qIHRydWUgaWYgcHJvY2Vzc2luZyBsYXN0IGJsb2NrICovXG4gIHRoaXMud3JhcCA9IDA7ICAgICAgICAgICAgICAvKiBiaXQgMCB0cnVlIGZvciB6bGliLCBiaXQgMSB0cnVlIGZvciBnemlwICovXG4gIHRoaXMuaGF2ZWRpY3QgPSBmYWxzZTsgICAgICAvKiB0cnVlIGlmIGRpY3Rpb25hcnkgcHJvdmlkZWQgKi9cbiAgdGhpcy5mbGFncyA9IDA7ICAgICAgICAgICAgIC8qIGd6aXAgaGVhZGVyIG1ldGhvZCBhbmQgZmxhZ3MgKDAgaWYgemxpYikgKi9cbiAgdGhpcy5kbWF4ID0gMDsgICAgICAgICAgICAgIC8qIHpsaWIgaGVhZGVyIG1heCBkaXN0YW5jZSAoSU5GTEFURV9TVFJJQ1QpICovXG4gIHRoaXMuY2hlY2sgPSAwOyAgICAgICAgICAgICAvKiBwcm90ZWN0ZWQgY29weSBvZiBjaGVjayB2YWx1ZSAqL1xuICB0aGlzLnRvdGFsID0gMDsgICAgICAgICAgICAgLyogcHJvdGVjdGVkIGNvcHkgb2Ygb3V0cHV0IGNvdW50ICovXG4gIC8vIFRPRE86IG1heSBiZSB7fVxuICB0aGlzLmhlYWQgPSBudWxsOyAgICAgICAgICAgLyogd2hlcmUgdG8gc2F2ZSBnemlwIGhlYWRlciBpbmZvcm1hdGlvbiAqL1xuXG4gIC8qIHNsaWRpbmcgd2luZG93ICovXG4gIHRoaXMud2JpdHMgPSAwOyAgICAgICAgICAgICAvKiBsb2cgYmFzZSAyIG9mIHJlcXVlc3RlZCB3aW5kb3cgc2l6ZSAqL1xuICB0aGlzLndzaXplID0gMDsgICAgICAgICAgICAgLyogd2luZG93IHNpemUgb3IgemVybyBpZiBub3QgdXNpbmcgd2luZG93ICovXG4gIHRoaXMud2hhdmUgPSAwOyAgICAgICAgICAgICAvKiB2YWxpZCBieXRlcyBpbiB0aGUgd2luZG93ICovXG4gIHRoaXMud25leHQgPSAwOyAgICAgICAgICAgICAvKiB3aW5kb3cgd3JpdGUgaW5kZXggKi9cbiAgdGhpcy53aW5kb3cgPSBudWxsOyAgICAgICAgIC8qIGFsbG9jYXRlZCBzbGlkaW5nIHdpbmRvdywgaWYgbmVlZGVkICovXG5cbiAgLyogYml0IGFjY3VtdWxhdG9yICovXG4gIHRoaXMuaG9sZCA9IDA7ICAgICAgICAgICAgICAvKiBpbnB1dCBiaXQgYWNjdW11bGF0b3IgKi9cbiAgdGhpcy5iaXRzID0gMDsgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBiaXRzIGluIFwiaW5cIiAqL1xuXG4gIC8qIGZvciBzdHJpbmcgYW5kIHN0b3JlZCBibG9jayBjb3B5aW5nICovXG4gIHRoaXMubGVuZ3RoID0gMDsgICAgICAgICAgICAvKiBsaXRlcmFsIG9yIGxlbmd0aCBvZiBkYXRhIHRvIGNvcHkgKi9cbiAgdGhpcy5vZmZzZXQgPSAwOyAgICAgICAgICAgIC8qIGRpc3RhbmNlIGJhY2sgdG8gY29weSBzdHJpbmcgZnJvbSAqL1xuXG4gIC8qIGZvciB0YWJsZSBhbmQgY29kZSBkZWNvZGluZyAqL1xuICB0aGlzLmV4dHJhID0gMDsgICAgICAgICAgICAgLyogZXh0cmEgYml0cyBuZWVkZWQgKi9cblxuICAvKiBmaXhlZCBhbmQgZHluYW1pYyBjb2RlIHRhYmxlcyAqL1xuICB0aGlzLmxlbmNvZGUgPSBudWxsOyAgICAgICAgICAvKiBzdGFydGluZyB0YWJsZSBmb3IgbGVuZ3RoL2xpdGVyYWwgY29kZXMgKi9cbiAgdGhpcy5kaXN0Y29kZSA9IG51bGw7ICAgICAgICAgLyogc3RhcnRpbmcgdGFibGUgZm9yIGRpc3RhbmNlIGNvZGVzICovXG4gIHRoaXMubGVuYml0cyA9IDA7ICAgICAgICAgICAvKiBpbmRleCBiaXRzIGZvciBsZW5jb2RlICovXG4gIHRoaXMuZGlzdGJpdHMgPSAwOyAgICAgICAgICAvKiBpbmRleCBiaXRzIGZvciBkaXN0Y29kZSAqL1xuXG4gIC8qIGR5bmFtaWMgdGFibGUgYnVpbGRpbmcgKi9cbiAgdGhpcy5uY29kZSA9IDA7ICAgICAgICAgICAgIC8qIG51bWJlciBvZiBjb2RlIGxlbmd0aCBjb2RlIGxlbmd0aHMgKi9cbiAgdGhpcy5ubGVuID0gMDsgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBsZW5ndGggY29kZSBsZW5ndGhzICovXG4gIHRoaXMubmRpc3QgPSAwOyAgICAgICAgICAgICAvKiBudW1iZXIgb2YgZGlzdGFuY2UgY29kZSBsZW5ndGhzICovXG4gIHRoaXMuaGF2ZSA9IDA7ICAgICAgICAgICAgICAvKiBudW1iZXIgb2YgY29kZSBsZW5ndGhzIGluIGxlbnNbXSAqL1xuICB0aGlzLm5leHQgPSBudWxsOyAgICAgICAgICAgICAgLyogbmV4dCBhdmFpbGFibGUgc3BhY2UgaW4gY29kZXNbXSAqL1xuXG4gIHRoaXMubGVucyA9IG5ldyB1dGlscy5CdWYxNigzMjApOyAvKiB0ZW1wb3Jhcnkgc3RvcmFnZSBmb3IgY29kZSBsZW5ndGhzICovXG4gIHRoaXMud29yayA9IG5ldyB1dGlscy5CdWYxNigyODgpOyAvKiB3b3JrIGFyZWEgZm9yIGNvZGUgdGFibGUgYnVpbGRpbmcgKi9cblxuICAvKlxuICAgYmVjYXVzZSB3ZSBkb24ndCBoYXZlIHBvaW50ZXJzIGluIGpzLCB3ZSB1c2UgbGVuY29kZSBhbmQgZGlzdGNvZGUgZGlyZWN0bHlcbiAgIGFzIGJ1ZmZlcnMgc28gd2UgZG9uJ3QgbmVlZCBjb2Rlc1xuICAqL1xuICAvL3RoaXMuY29kZXMgPSBuZXcgdXRpbHMuQnVmMzIoRU5PVUdIKTsgICAgICAgLyogc3BhY2UgZm9yIGNvZGUgdGFibGVzICovXG4gIHRoaXMubGVuZHluID0gbnVsbDsgICAgICAgICAgICAgIC8qIGR5bmFtaWMgdGFibGUgZm9yIGxlbmd0aC9saXRlcmFsIGNvZGVzIChKUyBzcGVjaWZpYykgKi9cbiAgdGhpcy5kaXN0ZHluID0gbnVsbDsgICAgICAgICAgICAgLyogZHluYW1pYyB0YWJsZSBmb3IgZGlzdGFuY2UgY29kZXMgKEpTIHNwZWNpZmljKSAqL1xuICB0aGlzLnNhbmUgPSAwOyAgICAgICAgICAgICAgICAgICAvKiBpZiBmYWxzZSwgYWxsb3cgaW52YWxpZCBkaXN0YW5jZSB0b28gZmFyICovXG4gIHRoaXMuYmFjayA9IDA7ICAgICAgICAgICAgICAgICAgIC8qIGJpdHMgYmFjayBvZiBsYXN0IHVucHJvY2Vzc2VkIGxlbmd0aC9saXQgKi9cbiAgdGhpcy53YXMgPSAwOyAgICAgICAgICAgICAgICAgICAgLyogaW5pdGlhbCBsZW5ndGggb2YgbWF0Y2ggKi9cbn1cblxuZnVuY3Rpb24gaW5mbGF0ZVJlc2V0S2VlcChzdHJtKSB7XG4gIHZhciBzdGF0ZTtcblxuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUpIHsgcmV0dXJuIFpfU1RSRUFNX0VSUk9SOyB9XG4gIHN0YXRlID0gc3RybS5zdGF0ZTtcbiAgc3RybS50b3RhbF9pbiA9IHN0cm0udG90YWxfb3V0ID0gc3RhdGUudG90YWwgPSAwO1xuICBzdHJtLm1zZyA9ICcnOyAvKlpfTlVMTCovXG4gIGlmIChzdGF0ZS53cmFwKSB7ICAgICAgIC8qIHRvIHN1cHBvcnQgaWxsLWNvbmNlaXZlZCBKYXZhIHRlc3Qgc3VpdGUgKi9cbiAgICBzdHJtLmFkbGVyID0gc3RhdGUud3JhcCAmIDE7XG4gIH1cbiAgc3RhdGUubW9kZSA9IEhFQUQ7XG4gIHN0YXRlLmxhc3QgPSAwO1xuICBzdGF0ZS5oYXZlZGljdCA9IDA7XG4gIHN0YXRlLmRtYXggPSAzMjc2ODtcbiAgc3RhdGUuaGVhZCA9IG51bGwvKlpfTlVMTCovO1xuICBzdGF0ZS5ob2xkID0gMDtcbiAgc3RhdGUuYml0cyA9IDA7XG4gIC8vc3RhdGUubGVuY29kZSA9IHN0YXRlLmRpc3Rjb2RlID0gc3RhdGUubmV4dCA9IHN0YXRlLmNvZGVzO1xuICBzdGF0ZS5sZW5jb2RlID0gc3RhdGUubGVuZHluID0gbmV3IHV0aWxzLkJ1ZjMyKEVOT1VHSF9MRU5TKTtcbiAgc3RhdGUuZGlzdGNvZGUgPSBzdGF0ZS5kaXN0ZHluID0gbmV3IHV0aWxzLkJ1ZjMyKEVOT1VHSF9ESVNUUyk7XG5cbiAgc3RhdGUuc2FuZSA9IDE7XG4gIHN0YXRlLmJhY2sgPSAtMTtcbiAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiByZXNldFxcblwiKSk7XG4gIHJldHVybiBaX09LO1xufVxuXG5mdW5jdGlvbiBpbmZsYXRlUmVzZXQoc3RybSkge1xuICB2YXIgc3RhdGU7XG5cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlKSB7IHJldHVybiBaX1NUUkVBTV9FUlJPUjsgfVxuICBzdGF0ZSA9IHN0cm0uc3RhdGU7XG4gIHN0YXRlLndzaXplID0gMDtcbiAgc3RhdGUud2hhdmUgPSAwO1xuICBzdGF0ZS53bmV4dCA9IDA7XG4gIHJldHVybiBpbmZsYXRlUmVzZXRLZWVwKHN0cm0pO1xuXG59XG5cbmZ1bmN0aW9uIGluZmxhdGVSZXNldDIoc3RybSwgd2luZG93Qml0cykge1xuICB2YXIgd3JhcDtcbiAgdmFyIHN0YXRlO1xuXG4gIC8qIGdldCB0aGUgc3RhdGUgKi9cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlKSB7IHJldHVybiBaX1NUUkVBTV9FUlJPUjsgfVxuICBzdGF0ZSA9IHN0cm0uc3RhdGU7XG5cbiAgLyogZXh0cmFjdCB3cmFwIHJlcXVlc3QgZnJvbSB3aW5kb3dCaXRzIHBhcmFtZXRlciAqL1xuICBpZiAod2luZG93Qml0cyA8IDApIHtcbiAgICB3cmFwID0gMDtcbiAgICB3aW5kb3dCaXRzID0gLXdpbmRvd0JpdHM7XG4gIH1cbiAgZWxzZSB7XG4gICAgd3JhcCA9ICh3aW5kb3dCaXRzID4+IDQpICsgMTtcbiAgICBpZiAod2luZG93Qml0cyA8IDQ4KSB7XG4gICAgICB3aW5kb3dCaXRzICY9IDE1O1xuICAgIH1cbiAgfVxuXG4gIC8qIHNldCBudW1iZXIgb2Ygd2luZG93IGJpdHMsIGZyZWUgd2luZG93IGlmIGRpZmZlcmVudCAqL1xuICBpZiAod2luZG93Qml0cyAmJiAod2luZG93Qml0cyA8IDggfHwgd2luZG93Qml0cyA+IDE1KSkge1xuICAgIHJldHVybiBaX1NUUkVBTV9FUlJPUjtcbiAgfVxuICBpZiAoc3RhdGUud2luZG93ICE9PSBudWxsICYmIHN0YXRlLndiaXRzICE9PSB3aW5kb3dCaXRzKSB7XG4gICAgc3RhdGUud2luZG93ID0gbnVsbDtcbiAgfVxuXG4gIC8qIHVwZGF0ZSBzdGF0ZSBhbmQgcmVzZXQgdGhlIHJlc3Qgb2YgaXQgKi9cbiAgc3RhdGUud3JhcCA9IHdyYXA7XG4gIHN0YXRlLndiaXRzID0gd2luZG93Qml0cztcbiAgcmV0dXJuIGluZmxhdGVSZXNldChzdHJtKTtcbn1cblxuZnVuY3Rpb24gaW5mbGF0ZUluaXQyKHN0cm0sIHdpbmRvd0JpdHMpIHtcbiAgdmFyIHJldDtcbiAgdmFyIHN0YXRlO1xuXG4gIGlmICghc3RybSkgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cbiAgLy9zdHJtLm1zZyA9IFpfTlVMTDsgICAgICAgICAgICAgICAgIC8qIGluIGNhc2Ugd2UgcmV0dXJuIGFuIGVycm9yICovXG5cbiAgc3RhdGUgPSBuZXcgSW5mbGF0ZVN0YXRlKCk7XG5cbiAgLy9pZiAoc3RhdGUgPT09IFpfTlVMTCkgcmV0dXJuIFpfTUVNX0VSUk9SO1xuICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6IGFsbG9jYXRlZFxcblwiKSk7XG4gIHN0cm0uc3RhdGUgPSBzdGF0ZTtcbiAgc3RhdGUud2luZG93ID0gbnVsbC8qWl9OVUxMKi87XG4gIHJldCA9IGluZmxhdGVSZXNldDIoc3RybSwgd2luZG93Qml0cyk7XG4gIGlmIChyZXQgIT09IFpfT0spIHtcbiAgICBzdHJtLnN0YXRlID0gbnVsbC8qWl9OVUxMKi87XG4gIH1cbiAgcmV0dXJuIHJldDtcbn1cblxuZnVuY3Rpb24gaW5mbGF0ZUluaXQoc3RybSkge1xuICByZXR1cm4gaW5mbGF0ZUluaXQyKHN0cm0sIERFRl9XQklUUyk7XG59XG5cblxuLypcbiBSZXR1cm4gc3RhdGUgd2l0aCBsZW5ndGggYW5kIGRpc3RhbmNlIGRlY29kaW5nIHRhYmxlcyBhbmQgaW5kZXggc2l6ZXMgc2V0IHRvXG4gZml4ZWQgY29kZSBkZWNvZGluZy4gIE5vcm1hbGx5IHRoaXMgcmV0dXJucyBmaXhlZCB0YWJsZXMgZnJvbSBpbmZmaXhlZC5oLlxuIElmIEJVSUxERklYRUQgaXMgZGVmaW5lZCwgdGhlbiBpbnN0ZWFkIHRoaXMgcm91dGluZSBidWlsZHMgdGhlIHRhYmxlcyB0aGVcbiBmaXJzdCB0aW1lIGl0J3MgY2FsbGVkLCBhbmQgcmV0dXJucyB0aG9zZSB0YWJsZXMgdGhlIGZpcnN0IHRpbWUgYW5kXG4gdGhlcmVhZnRlci4gIFRoaXMgcmVkdWNlcyB0aGUgc2l6ZSBvZiB0aGUgY29kZSBieSBhYm91dCAySyBieXRlcywgaW5cbiBleGNoYW5nZSBmb3IgYSBsaXR0bGUgZXhlY3V0aW9uIHRpbWUuICBIb3dldmVyLCBCVUlMREZJWEVEIHNob3VsZCBub3QgYmVcbiB1c2VkIGZvciB0aHJlYWRlZCBhcHBsaWNhdGlvbnMsIHNpbmNlIHRoZSByZXdyaXRpbmcgb2YgdGhlIHRhYmxlcyBhbmQgdmlyZ2luXG4gbWF5IG5vdCBiZSB0aHJlYWQtc2FmZS5cbiAqL1xudmFyIHZpcmdpbiA9IHRydWU7XG5cbnZhciBsZW5maXgsIGRpc3RmaXg7IC8vIFdlIGhhdmUgbm8gcG9pbnRlcnMgaW4gSlMsIHNvIGtlZXAgdGFibGVzIHNlcGFyYXRlXG5cbmZ1bmN0aW9uIGZpeGVkdGFibGVzKHN0YXRlKSB7XG4gIC8qIGJ1aWxkIGZpeGVkIGh1ZmZtYW4gdGFibGVzIGlmIGZpcnN0IGNhbGwgKG1heSBub3QgYmUgdGhyZWFkIHNhZmUpICovXG4gIGlmICh2aXJnaW4pIHtcbiAgICB2YXIgc3ltO1xuXG4gICAgbGVuZml4ID0gbmV3IHV0aWxzLkJ1ZjMyKDUxMik7XG4gICAgZGlzdGZpeCA9IG5ldyB1dGlscy5CdWYzMigzMik7XG5cbiAgICAvKiBsaXRlcmFsL2xlbmd0aCB0YWJsZSAqL1xuICAgIHN5bSA9IDA7XG4gICAgd2hpbGUgKHN5bSA8IDE0NCkgeyBzdGF0ZS5sZW5zW3N5bSsrXSA9IDg7IH1cbiAgICB3aGlsZSAoc3ltIDwgMjU2KSB7IHN0YXRlLmxlbnNbc3ltKytdID0gOTsgfVxuICAgIHdoaWxlIChzeW0gPCAyODApIHsgc3RhdGUubGVuc1tzeW0rK10gPSA3OyB9XG4gICAgd2hpbGUgKHN5bSA8IDI4OCkgeyBzdGF0ZS5sZW5zW3N5bSsrXSA9IDg7IH1cblxuICAgIGluZmxhdGVfdGFibGUoTEVOUywgIHN0YXRlLmxlbnMsIDAsIDI4OCwgbGVuZml4LCAgIDAsIHN0YXRlLndvcmssIHtiaXRzOiA5fSk7XG5cbiAgICAvKiBkaXN0YW5jZSB0YWJsZSAqL1xuICAgIHN5bSA9IDA7XG4gICAgd2hpbGUgKHN5bSA8IDMyKSB7IHN0YXRlLmxlbnNbc3ltKytdID0gNTsgfVxuXG4gICAgaW5mbGF0ZV90YWJsZShESVNUUywgc3RhdGUubGVucywgMCwgMzIsICAgZGlzdGZpeCwgMCwgc3RhdGUud29yaywge2JpdHM6IDV9KTtcblxuICAgIC8qIGRvIHRoaXMganVzdCBvbmNlICovXG4gICAgdmlyZ2luID0gZmFsc2U7XG4gIH1cblxuICBzdGF0ZS5sZW5jb2RlID0gbGVuZml4O1xuICBzdGF0ZS5sZW5iaXRzID0gOTtcbiAgc3RhdGUuZGlzdGNvZGUgPSBkaXN0Zml4O1xuICBzdGF0ZS5kaXN0Yml0cyA9IDU7XG59XG5cblxuLypcbiBVcGRhdGUgdGhlIHdpbmRvdyB3aXRoIHRoZSBsYXN0IHdzaXplIChub3JtYWxseSAzMkspIGJ5dGVzIHdyaXR0ZW4gYmVmb3JlXG4gcmV0dXJuaW5nLiAgSWYgd2luZG93IGRvZXMgbm90IGV4aXN0IHlldCwgY3JlYXRlIGl0LiAgVGhpcyBpcyBvbmx5IGNhbGxlZFxuIHdoZW4gYSB3aW5kb3cgaXMgYWxyZWFkeSBpbiB1c2UsIG9yIHdoZW4gb3V0cHV0IGhhcyBiZWVuIHdyaXR0ZW4gZHVyaW5nIHRoaXNcbiBpbmZsYXRlIGNhbGwsIGJ1dCB0aGUgZW5kIG9mIHRoZSBkZWZsYXRlIHN0cmVhbSBoYXMgbm90IGJlZW4gcmVhY2hlZCB5ZXQuXG4gSXQgaXMgYWxzbyBjYWxsZWQgdG8gY3JlYXRlIGEgd2luZG93IGZvciBkaWN0aW9uYXJ5IGRhdGEgd2hlbiBhIGRpY3Rpb25hcnlcbiBpcyBsb2FkZWQuXG5cbiBQcm92aWRpbmcgb3V0cHV0IGJ1ZmZlcnMgbGFyZ2VyIHRoYW4gMzJLIHRvIGluZmxhdGUoKSBzaG91bGQgcHJvdmlkZSBhIHNwZWVkXG4gYWR2YW50YWdlLCBzaW5jZSBvbmx5IHRoZSBsYXN0IDMySyBvZiBvdXRwdXQgaXMgY29waWVkIHRvIHRoZSBzbGlkaW5nIHdpbmRvd1xuIHVwb24gcmV0dXJuIGZyb20gaW5mbGF0ZSgpLCBhbmQgc2luY2UgYWxsIGRpc3RhbmNlcyBhZnRlciB0aGUgZmlyc3QgMzJLIG9mXG4gb3V0cHV0IHdpbGwgZmFsbCBpbiB0aGUgb3V0cHV0IGRhdGEsIG1ha2luZyBtYXRjaCBjb3BpZXMgc2ltcGxlciBhbmQgZmFzdGVyLlxuIFRoZSBhZHZhbnRhZ2UgbWF5IGJlIGRlcGVuZGVudCBvbiB0aGUgc2l6ZSBvZiB0aGUgcHJvY2Vzc29yJ3MgZGF0YSBjYWNoZXMuXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZXdpbmRvdyhzdHJtLCBzcmMsIGVuZCwgY29weSkge1xuICB2YXIgZGlzdDtcbiAgdmFyIHN0YXRlID0gc3RybS5zdGF0ZTtcblxuICAvKiBpZiBpdCBoYXNuJ3QgYmVlbiBkb25lIGFscmVhZHksIGFsbG9jYXRlIHNwYWNlIGZvciB0aGUgd2luZG93ICovXG4gIGlmIChzdGF0ZS53aW5kb3cgPT09IG51bGwpIHtcbiAgICBzdGF0ZS53c2l6ZSA9IDEgPDwgc3RhdGUud2JpdHM7XG4gICAgc3RhdGUud25leHQgPSAwO1xuICAgIHN0YXRlLndoYXZlID0gMDtcblxuICAgIHN0YXRlLndpbmRvdyA9IG5ldyB1dGlscy5CdWY4KHN0YXRlLndzaXplKTtcbiAgfVxuXG4gIC8qIGNvcHkgc3RhdGUtPndzaXplIG9yIGxlc3Mgb3V0cHV0IGJ5dGVzIGludG8gdGhlIGNpcmN1bGFyIHdpbmRvdyAqL1xuICBpZiAoY29weSA+PSBzdGF0ZS53c2l6ZSkge1xuICAgIHV0aWxzLmFycmF5U2V0KHN0YXRlLndpbmRvdyxzcmMsIGVuZCAtIHN0YXRlLndzaXplLCBzdGF0ZS53c2l6ZSwgMCk7XG4gICAgc3RhdGUud25leHQgPSAwO1xuICAgIHN0YXRlLndoYXZlID0gc3RhdGUud3NpemU7XG4gIH1cbiAgZWxzZSB7XG4gICAgZGlzdCA9IHN0YXRlLndzaXplIC0gc3RhdGUud25leHQ7XG4gICAgaWYgKGRpc3QgPiBjb3B5KSB7XG4gICAgICBkaXN0ID0gY29weTtcbiAgICB9XG4gICAgLy96bWVtY3B5KHN0YXRlLT53aW5kb3cgKyBzdGF0ZS0+d25leHQsIGVuZCAtIGNvcHksIGRpc3QpO1xuICAgIHV0aWxzLmFycmF5U2V0KHN0YXRlLndpbmRvdyxzcmMsIGVuZCAtIGNvcHksIGRpc3QsIHN0YXRlLnduZXh0KTtcbiAgICBjb3B5IC09IGRpc3Q7XG4gICAgaWYgKGNvcHkpIHtcbiAgICAgIC8vem1lbWNweShzdGF0ZS0+d2luZG93LCBlbmQgLSBjb3B5LCBjb3B5KTtcbiAgICAgIHV0aWxzLmFycmF5U2V0KHN0YXRlLndpbmRvdyxzcmMsIGVuZCAtIGNvcHksIGNvcHksIDApO1xuICAgICAgc3RhdGUud25leHQgPSBjb3B5O1xuICAgICAgc3RhdGUud2hhdmUgPSBzdGF0ZS53c2l6ZTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBzdGF0ZS53bmV4dCArPSBkaXN0O1xuICAgICAgaWYgKHN0YXRlLnduZXh0ID09PSBzdGF0ZS53c2l6ZSkgeyBzdGF0ZS53bmV4dCA9IDA7IH1cbiAgICAgIGlmIChzdGF0ZS53aGF2ZSA8IHN0YXRlLndzaXplKSB7IHN0YXRlLndoYXZlICs9IGRpc3Q7IH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIDA7XG59XG5cbmZ1bmN0aW9uIGluZmxhdGUoc3RybSwgZmx1c2gpIHtcbiAgdmFyIHN0YXRlO1xuICB2YXIgaW5wdXQsIG91dHB1dDsgICAgICAgICAgLy8gaW5wdXQvb3V0cHV0IGJ1ZmZlcnNcbiAgdmFyIG5leHQ7ICAgICAgICAgICAgICAgICAgIC8qIG5leHQgaW5wdXQgSU5ERVggKi9cbiAgdmFyIHB1dDsgICAgICAgICAgICAgICAgICAgIC8qIG5leHQgb3V0cHV0IElOREVYICovXG4gIHZhciBoYXZlLCBsZWZ0OyAgICAgICAgICAgICAvKiBhdmFpbGFibGUgaW5wdXQgYW5kIG91dHB1dCAqL1xuICB2YXIgaG9sZDsgICAgICAgICAgICAgICAgICAgLyogYml0IGJ1ZmZlciAqL1xuICB2YXIgYml0czsgICAgICAgICAgICAgICAgICAgLyogYml0cyBpbiBiaXQgYnVmZmVyICovXG4gIHZhciBfaW4sIF9vdXQ7ICAgICAgICAgICAgICAvKiBzYXZlIHN0YXJ0aW5nIGF2YWlsYWJsZSBpbnB1dCBhbmQgb3V0cHV0ICovXG4gIHZhciBjb3B5OyAgICAgICAgICAgICAgICAgICAvKiBudW1iZXIgb2Ygc3RvcmVkIG9yIG1hdGNoIGJ5dGVzIHRvIGNvcHkgKi9cbiAgdmFyIGZyb207ICAgICAgICAgICAgICAgICAgIC8qIHdoZXJlIHRvIGNvcHkgbWF0Y2ggYnl0ZXMgZnJvbSAqL1xuICB2YXIgZnJvbV9zb3VyY2U7XG4gIHZhciBoZXJlID0gMDsgICAgICAgICAgICAgICAvKiBjdXJyZW50IGRlY29kaW5nIHRhYmxlIGVudHJ5ICovXG4gIHZhciBoZXJlX2JpdHMsIGhlcmVfb3AsIGhlcmVfdmFsOyAvLyBwYWtlZCBcImhlcmVcIiBkZW5vcm1hbGl6ZWQgKEpTIHNwZWNpZmljKVxuICAvL3ZhciBsYXN0OyAgICAgICAgICAgICAgICAgICAvKiBwYXJlbnQgdGFibGUgZW50cnkgKi9cbiAgdmFyIGxhc3RfYml0cywgbGFzdF9vcCwgbGFzdF92YWw7IC8vIHBha2VkIFwibGFzdFwiIGRlbm9ybWFsaXplZCAoSlMgc3BlY2lmaWMpXG4gIHZhciBsZW47ICAgICAgICAgICAgICAgICAgICAvKiBsZW5ndGggdG8gY29weSBmb3IgcmVwZWF0cywgYml0cyB0byBkcm9wICovXG4gIHZhciByZXQ7ICAgICAgICAgICAgICAgICAgICAvKiByZXR1cm4gY29kZSAqL1xuICB2YXIgaGJ1ZiA9IG5ldyB1dGlscy5CdWY4KDQpOyAgICAvKiBidWZmZXIgZm9yIGd6aXAgaGVhZGVyIGNyYyBjYWxjdWxhdGlvbiAqL1xuICB2YXIgb3B0cztcblxuICB2YXIgbjsgLy8gdGVtcG9yYXJ5IHZhciBmb3IgTkVFRF9CSVRTXG5cbiAgdmFyIG9yZGVyID0gLyogcGVybXV0YXRpb24gb2YgY29kZSBsZW5ndGhzICovXG4gICAgWzE2LCAxNywgMTgsIDAsIDgsIDcsIDksIDYsIDEwLCA1LCAxMSwgNCwgMTIsIDMsIDEzLCAyLCAxNCwgMSwgMTVdO1xuXG5cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlIHx8ICFzdHJtLm91dHB1dCB8fFxuICAgICAgKCFzdHJtLmlucHV0ICYmIHN0cm0uYXZhaWxfaW4gIT09IDApKSB7XG4gICAgcmV0dXJuIFpfU1RSRUFNX0VSUk9SO1xuICB9XG5cbiAgc3RhdGUgPSBzdHJtLnN0YXRlO1xuICBpZiAoc3RhdGUubW9kZSA9PT0gVFlQRSkgeyBzdGF0ZS5tb2RlID0gVFlQRURPOyB9ICAgIC8qIHNraXAgY2hlY2sgKi9cblxuXG4gIC8vLS0tIExPQUQoKSAtLS1cbiAgcHV0ID0gc3RybS5uZXh0X291dDtcbiAgb3V0cHV0ID0gc3RybS5vdXRwdXQ7XG4gIGxlZnQgPSBzdHJtLmF2YWlsX291dDtcbiAgbmV4dCA9IHN0cm0ubmV4dF9pbjtcbiAgaW5wdXQgPSBzdHJtLmlucHV0O1xuICBoYXZlID0gc3RybS5hdmFpbF9pbjtcbiAgaG9sZCA9IHN0YXRlLmhvbGQ7XG4gIGJpdHMgPSBzdGF0ZS5iaXRzO1xuICAvLy0tLVxuXG4gIF9pbiA9IGhhdmU7XG4gIF9vdXQgPSBsZWZ0O1xuICByZXQgPSBaX09LO1xuXG4gIGluZl9sZWF2ZTogLy8gZ290byBlbXVsYXRpb25cbiAgZm9yICg7Oykge1xuICAgIHN3aXRjaCAoc3RhdGUubW9kZSkge1xuICAgIGNhc2UgSEVBRDpcbiAgICAgIGlmIChzdGF0ZS53cmFwID09PSAwKSB7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBUWVBFRE87XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgLy89PT0gTkVFREJJVFMoMTYpO1xuICAgICAgd2hpbGUgKGJpdHMgPCAxNikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgaWYgKChzdGF0ZS53cmFwICYgMikgJiYgaG9sZCA9PT0gMHg4YjFmKSB7ICAvKiBnemlwIGhlYWRlciAqL1xuICAgICAgICBzdGF0ZS5jaGVjayA9IDAvKmNyYzMyKDBMLCBaX05VTEwsIDApKi87XG4gICAgICAgIC8vPT09IENSQzIoc3RhdGUuY2hlY2ssIGhvbGQpO1xuICAgICAgICBoYnVmWzBdID0gaG9sZCAmIDB4ZmY7XG4gICAgICAgIGhidWZbMV0gPSAoaG9sZCA+Pj4gOCkgJiAweGZmO1xuICAgICAgICBzdGF0ZS5jaGVjayA9IGNyYzMyKHN0YXRlLmNoZWNrLCBoYnVmLCAyLCAwKTtcbiAgICAgICAgLy89PT0vL1xuXG4gICAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICAgIGhvbGQgPSAwO1xuICAgICAgICBiaXRzID0gMDtcbiAgICAgICAgLy89PT0vL1xuICAgICAgICBzdGF0ZS5tb2RlID0gRkxBR1M7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUuZmxhZ3MgPSAwOyAgICAgICAgICAgLyogZXhwZWN0IHpsaWIgaGVhZGVyICovXG4gICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLmRvbmUgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGlmICghKHN0YXRlLndyYXAgJiAxKSB8fCAgIC8qIGNoZWNrIGlmIHpsaWIgaGVhZGVyIGFsbG93ZWQgKi9cbiAgICAgICAgKCgoaG9sZCAmIDB4ZmYpLypCSVRTKDgpKi8gPDwgOCkgKyAoaG9sZCA+PiA4KSkgJSAzMSkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbmNvcnJlY3QgaGVhZGVyIGNoZWNrJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoKGhvbGQgJiAweDBmKS8qQklUUyg0KSovICE9PSBaX0RFRkxBVEVEKSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ3Vua25vd24gY29tcHJlc3Npb24gbWV0aG9kJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvLy0tLSBEUk9QQklUUyg0KSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IDQ7XG4gICAgICBiaXRzIC09IDQ7XG4gICAgICAvLy0tLS8vXG4gICAgICBsZW4gPSAoaG9sZCAmIDB4MGYpLypCSVRTKDQpKi8gKyA4O1xuICAgICAgaWYgKHN0YXRlLndiaXRzID09PSAwKSB7XG4gICAgICAgIHN0YXRlLndiaXRzID0gbGVuO1xuICAgICAgfVxuICAgICAgZWxzZSBpZiAobGVuID4gc3RhdGUud2JpdHMpIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCB3aW5kb3cgc2l6ZSc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUuZG1heCA9IDEgPDwgbGVuO1xuICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgIHpsaWIgaGVhZGVyIG9rXFxuXCIpKTtcbiAgICAgIHN0cm0uYWRsZXIgPSBzdGF0ZS5jaGVjayA9IDEvKmFkbGVyMzIoMEwsIFpfTlVMTCwgMCkqLztcbiAgICAgIHN0YXRlLm1vZGUgPSBob2xkICYgMHgyMDAgPyBESUNUSUQgOiBUWVBFO1xuICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgIGhvbGQgPSAwO1xuICAgICAgYml0cyA9IDA7XG4gICAgICAvLz09PS8vXG4gICAgICBicmVhaztcbiAgICBjYXNlIEZMQUdTOlxuICAgICAgLy89PT0gTkVFREJJVFMoMTYpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAxNikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUuZmxhZ3MgPSBob2xkO1xuICAgICAgaWYgKChzdGF0ZS5mbGFncyAmIDB4ZmYpICE9PSBaX0RFRkxBVEVEKSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ3Vua25vd24gY29tcHJlc3Npb24gbWV0aG9kJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweGUwMDApIHtcbiAgICAgICAgc3RybS5tc2cgPSAndW5rbm93biBoZWFkZXIgZmxhZ3Mgc2V0JztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLnRleHQgPSAoKGhvbGQgPj4gOCkgJiAxKTtcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAvLz09PSBDUkMyKHN0YXRlLmNoZWNrLCBob2xkKTtcbiAgICAgICAgaGJ1ZlswXSA9IGhvbGQgJiAweGZmO1xuICAgICAgICBoYnVmWzFdID0gKGhvbGQgPj4+IDgpICYgMHhmZjtcbiAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaGJ1ZiwgMiwgMCk7XG4gICAgICAgIC8vPT09Ly9cbiAgICAgIH1cbiAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICBob2xkID0gMDtcbiAgICAgIGJpdHMgPSAwO1xuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubW9kZSA9IFRJTUU7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBUSU1FOlxuICAgICAgLy89PT0gTkVFREJJVFMoMzIpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAzMikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgaWYgKHN0YXRlLmhlYWQpIHtcbiAgICAgICAgc3RhdGUuaGVhZC50aW1lID0gaG9sZDtcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAvLz09PSBDUkM0KHN0YXRlLmNoZWNrLCBob2xkKVxuICAgICAgICBoYnVmWzBdID0gaG9sZCAmIDB4ZmY7XG4gICAgICAgIGhidWZbMV0gPSAoaG9sZCA+Pj4gOCkgJiAweGZmO1xuICAgICAgICBoYnVmWzJdID0gKGhvbGQgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgIGhidWZbM10gPSAoaG9sZCA+Pj4gMjQpICYgMHhmZjtcbiAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaGJ1ZiwgNCwgMCk7XG4gICAgICAgIC8vPT09XG4gICAgICB9XG4gICAgICAvLz09PSBJTklUQklUUygpO1xuICAgICAgaG9sZCA9IDA7XG4gICAgICBiaXRzID0gMDtcbiAgICAgIC8vPT09Ly9cbiAgICAgIHN0YXRlLm1vZGUgPSBPUztcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIE9TOlxuICAgICAgLy89PT0gTkVFREJJVFMoMTYpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAxNikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgaWYgKHN0YXRlLmhlYWQpIHtcbiAgICAgICAgc3RhdGUuaGVhZC54ZmxhZ3MgPSAoaG9sZCAmIDB4ZmYpO1xuICAgICAgICBzdGF0ZS5oZWFkLm9zID0gKGhvbGQgPj4gOCk7XG4gICAgICB9XG4gICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDAyMDApIHtcbiAgICAgICAgLy89PT0gQ1JDMihzdGF0ZS5jaGVjaywgaG9sZCk7XG4gICAgICAgIGhidWZbMF0gPSBob2xkICYgMHhmZjtcbiAgICAgICAgaGJ1ZlsxXSA9IChob2xkID4+PiA4KSAmIDB4ZmY7XG4gICAgICAgIHN0YXRlLmNoZWNrID0gY3JjMzIoc3RhdGUuY2hlY2ssIGhidWYsIDIsIDApO1xuICAgICAgICAvLz09PS8vXG4gICAgICB9XG4gICAgICAvLz09PSBJTklUQklUUygpO1xuICAgICAgaG9sZCA9IDA7XG4gICAgICBiaXRzID0gMDtcbiAgICAgIC8vPT09Ly9cbiAgICAgIHN0YXRlLm1vZGUgPSBFWExFTjtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIEVYTEVOOlxuICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwNDAwKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKDE2KTsgKi9cbiAgICAgICAgd2hpbGUgKGJpdHMgPCAxNikge1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0vL1xuICAgICAgICBzdGF0ZS5sZW5ndGggPSBob2xkO1xuICAgICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICAgIHN0YXRlLmhlYWQuZXh0cmFfbGVuID0gaG9sZDtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDAyMDApIHtcbiAgICAgICAgICAvLz09PSBDUkMyKHN0YXRlLmNoZWNrLCBob2xkKTtcbiAgICAgICAgICBoYnVmWzBdID0gaG9sZCAmIDB4ZmY7XG4gICAgICAgICAgaGJ1ZlsxXSA9IChob2xkID4+PiA4KSAmIDB4ZmY7XG4gICAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaGJ1ZiwgMiwgMCk7XG4gICAgICAgICAgLy89PT0vL1xuICAgICAgICB9XG4gICAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICAgIGhvbGQgPSAwO1xuICAgICAgICBiaXRzID0gMDtcbiAgICAgICAgLy89PT0vL1xuICAgICAgfVxuICAgICAgZWxzZSBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLmV4dHJhID0gbnVsbC8qWl9OVUxMKi87XG4gICAgICB9XG4gICAgICBzdGF0ZS5tb2RlID0gRVhUUkE7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBFWFRSQTpcbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDQwMCkge1xuICAgICAgICBjb3B5ID0gc3RhdGUubGVuZ3RoO1xuICAgICAgICBpZiAoY29weSA+IGhhdmUpIHsgY29weSA9IGhhdmU7IH1cbiAgICAgICAgaWYgKGNvcHkpIHtcbiAgICAgICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICAgICAgbGVuID0gc3RhdGUuaGVhZC5leHRyYV9sZW4gLSBzdGF0ZS5sZW5ndGg7XG4gICAgICAgICAgICBpZiAoIXN0YXRlLmhlYWQuZXh0cmEpIHtcbiAgICAgICAgICAgICAgLy8gVXNlIHVudHlwZWQgYXJyYXkgZm9yIG1vcmUgY29udmVuaWVuZCBwcm9jZXNzaW5nIGxhdGVyXG4gICAgICAgICAgICAgIHN0YXRlLmhlYWQuZXh0cmEgPSBuZXcgQXJyYXkoc3RhdGUuaGVhZC5leHRyYV9sZW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdXRpbHMuYXJyYXlTZXQoXG4gICAgICAgICAgICAgIHN0YXRlLmhlYWQuZXh0cmEsXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICBuZXh0LFxuICAgICAgICAgICAgICAvLyBleHRyYSBmaWVsZCBpcyBsaW1pdGVkIHRvIDY1NTM2IGJ5dGVzXG4gICAgICAgICAgICAgIC8vIC0gbm8gbmVlZCBmb3IgYWRkaXRpb25hbCBzaXplIGNoZWNrXG4gICAgICAgICAgICAgIGNvcHksXG4gICAgICAgICAgICAgIC8qbGVuICsgY29weSA+IHN0YXRlLmhlYWQuZXh0cmFfbWF4IC0gbGVuID8gc3RhdGUuaGVhZC5leHRyYV9tYXggOiBjb3B5LCovXG4gICAgICAgICAgICAgIGxlblxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIC8vem1lbWNweShzdGF0ZS5oZWFkLmV4dHJhICsgbGVuLCBuZXh0LFxuICAgICAgICAgICAgLy8gICAgICAgIGxlbiArIGNvcHkgPiBzdGF0ZS5oZWFkLmV4dHJhX21heCA/XG4gICAgICAgICAgICAvLyAgICAgICAgc3RhdGUuaGVhZC5leHRyYV9tYXggLSBsZW4gOiBjb3B5KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgICAgICBzdGF0ZS5jaGVjayA9IGNyYzMyKHN0YXRlLmNoZWNrLCBpbnB1dCwgY29weSwgbmV4dCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGhhdmUgLT0gY29weTtcbiAgICAgICAgICBuZXh0ICs9IGNvcHk7XG4gICAgICAgICAgc3RhdGUubGVuZ3RoIC09IGNvcHk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0YXRlLmxlbmd0aCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgIH1cbiAgICAgIHN0YXRlLmxlbmd0aCA9IDA7XG4gICAgICBzdGF0ZS5tb2RlID0gTkFNRTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIE5BTUU6XG4gICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDA4MDApIHtcbiAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgIGNvcHkgPSAwO1xuICAgICAgICBkbyB7XG4gICAgICAgICAgLy8gVE9ETzogMiBvciAxIGJ5dGVzP1xuICAgICAgICAgIGxlbiA9IGlucHV0W25leHQgKyBjb3B5KytdO1xuICAgICAgICAgIC8qIHVzZSBjb25zdGFudCBsaW1pdCBiZWNhdXNlIGluIGpzIHdlIHNob3VsZCBub3QgcHJlYWxsb2NhdGUgbWVtb3J5ICovXG4gICAgICAgICAgaWYgKHN0YXRlLmhlYWQgJiYgbGVuICYmXG4gICAgICAgICAgICAgIChzdGF0ZS5sZW5ndGggPCA2NTUzNiAvKnN0YXRlLmhlYWQubmFtZV9tYXgqLykpIHtcbiAgICAgICAgICAgIHN0YXRlLmhlYWQubmFtZSArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGxlbik7XG4gICAgICAgICAgfVxuICAgICAgICB9IHdoaWxlIChsZW4gJiYgY29weSA8IGhhdmUpO1xuXG4gICAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAgIHN0YXRlLmNoZWNrID0gY3JjMzIoc3RhdGUuY2hlY2ssIGlucHV0LCBjb3B5LCBuZXh0KTtcbiAgICAgICAgfVxuICAgICAgICBoYXZlIC09IGNvcHk7XG4gICAgICAgIG5leHQgKz0gY29weTtcbiAgICAgICAgaWYgKGxlbikgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgIH1cbiAgICAgIGVsc2UgaWYgKHN0YXRlLmhlYWQpIHtcbiAgICAgICAgc3RhdGUuaGVhZC5uYW1lID0gbnVsbDtcbiAgICAgIH1cbiAgICAgIHN0YXRlLmxlbmd0aCA9IDA7XG4gICAgICBzdGF0ZS5tb2RlID0gQ09NTUVOVDtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIENPTU1FTlQ6XG4gICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDEwMDApIHtcbiAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgIGNvcHkgPSAwO1xuICAgICAgICBkbyB7XG4gICAgICAgICAgbGVuID0gaW5wdXRbbmV4dCArIGNvcHkrK107XG4gICAgICAgICAgLyogdXNlIGNvbnN0YW50IGxpbWl0IGJlY2F1c2UgaW4ganMgd2Ugc2hvdWxkIG5vdCBwcmVhbGxvY2F0ZSBtZW1vcnkgKi9cbiAgICAgICAgICBpZiAoc3RhdGUuaGVhZCAmJiBsZW4gJiZcbiAgICAgICAgICAgICAgKHN0YXRlLmxlbmd0aCA8IDY1NTM2IC8qc3RhdGUuaGVhZC5jb21tX21heCovKSkge1xuICAgICAgICAgICAgc3RhdGUuaGVhZC5jb21tZW50ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUobGVuKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gd2hpbGUgKGxlbiAmJiBjb3B5IDwgaGF2ZSk7XG4gICAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAgIHN0YXRlLmNoZWNrID0gY3JjMzIoc3RhdGUuY2hlY2ssIGlucHV0LCBjb3B5LCBuZXh0KTtcbiAgICAgICAgfVxuICAgICAgICBoYXZlIC09IGNvcHk7XG4gICAgICAgIG5leHQgKz0gY29weTtcbiAgICAgICAgaWYgKGxlbikgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgIH1cbiAgICAgIGVsc2UgaWYgKHN0YXRlLmhlYWQpIHtcbiAgICAgICAgc3RhdGUuaGVhZC5jb21tZW50ID0gbnVsbDtcbiAgICAgIH1cbiAgICAgIHN0YXRlLm1vZGUgPSBIQ1JDO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgSENSQzpcbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAvLz09PSBORUVEQklUUygxNik7ICovXG4gICAgICAgIHdoaWxlIChiaXRzIDwgMTYpIHtcbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgaWYgKGhvbGQgIT09IChzdGF0ZS5jaGVjayAmIDB4ZmZmZikpIHtcbiAgICAgICAgICBzdHJtLm1zZyA9ICdoZWFkZXIgY3JjIG1pc21hdGNoJztcbiAgICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICAgIGhvbGQgPSAwO1xuICAgICAgICBiaXRzID0gMDtcbiAgICAgICAgLy89PT0vL1xuICAgICAgfVxuICAgICAgaWYgKHN0YXRlLmhlYWQpIHtcbiAgICAgICAgc3RhdGUuaGVhZC5oY3JjID0gKChzdGF0ZS5mbGFncyA+PiA5KSAmIDEpO1xuICAgICAgICBzdGF0ZS5oZWFkLmRvbmUgPSB0cnVlO1xuICAgICAgfVxuICAgICAgc3RybS5hZGxlciA9IHN0YXRlLmNoZWNrID0gMCAvKmNyYzMyKDBMLCBaX05VTEwsIDApKi87XG4gICAgICBzdGF0ZS5tb2RlID0gVFlQRTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgRElDVElEOlxuICAgICAgLy89PT0gTkVFREJJVFMoMzIpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAzMikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgc3RybS5hZGxlciA9IHN0YXRlLmNoZWNrID0gWlNXQVAzMihob2xkKTtcbiAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICBob2xkID0gMDtcbiAgICAgIGJpdHMgPSAwO1xuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubW9kZSA9IERJQ1Q7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBESUNUOlxuICAgICAgaWYgKHN0YXRlLmhhdmVkaWN0ID09PSAwKSB7XG4gICAgICAgIC8vLS0tIFJFU1RPUkUoKSAtLS1cbiAgICAgICAgc3RybS5uZXh0X291dCA9IHB1dDtcbiAgICAgICAgc3RybS5hdmFpbF9vdXQgPSBsZWZ0O1xuICAgICAgICBzdHJtLm5leHRfaW4gPSBuZXh0O1xuICAgICAgICBzdHJtLmF2YWlsX2luID0gaGF2ZTtcbiAgICAgICAgc3RhdGUuaG9sZCA9IGhvbGQ7XG4gICAgICAgIHN0YXRlLmJpdHMgPSBiaXRzO1xuICAgICAgICAvLy0tLVxuICAgICAgICByZXR1cm4gWl9ORUVEX0RJQ1Q7XG4gICAgICB9XG4gICAgICBzdHJtLmFkbGVyID0gc3RhdGUuY2hlY2sgPSAxLyphZGxlcjMyKDBMLCBaX05VTEwsIDApKi87XG4gICAgICBzdGF0ZS5tb2RlID0gVFlQRTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIFRZUEU6XG4gICAgICBpZiAoZmx1c2ggPT09IFpfQkxPQ0sgfHwgZmx1c2ggPT09IFpfVFJFRVMpIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBUWVBFRE86XG4gICAgICBpZiAoc3RhdGUubGFzdCkge1xuICAgICAgICAvLy0tLSBCWVRFQklUUygpIC0tLS8vXG4gICAgICAgIGhvbGQgPj4+PSBiaXRzICYgNztcbiAgICAgICAgYml0cyAtPSBiaXRzICYgNztcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBzdGF0ZS5tb2RlID0gQ0hFQ0s7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgLy89PT0gTkVFREJJVFMoMyk7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDMpIHtcbiAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgIGhhdmUtLTtcbiAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgIGJpdHMgKz0gODtcbiAgICAgIH1cbiAgICAgIC8vPT09Ly9cbiAgICAgIHN0YXRlLmxhc3QgPSAoaG9sZCAmIDB4MDEpLypCSVRTKDEpKi87XG4gICAgICAvLy0tLSBEUk9QQklUUygxKSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IDE7XG4gICAgICBiaXRzIC09IDE7XG4gICAgICAvLy0tLS8vXG5cbiAgICAgIHN3aXRjaCAoKGhvbGQgJiAweDAzKS8qQklUUygyKSovKSB7XG4gICAgICBjYXNlIDA6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBzdG9yZWQgYmxvY2sgKi9cbiAgICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgc3RvcmVkIGJsb2NrJXNcXG5cIixcbiAgICAgICAgLy8gICAgICAgIHN0YXRlLmxhc3QgPyBcIiAobGFzdClcIiA6IFwiXCIpKTtcbiAgICAgICAgc3RhdGUubW9kZSA9IFNUT1JFRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDE6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBmaXhlZCBibG9jayAqL1xuICAgICAgICBmaXhlZHRhYmxlcyhzdGF0ZSk7XG4gICAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgIGZpeGVkIGNvZGVzIGJsb2NrJXNcXG5cIixcbiAgICAgICAgLy8gICAgICAgIHN0YXRlLmxhc3QgPyBcIiAobGFzdClcIiA6IFwiXCIpKTtcbiAgICAgICAgc3RhdGUubW9kZSA9IExFTl87ICAgICAgICAgICAgIC8qIGRlY29kZSBjb2RlcyAqL1xuICAgICAgICBpZiAoZmx1c2ggPT09IFpfVFJFRVMpIHtcbiAgICAgICAgICAvLy0tLSBEUk9QQklUUygyKSAtLS0vL1xuICAgICAgICAgIGhvbGQgPj4+PSAyO1xuICAgICAgICAgIGJpdHMgLT0gMjtcbiAgICAgICAgICAvLy0tLS8vXG4gICAgICAgICAgYnJlYWsgaW5mX2xlYXZlO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAyOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLyogZHluYW1pYyBibG9jayAqL1xuICAgICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICBkeW5hbWljIGNvZGVzIGJsb2NrJXNcXG5cIixcbiAgICAgICAgLy8gICAgICAgIHN0YXRlLmxhc3QgPyBcIiAobGFzdClcIiA6IFwiXCIpKTtcbiAgICAgICAgc3RhdGUubW9kZSA9IFRBQkxFO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBibG9jayB0eXBlJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgIH1cbiAgICAgIC8vLS0tIERST1BCSVRTKDIpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gMjtcbiAgICAgIGJpdHMgLT0gMjtcbiAgICAgIC8vLS0tLy9cbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgU1RPUkVEOlxuICAgICAgLy8tLS0gQllURUJJVFMoKSAtLS0vLyAvKiBnbyB0byBieXRlIGJvdW5kYXJ5ICovXG4gICAgICBob2xkID4+Pj0gYml0cyAmIDc7XG4gICAgICBiaXRzIC09IGJpdHMgJiA3O1xuICAgICAgLy8tLS0vL1xuICAgICAgLy89PT0gTkVFREJJVFMoMzIpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAzMikge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgaWYgKChob2xkICYgMHhmZmZmKSAhPT0gKChob2xkID4+PiAxNikgXiAweGZmZmYpKSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgc3RvcmVkIGJsb2NrIGxlbmd0aHMnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHN0YXRlLmxlbmd0aCA9IGhvbGQgJiAweGZmZmY7XG4gICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgIHN0b3JlZCBsZW5ndGggJXVcXG5cIixcbiAgICAgIC8vICAgICAgICBzdGF0ZS5sZW5ndGgpKTtcbiAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICBob2xkID0gMDtcbiAgICAgIGJpdHMgPSAwO1xuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubW9kZSA9IENPUFlfO1xuICAgICAgaWYgKGZsdXNoID09PSBaX1RSRUVTKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgQ09QWV86XG4gICAgICBzdGF0ZS5tb2RlID0gQ09QWTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIENPUFk6XG4gICAgICBjb3B5ID0gc3RhdGUubGVuZ3RoO1xuICAgICAgaWYgKGNvcHkpIHtcbiAgICAgICAgaWYgKGNvcHkgPiBoYXZlKSB7IGNvcHkgPSBoYXZlOyB9XG4gICAgICAgIGlmIChjb3B5ID4gbGVmdCkgeyBjb3B5ID0gbGVmdDsgfVxuICAgICAgICBpZiAoY29weSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgLy8tLS0gem1lbWNweShwdXQsIG5leHQsIGNvcHkpOyAtLS1cbiAgICAgICAgdXRpbHMuYXJyYXlTZXQob3V0cHV0LCBpbnB1dCwgbmV4dCwgY29weSwgcHV0KTtcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBoYXZlIC09IGNvcHk7XG4gICAgICAgIG5leHQgKz0gY29weTtcbiAgICAgICAgbGVmdCAtPSBjb3B5O1xuICAgICAgICBwdXQgKz0gY29weTtcbiAgICAgICAgc3RhdGUubGVuZ3RoIC09IGNvcHk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgICBzdG9yZWQgZW5kXFxuXCIpKTtcbiAgICAgIHN0YXRlLm1vZGUgPSBUWVBFO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBUQUJMRTpcbiAgICAgIC8vPT09IE5FRURCSVRTKDE0KTsgKi9cbiAgICAgIHdoaWxlIChiaXRzIDwgMTQpIHtcbiAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgIGhhdmUtLTtcbiAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgIGJpdHMgKz0gODtcbiAgICAgIH1cbiAgICAgIC8vPT09Ly9cbiAgICAgIHN0YXRlLm5sZW4gPSAoaG9sZCAmIDB4MWYpLypCSVRTKDUpKi8gKyAyNTc7XG4gICAgICAvLy0tLSBEUk9QQklUUyg1KSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IDU7XG4gICAgICBiaXRzIC09IDU7XG4gICAgICAvLy0tLS8vXG4gICAgICBzdGF0ZS5uZGlzdCA9IChob2xkICYgMHgxZikvKkJJVFMoNSkqLyArIDE7XG4gICAgICAvLy0tLSBEUk9QQklUUyg1KSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IDU7XG4gICAgICBiaXRzIC09IDU7XG4gICAgICAvLy0tLS8vXG4gICAgICBzdGF0ZS5uY29kZSA9IChob2xkICYgMHgwZikvKkJJVFMoNCkqLyArIDQ7XG4gICAgICAvLy0tLSBEUk9QQklUUyg0KSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IDQ7XG4gICAgICBiaXRzIC09IDQ7XG4gICAgICAvLy0tLS8vXG4vLyNpZm5kZWYgUEtaSVBfQlVHX1dPUktBUk9VTkRcbiAgICAgIGlmIChzdGF0ZS5ubGVuID4gMjg2IHx8IHN0YXRlLm5kaXN0ID4gMzApIHtcbiAgICAgICAgc3RybS5tc2cgPSAndG9vIG1hbnkgbGVuZ3RoIG9yIGRpc3RhbmNlIHN5bWJvbHMnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbi8vI2VuZGlmXG4gICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgIHRhYmxlIHNpemVzIG9rXFxuXCIpKTtcbiAgICAgIHN0YXRlLmhhdmUgPSAwO1xuICAgICAgc3RhdGUubW9kZSA9IExFTkxFTlM7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBMRU5MRU5TOlxuICAgICAgd2hpbGUgKHN0YXRlLmhhdmUgPCBzdGF0ZS5uY29kZSkge1xuICAgICAgICAvLz09PSBORUVEQklUUygzKTtcbiAgICAgICAgd2hpbGUgKGJpdHMgPCAzKSB7XG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgfVxuICAgICAgICAvLz09PS8vXG4gICAgICAgIHN0YXRlLmxlbnNbb3JkZXJbc3RhdGUuaGF2ZSsrXV0gPSAoaG9sZCAmIDB4MDcpOy8vQklUUygzKTtcbiAgICAgICAgLy8tLS0gRFJPUEJJVFMoMykgLS0tLy9cbiAgICAgICAgaG9sZCA+Pj49IDM7XG4gICAgICAgIGJpdHMgLT0gMztcbiAgICAgICAgLy8tLS0vL1xuICAgICAgfVxuICAgICAgd2hpbGUgKHN0YXRlLmhhdmUgPCAxOSkge1xuICAgICAgICBzdGF0ZS5sZW5zW29yZGVyW3N0YXRlLmhhdmUrK11dID0gMDtcbiAgICAgIH1cbiAgICAgIC8vIFdlIGhhdmUgc2VwYXJhdGUgdGFibGVzICYgbm8gcG9pbnRlcnMuIDIgY29tbWVudGVkIGxpbmVzIGJlbG93IG5vdCBuZWVkZWQuXG4gICAgICAvL3N0YXRlLm5leHQgPSBzdGF0ZS5jb2RlcztcbiAgICAgIC8vc3RhdGUubGVuY29kZSA9IHN0YXRlLm5leHQ7XG4gICAgICAvLyBTd2l0Y2ggdG8gdXNlIGR5bmFtaWMgdGFibGVcbiAgICAgIHN0YXRlLmxlbmNvZGUgPSBzdGF0ZS5sZW5keW47XG4gICAgICBzdGF0ZS5sZW5iaXRzID0gNztcblxuICAgICAgb3B0cyA9IHtiaXRzOiBzdGF0ZS5sZW5iaXRzfTtcbiAgICAgIHJldCA9IGluZmxhdGVfdGFibGUoQ09ERVMsIHN0YXRlLmxlbnMsIDAsIDE5LCBzdGF0ZS5sZW5jb2RlLCAwLCBzdGF0ZS53b3JrLCBvcHRzKTtcbiAgICAgIHN0YXRlLmxlbmJpdHMgPSBvcHRzLmJpdHM7XG5cbiAgICAgIGlmIChyZXQpIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBjb2RlIGxlbmd0aHMgc2V0JztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgIGNvZGUgbGVuZ3RocyBva1xcblwiKSk7XG4gICAgICBzdGF0ZS5oYXZlID0gMDtcbiAgICAgIHN0YXRlLm1vZGUgPSBDT0RFTEVOUztcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIENPREVMRU5TOlxuICAgICAgd2hpbGUgKHN0YXRlLmhhdmUgPCBzdGF0ZS5ubGVuICsgc3RhdGUubmRpc3QpIHtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGhlcmUgPSBzdGF0ZS5sZW5jb2RlW2hvbGQgJiAoKDEgPDwgc3RhdGUubGVuYml0cykgLSAxKV07LypCSVRTKHN0YXRlLmxlbmJpdHMpKi9cbiAgICAgICAgICBoZXJlX2JpdHMgPSBoZXJlID4+PiAyNDtcbiAgICAgICAgICBoZXJlX29wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgICAgaGVyZV92YWwgPSBoZXJlICYgMHhmZmZmO1xuXG4gICAgICAgICAgaWYgKChoZXJlX2JpdHMpIDw9IGJpdHMpIHsgYnJlYWs7IH1cbiAgICAgICAgICAvLy0tLSBQVUxMQllURSgpIC0tLS8vXG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAvLy0tLS8vXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGhlcmVfdmFsIDwgMTYpIHtcbiAgICAgICAgICAvLy0tLSBEUk9QQklUUyhoZXJlLmJpdHMpIC0tLS8vXG4gICAgICAgICAgaG9sZCA+Pj49IGhlcmVfYml0cztcbiAgICAgICAgICBiaXRzIC09IGhlcmVfYml0cztcbiAgICAgICAgICAvLy0tLS8vXG4gICAgICAgICAgc3RhdGUubGVuc1tzdGF0ZS5oYXZlKytdID0gaGVyZV92YWw7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgaWYgKGhlcmVfdmFsID09PSAxNikge1xuICAgICAgICAgICAgLy89PT0gTkVFREJJVFMoaGVyZS5iaXRzICsgMik7XG4gICAgICAgICAgICBuID0gaGVyZV9iaXRzICsgMjtcbiAgICAgICAgICAgIHdoaWxlIChiaXRzIDwgbikge1xuICAgICAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy89PT0vL1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoaGVyZS5iaXRzKSAtLS0vL1xuICAgICAgICAgICAgaG9sZCA+Pj49IGhlcmVfYml0cztcbiAgICAgICAgICAgIGJpdHMgLT0gaGVyZV9iaXRzO1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgICAgaWYgKHN0YXRlLmhhdmUgPT09IDApIHtcbiAgICAgICAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBiaXQgbGVuZ3RoIHJlcGVhdCc7XG4gICAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGVuID0gc3RhdGUubGVuc1tzdGF0ZS5oYXZlIC0gMV07XG4gICAgICAgICAgICBjb3B5ID0gMyArIChob2xkICYgMHgwMyk7Ly9CSVRTKDIpO1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoMikgLS0tLy9cbiAgICAgICAgICAgIGhvbGQgPj4+PSAyO1xuICAgICAgICAgICAgYml0cyAtPSAyO1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgIH1cbiAgICAgICAgICBlbHNlIGlmIChoZXJlX3ZhbCA9PT0gMTcpIHtcbiAgICAgICAgICAgIC8vPT09IE5FRURCSVRTKGhlcmUuYml0cyArIDMpO1xuICAgICAgICAgICAgbiA9IGhlcmVfYml0cyArIDM7XG4gICAgICAgICAgICB3aGlsZSAoYml0cyA8IG4pIHtcbiAgICAgICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vPT09Ly9cbiAgICAgICAgICAgIC8vLS0tIERST1BCSVRTKGhlcmUuYml0cykgLS0tLy9cbiAgICAgICAgICAgIGhvbGQgPj4+PSBoZXJlX2JpdHM7XG4gICAgICAgICAgICBiaXRzIC09IGhlcmVfYml0cztcbiAgICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgICAgIGxlbiA9IDA7XG4gICAgICAgICAgICBjb3B5ID0gMyArIChob2xkICYgMHgwNyk7Ly9CSVRTKDMpO1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoMykgLS0tLy9cbiAgICAgICAgICAgIGhvbGQgPj4+PSAzO1xuICAgICAgICAgICAgYml0cyAtPSAzO1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgIH1cbiAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vPT09IE5FRURCSVRTKGhlcmUuYml0cyArIDcpO1xuICAgICAgICAgICAgbiA9IGhlcmVfYml0cyArIDc7XG4gICAgICAgICAgICB3aGlsZSAoYml0cyA8IG4pIHtcbiAgICAgICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vPT09Ly9cbiAgICAgICAgICAgIC8vLS0tIERST1BCSVRTKGhlcmUuYml0cykgLS0tLy9cbiAgICAgICAgICAgIGhvbGQgPj4+PSBoZXJlX2JpdHM7XG4gICAgICAgICAgICBiaXRzIC09IGhlcmVfYml0cztcbiAgICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgICAgIGxlbiA9IDA7XG4gICAgICAgICAgICBjb3B5ID0gMTEgKyAoaG9sZCAmIDB4N2YpOy8vQklUUyg3KTtcbiAgICAgICAgICAgIC8vLS0tIERST1BCSVRTKDcpIC0tLS8vXG4gICAgICAgICAgICBob2xkID4+Pj0gNztcbiAgICAgICAgICAgIGJpdHMgLT0gNztcbiAgICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHN0YXRlLmhhdmUgKyBjb3B5ID4gc3RhdGUubmxlbiArIHN0YXRlLm5kaXN0KSB7XG4gICAgICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0JztcbiAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgICAgd2hpbGUgKGNvcHktLSkge1xuICAgICAgICAgICAgc3RhdGUubGVuc1tzdGF0ZS5oYXZlKytdID0gbGVuO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvKiBoYW5kbGUgZXJyb3IgYnJlYWtzIGluIHdoaWxlICovXG4gICAgICBpZiAoc3RhdGUubW9kZSA9PT0gQkFEKSB7IGJyZWFrOyB9XG5cbiAgICAgIC8qIGNoZWNrIGZvciBlbmQtb2YtYmxvY2sgY29kZSAoYmV0dGVyIGhhdmUgb25lKSAqL1xuICAgICAgaWYgKHN0YXRlLmxlbnNbMjU2XSA9PT0gMCkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGNvZGUgLS0gbWlzc2luZyBlbmQtb2YtYmxvY2snO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgLyogYnVpbGQgY29kZSB0YWJsZXMgLS0gbm90ZTogZG8gbm90IGNoYW5nZSB0aGUgbGVuYml0cyBvciBkaXN0Yml0c1xuICAgICAgICAgdmFsdWVzIGhlcmUgKDkgYW5kIDYpIHdpdGhvdXQgcmVhZGluZyB0aGUgY29tbWVudHMgaW4gaW5mdHJlZXMuaFxuICAgICAgICAgY29uY2VybmluZyB0aGUgRU5PVUdIIGNvbnN0YW50cywgd2hpY2ggZGVwZW5kIG9uIHRob3NlIHZhbHVlcyAqL1xuICAgICAgc3RhdGUubGVuYml0cyA9IDk7XG5cbiAgICAgIG9wdHMgPSB7Yml0czogc3RhdGUubGVuYml0c307XG4gICAgICByZXQgPSBpbmZsYXRlX3RhYmxlKExFTlMsIHN0YXRlLmxlbnMsIDAsIHN0YXRlLm5sZW4sIHN0YXRlLmxlbmNvZGUsIDAsIHN0YXRlLndvcmssIG9wdHMpO1xuICAgICAgLy8gV2UgaGF2ZSBzZXBhcmF0ZSB0YWJsZXMgJiBubyBwb2ludGVycy4gMiBjb21tZW50ZWQgbGluZXMgYmVsb3cgbm90IG5lZWRlZC5cbiAgICAgIC8vIHN0YXRlLm5leHRfaW5kZXggPSBvcHRzLnRhYmxlX2luZGV4O1xuICAgICAgc3RhdGUubGVuYml0cyA9IG9wdHMuYml0cztcbiAgICAgIC8vIHN0YXRlLmxlbmNvZGUgPSBzdGF0ZS5uZXh0O1xuXG4gICAgICBpZiAocmV0KSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgbGl0ZXJhbC9sZW5ndGhzIHNldCc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICBzdGF0ZS5kaXN0Yml0cyA9IDY7XG4gICAgICAvL3N0YXRlLmRpc3Rjb2RlLmNvcHkoc3RhdGUuY29kZXMpO1xuICAgICAgLy8gU3dpdGNoIHRvIHVzZSBkeW5hbWljIHRhYmxlXG4gICAgICBzdGF0ZS5kaXN0Y29kZSA9IHN0YXRlLmRpc3RkeW47XG4gICAgICBvcHRzID0ge2JpdHM6IHN0YXRlLmRpc3RiaXRzfTtcbiAgICAgIHJldCA9IGluZmxhdGVfdGFibGUoRElTVFMsIHN0YXRlLmxlbnMsIHN0YXRlLm5sZW4sIHN0YXRlLm5kaXN0LCBzdGF0ZS5kaXN0Y29kZSwgMCwgc3RhdGUud29yaywgb3B0cyk7XG4gICAgICAvLyBXZSBoYXZlIHNlcGFyYXRlIHRhYmxlcyAmIG5vIHBvaW50ZXJzLiAyIGNvbW1lbnRlZCBsaW5lcyBiZWxvdyBub3QgbmVlZGVkLlxuICAgICAgLy8gc3RhdGUubmV4dF9pbmRleCA9IG9wdHMudGFibGVfaW5kZXg7XG4gICAgICBzdGF0ZS5kaXN0Yml0cyA9IG9wdHMuYml0cztcbiAgICAgIC8vIHN0YXRlLmRpc3Rjb2RlID0gc3RhdGUubmV4dDtcblxuICAgICAgaWYgKHJldCkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGRpc3RhbmNlcyBzZXQnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIC8vVHJhY2V2KChzdGRlcnIsICdpbmZsYXRlOiAgICAgICBjb2RlcyBva1xcbicpKTtcbiAgICAgIHN0YXRlLm1vZGUgPSBMRU5fO1xuICAgICAgaWYgKGZsdXNoID09PSBaX1RSRUVTKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgTEVOXzpcbiAgICAgIHN0YXRlLm1vZGUgPSBMRU47XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBMRU46XG4gICAgICBpZiAoaGF2ZSA+PSA2ICYmIGxlZnQgPj0gMjU4KSB7XG4gICAgICAgIC8vLS0tIFJFU1RPUkUoKSAtLS1cbiAgICAgICAgc3RybS5uZXh0X291dCA9IHB1dDtcbiAgICAgICAgc3RybS5hdmFpbF9vdXQgPSBsZWZ0O1xuICAgICAgICBzdHJtLm5leHRfaW4gPSBuZXh0O1xuICAgICAgICBzdHJtLmF2YWlsX2luID0gaGF2ZTtcbiAgICAgICAgc3RhdGUuaG9sZCA9IGhvbGQ7XG4gICAgICAgIHN0YXRlLmJpdHMgPSBiaXRzO1xuICAgICAgICAvLy0tLVxuICAgICAgICBpbmZsYXRlX2Zhc3Qoc3RybSwgX291dCk7XG4gICAgICAgIC8vLS0tIExPQUQoKSAtLS1cbiAgICAgICAgcHV0ID0gc3RybS5uZXh0X291dDtcbiAgICAgICAgb3V0cHV0ID0gc3RybS5vdXRwdXQ7XG4gICAgICAgIGxlZnQgPSBzdHJtLmF2YWlsX291dDtcbiAgICAgICAgbmV4dCA9IHN0cm0ubmV4dF9pbjtcbiAgICAgICAgaW5wdXQgPSBzdHJtLmlucHV0O1xuICAgICAgICBoYXZlID0gc3RybS5hdmFpbF9pbjtcbiAgICAgICAgaG9sZCA9IHN0YXRlLmhvbGQ7XG4gICAgICAgIGJpdHMgPSBzdGF0ZS5iaXRzO1xuICAgICAgICAvLy0tLVxuXG4gICAgICAgIGlmIChzdGF0ZS5tb2RlID09PSBUWVBFKSB7XG4gICAgICAgICAgc3RhdGUuYmFjayA9IC0xO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUuYmFjayA9IDA7XG4gICAgICBmb3IgKDs7KSB7XG4gICAgICAgIGhlcmUgPSBzdGF0ZS5sZW5jb2RlW2hvbGQgJiAoKDEgPDwgc3RhdGUubGVuYml0cykgLTEpXTsgIC8qQklUUyhzdGF0ZS5sZW5iaXRzKSovXG4gICAgICAgIGhlcmVfYml0cyA9IGhlcmUgPj4+IDI0O1xuICAgICAgICBoZXJlX29wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgIGhlcmVfdmFsID0gaGVyZSAmIDB4ZmZmZjtcblxuICAgICAgICBpZiAoaGVyZV9iaXRzIDw9IGJpdHMpIHsgYnJlYWs7IH1cbiAgICAgICAgLy8tLS0gUFVMTEJZVEUoKSAtLS0vL1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAvLy0tLS8vXG4gICAgICB9XG4gICAgICBpZiAoaGVyZV9vcCAmJiAoaGVyZV9vcCAmIDB4ZjApID09PSAwKSB7XG4gICAgICAgIGxhc3RfYml0cyA9IGhlcmVfYml0cztcbiAgICAgICAgbGFzdF9vcCA9IGhlcmVfb3A7XG4gICAgICAgIGxhc3RfdmFsID0gaGVyZV92YWw7XG4gICAgICAgIGZvciAoOzspIHtcbiAgICAgICAgICBoZXJlID0gc3RhdGUubGVuY29kZVtsYXN0X3ZhbCArXG4gICAgICAgICAgICAgICAgICAoKGhvbGQgJiAoKDEgPDwgKGxhc3RfYml0cyArIGxhc3Rfb3ApKSAtMSkpLypCSVRTKGxhc3QuYml0cyArIGxhc3Qub3ApKi8gPj4gbGFzdF9iaXRzKV07XG4gICAgICAgICAgaGVyZV9iaXRzID0gaGVyZSA+Pj4gMjQ7XG4gICAgICAgICAgaGVyZV9vcCA9IChoZXJlID4+PiAxNikgJiAweGZmO1xuICAgICAgICAgIGhlcmVfdmFsID0gaGVyZSAmIDB4ZmZmZjtcblxuICAgICAgICAgIGlmICgobGFzdF9iaXRzICsgaGVyZV9iaXRzKSA8PSBiaXRzKSB7IGJyZWFrOyB9XG4gICAgICAgICAgLy8tLS0gUFVMTEJZVEUoKSAtLS0vL1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgICAgLy8tLS0vL1xuICAgICAgICB9XG4gICAgICAgIC8vLS0tIERST1BCSVRTKGxhc3QuYml0cykgLS0tLy9cbiAgICAgICAgaG9sZCA+Pj49IGxhc3RfYml0cztcbiAgICAgICAgYml0cyAtPSBsYXN0X2JpdHM7XG4gICAgICAgIC8vLS0tLy9cbiAgICAgICAgc3RhdGUuYmFjayArPSBsYXN0X2JpdHM7XG4gICAgICB9XG4gICAgICAvLy0tLSBEUk9QQklUUyhoZXJlLmJpdHMpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gaGVyZV9iaXRzO1xuICAgICAgYml0cyAtPSBoZXJlX2JpdHM7XG4gICAgICAvLy0tLS8vXG4gICAgICBzdGF0ZS5iYWNrICs9IGhlcmVfYml0cztcbiAgICAgIHN0YXRlLmxlbmd0aCA9IGhlcmVfdmFsO1xuICAgICAgaWYgKGhlcmVfb3AgPT09IDApIHtcbiAgICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIGhlcmUudmFsID49IDB4MjAgJiYgaGVyZS52YWwgPCAweDdmID9cbiAgICAgICAgLy8gICAgICAgIFwiaW5mbGF0ZTogICAgICAgICBsaXRlcmFsICclYydcXG5cIiA6XG4gICAgICAgIC8vICAgICAgICBcImluZmxhdGU6ICAgICAgICAgbGl0ZXJhbCAweCUwMnhcXG5cIiwgaGVyZS52YWwpKTtcbiAgICAgICAgc3RhdGUubW9kZSA9IExJVDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoaGVyZV9vcCAmIDMyKSB7XG4gICAgICAgIC8vVHJhY2V2digoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgICAgZW5kIG9mIGJsb2NrXFxuXCIpKTtcbiAgICAgICAgc3RhdGUuYmFjayA9IC0xO1xuICAgICAgICBzdGF0ZS5tb2RlID0gVFlQRTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoaGVyZV9vcCAmIDY0KSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgbGl0ZXJhbC9sZW5ndGggY29kZSc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUuZXh0cmEgPSBoZXJlX29wICYgMTU7XG4gICAgICBzdGF0ZS5tb2RlID0gTEVORVhUO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgTEVORVhUOlxuICAgICAgaWYgKHN0YXRlLmV4dHJhKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKHN0YXRlLmV4dHJhKTtcbiAgICAgICAgbiA9IHN0YXRlLmV4dHJhO1xuICAgICAgICB3aGlsZSAoYml0cyA8IG4pIHtcbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgc3RhdGUubGVuZ3RoICs9IGhvbGQgJiAoKDEgPDwgc3RhdGUuZXh0cmEpIC0xKS8qQklUUyhzdGF0ZS5leHRyYSkqLztcbiAgICAgICAgLy8tLS0gRFJPUEJJVFMoc3RhdGUuZXh0cmEpIC0tLS8vXG4gICAgICAgIGhvbGQgPj4+PSBzdGF0ZS5leHRyYTtcbiAgICAgICAgYml0cyAtPSBzdGF0ZS5leHRyYTtcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBzdGF0ZS5iYWNrICs9IHN0YXRlLmV4dHJhO1xuICAgICAgfVxuICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgICBsZW5ndGggJXVcXG5cIiwgc3RhdGUubGVuZ3RoKSk7XG4gICAgICBzdGF0ZS53YXMgPSBzdGF0ZS5sZW5ndGg7XG4gICAgICBzdGF0ZS5tb2RlID0gRElTVDtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIERJU1Q6XG4gICAgICBmb3IgKDs7KSB7XG4gICAgICAgIGhlcmUgPSBzdGF0ZS5kaXN0Y29kZVtob2xkICYgKCgxIDw8IHN0YXRlLmRpc3RiaXRzKSAtMSldOy8qQklUUyhzdGF0ZS5kaXN0Yml0cykqL1xuICAgICAgICBoZXJlX2JpdHMgPSBoZXJlID4+PiAyNDtcbiAgICAgICAgaGVyZV9vcCA9IChoZXJlID4+PiAxNikgJiAweGZmO1xuICAgICAgICBoZXJlX3ZhbCA9IGhlcmUgJiAweGZmZmY7XG5cbiAgICAgICAgaWYgKChoZXJlX2JpdHMpIDw9IGJpdHMpIHsgYnJlYWs7IH1cbiAgICAgICAgLy8tLS0gUFVMTEJZVEUoKSAtLS0vL1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAvLy0tLS8vXG4gICAgICB9XG4gICAgICBpZiAoKGhlcmVfb3AgJiAweGYwKSA9PT0gMCkge1xuICAgICAgICBsYXN0X2JpdHMgPSBoZXJlX2JpdHM7XG4gICAgICAgIGxhc3Rfb3AgPSBoZXJlX29wO1xuICAgICAgICBsYXN0X3ZhbCA9IGhlcmVfdmFsO1xuICAgICAgICBmb3IgKDs7KSB7XG4gICAgICAgICAgaGVyZSA9IHN0YXRlLmRpc3Rjb2RlW2xhc3RfdmFsICtcbiAgICAgICAgICAgICAgICAgICgoaG9sZCAmICgoMSA8PCAobGFzdF9iaXRzICsgbGFzdF9vcCkpIC0xKSkvKkJJVFMobGFzdC5iaXRzICsgbGFzdC5vcCkqLyA+PiBsYXN0X2JpdHMpXTtcbiAgICAgICAgICBoZXJlX2JpdHMgPSBoZXJlID4+PiAyNDtcbiAgICAgICAgICBoZXJlX29wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgICAgaGVyZV92YWwgPSBoZXJlICYgMHhmZmZmO1xuXG4gICAgICAgICAgaWYgKChsYXN0X2JpdHMgKyBoZXJlX2JpdHMpIDw9IGJpdHMpIHsgYnJlYWs7IH1cbiAgICAgICAgICAvLy0tLSBQVUxMQllURSgpIC0tLS8vXG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAvLy0tLS8vXG4gICAgICAgIH1cbiAgICAgICAgLy8tLS0gRFJPUEJJVFMobGFzdC5iaXRzKSAtLS0vL1xuICAgICAgICBob2xkID4+Pj0gbGFzdF9iaXRzO1xuICAgICAgICBiaXRzIC09IGxhc3RfYml0cztcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBzdGF0ZS5iYWNrICs9IGxhc3RfYml0cztcbiAgICAgIH1cbiAgICAgIC8vLS0tIERST1BCSVRTKGhlcmUuYml0cykgLS0tLy9cbiAgICAgIGhvbGQgPj4+PSBoZXJlX2JpdHM7XG4gICAgICBiaXRzIC09IGhlcmVfYml0cztcbiAgICAgIC8vLS0tLy9cbiAgICAgIHN0YXRlLmJhY2sgKz0gaGVyZV9iaXRzO1xuICAgICAgaWYgKGhlcmVfb3AgJiA2NCkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGRpc3RhbmNlIGNvZGUnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHN0YXRlLm9mZnNldCA9IGhlcmVfdmFsO1xuICAgICAgc3RhdGUuZXh0cmEgPSAoaGVyZV9vcCkgJiAxNTtcbiAgICAgIHN0YXRlLm1vZGUgPSBESVNURVhUO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgRElTVEVYVDpcbiAgICAgIGlmIChzdGF0ZS5leHRyYSkge1xuICAgICAgICAvLz09PSBORUVEQklUUyhzdGF0ZS5leHRyYSk7XG4gICAgICAgIG4gPSBzdGF0ZS5leHRyYTtcbiAgICAgICAgd2hpbGUgKGJpdHMgPCBuKSB7XG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgfVxuICAgICAgICAvLz09PS8vXG4gICAgICAgIHN0YXRlLm9mZnNldCArPSBob2xkICYgKCgxIDw8IHN0YXRlLmV4dHJhKSAtMSkvKkJJVFMoc3RhdGUuZXh0cmEpKi87XG4gICAgICAgIC8vLS0tIERST1BCSVRTKHN0YXRlLmV4dHJhKSAtLS0vL1xuICAgICAgICBob2xkID4+Pj0gc3RhdGUuZXh0cmE7XG4gICAgICAgIGJpdHMgLT0gc3RhdGUuZXh0cmE7XG4gICAgICAgIC8vLS0tLy9cbiAgICAgICAgc3RhdGUuYmFjayArPSBzdGF0ZS5leHRyYTtcbiAgICAgIH1cbi8vI2lmZGVmIElORkxBVEVfU1RSSUNUXG4gICAgICBpZiAoc3RhdGUub2Zmc2V0ID4gc3RhdGUuZG1heCkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGRpc3RhbmNlIHRvbyBmYXIgYmFjayc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuLy8jZW5kaWZcbiAgICAgIC8vVHJhY2V2digoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgICAgZGlzdGFuY2UgJXVcXG5cIiwgc3RhdGUub2Zmc2V0KSk7XG4gICAgICBzdGF0ZS5tb2RlID0gTUFUQ0g7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBNQVRDSDpcbiAgICAgIGlmIChsZWZ0ID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgY29weSA9IF9vdXQgLSBsZWZ0O1xuICAgICAgaWYgKHN0YXRlLm9mZnNldCA+IGNvcHkpIHsgICAgICAgICAvKiBjb3B5IGZyb20gd2luZG93ICovXG4gICAgICAgIGNvcHkgPSBzdGF0ZS5vZmZzZXQgLSBjb3B5O1xuICAgICAgICBpZiAoY29weSA+IHN0YXRlLndoYXZlKSB7XG4gICAgICAgICAgaWYgKHN0YXRlLnNhbmUpIHtcbiAgICAgICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgZGlzdGFuY2UgdG9vIGZhciBiYWNrJztcbiAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4vLyAoISkgVGhpcyBibG9jayBpcyBkaXNhYmxlZCBpbiB6bGliIGRlZmFpbHRzLFxuLy8gZG9uJ3QgZW5hYmxlIGl0IGZvciBiaW5hcnkgY29tcGF0aWJpbGl0eVxuLy8jaWZkZWYgSU5GTEFURV9BTExPV19JTlZBTElEX0RJU1RBTkNFX1RPT0ZBUl9BUlJSXG4vLyAgICAgICAgICBUcmFjZSgoc3RkZXJyLCBcImluZmxhdGUuYyB0b28gZmFyXFxuXCIpKTtcbi8vICAgICAgICAgIGNvcHkgLT0gc3RhdGUud2hhdmU7XG4vLyAgICAgICAgICBpZiAoY29weSA+IHN0YXRlLmxlbmd0aCkgeyBjb3B5ID0gc3RhdGUubGVuZ3RoOyB9XG4vLyAgICAgICAgICBpZiAoY29weSA+IGxlZnQpIHsgY29weSA9IGxlZnQ7IH1cbi8vICAgICAgICAgIGxlZnQgLT0gY29weTtcbi8vICAgICAgICAgIHN0YXRlLmxlbmd0aCAtPSBjb3B5O1xuLy8gICAgICAgICAgZG8ge1xuLy8gICAgICAgICAgICBvdXRwdXRbcHV0KytdID0gMDtcbi8vICAgICAgICAgIH0gd2hpbGUgKC0tY29weSk7XG4vLyAgICAgICAgICBpZiAoc3RhdGUubGVuZ3RoID09PSAwKSB7IHN0YXRlLm1vZGUgPSBMRU47IH1cbi8vICAgICAgICAgIGJyZWFrO1xuLy8jZW5kaWZcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29weSA+IHN0YXRlLnduZXh0KSB7XG4gICAgICAgICAgY29weSAtPSBzdGF0ZS53bmV4dDtcbiAgICAgICAgICBmcm9tID0gc3RhdGUud3NpemUgLSBjb3B5O1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgIGZyb20gPSBzdGF0ZS53bmV4dCAtIGNvcHk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvcHkgPiBzdGF0ZS5sZW5ndGgpIHsgY29weSA9IHN0YXRlLmxlbmd0aDsgfVxuICAgICAgICBmcm9tX3NvdXJjZSA9IHN0YXRlLndpbmRvdztcbiAgICAgIH1cbiAgICAgIGVsc2UgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIGNvcHkgZnJvbSBvdXRwdXQgKi9cbiAgICAgICAgZnJvbV9zb3VyY2UgPSBvdXRwdXQ7XG4gICAgICAgIGZyb20gPSBwdXQgLSBzdGF0ZS5vZmZzZXQ7XG4gICAgICAgIGNvcHkgPSBzdGF0ZS5sZW5ndGg7XG4gICAgICB9XG4gICAgICBpZiAoY29weSA+IGxlZnQpIHsgY29weSA9IGxlZnQ7IH1cbiAgICAgIGxlZnQgLT0gY29weTtcbiAgICAgIHN0YXRlLmxlbmd0aCAtPSBjb3B5O1xuICAgICAgZG8ge1xuICAgICAgICBvdXRwdXRbcHV0KytdID0gZnJvbV9zb3VyY2VbZnJvbSsrXTtcbiAgICAgIH0gd2hpbGUgKC0tY29weSk7XG4gICAgICBpZiAoc3RhdGUubGVuZ3RoID09PSAwKSB7IHN0YXRlLm1vZGUgPSBMRU47IH1cbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgTElUOlxuICAgICAgaWYgKGxlZnQgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICBvdXRwdXRbcHV0KytdID0gc3RhdGUubGVuZ3RoO1xuICAgICAgbGVmdC0tO1xuICAgICAgc3RhdGUubW9kZSA9IExFTjtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgQ0hFQ0s6XG4gICAgICBpZiAoc3RhdGUud3JhcCkge1xuICAgICAgICAvLz09PSBORUVEQklUUygzMik7XG4gICAgICAgIHdoaWxlIChiaXRzIDwgMzIpIHtcbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgLy8gVXNlICd8JyBpbnNkZWFkIG9mICcrJyB0byBtYWtlIHN1cmUgdGhhdCByZXN1bHQgaXMgc2lnbmVkXG4gICAgICAgICAgaG9sZCB8PSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgX291dCAtPSBsZWZ0O1xuICAgICAgICBzdHJtLnRvdGFsX291dCArPSBfb3V0O1xuICAgICAgICBzdGF0ZS50b3RhbCArPSBfb3V0O1xuICAgICAgICBpZiAoX291dCkge1xuICAgICAgICAgIHN0cm0uYWRsZXIgPSBzdGF0ZS5jaGVjayA9XG4gICAgICAgICAgICAgIC8qVVBEQVRFKHN0YXRlLmNoZWNrLCBwdXQgLSBfb3V0LCBfb3V0KTsqL1xuICAgICAgICAgICAgICAoc3RhdGUuZmxhZ3MgPyBjcmMzMihzdGF0ZS5jaGVjaywgb3V0cHV0LCBfb3V0LCBwdXQgLSBfb3V0KSA6IGFkbGVyMzIoc3RhdGUuY2hlY2ssIG91dHB1dCwgX291dCwgcHV0IC0gX291dCkpO1xuXG4gICAgICAgIH1cbiAgICAgICAgX291dCA9IGxlZnQ7XG4gICAgICAgIC8vIE5COiBjcmMzMiBzdG9yZWQgYXMgc2lnbmVkIDMyLWJpdCBpbnQsIFpTV0FQMzIgcmV0dXJucyBzaWduZWQgdG9vXG4gICAgICAgIGlmICgoc3RhdGUuZmxhZ3MgPyBob2xkIDogWlNXQVAzMihob2xkKSkgIT09IHN0YXRlLmNoZWNrKSB7XG4gICAgICAgICAgc3RybS5tc2cgPSAnaW5jb3JyZWN0IGRhdGEgY2hlY2snO1xuICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgICAgaG9sZCA9IDA7XG4gICAgICAgIGJpdHMgPSAwO1xuICAgICAgICAvLz09PS8vXG4gICAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICBjaGVjayBtYXRjaGVzIHRyYWlsZXJcXG5cIikpO1xuICAgICAgfVxuICAgICAgc3RhdGUubW9kZSA9IExFTkdUSDtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIExFTkdUSDpcbiAgICAgIGlmIChzdGF0ZS53cmFwICYmIHN0YXRlLmZsYWdzKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKDMyKTtcbiAgICAgICAgd2hpbGUgKGJpdHMgPCAzMikge1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0vL1xuICAgICAgICBpZiAoaG9sZCAhPT0gKHN0YXRlLnRvdGFsICYgMHhmZmZmZmZmZikpIHtcbiAgICAgICAgICBzdHJtLm1zZyA9ICdpbmNvcnJlY3QgbGVuZ3RoIGNoZWNrJztcbiAgICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICAgIGhvbGQgPSAwO1xuICAgICAgICBiaXRzID0gMDtcbiAgICAgICAgLy89PT0vL1xuICAgICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgbGVuZ3RoIG1hdGNoZXMgdHJhaWxlclxcblwiKSk7XG4gICAgICB9XG4gICAgICBzdGF0ZS5tb2RlID0gRE9ORTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIERPTkU6XG4gICAgICByZXQgPSBaX1NUUkVBTV9FTkQ7XG4gICAgICBicmVhayBpbmZfbGVhdmU7XG4gICAgY2FzZSBCQUQ6XG4gICAgICByZXQgPSBaX0RBVEFfRVJST1I7XG4gICAgICBicmVhayBpbmZfbGVhdmU7XG4gICAgY2FzZSBNRU06XG4gICAgICByZXR1cm4gWl9NRU1fRVJST1I7XG4gICAgY2FzZSBTWU5DOlxuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gWl9TVFJFQU1fRVJST1I7XG4gICAgfVxuICB9XG5cbiAgLy8gaW5mX2xlYXZlIDwtIGhlcmUgaXMgcmVhbCBwbGFjZSBmb3IgXCJnb3RvIGluZl9sZWF2ZVwiLCBlbXVsYXRlZCB2aWEgXCJicmVhayBpbmZfbGVhdmVcIlxuXG4gIC8qXG4gICAgIFJldHVybiBmcm9tIGluZmxhdGUoKSwgdXBkYXRpbmcgdGhlIHRvdGFsIGNvdW50cyBhbmQgdGhlIGNoZWNrIHZhbHVlLlxuICAgICBJZiB0aGVyZSB3YXMgbm8gcHJvZ3Jlc3MgZHVyaW5nIHRoZSBpbmZsYXRlKCkgY2FsbCwgcmV0dXJuIGEgYnVmZmVyXG4gICAgIGVycm9yLiAgQ2FsbCB1cGRhdGV3aW5kb3coKSB0byBjcmVhdGUgYW5kL29yIHVwZGF0ZSB0aGUgd2luZG93IHN0YXRlLlxuICAgICBOb3RlOiBhIG1lbW9yeSBlcnJvciBmcm9tIGluZmxhdGUoKSBpcyBub24tcmVjb3ZlcmFibGUuXG4gICAqL1xuXG4gIC8vLS0tIFJFU1RPUkUoKSAtLS1cbiAgc3RybS5uZXh0X291dCA9IHB1dDtcbiAgc3RybS5hdmFpbF9vdXQgPSBsZWZ0O1xuICBzdHJtLm5leHRfaW4gPSBuZXh0O1xuICBzdHJtLmF2YWlsX2luID0gaGF2ZTtcbiAgc3RhdGUuaG9sZCA9IGhvbGQ7XG4gIHN0YXRlLmJpdHMgPSBiaXRzO1xuICAvLy0tLVxuXG4gIGlmIChzdGF0ZS53c2l6ZSB8fCAoX291dCAhPT0gc3RybS5hdmFpbF9vdXQgJiYgc3RhdGUubW9kZSA8IEJBRCAmJlxuICAgICAgICAgICAgICAgICAgICAgIChzdGF0ZS5tb2RlIDwgQ0hFQ0sgfHwgZmx1c2ggIT09IFpfRklOSVNIKSkpIHtcbiAgICBpZiAodXBkYXRld2luZG93KHN0cm0sIHN0cm0ub3V0cHV0LCBzdHJtLm5leHRfb3V0LCBfb3V0IC0gc3RybS5hdmFpbF9vdXQpKSB7XG4gICAgICBzdGF0ZS5tb2RlID0gTUVNO1xuICAgICAgcmV0dXJuIFpfTUVNX0VSUk9SO1xuICAgIH1cbiAgfVxuICBfaW4gLT0gc3RybS5hdmFpbF9pbjtcbiAgX291dCAtPSBzdHJtLmF2YWlsX291dDtcbiAgc3RybS50b3RhbF9pbiArPSBfaW47XG4gIHN0cm0udG90YWxfb3V0ICs9IF9vdXQ7XG4gIHN0YXRlLnRvdGFsICs9IF9vdXQ7XG4gIGlmIChzdGF0ZS53cmFwICYmIF9vdXQpIHtcbiAgICBzdHJtLmFkbGVyID0gc3RhdGUuY2hlY2sgPSAvKlVQREFURShzdGF0ZS5jaGVjaywgc3RybS5uZXh0X291dCAtIF9vdXQsIF9vdXQpOyovXG4gICAgICAoc3RhdGUuZmxhZ3MgPyBjcmMzMihzdGF0ZS5jaGVjaywgb3V0cHV0LCBfb3V0LCBzdHJtLm5leHRfb3V0IC0gX291dCkgOiBhZGxlcjMyKHN0YXRlLmNoZWNrLCBvdXRwdXQsIF9vdXQsIHN0cm0ubmV4dF9vdXQgLSBfb3V0KSk7XG4gIH1cbiAgc3RybS5kYXRhX3R5cGUgPSBzdGF0ZS5iaXRzICsgKHN0YXRlLmxhc3QgPyA2NCA6IDApICtcbiAgICAgICAgICAgICAgICAgICAgKHN0YXRlLm1vZGUgPT09IFRZUEUgPyAxMjggOiAwKSArXG4gICAgICAgICAgICAgICAgICAgIChzdGF0ZS5tb2RlID09PSBMRU5fIHx8IHN0YXRlLm1vZGUgPT09IENPUFlfID8gMjU2IDogMCk7XG4gIGlmICgoKF9pbiA9PT0gMCAmJiBfb3V0ID09PSAwKSB8fCBmbHVzaCA9PT0gWl9GSU5JU0gpICYmIHJldCA9PT0gWl9PSykge1xuICAgIHJldCA9IFpfQlVGX0VSUk9SO1xuICB9XG4gIHJldHVybiByZXQ7XG59XG5cbmZ1bmN0aW9uIGluZmxhdGVFbmQoc3RybSkge1xuXG4gIGlmICghc3RybSB8fCAhc3RybS5zdGF0ZSAvKnx8IHN0cm0tPnpmcmVlID09IChmcmVlX2Z1bmMpMCovKSB7XG4gICAgcmV0dXJuIFpfU1RSRUFNX0VSUk9SO1xuICB9XG5cbiAgdmFyIHN0YXRlID0gc3RybS5zdGF0ZTtcbiAgaWYgKHN0YXRlLndpbmRvdykge1xuICAgIHN0YXRlLndpbmRvdyA9IG51bGw7XG4gIH1cbiAgc3RybS5zdGF0ZSA9IG51bGw7XG4gIHJldHVybiBaX09LO1xufVxuXG5mdW5jdGlvbiBpbmZsYXRlR2V0SGVhZGVyKHN0cm0sIGhlYWQpIHtcbiAgdmFyIHN0YXRlO1xuXG4gIC8qIGNoZWNrIHN0YXRlICovXG4gIGlmICghc3RybSB8fCAhc3RybS5zdGF0ZSkgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cbiAgc3RhdGUgPSBzdHJtLnN0YXRlO1xuICBpZiAoKHN0YXRlLndyYXAgJiAyKSA9PT0gMCkgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cblxuICAvKiBzYXZlIGhlYWRlciBzdHJ1Y3R1cmUgKi9cbiAgc3RhdGUuaGVhZCA9IGhlYWQ7XG4gIGhlYWQuZG9uZSA9IGZhbHNlO1xuICByZXR1cm4gWl9PSztcbn1cblxuXG5leHBvcnRzLmluZmxhdGVSZXNldCA9IGluZmxhdGVSZXNldDtcbmV4cG9ydHMuaW5mbGF0ZVJlc2V0MiA9IGluZmxhdGVSZXNldDI7XG5leHBvcnRzLmluZmxhdGVSZXNldEtlZXAgPSBpbmZsYXRlUmVzZXRLZWVwO1xuZXhwb3J0cy5pbmZsYXRlSW5pdCA9IGluZmxhdGVJbml0O1xuZXhwb3J0cy5pbmZsYXRlSW5pdDIgPSBpbmZsYXRlSW5pdDI7XG5leHBvcnRzLmluZmxhdGUgPSBpbmZsYXRlO1xuZXhwb3J0cy5pbmZsYXRlRW5kID0gaW5mbGF0ZUVuZDtcbmV4cG9ydHMuaW5mbGF0ZUdldEhlYWRlciA9IGluZmxhdGVHZXRIZWFkZXI7XG5leHBvcnRzLmluZmxhdGVJbmZvID0gJ3Bha28gaW5mbGF0ZSAoZnJvbSBOb2RlY2EgcHJvamVjdCknO1xuXG4vKiBOb3QgaW1wbGVtZW50ZWRcbmV4cG9ydHMuaW5mbGF0ZUNvcHkgPSBpbmZsYXRlQ29weTtcbmV4cG9ydHMuaW5mbGF0ZUdldERpY3Rpb25hcnkgPSBpbmZsYXRlR2V0RGljdGlvbmFyeTtcbmV4cG9ydHMuaW5mbGF0ZU1hcmsgPSBpbmZsYXRlTWFyaztcbmV4cG9ydHMuaW5mbGF0ZVByaW1lID0gaW5mbGF0ZVByaW1lO1xuZXhwb3J0cy5pbmZsYXRlU2V0RGljdGlvbmFyeSA9IGluZmxhdGVTZXREaWN0aW9uYXJ5O1xuZXhwb3J0cy5pbmZsYXRlU3luYyA9IGluZmxhdGVTeW5jO1xuZXhwb3J0cy5pbmZsYXRlU3luY1BvaW50ID0gaW5mbGF0ZVN5bmNQb2ludDtcbmV4cG9ydHMuaW5mbGF0ZVVuZGVybWluZSA9IGluZmxhdGVVbmRlcm1pbmU7XG4qLyIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy9jb21tb24nKTtcblxudmFyIE1BWEJJVFMgPSAxNTtcbnZhciBFTk9VR0hfTEVOUyA9IDg1MjtcbnZhciBFTk9VR0hfRElTVFMgPSA1OTI7XG4vL3ZhciBFTk9VR0ggPSAoRU5PVUdIX0xFTlMrRU5PVUdIX0RJU1RTKTtcblxudmFyIENPREVTID0gMDtcbnZhciBMRU5TID0gMTtcbnZhciBESVNUUyA9IDI7XG5cbnZhciBsYmFzZSA9IFsgLyogTGVuZ3RoIGNvZGVzIDI1Ny4uMjg1IGJhc2UgKi9cbiAgMywgNCwgNSwgNiwgNywgOCwgOSwgMTAsIDExLCAxMywgMTUsIDE3LCAxOSwgMjMsIDI3LCAzMSxcbiAgMzUsIDQzLCA1MSwgNTksIDY3LCA4MywgOTksIDExNSwgMTMxLCAxNjMsIDE5NSwgMjI3LCAyNTgsIDAsIDBcbl07XG5cbnZhciBsZXh0ID0gWyAvKiBMZW5ndGggY29kZXMgMjU3Li4yODUgZXh0cmEgKi9cbiAgMTYsIDE2LCAxNiwgMTYsIDE2LCAxNiwgMTYsIDE2LCAxNywgMTcsIDE3LCAxNywgMTgsIDE4LCAxOCwgMTgsXG4gIDE5LCAxOSwgMTksIDE5LCAyMCwgMjAsIDIwLCAyMCwgMjEsIDIxLCAyMSwgMjEsIDE2LCA3MiwgNzhcbl07XG5cbnZhciBkYmFzZSA9IFsgLyogRGlzdGFuY2UgY29kZXMgMC4uMjkgYmFzZSAqL1xuICAxLCAyLCAzLCA0LCA1LCA3LCA5LCAxMywgMTcsIDI1LCAzMywgNDksIDY1LCA5NywgMTI5LCAxOTMsXG4gIDI1NywgMzg1LCA1MTMsIDc2OSwgMTAyNSwgMTUzNywgMjA0OSwgMzA3MywgNDA5NywgNjE0NSxcbiAgODE5MywgMTIyODksIDE2Mzg1LCAyNDU3NywgMCwgMFxuXTtcblxudmFyIGRleHQgPSBbIC8qIERpc3RhbmNlIGNvZGVzIDAuLjI5IGV4dHJhICovXG4gIDE2LCAxNiwgMTYsIDE2LCAxNywgMTcsIDE4LCAxOCwgMTksIDE5LCAyMCwgMjAsIDIxLCAyMSwgMjIsIDIyLFxuICAyMywgMjMsIDI0LCAyNCwgMjUsIDI1LCAyNiwgMjYsIDI3LCAyNyxcbiAgMjgsIDI4LCAyOSwgMjksIDY0LCA2NFxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBpbmZsYXRlX3RhYmxlKHR5cGUsIGxlbnMsIGxlbnNfaW5kZXgsIGNvZGVzLCB0YWJsZSwgdGFibGVfaW5kZXgsIHdvcmssIG9wdHMpXG57XG4gIHZhciBiaXRzID0gb3B0cy5iaXRzO1xuICAgICAgLy9oZXJlID0gb3B0cy5oZXJlOyAvKiB0YWJsZSBlbnRyeSBmb3IgZHVwbGljYXRpb24gKi9cblxuICB2YXIgbGVuID0gMDsgICAgICAgICAgICAgICAvKiBhIGNvZGUncyBsZW5ndGggaW4gYml0cyAqL1xuICB2YXIgc3ltID0gMDsgICAgICAgICAgICAgICAvKiBpbmRleCBvZiBjb2RlIHN5bWJvbHMgKi9cbiAgdmFyIG1pbiA9IDAsIG1heCA9IDA7ICAgICAgICAgIC8qIG1pbmltdW0gYW5kIG1heGltdW0gY29kZSBsZW5ndGhzICovXG4gIHZhciByb290ID0gMDsgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBpbmRleCBiaXRzIGZvciByb290IHRhYmxlICovXG4gIHZhciBjdXJyID0gMDsgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBpbmRleCBiaXRzIGZvciBjdXJyZW50IHRhYmxlICovXG4gIHZhciBkcm9wID0gMDsgICAgICAgICAgICAgIC8qIGNvZGUgYml0cyB0byBkcm9wIGZvciBzdWItdGFibGUgKi9cbiAgdmFyIGxlZnQgPSAwOyAgICAgICAgICAgICAgICAgICAvKiBudW1iZXIgb2YgcHJlZml4IGNvZGVzIGF2YWlsYWJsZSAqL1xuICB2YXIgdXNlZCA9IDA7ICAgICAgICAgICAgICAvKiBjb2RlIGVudHJpZXMgaW4gdGFibGUgdXNlZCAqL1xuICB2YXIgaHVmZiA9IDA7ICAgICAgICAgICAgICAvKiBIdWZmbWFuIGNvZGUgKi9cbiAgdmFyIGluY3I7ICAgICAgICAgICAgICAvKiBmb3IgaW5jcmVtZW50aW5nIGNvZGUsIGluZGV4ICovXG4gIHZhciBmaWxsOyAgICAgICAgICAgICAgLyogaW5kZXggZm9yIHJlcGxpY2F0aW5nIGVudHJpZXMgKi9cbiAgdmFyIGxvdzsgICAgICAgICAgICAgICAvKiBsb3cgYml0cyBmb3IgY3VycmVudCByb290IGVudHJ5ICovXG4gIHZhciBtYXNrOyAgICAgICAgICAgICAgLyogbWFzayBmb3IgbG93IHJvb3QgYml0cyAqL1xuICB2YXIgbmV4dDsgICAgICAgICAgICAgLyogbmV4dCBhdmFpbGFibGUgc3BhY2UgaW4gdGFibGUgKi9cbiAgdmFyIGJhc2UgPSBudWxsOyAgICAgLyogYmFzZSB2YWx1ZSB0YWJsZSB0byB1c2UgKi9cbiAgdmFyIGJhc2VfaW5kZXggPSAwO1xuLy8gIHZhciBzaG9leHRyYTsgICAgLyogZXh0cmEgYml0cyB0YWJsZSB0byB1c2UgKi9cbiAgdmFyIGVuZDsgICAgICAgICAgICAgICAgICAgIC8qIHVzZSBiYXNlIGFuZCBleHRyYSBmb3Igc3ltYm9sID4gZW5kICovXG4gIHZhciBjb3VudCA9IG5ldyB1dGlscy5CdWYxNihNQVhCSVRTKzEpOyAvL1tNQVhCSVRTKzFdOyAgICAvKiBudW1iZXIgb2YgY29kZXMgb2YgZWFjaCBsZW5ndGggKi9cbiAgdmFyIG9mZnMgPSBuZXcgdXRpbHMuQnVmMTYoTUFYQklUUysxKTsgLy9bTUFYQklUUysxXTsgICAgIC8qIG9mZnNldHMgaW4gdGFibGUgZm9yIGVhY2ggbGVuZ3RoICovXG4gIHZhciBleHRyYSA9IG51bGw7XG4gIHZhciBleHRyYV9pbmRleCA9IDA7XG5cbiAgdmFyIGhlcmVfYml0cywgaGVyZV9vcCwgaGVyZV92YWw7XG5cbiAgLypcbiAgIFByb2Nlc3MgYSBzZXQgb2YgY29kZSBsZW5ndGhzIHRvIGNyZWF0ZSBhIGNhbm9uaWNhbCBIdWZmbWFuIGNvZGUuICBUaGVcbiAgIGNvZGUgbGVuZ3RocyBhcmUgbGVuc1swLi5jb2Rlcy0xXS4gIEVhY2ggbGVuZ3RoIGNvcnJlc3BvbmRzIHRvIHRoZVxuICAgc3ltYm9scyAwLi5jb2Rlcy0xLiAgVGhlIEh1ZmZtYW4gY29kZSBpcyBnZW5lcmF0ZWQgYnkgZmlyc3Qgc29ydGluZyB0aGVcbiAgIHN5bWJvbHMgYnkgbGVuZ3RoIGZyb20gc2hvcnQgdG8gbG9uZywgYW5kIHJldGFpbmluZyB0aGUgc3ltYm9sIG9yZGVyXG4gICBmb3IgY29kZXMgd2l0aCBlcXVhbCBsZW5ndGhzLiAgVGhlbiB0aGUgY29kZSBzdGFydHMgd2l0aCBhbGwgemVybyBiaXRzXG4gICBmb3IgdGhlIGZpcnN0IGNvZGUgb2YgdGhlIHNob3J0ZXN0IGxlbmd0aCwgYW5kIHRoZSBjb2RlcyBhcmUgaW50ZWdlclxuICAgaW5jcmVtZW50cyBmb3IgdGhlIHNhbWUgbGVuZ3RoLCBhbmQgemVyb3MgYXJlIGFwcGVuZGVkIGFzIHRoZSBsZW5ndGhcbiAgIGluY3JlYXNlcy4gIEZvciB0aGUgZGVmbGF0ZSBmb3JtYXQsIHRoZXNlIGJpdHMgYXJlIHN0b3JlZCBiYWNrd2FyZHNcbiAgIGZyb20gdGhlaXIgbW9yZSBuYXR1cmFsIGludGVnZXIgaW5jcmVtZW50IG9yZGVyaW5nLCBhbmQgc28gd2hlbiB0aGVcbiAgIGRlY29kaW5nIHRhYmxlcyBhcmUgYnVpbHQgaW4gdGhlIGxhcmdlIGxvb3AgYmVsb3csIHRoZSBpbnRlZ2VyIGNvZGVzXG4gICBhcmUgaW5jcmVtZW50ZWQgYmFja3dhcmRzLlxuXG4gICBUaGlzIHJvdXRpbmUgYXNzdW1lcywgYnV0IGRvZXMgbm90IGNoZWNrLCB0aGF0IGFsbCBvZiB0aGUgZW50cmllcyBpblxuICAgbGVuc1tdIGFyZSBpbiB0aGUgcmFuZ2UgMC4uTUFYQklUUy4gIFRoZSBjYWxsZXIgbXVzdCBhc3N1cmUgdGhpcy5cbiAgIDEuLk1BWEJJVFMgaXMgaW50ZXJwcmV0ZWQgYXMgdGhhdCBjb2RlIGxlbmd0aC4gIHplcm8gbWVhbnMgdGhhdCB0aGF0XG4gICBzeW1ib2wgZG9lcyBub3Qgb2NjdXIgaW4gdGhpcyBjb2RlLlxuXG4gICBUaGUgY29kZXMgYXJlIHNvcnRlZCBieSBjb21wdXRpbmcgYSBjb3VudCBvZiBjb2RlcyBmb3IgZWFjaCBsZW5ndGgsXG4gICBjcmVhdGluZyBmcm9tIHRoYXQgYSB0YWJsZSBvZiBzdGFydGluZyBpbmRpY2VzIGZvciBlYWNoIGxlbmd0aCBpbiB0aGVcbiAgIHNvcnRlZCB0YWJsZSwgYW5kIHRoZW4gZW50ZXJpbmcgdGhlIHN5bWJvbHMgaW4gb3JkZXIgaW4gdGhlIHNvcnRlZFxuICAgdGFibGUuICBUaGUgc29ydGVkIHRhYmxlIGlzIHdvcmtbXSwgd2l0aCB0aGF0IHNwYWNlIGJlaW5nIHByb3ZpZGVkIGJ5XG4gICB0aGUgY2FsbGVyLlxuXG4gICBUaGUgbGVuZ3RoIGNvdW50cyBhcmUgdXNlZCBmb3Igb3RoZXIgcHVycG9zZXMgYXMgd2VsbCwgaS5lLiBmaW5kaW5nXG4gICB0aGUgbWluaW11bSBhbmQgbWF4aW11bSBsZW5ndGggY29kZXMsIGRldGVybWluaW5nIGlmIHRoZXJlIGFyZSBhbnlcbiAgIGNvZGVzIGF0IGFsbCwgY2hlY2tpbmcgZm9yIGEgdmFsaWQgc2V0IG9mIGxlbmd0aHMsIGFuZCBsb29raW5nIGFoZWFkXG4gICBhdCBsZW5ndGggY291bnRzIHRvIGRldGVybWluZSBzdWItdGFibGUgc2l6ZXMgd2hlbiBidWlsZGluZyB0aGVcbiAgIGRlY29kaW5nIHRhYmxlcy5cbiAgICovXG5cbiAgLyogYWNjdW11bGF0ZSBsZW5ndGhzIGZvciBjb2RlcyAoYXNzdW1lcyBsZW5zW10gYWxsIGluIDAuLk1BWEJJVFMpICovXG4gIGZvciAobGVuID0gMDsgbGVuIDw9IE1BWEJJVFM7IGxlbisrKSB7XG4gICAgY291bnRbbGVuXSA9IDA7XG4gIH1cbiAgZm9yIChzeW0gPSAwOyBzeW0gPCBjb2Rlczsgc3ltKyspIHtcbiAgICBjb3VudFtsZW5zW2xlbnNfaW5kZXggKyBzeW1dXSsrO1xuICB9XG5cbiAgLyogYm91bmQgY29kZSBsZW5ndGhzLCBmb3JjZSByb290IHRvIGJlIHdpdGhpbiBjb2RlIGxlbmd0aHMgKi9cbiAgcm9vdCA9IGJpdHM7XG4gIGZvciAobWF4ID0gTUFYQklUUzsgbWF4ID49IDE7IG1heC0tKSB7XG4gICAgaWYgKGNvdW50W21heF0gIT09IDApIHsgYnJlYWs7IH1cbiAgfVxuICBpZiAocm9vdCA+IG1heCkge1xuICAgIHJvb3QgPSBtYXg7XG4gIH1cbiAgaWYgKG1heCA9PT0gMCkgeyAgICAgICAgICAgICAgICAgICAgIC8qIG5vIHN5bWJvbHMgdG8gY29kZSBhdCBhbGwgKi9cbiAgICAvL3RhYmxlLm9wW29wdHMudGFibGVfaW5kZXhdID0gNjQ7ICAvL2hlcmUub3AgPSAodmFyIGNoYXIpNjQ7ICAgIC8qIGludmFsaWQgY29kZSBtYXJrZXIgKi9cbiAgICAvL3RhYmxlLmJpdHNbb3B0cy50YWJsZV9pbmRleF0gPSAxOyAgIC8vaGVyZS5iaXRzID0gKHZhciBjaGFyKTE7XG4gICAgLy90YWJsZS52YWxbb3B0cy50YWJsZV9pbmRleCsrXSA9IDA7ICAgLy9oZXJlLnZhbCA9ICh2YXIgc2hvcnQpMDtcbiAgICB0YWJsZVt0YWJsZV9pbmRleCsrXSA9ICgxIDw8IDI0KSB8ICg2NCA8PCAxNikgfCAwO1xuXG5cbiAgICAvL3RhYmxlLm9wW29wdHMudGFibGVfaW5kZXhdID0gNjQ7XG4gICAgLy90YWJsZS5iaXRzW29wdHMudGFibGVfaW5kZXhdID0gMTtcbiAgICAvL3RhYmxlLnZhbFtvcHRzLnRhYmxlX2luZGV4KytdID0gMDtcbiAgICB0YWJsZVt0YWJsZV9pbmRleCsrXSA9ICgxIDw8IDI0KSB8ICg2NCA8PCAxNikgfCAwO1xuXG4gICAgb3B0cy5iaXRzID0gMTtcbiAgICByZXR1cm4gMDsgICAgIC8qIG5vIHN5bWJvbHMsIGJ1dCB3YWl0IGZvciBkZWNvZGluZyB0byByZXBvcnQgZXJyb3IgKi9cbiAgfVxuICBmb3IgKG1pbiA9IDE7IG1pbiA8IG1heDsgbWluKyspIHtcbiAgICBpZiAoY291bnRbbWluXSAhPT0gMCkgeyBicmVhazsgfVxuICB9XG4gIGlmIChyb290IDwgbWluKSB7XG4gICAgcm9vdCA9IG1pbjtcbiAgfVxuXG4gIC8qIGNoZWNrIGZvciBhbiBvdmVyLXN1YnNjcmliZWQgb3IgaW5jb21wbGV0ZSBzZXQgb2YgbGVuZ3RocyAqL1xuICBsZWZ0ID0gMTtcbiAgZm9yIChsZW4gPSAxOyBsZW4gPD0gTUFYQklUUzsgbGVuKyspIHtcbiAgICBsZWZ0IDw8PSAxO1xuICAgIGxlZnQgLT0gY291bnRbbGVuXTtcbiAgICBpZiAobGVmdCA8IDApIHtcbiAgICAgIHJldHVybiAtMTtcbiAgICB9ICAgICAgICAvKiBvdmVyLXN1YnNjcmliZWQgKi9cbiAgfVxuICBpZiAobGVmdCA+IDAgJiYgKHR5cGUgPT09IENPREVTIHx8IG1heCAhPT0gMSkpIHtcbiAgICByZXR1cm4gLTE7ICAgICAgICAgICAgICAgICAgICAgIC8qIGluY29tcGxldGUgc2V0ICovXG4gIH1cblxuICAvKiBnZW5lcmF0ZSBvZmZzZXRzIGludG8gc3ltYm9sIHRhYmxlIGZvciBlYWNoIGxlbmd0aCBmb3Igc29ydGluZyAqL1xuICBvZmZzWzFdID0gMDtcbiAgZm9yIChsZW4gPSAxOyBsZW4gPCBNQVhCSVRTOyBsZW4rKykge1xuICAgIG9mZnNbbGVuICsgMV0gPSBvZmZzW2xlbl0gKyBjb3VudFtsZW5dO1xuICB9XG5cbiAgLyogc29ydCBzeW1ib2xzIGJ5IGxlbmd0aCwgYnkgc3ltYm9sIG9yZGVyIHdpdGhpbiBlYWNoIGxlbmd0aCAqL1xuICBmb3IgKHN5bSA9IDA7IHN5bSA8IGNvZGVzOyBzeW0rKykge1xuICAgIGlmIChsZW5zW2xlbnNfaW5kZXggKyBzeW1dICE9PSAwKSB7XG4gICAgICB3b3JrW29mZnNbbGVuc1tsZW5zX2luZGV4ICsgc3ltXV0rK10gPSBzeW07XG4gICAgfVxuICB9XG5cbiAgLypcbiAgIENyZWF0ZSBhbmQgZmlsbCBpbiBkZWNvZGluZyB0YWJsZXMuICBJbiB0aGlzIGxvb3AsIHRoZSB0YWJsZSBiZWluZ1xuICAgZmlsbGVkIGlzIGF0IG5leHQgYW5kIGhhcyBjdXJyIGluZGV4IGJpdHMuICBUaGUgY29kZSBiZWluZyB1c2VkIGlzIGh1ZmZcbiAgIHdpdGggbGVuZ3RoIGxlbi4gIFRoYXQgY29kZSBpcyBjb252ZXJ0ZWQgdG8gYW4gaW5kZXggYnkgZHJvcHBpbmcgZHJvcFxuICAgYml0cyBvZmYgb2YgdGhlIGJvdHRvbS4gIEZvciBjb2RlcyB3aGVyZSBsZW4gaXMgbGVzcyB0aGFuIGRyb3AgKyBjdXJyLFxuICAgdGhvc2UgdG9wIGRyb3AgKyBjdXJyIC0gbGVuIGJpdHMgYXJlIGluY3JlbWVudGVkIHRocm91Z2ggYWxsIHZhbHVlcyB0b1xuICAgZmlsbCB0aGUgdGFibGUgd2l0aCByZXBsaWNhdGVkIGVudHJpZXMuXG5cbiAgIHJvb3QgaXMgdGhlIG51bWJlciBvZiBpbmRleCBiaXRzIGZvciB0aGUgcm9vdCB0YWJsZS4gIFdoZW4gbGVuIGV4Y2VlZHNcbiAgIHJvb3QsIHN1Yi10YWJsZXMgYXJlIGNyZWF0ZWQgcG9pbnRlZCB0byBieSB0aGUgcm9vdCBlbnRyeSB3aXRoIGFuIGluZGV4XG4gICBvZiB0aGUgbG93IHJvb3QgYml0cyBvZiBodWZmLiAgVGhpcyBpcyBzYXZlZCBpbiBsb3cgdG8gY2hlY2sgZm9yIHdoZW4gYVxuICAgbmV3IHN1Yi10YWJsZSBzaG91bGQgYmUgc3RhcnRlZC4gIGRyb3AgaXMgemVybyB3aGVuIHRoZSByb290IHRhYmxlIGlzXG4gICBiZWluZyBmaWxsZWQsIGFuZCBkcm9wIGlzIHJvb3Qgd2hlbiBzdWItdGFibGVzIGFyZSBiZWluZyBmaWxsZWQuXG5cbiAgIFdoZW4gYSBuZXcgc3ViLXRhYmxlIGlzIG5lZWRlZCwgaXQgaXMgbmVjZXNzYXJ5IHRvIGxvb2sgYWhlYWQgaW4gdGhlXG4gICBjb2RlIGxlbmd0aHMgdG8gZGV0ZXJtaW5lIHdoYXQgc2l6ZSBzdWItdGFibGUgaXMgbmVlZGVkLiAgVGhlIGxlbmd0aFxuICAgY291bnRzIGFyZSB1c2VkIGZvciB0aGlzLCBhbmQgc28gY291bnRbXSBpcyBkZWNyZW1lbnRlZCBhcyBjb2RlcyBhcmVcbiAgIGVudGVyZWQgaW4gdGhlIHRhYmxlcy5cblxuICAgdXNlZCBrZWVwcyB0cmFjayBvZiBob3cgbWFueSB0YWJsZSBlbnRyaWVzIGhhdmUgYmVlbiBhbGxvY2F0ZWQgZnJvbSB0aGVcbiAgIHByb3ZpZGVkICp0YWJsZSBzcGFjZS4gIEl0IGlzIGNoZWNrZWQgZm9yIExFTlMgYW5kIERJU1QgdGFibGVzIGFnYWluc3RcbiAgIHRoZSBjb25zdGFudHMgRU5PVUdIX0xFTlMgYW5kIEVOT1VHSF9ESVNUUyB0byBndWFyZCBhZ2FpbnN0IGNoYW5nZXMgaW5cbiAgIHRoZSBpbml0aWFsIHJvb3QgdGFibGUgc2l6ZSBjb25zdGFudHMuICBTZWUgdGhlIGNvbW1lbnRzIGluIGluZnRyZWVzLmhcbiAgIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuXG4gICBzeW0gaW5jcmVtZW50cyB0aHJvdWdoIGFsbCBzeW1ib2xzLCBhbmQgdGhlIGxvb3AgdGVybWluYXRlcyB3aGVuXG4gICBhbGwgY29kZXMgb2YgbGVuZ3RoIG1heCwgaS5lLiBhbGwgY29kZXMsIGhhdmUgYmVlbiBwcm9jZXNzZWQuICBUaGlzXG4gICByb3V0aW5lIHBlcm1pdHMgaW5jb21wbGV0ZSBjb2Rlcywgc28gYW5vdGhlciBsb29wIGFmdGVyIHRoaXMgb25lIGZpbGxzXG4gICBpbiB0aGUgcmVzdCBvZiB0aGUgZGVjb2RpbmcgdGFibGVzIHdpdGggaW52YWxpZCBjb2RlIG1hcmtlcnMuXG4gICAqL1xuXG4gIC8qIHNldCB1cCBmb3IgY29kZSB0eXBlICovXG4gIC8vIHBvb3IgbWFuIG9wdGltaXphdGlvbiAtIHVzZSBpZi1lbHNlIGluc3RlYWQgb2Ygc3dpdGNoLFxuICAvLyB0byBhdm9pZCBkZW9wdHMgaW4gb2xkIHY4XG4gIGlmICh0eXBlID09PSBDT0RFUykge1xuICAgICAgYmFzZSA9IGV4dHJhID0gd29yazsgICAgLyogZHVtbXkgdmFsdWUtLW5vdCB1c2VkICovXG4gICAgICBlbmQgPSAxOTtcbiAgfSBlbHNlIGlmICh0eXBlID09PSBMRU5TKSB7XG4gICAgICBiYXNlID0gbGJhc2U7XG4gICAgICBiYXNlX2luZGV4IC09IDI1NztcbiAgICAgIGV4dHJhID0gbGV4dDtcbiAgICAgIGV4dHJhX2luZGV4IC09IDI1NztcbiAgICAgIGVuZCA9IDI1NjtcbiAgfSBlbHNlIHsgICAgICAgICAgICAgICAgICAgIC8qIERJU1RTICovXG4gICAgICBiYXNlID0gZGJhc2U7XG4gICAgICBleHRyYSA9IGRleHQ7XG4gICAgICBlbmQgPSAtMTtcbiAgfVxuXG4gIC8qIGluaXRpYWxpemUgb3B0cyBmb3IgbG9vcCAqL1xuICBodWZmID0gMDsgICAgICAgICAgICAgICAgICAgLyogc3RhcnRpbmcgY29kZSAqL1xuICBzeW0gPSAwOyAgICAgICAgICAgICAgICAgICAgLyogc3RhcnRpbmcgY29kZSBzeW1ib2wgKi9cbiAgbGVuID0gbWluOyAgICAgICAgICAgICAgICAgIC8qIHN0YXJ0aW5nIGNvZGUgbGVuZ3RoICovXG4gIG5leHQgPSB0YWJsZV9pbmRleDsgICAgICAgICAgICAgIC8qIGN1cnJlbnQgdGFibGUgdG8gZmlsbCBpbiAqL1xuICBjdXJyID0gcm9vdDsgICAgICAgICAgICAgICAgLyogY3VycmVudCB0YWJsZSBpbmRleCBiaXRzICovXG4gIGRyb3AgPSAwOyAgICAgICAgICAgICAgICAgICAvKiBjdXJyZW50IGJpdHMgdG8gZHJvcCBmcm9tIGNvZGUgZm9yIGluZGV4ICovXG4gIGxvdyA9IC0xOyAgICAgICAgICAgICAgICAgICAvKiB0cmlnZ2VyIG5ldyBzdWItdGFibGUgd2hlbiBsZW4gPiByb290ICovXG4gIHVzZWQgPSAxIDw8IHJvb3Q7ICAgICAgICAgIC8qIHVzZSByb290IHRhYmxlIGVudHJpZXMgKi9cbiAgbWFzayA9IHVzZWQgLSAxOyAgICAgICAgICAgIC8qIG1hc2sgZm9yIGNvbXBhcmluZyBsb3cgKi9cblxuICAvKiBjaGVjayBhdmFpbGFibGUgdGFibGUgc3BhY2UgKi9cbiAgaWYgKCh0eXBlID09PSBMRU5TICYmIHVzZWQgPiBFTk9VR0hfTEVOUykgfHxcbiAgICAodHlwZSA9PT0gRElTVFMgJiYgdXNlZCA+IEVOT1VHSF9ESVNUUykpIHtcbiAgICByZXR1cm4gMTtcbiAgfVxuXG4gIHZhciBpPTA7XG4gIC8qIHByb2Nlc3MgYWxsIGNvZGVzIGFuZCBtYWtlIHRhYmxlIGVudHJpZXMgKi9cbiAgZm9yICg7Oykge1xuICAgIGkrKztcbiAgICAvKiBjcmVhdGUgdGFibGUgZW50cnkgKi9cbiAgICBoZXJlX2JpdHMgPSBsZW4gLSBkcm9wO1xuICAgIGlmICh3b3JrW3N5bV0gPCBlbmQpIHtcbiAgICAgIGhlcmVfb3AgPSAwO1xuICAgICAgaGVyZV92YWwgPSB3b3JrW3N5bV07XG4gICAgfVxuICAgIGVsc2UgaWYgKHdvcmtbc3ltXSA+IGVuZCkge1xuICAgICAgaGVyZV9vcCA9IGV4dHJhW2V4dHJhX2luZGV4ICsgd29ya1tzeW1dXTtcbiAgICAgIGhlcmVfdmFsID0gYmFzZVtiYXNlX2luZGV4ICsgd29ya1tzeW1dXTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBoZXJlX29wID0gMzIgKyA2NDsgICAgICAgICAvKiBlbmQgb2YgYmxvY2sgKi9cbiAgICAgIGhlcmVfdmFsID0gMDtcbiAgICB9XG5cbiAgICAvKiByZXBsaWNhdGUgZm9yIHRob3NlIGluZGljZXMgd2l0aCBsb3cgbGVuIGJpdHMgZXF1YWwgdG8gaHVmZiAqL1xuICAgIGluY3IgPSAxIDw8IChsZW4gLSBkcm9wKTtcbiAgICBmaWxsID0gMSA8PCBjdXJyO1xuICAgIG1pbiA9IGZpbGw7ICAgICAgICAgICAgICAgICAvKiBzYXZlIG9mZnNldCB0byBuZXh0IHRhYmxlICovXG4gICAgZG8ge1xuICAgICAgZmlsbCAtPSBpbmNyO1xuICAgICAgdGFibGVbbmV4dCArIChodWZmID4+IGRyb3ApICsgZmlsbF0gPSAoaGVyZV9iaXRzIDw8IDI0KSB8IChoZXJlX29wIDw8IDE2KSB8IGhlcmVfdmFsIHwwO1xuICAgIH0gd2hpbGUgKGZpbGwgIT09IDApO1xuXG4gICAgLyogYmFja3dhcmRzIGluY3JlbWVudCB0aGUgbGVuLWJpdCBjb2RlIGh1ZmYgKi9cbiAgICBpbmNyID0gMSA8PCAobGVuIC0gMSk7XG4gICAgd2hpbGUgKGh1ZmYgJiBpbmNyKSB7XG4gICAgICBpbmNyID4+PSAxO1xuICAgIH1cbiAgICBpZiAoaW5jciAhPT0gMCkge1xuICAgICAgaHVmZiAmPSBpbmNyIC0gMTtcbiAgICAgIGh1ZmYgKz0gaW5jcjtcbiAgICB9IGVsc2Uge1xuICAgICAgaHVmZiA9IDA7XG4gICAgfVxuXG4gICAgLyogZ28gdG8gbmV4dCBzeW1ib2wsIHVwZGF0ZSBjb3VudCwgbGVuICovXG4gICAgc3ltKys7XG4gICAgaWYgKC0tY291bnRbbGVuXSA9PT0gMCkge1xuICAgICAgaWYgKGxlbiA9PT0gbWF4KSB7IGJyZWFrOyB9XG4gICAgICBsZW4gPSBsZW5zW2xlbnNfaW5kZXggKyB3b3JrW3N5bV1dO1xuICAgIH1cblxuICAgIC8qIGNyZWF0ZSBuZXcgc3ViLXRhYmxlIGlmIG5lZWRlZCAqL1xuICAgIGlmIChsZW4gPiByb290ICYmIChodWZmICYgbWFzaykgIT09IGxvdykge1xuICAgICAgLyogaWYgZmlyc3QgdGltZSwgdHJhbnNpdGlvbiB0byBzdWItdGFibGVzICovXG4gICAgICBpZiAoZHJvcCA9PT0gMCkge1xuICAgICAgICBkcm9wID0gcm9vdDtcbiAgICAgIH1cblxuICAgICAgLyogaW5jcmVtZW50IHBhc3QgbGFzdCB0YWJsZSAqL1xuICAgICAgbmV4dCArPSBtaW47ICAgICAgICAgICAgLyogaGVyZSBtaW4gaXMgMSA8PCBjdXJyICovXG5cbiAgICAgIC8qIGRldGVybWluZSBsZW5ndGggb2YgbmV4dCB0YWJsZSAqL1xuICAgICAgY3VyciA9IGxlbiAtIGRyb3A7XG4gICAgICBsZWZ0ID0gMSA8PCBjdXJyO1xuICAgICAgd2hpbGUgKGN1cnIgKyBkcm9wIDwgbWF4KSB7XG4gICAgICAgIGxlZnQgLT0gY291bnRbY3VyciArIGRyb3BdO1xuICAgICAgICBpZiAobGVmdCA8PSAwKSB7IGJyZWFrOyB9XG4gICAgICAgIGN1cnIrKztcbiAgICAgICAgbGVmdCA8PD0gMTtcbiAgICAgIH1cblxuICAgICAgLyogY2hlY2sgZm9yIGVub3VnaCBzcGFjZSAqL1xuICAgICAgdXNlZCArPSAxIDw8IGN1cnI7XG4gICAgICBpZiAoKHR5cGUgPT09IExFTlMgJiYgdXNlZCA+IEVOT1VHSF9MRU5TKSB8fFxuICAgICAgICAodHlwZSA9PT0gRElTVFMgJiYgdXNlZCA+IEVOT1VHSF9ESVNUUykpIHtcbiAgICAgICAgcmV0dXJuIDE7XG4gICAgICB9XG5cbiAgICAgIC8qIHBvaW50IGVudHJ5IGluIHJvb3QgdGFibGUgdG8gc3ViLXRhYmxlICovXG4gICAgICBsb3cgPSBodWZmICYgbWFzaztcbiAgICAgIC8qdGFibGUub3BbbG93XSA9IGN1cnI7XG4gICAgICB0YWJsZS5iaXRzW2xvd10gPSByb290O1xuICAgICAgdGFibGUudmFsW2xvd10gPSBuZXh0IC0gb3B0cy50YWJsZV9pbmRleDsqL1xuICAgICAgdGFibGVbbG93XSA9IChyb290IDw8IDI0KSB8IChjdXJyIDw8IDE2KSB8IChuZXh0IC0gdGFibGVfaW5kZXgpIHwwO1xuICAgIH1cbiAgfVxuXG4gIC8qIGZpbGwgaW4gcmVtYWluaW5nIHRhYmxlIGVudHJ5IGlmIGNvZGUgaXMgaW5jb21wbGV0ZSAoZ3VhcmFudGVlZCB0byBoYXZlXG4gICBhdCBtb3N0IG9uZSByZW1haW5pbmcgZW50cnksIHNpbmNlIGlmIHRoZSBjb2RlIGlzIGluY29tcGxldGUsIHRoZVxuICAgbWF4aW11bSBjb2RlIGxlbmd0aCB0aGF0IHdhcyBhbGxvd2VkIHRvIGdldCB0aGlzIGZhciBpcyBvbmUgYml0KSAqL1xuICBpZiAoaHVmZiAhPT0gMCkge1xuICAgIC8vdGFibGUub3BbbmV4dCArIGh1ZmZdID0gNjQ7ICAgICAgICAgICAgLyogaW52YWxpZCBjb2RlIG1hcmtlciAqL1xuICAgIC8vdGFibGUuYml0c1tuZXh0ICsgaHVmZl0gPSBsZW4gLSBkcm9wO1xuICAgIC8vdGFibGUudmFsW25leHQgKyBodWZmXSA9IDA7XG4gICAgdGFibGVbbmV4dCArIGh1ZmZdID0gKChsZW4gLSBkcm9wKSA8PCAyNCkgfCAoNjQgPDwgMTYpIHwwO1xuICB9XG5cbiAgLyogc2V0IHJldHVybiBwYXJhbWV0ZXJzICovXG4gIC8vb3B0cy50YWJsZV9pbmRleCArPSB1c2VkO1xuICBvcHRzLmJpdHMgPSByb290O1xuICByZXR1cm4gMDtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAnMic6ICAgICduZWVkIGRpY3Rpb25hcnknLCAgICAgLyogWl9ORUVEX0RJQ1QgICAgICAgMiAgKi9cbiAgJzEnOiAgICAnc3RyZWFtIGVuZCcsICAgICAgICAgIC8qIFpfU1RSRUFNX0VORCAgICAgIDEgICovXG4gICcwJzogICAgJycsICAgICAgICAgICAgICAgICAgICAvKiBaX09LICAgICAgICAgICAgICAwICAqL1xuICAnLTEnOiAgICdmaWxlIGVycm9yJywgICAgICAgICAgLyogWl9FUlJOTyAgICAgICAgICgtMSkgKi9cbiAgJy0yJzogICAnc3RyZWFtIGVycm9yJywgICAgICAgIC8qIFpfU1RSRUFNX0VSUk9SICAoLTIpICovXG4gICctMyc6ICAgJ2RhdGEgZXJyb3InLCAgICAgICAgICAvKiBaX0RBVEFfRVJST1IgICAgKC0zKSAqL1xuICAnLTQnOiAgICdpbnN1ZmZpY2llbnQgbWVtb3J5JywgLyogWl9NRU1fRVJST1IgICAgICgtNCkgKi9cbiAgJy01JzogICAnYnVmZmVyIGVycm9yJywgICAgICAgIC8qIFpfQlVGX0VSUk9SICAgICAoLTUpICovXG4gICctNic6ICAgJ2luY29tcGF0aWJsZSB2ZXJzaW9uJyAvKiBaX1ZFUlNJT05fRVJST1IgKC02KSAqL1xufTsiLCIndXNlIHN0cmljdCc7XG5cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvY29tbW9uJyk7XG5cbi8qIFB1YmxpYyBjb25zdGFudHMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5cbi8vdmFyIFpfRklMVEVSRUQgICAgICAgICAgPSAxO1xuLy92YXIgWl9IVUZGTUFOX09OTFkgICAgICA9IDI7XG4vL3ZhciBaX1JMRSAgICAgICAgICAgICAgID0gMztcbnZhciBaX0ZJWEVEICAgICAgICAgICAgICAgPSA0O1xuLy92YXIgWl9ERUZBVUxUX1NUUkFURUdZICA9IDA7XG5cbi8qIFBvc3NpYmxlIHZhbHVlcyBvZiB0aGUgZGF0YV90eXBlIGZpZWxkICh0aG91Z2ggc2VlIGluZmxhdGUoKSkgKi9cbnZhciBaX0JJTkFSWSAgICAgICAgICAgICAgPSAwO1xudmFyIFpfVEVYVCAgICAgICAgICAgICAgICA9IDE7XG4vL3ZhciBaX0FTQ0lJICAgICAgICAgICAgID0gMTsgLy8gPSBaX1RFWFRcbnZhciBaX1VOS05PV04gICAgICAgICAgICAgPSAyO1xuXG4vKj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5cbmZ1bmN0aW9uIHplcm8oYnVmKSB7IHZhciBsZW4gPSBidWYubGVuZ3RoOyB3aGlsZSAoLS1sZW4gPj0gMCkgeyBidWZbbGVuXSA9IDA7IH0gfVxuXG4vLyBGcm9tIHp1dGlsLmhcblxudmFyIFNUT1JFRF9CTE9DSyA9IDA7XG52YXIgU1RBVElDX1RSRUVTID0gMTtcbnZhciBEWU5fVFJFRVMgICAgPSAyO1xuLyogVGhlIHRocmVlIGtpbmRzIG9mIGJsb2NrIHR5cGUgKi9cblxudmFyIE1JTl9NQVRDSCAgICA9IDM7XG52YXIgTUFYX01BVENIICAgID0gMjU4O1xuLyogVGhlIG1pbmltdW0gYW5kIG1heGltdW0gbWF0Y2ggbGVuZ3RocyAqL1xuXG4vLyBGcm9tIGRlZmxhdGUuaFxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBJbnRlcm5hbCBjb21wcmVzc2lvbiBzdGF0ZS5cbiAqL1xuXG52YXIgTEVOR1RIX0NPREVTICA9IDI5O1xuLyogbnVtYmVyIG9mIGxlbmd0aCBjb2Rlcywgbm90IGNvdW50aW5nIHRoZSBzcGVjaWFsIEVORF9CTE9DSyBjb2RlICovXG5cbnZhciBMSVRFUkFMUyAgICAgID0gMjU2O1xuLyogbnVtYmVyIG9mIGxpdGVyYWwgYnl0ZXMgMC4uMjU1ICovXG5cbnZhciBMX0NPREVTICAgICAgID0gTElURVJBTFMgKyAxICsgTEVOR1RIX0NPREVTO1xuLyogbnVtYmVyIG9mIExpdGVyYWwgb3IgTGVuZ3RoIGNvZGVzLCBpbmNsdWRpbmcgdGhlIEVORF9CTE9DSyBjb2RlICovXG5cbnZhciBEX0NPREVTICAgICAgID0gMzA7XG4vKiBudW1iZXIgb2YgZGlzdGFuY2UgY29kZXMgKi9cblxudmFyIEJMX0NPREVTICAgICAgPSAxOTtcbi8qIG51bWJlciBvZiBjb2RlcyB1c2VkIHRvIHRyYW5zZmVyIHRoZSBiaXQgbGVuZ3RocyAqL1xuXG52YXIgSEVBUF9TSVpFICAgICA9IDIqTF9DT0RFUyArIDE7XG4vKiBtYXhpbXVtIGhlYXAgc2l6ZSAqL1xuXG52YXIgTUFYX0JJVFMgICAgICA9IDE1O1xuLyogQWxsIGNvZGVzIG11c3Qgbm90IGV4Y2VlZCBNQVhfQklUUyBiaXRzICovXG5cbnZhciBCdWZfc2l6ZSAgICAgID0gMTY7XG4vKiBzaXplIG9mIGJpdCBidWZmZXIgaW4gYmlfYnVmICovXG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb25zdGFudHNcbiAqL1xuXG52YXIgTUFYX0JMX0JJVFMgPSA3O1xuLyogQml0IGxlbmd0aCBjb2RlcyBtdXN0IG5vdCBleGNlZWQgTUFYX0JMX0JJVFMgYml0cyAqL1xuXG52YXIgRU5EX0JMT0NLICAgPSAyNTY7XG4vKiBlbmQgb2YgYmxvY2sgbGl0ZXJhbCBjb2RlICovXG5cbnZhciBSRVBfM182ICAgICA9IDE2O1xuLyogcmVwZWF0IHByZXZpb3VzIGJpdCBsZW5ndGggMy02IHRpbWVzICgyIGJpdHMgb2YgcmVwZWF0IGNvdW50KSAqL1xuXG52YXIgUkVQWl8zXzEwICAgPSAxNztcbi8qIHJlcGVhdCBhIHplcm8gbGVuZ3RoIDMtMTAgdGltZXMgICgzIGJpdHMgb2YgcmVwZWF0IGNvdW50KSAqL1xuXG52YXIgUkVQWl8xMV8xMzggPSAxODtcbi8qIHJlcGVhdCBhIHplcm8gbGVuZ3RoIDExLTEzOCB0aW1lcyAgKDcgYml0cyBvZiByZXBlYXQgY291bnQpICovXG5cbnZhciBleHRyYV9sYml0cyA9ICAgLyogZXh0cmEgYml0cyBmb3IgZWFjaCBsZW5ndGggY29kZSAqL1xuICBbMCwwLDAsMCwwLDAsMCwwLDEsMSwxLDEsMiwyLDIsMiwzLDMsMywzLDQsNCw0LDQsNSw1LDUsNSwwXTtcblxudmFyIGV4dHJhX2RiaXRzID0gICAvKiBleHRyYSBiaXRzIGZvciBlYWNoIGRpc3RhbmNlIGNvZGUgKi9cbiAgWzAsMCwwLDAsMSwxLDIsMiwzLDMsNCw0LDUsNSw2LDYsNyw3LDgsOCw5LDksMTAsMTAsMTEsMTEsMTIsMTIsMTMsMTNdO1xuXG52YXIgZXh0cmFfYmxiaXRzID0gIC8qIGV4dHJhIGJpdHMgZm9yIGVhY2ggYml0IGxlbmd0aCBjb2RlICovXG4gIFswLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDIsMyw3XTtcblxudmFyIGJsX29yZGVyID1cbiAgWzE2LDE3LDE4LDAsOCw3LDksNiwxMCw1LDExLDQsMTIsMywxMywyLDE0LDEsMTVdO1xuLyogVGhlIGxlbmd0aHMgb2YgdGhlIGJpdCBsZW5ndGggY29kZXMgYXJlIHNlbnQgaW4gb3JkZXIgb2YgZGVjcmVhc2luZ1xuICogcHJvYmFiaWxpdHksIHRvIGF2b2lkIHRyYW5zbWl0dGluZyB0aGUgbGVuZ3RocyBmb3IgdW51c2VkIGJpdCBsZW5ndGggY29kZXMuXG4gKi9cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBMb2NhbCBkYXRhLiBUaGVzZSBhcmUgaW5pdGlhbGl6ZWQgb25seSBvbmNlLlxuICovXG5cbi8vIFdlIHByZS1maWxsIGFycmF5cyB3aXRoIDAgdG8gYXZvaWQgdW5pbml0aWFsaXplZCBnYXBzXG5cbnZhciBESVNUX0NPREVfTEVOID0gNTEyOyAvKiBzZWUgZGVmaW5pdGlvbiBvZiBhcnJheSBkaXN0X2NvZGUgYmVsb3cgKi9cblxuLy8gISEhISBVc2UgZmxhdCBhcnJheSBpbnNkZWFkIG9mIHN0cnVjdHVyZSwgRnJlcSA9IGkqMiwgTGVuID0gaSoyKzFcbnZhciBzdGF0aWNfbHRyZWUgID0gbmV3IEFycmF5KChMX0NPREVTKzIpICogMik7XG56ZXJvKHN0YXRpY19sdHJlZSk7XG4vKiBUaGUgc3RhdGljIGxpdGVyYWwgdHJlZS4gU2luY2UgdGhlIGJpdCBsZW5ndGhzIGFyZSBpbXBvc2VkLCB0aGVyZSBpcyBub1xuICogbmVlZCBmb3IgdGhlIExfQ09ERVMgZXh0cmEgY29kZXMgdXNlZCBkdXJpbmcgaGVhcCBjb25zdHJ1Y3Rpb24uIEhvd2V2ZXJcbiAqIFRoZSBjb2RlcyAyODYgYW5kIDI4NyBhcmUgbmVlZGVkIHRvIGJ1aWxkIGEgY2Fub25pY2FsIHRyZWUgKHNlZSBfdHJfaW5pdFxuICogYmVsb3cpLlxuICovXG5cbnZhciBzdGF0aWNfZHRyZWUgID0gbmV3IEFycmF5KERfQ09ERVMgKiAyKTtcbnplcm8oc3RhdGljX2R0cmVlKTtcbi8qIFRoZSBzdGF0aWMgZGlzdGFuY2UgdHJlZS4gKEFjdHVhbGx5IGEgdHJpdmlhbCB0cmVlIHNpbmNlIGFsbCBjb2RlcyB1c2VcbiAqIDUgYml0cy4pXG4gKi9cblxudmFyIF9kaXN0X2NvZGUgICAgPSBuZXcgQXJyYXkoRElTVF9DT0RFX0xFTik7XG56ZXJvKF9kaXN0X2NvZGUpO1xuLyogRGlzdGFuY2UgY29kZXMuIFRoZSBmaXJzdCAyNTYgdmFsdWVzIGNvcnJlc3BvbmQgdG8gdGhlIGRpc3RhbmNlc1xuICogMyAuLiAyNTgsIHRoZSBsYXN0IDI1NiB2YWx1ZXMgY29ycmVzcG9uZCB0byB0aGUgdG9wIDggYml0cyBvZlxuICogdGhlIDE1IGJpdCBkaXN0YW5jZXMuXG4gKi9cblxudmFyIF9sZW5ndGhfY29kZSAgPSBuZXcgQXJyYXkoTUFYX01BVENILU1JTl9NQVRDSCsxKTtcbnplcm8oX2xlbmd0aF9jb2RlKTtcbi8qIGxlbmd0aCBjb2RlIGZvciBlYWNoIG5vcm1hbGl6ZWQgbWF0Y2ggbGVuZ3RoICgwID09IE1JTl9NQVRDSCkgKi9cblxudmFyIGJhc2VfbGVuZ3RoICAgPSBuZXcgQXJyYXkoTEVOR1RIX0NPREVTKTtcbnplcm8oYmFzZV9sZW5ndGgpO1xuLyogRmlyc3Qgbm9ybWFsaXplZCBsZW5ndGggZm9yIGVhY2ggY29kZSAoMCA9IE1JTl9NQVRDSCkgKi9cblxudmFyIGJhc2VfZGlzdCAgICAgPSBuZXcgQXJyYXkoRF9DT0RFUyk7XG56ZXJvKGJhc2VfZGlzdCk7XG4vKiBGaXJzdCBub3JtYWxpemVkIGRpc3RhbmNlIGZvciBlYWNoIGNvZGUgKDAgPSBkaXN0YW5jZSBvZiAxKSAqL1xuXG5cbnZhciBTdGF0aWNUcmVlRGVzYyA9IGZ1bmN0aW9uIChzdGF0aWNfdHJlZSwgZXh0cmFfYml0cywgZXh0cmFfYmFzZSwgZWxlbXMsIG1heF9sZW5ndGgpIHtcblxuICB0aGlzLnN0YXRpY190cmVlICA9IHN0YXRpY190cmVlOyAgLyogc3RhdGljIHRyZWUgb3IgTlVMTCAqL1xuICB0aGlzLmV4dHJhX2JpdHMgICA9IGV4dHJhX2JpdHM7ICAgLyogZXh0cmEgYml0cyBmb3IgZWFjaCBjb2RlIG9yIE5VTEwgKi9cbiAgdGhpcy5leHRyYV9iYXNlICAgPSBleHRyYV9iYXNlOyAgIC8qIGJhc2UgaW5kZXggZm9yIGV4dHJhX2JpdHMgKi9cbiAgdGhpcy5lbGVtcyAgICAgICAgPSBlbGVtczsgICAgICAgIC8qIG1heCBudW1iZXIgb2YgZWxlbWVudHMgaW4gdGhlIHRyZWUgKi9cbiAgdGhpcy5tYXhfbGVuZ3RoICAgPSBtYXhfbGVuZ3RoOyAgIC8qIG1heCBiaXQgbGVuZ3RoIGZvciB0aGUgY29kZXMgKi9cblxuICAvLyBzaG93IGlmIGBzdGF0aWNfdHJlZWAgaGFzIGRhdGEgb3IgZHVtbXkgLSBuZWVkZWQgZm9yIG1vbm9tb3JwaGljIG9iamVjdHNcbiAgdGhpcy5oYXNfc3RyZWUgICAgPSBzdGF0aWNfdHJlZSAmJiBzdGF0aWNfdHJlZS5sZW5ndGg7XG59O1xuXG5cbnZhciBzdGF0aWNfbF9kZXNjO1xudmFyIHN0YXRpY19kX2Rlc2M7XG52YXIgc3RhdGljX2JsX2Rlc2M7XG5cblxudmFyIFRyZWVEZXNjID0gZnVuY3Rpb24oZHluX3RyZWUsIHN0YXRfZGVzYykge1xuICB0aGlzLmR5bl90cmVlID0gZHluX3RyZWU7ICAgICAvKiB0aGUgZHluYW1pYyB0cmVlICovXG4gIHRoaXMubWF4X2NvZGUgPSAwOyAgICAgICAgICAgIC8qIGxhcmdlc3QgY29kZSB3aXRoIG5vbiB6ZXJvIGZyZXF1ZW5jeSAqL1xuICB0aGlzLnN0YXRfZGVzYyA9IHN0YXRfZGVzYzsgICAvKiB0aGUgY29ycmVzcG9uZGluZyBzdGF0aWMgdHJlZSAqL1xufTtcblxuXG5cbmZ1bmN0aW9uIGRfY29kZShkaXN0KSB7XG4gIHJldHVybiBkaXN0IDwgMjU2ID8gX2Rpc3RfY29kZVtkaXN0XSA6IF9kaXN0X2NvZGVbMjU2ICsgKGRpc3QgPj4+IDcpXTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIE91dHB1dCBhIHNob3J0IExTQiBmaXJzdCBvbiB0aGUgc3RyZWFtLlxuICogSU4gYXNzZXJ0aW9uOiB0aGVyZSBpcyBlbm91Z2ggcm9vbSBpbiBwZW5kaW5nQnVmLlxuICovXG5mdW5jdGlvbiBwdXRfc2hvcnQgKHMsIHcpIHtcbi8vICAgIHB1dF9ieXRlKHMsICh1Y2gpKCh3KSAmIDB4ZmYpKTtcbi8vICAgIHB1dF9ieXRlKHMsICh1Y2gpKCh1c2gpKHcpID4+IDgpKTtcbiAgcy5wZW5kaW5nX2J1ZltzLnBlbmRpbmcrK10gPSAodykgJiAweGZmO1xuICBzLnBlbmRpbmdfYnVmW3MucGVuZGluZysrXSA9ICh3ID4+PiA4KSAmIDB4ZmY7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTZW5kIGEgdmFsdWUgb24gYSBnaXZlbiBudW1iZXIgb2YgYml0cy5cbiAqIElOIGFzc2VydGlvbjogbGVuZ3RoIDw9IDE2IGFuZCB2YWx1ZSBmaXRzIGluIGxlbmd0aCBiaXRzLlxuICovXG5mdW5jdGlvbiBzZW5kX2JpdHMocywgdmFsdWUsIGxlbmd0aCkge1xuICBpZiAocy5iaV92YWxpZCA+IChCdWZfc2l6ZSAtIGxlbmd0aCkpIHtcbiAgICBzLmJpX2J1ZiB8PSAodmFsdWUgPDwgcy5iaV92YWxpZCkgJiAweGZmZmY7XG4gICAgcHV0X3Nob3J0KHMsIHMuYmlfYnVmKTtcbiAgICBzLmJpX2J1ZiA9IHZhbHVlID4+IChCdWZfc2l6ZSAtIHMuYmlfdmFsaWQpO1xuICAgIHMuYmlfdmFsaWQgKz0gbGVuZ3RoIC0gQnVmX3NpemU7XG4gIH0gZWxzZSB7XG4gICAgcy5iaV9idWYgfD0gKHZhbHVlIDw8IHMuYmlfdmFsaWQpICYgMHhmZmZmO1xuICAgIHMuYmlfdmFsaWQgKz0gbGVuZ3RoO1xuICB9XG59XG5cblxuZnVuY3Rpb24gc2VuZF9jb2RlKHMsIGMsIHRyZWUpIHtcbiAgc2VuZF9iaXRzKHMsIHRyZWVbYyoyXS8qLkNvZGUqLywgdHJlZVtjKjIgKyAxXS8qLkxlbiovKTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFJldmVyc2UgdGhlIGZpcnN0IGxlbiBiaXRzIG9mIGEgY29kZSwgdXNpbmcgc3RyYWlnaHRmb3J3YXJkIGNvZGUgKGEgZmFzdGVyXG4gKiBtZXRob2Qgd291bGQgdXNlIGEgdGFibGUpXG4gKiBJTiBhc3NlcnRpb246IDEgPD0gbGVuIDw9IDE1XG4gKi9cbmZ1bmN0aW9uIGJpX3JldmVyc2UoY29kZSwgbGVuKSB7XG4gIHZhciByZXMgPSAwO1xuICBkbyB7XG4gICAgcmVzIHw9IGNvZGUgJiAxO1xuICAgIGNvZGUgPj4+PSAxO1xuICAgIHJlcyA8PD0gMTtcbiAgfSB3aGlsZSAoLS1sZW4gPiAwKTtcbiAgcmV0dXJuIHJlcyA+Pj4gMTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEZsdXNoIHRoZSBiaXQgYnVmZmVyLCBrZWVwaW5nIGF0IG1vc3QgNyBiaXRzIGluIGl0LlxuICovXG5mdW5jdGlvbiBiaV9mbHVzaChzKSB7XG4gIGlmIChzLmJpX3ZhbGlkID09PSAxNikge1xuICAgIHB1dF9zaG9ydChzLCBzLmJpX2J1Zik7XG4gICAgcy5iaV9idWYgPSAwO1xuICAgIHMuYmlfdmFsaWQgPSAwO1xuXG4gIH0gZWxzZSBpZiAocy5iaV92YWxpZCA+PSA4KSB7XG4gICAgcy5wZW5kaW5nX2J1ZltzLnBlbmRpbmcrK10gPSBzLmJpX2J1ZiAmIDB4ZmY7XG4gICAgcy5iaV9idWYgPj49IDg7XG4gICAgcy5iaV92YWxpZCAtPSA4O1xuICB9XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb21wdXRlIHRoZSBvcHRpbWFsIGJpdCBsZW5ndGhzIGZvciBhIHRyZWUgYW5kIHVwZGF0ZSB0aGUgdG90YWwgYml0IGxlbmd0aFxuICogZm9yIHRoZSBjdXJyZW50IGJsb2NrLlxuICogSU4gYXNzZXJ0aW9uOiB0aGUgZmllbGRzIGZyZXEgYW5kIGRhZCBhcmUgc2V0LCBoZWFwW2hlYXBfbWF4XSBhbmRcbiAqICAgIGFib3ZlIGFyZSB0aGUgdHJlZSBub2RlcyBzb3J0ZWQgYnkgaW5jcmVhc2luZyBmcmVxdWVuY3kuXG4gKiBPVVQgYXNzZXJ0aW9uczogdGhlIGZpZWxkIGxlbiBpcyBzZXQgdG8gdGhlIG9wdGltYWwgYml0IGxlbmd0aCwgdGhlXG4gKiAgICAgYXJyYXkgYmxfY291bnQgY29udGFpbnMgdGhlIGZyZXF1ZW5jaWVzIGZvciBlYWNoIGJpdCBsZW5ndGguXG4gKiAgICAgVGhlIGxlbmd0aCBvcHRfbGVuIGlzIHVwZGF0ZWQ7IHN0YXRpY19sZW4gaXMgYWxzbyB1cGRhdGVkIGlmIHN0cmVlIGlzXG4gKiAgICAgbm90IG51bGwuXG4gKi9cbmZ1bmN0aW9uIGdlbl9iaXRsZW4ocywgZGVzYylcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICB0cmVlX2Rlc2MgKmRlc2M7ICAgIC8qIHRoZSB0cmVlIGRlc2NyaXB0b3IgKi9cbntcbiAgdmFyIHRyZWUgICAgICAgICAgICA9IGRlc2MuZHluX3RyZWU7XG4gIHZhciBtYXhfY29kZSAgICAgICAgPSBkZXNjLm1heF9jb2RlO1xuICB2YXIgc3RyZWUgICAgICAgICAgID0gZGVzYy5zdGF0X2Rlc2Muc3RhdGljX3RyZWU7XG4gIHZhciBoYXNfc3RyZWUgICAgICAgPSBkZXNjLnN0YXRfZGVzYy5oYXNfc3RyZWU7XG4gIHZhciBleHRyYSAgICAgICAgICAgPSBkZXNjLnN0YXRfZGVzYy5leHRyYV9iaXRzO1xuICB2YXIgYmFzZSAgICAgICAgICAgID0gZGVzYy5zdGF0X2Rlc2MuZXh0cmFfYmFzZTtcbiAgdmFyIG1heF9sZW5ndGggICAgICA9IGRlc2Muc3RhdF9kZXNjLm1heF9sZW5ndGg7XG4gIHZhciBoOyAgICAgICAgICAgICAgLyogaGVhcCBpbmRleCAqL1xuICB2YXIgbiwgbTsgICAgICAgICAgIC8qIGl0ZXJhdGUgb3ZlciB0aGUgdHJlZSBlbGVtZW50cyAqL1xuICB2YXIgYml0czsgICAgICAgICAgIC8qIGJpdCBsZW5ndGggKi9cbiAgdmFyIHhiaXRzOyAgICAgICAgICAvKiBleHRyYSBiaXRzICovXG4gIHZhciBmOyAgICAgICAgICAgICAgLyogZnJlcXVlbmN5ICovXG4gIHZhciBvdmVyZmxvdyA9IDA7ICAgLyogbnVtYmVyIG9mIGVsZW1lbnRzIHdpdGggYml0IGxlbmd0aCB0b28gbGFyZ2UgKi9cblxuICBmb3IgKGJpdHMgPSAwOyBiaXRzIDw9IE1BWF9CSVRTOyBiaXRzKyspIHtcbiAgICBzLmJsX2NvdW50W2JpdHNdID0gMDtcbiAgfVxuXG4gIC8qIEluIGEgZmlyc3QgcGFzcywgY29tcHV0ZSB0aGUgb3B0aW1hbCBiaXQgbGVuZ3RocyAod2hpY2ggbWF5XG4gICAqIG92ZXJmbG93IGluIHRoZSBjYXNlIG9mIHRoZSBiaXQgbGVuZ3RoIHRyZWUpLlxuICAgKi9cbiAgdHJlZVtzLmhlYXBbcy5oZWFwX21heF0qMiArIDFdLyouTGVuKi8gPSAwOyAvKiByb290IG9mIHRoZSBoZWFwICovXG5cbiAgZm9yIChoID0gcy5oZWFwX21heCsxOyBoIDwgSEVBUF9TSVpFOyBoKyspIHtcbiAgICBuID0gcy5oZWFwW2hdO1xuICAgIGJpdHMgPSB0cmVlW3RyZWVbbioyICsxXS8qLkRhZCovICogMiArIDFdLyouTGVuKi8gKyAxO1xuICAgIGlmIChiaXRzID4gbWF4X2xlbmd0aCkge1xuICAgICAgYml0cyA9IG1heF9sZW5ndGg7XG4gICAgICBvdmVyZmxvdysrO1xuICAgIH1cbiAgICB0cmVlW24qMiArIDFdLyouTGVuKi8gPSBiaXRzO1xuICAgIC8qIFdlIG92ZXJ3cml0ZSB0cmVlW25dLkRhZCB3aGljaCBpcyBubyBsb25nZXIgbmVlZGVkICovXG5cbiAgICBpZiAobiA+IG1heF9jb2RlKSB7IGNvbnRpbnVlOyB9IC8qIG5vdCBhIGxlYWYgbm9kZSAqL1xuXG4gICAgcy5ibF9jb3VudFtiaXRzXSsrO1xuICAgIHhiaXRzID0gMDtcbiAgICBpZiAobiA+PSBiYXNlKSB7XG4gICAgICB4Yml0cyA9IGV4dHJhW24tYmFzZV07XG4gICAgfVxuICAgIGYgPSB0cmVlW24gKiAyXS8qLkZyZXEqLztcbiAgICBzLm9wdF9sZW4gKz0gZiAqIChiaXRzICsgeGJpdHMpO1xuICAgIGlmIChoYXNfc3RyZWUpIHtcbiAgICAgIHMuc3RhdGljX2xlbiArPSBmICogKHN0cmVlW24qMiArIDFdLyouTGVuKi8gKyB4Yml0cyk7XG4gICAgfVxuICB9XG4gIGlmIChvdmVyZmxvdyA9PT0gMCkgeyByZXR1cm47IH1cblxuICAvLyBUcmFjZSgoc3RkZXJyLFwiXFxuYml0IGxlbmd0aCBvdmVyZmxvd1xcblwiKSk7XG4gIC8qIFRoaXMgaGFwcGVucyBmb3IgZXhhbXBsZSBvbiBvYmoyIGFuZCBwaWMgb2YgdGhlIENhbGdhcnkgY29ycHVzICovXG5cbiAgLyogRmluZCB0aGUgZmlyc3QgYml0IGxlbmd0aCB3aGljaCBjb3VsZCBpbmNyZWFzZTogKi9cbiAgZG8ge1xuICAgIGJpdHMgPSBtYXhfbGVuZ3RoLTE7XG4gICAgd2hpbGUgKHMuYmxfY291bnRbYml0c10gPT09IDApIHsgYml0cy0tOyB9XG4gICAgcy5ibF9jb3VudFtiaXRzXS0tOyAgICAgIC8qIG1vdmUgb25lIGxlYWYgZG93biB0aGUgdHJlZSAqL1xuICAgIHMuYmxfY291bnRbYml0cysxXSArPSAyOyAvKiBtb3ZlIG9uZSBvdmVyZmxvdyBpdGVtIGFzIGl0cyBicm90aGVyICovXG4gICAgcy5ibF9jb3VudFttYXhfbGVuZ3RoXS0tO1xuICAgIC8qIFRoZSBicm90aGVyIG9mIHRoZSBvdmVyZmxvdyBpdGVtIGFsc28gbW92ZXMgb25lIHN0ZXAgdXAsXG4gICAgICogYnV0IHRoaXMgZG9lcyBub3QgYWZmZWN0IGJsX2NvdW50W21heF9sZW5ndGhdXG4gICAgICovXG4gICAgb3ZlcmZsb3cgLT0gMjtcbiAgfSB3aGlsZSAob3ZlcmZsb3cgPiAwKTtcblxuICAvKiBOb3cgcmVjb21wdXRlIGFsbCBiaXQgbGVuZ3Rocywgc2Nhbm5pbmcgaW4gaW5jcmVhc2luZyBmcmVxdWVuY3kuXG4gICAqIGggaXMgc3RpbGwgZXF1YWwgdG8gSEVBUF9TSVpFLiAoSXQgaXMgc2ltcGxlciB0byByZWNvbnN0cnVjdCBhbGxcbiAgICogbGVuZ3RocyBpbnN0ZWFkIG9mIGZpeGluZyBvbmx5IHRoZSB3cm9uZyBvbmVzLiBUaGlzIGlkZWEgaXMgdGFrZW5cbiAgICogZnJvbSAnYXInIHdyaXR0ZW4gYnkgSGFydWhpa28gT2t1bXVyYS4pXG4gICAqL1xuICBmb3IgKGJpdHMgPSBtYXhfbGVuZ3RoOyBiaXRzICE9PSAwOyBiaXRzLS0pIHtcbiAgICBuID0gcy5ibF9jb3VudFtiaXRzXTtcbiAgICB3aGlsZSAobiAhPT0gMCkge1xuICAgICAgbSA9IHMuaGVhcFstLWhdO1xuICAgICAgaWYgKG0gPiBtYXhfY29kZSkgeyBjb250aW51ZTsgfVxuICAgICAgaWYgKHRyZWVbbSoyICsgMV0vKi5MZW4qLyAhPT0gYml0cykge1xuICAgICAgICAvLyBUcmFjZSgoc3RkZXJyLFwiY29kZSAlZCBiaXRzICVkLT4lZFxcblwiLCBtLCB0cmVlW21dLkxlbiwgYml0cykpO1xuICAgICAgICBzLm9wdF9sZW4gKz0gKGJpdHMgLSB0cmVlW20qMiArIDFdLyouTGVuKi8pKnRyZWVbbSoyXS8qLkZyZXEqLztcbiAgICAgICAgdHJlZVttKjIgKyAxXS8qLkxlbiovID0gYml0cztcbiAgICAgIH1cbiAgICAgIG4tLTtcbiAgICB9XG4gIH1cbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEdlbmVyYXRlIHRoZSBjb2RlcyBmb3IgYSBnaXZlbiB0cmVlIGFuZCBiaXQgY291bnRzICh3aGljaCBuZWVkIG5vdCBiZVxuICogb3B0aW1hbCkuXG4gKiBJTiBhc3NlcnRpb246IHRoZSBhcnJheSBibF9jb3VudCBjb250YWlucyB0aGUgYml0IGxlbmd0aCBzdGF0aXN0aWNzIGZvclxuICogdGhlIGdpdmVuIHRyZWUgYW5kIHRoZSBmaWVsZCBsZW4gaXMgc2V0IGZvciBhbGwgdHJlZSBlbGVtZW50cy5cbiAqIE9VVCBhc3NlcnRpb246IHRoZSBmaWVsZCBjb2RlIGlzIHNldCBmb3IgYWxsIHRyZWUgZWxlbWVudHMgb2Ygbm9uXG4gKiAgICAgemVybyBjb2RlIGxlbmd0aC5cbiAqL1xuZnVuY3Rpb24gZ2VuX2NvZGVzKHRyZWUsIG1heF9jb2RlLCBibF9jb3VudClcbi8vICAgIGN0X2RhdGEgKnRyZWU7ICAgICAgICAgICAgIC8qIHRoZSB0cmVlIHRvIGRlY29yYXRlICovXG4vLyAgICBpbnQgbWF4X2NvZGU7ICAgICAgICAgICAgICAvKiBsYXJnZXN0IGNvZGUgd2l0aCBub24gemVybyBmcmVxdWVuY3kgKi9cbi8vICAgIHVzaGYgKmJsX2NvdW50OyAgICAgICAgICAgIC8qIG51bWJlciBvZiBjb2RlcyBhdCBlYWNoIGJpdCBsZW5ndGggKi9cbntcbiAgdmFyIG5leHRfY29kZSA9IG5ldyBBcnJheShNQVhfQklUUysxKTsgLyogbmV4dCBjb2RlIHZhbHVlIGZvciBlYWNoIGJpdCBsZW5ndGggKi9cbiAgdmFyIGNvZGUgPSAwOyAgICAgICAgICAgICAgLyogcnVubmluZyBjb2RlIHZhbHVlICovXG4gIHZhciBiaXRzOyAgICAgICAgICAgICAgICAgIC8qIGJpdCBpbmRleCAqL1xuICB2YXIgbjsgICAgICAgICAgICAgICAgICAgICAvKiBjb2RlIGluZGV4ICovXG5cbiAgLyogVGhlIGRpc3RyaWJ1dGlvbiBjb3VudHMgYXJlIGZpcnN0IHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGNvZGUgdmFsdWVzXG4gICAqIHdpdGhvdXQgYml0IHJldmVyc2FsLlxuICAgKi9cbiAgZm9yIChiaXRzID0gMTsgYml0cyA8PSBNQVhfQklUUzsgYml0cysrKSB7XG4gICAgbmV4dF9jb2RlW2JpdHNdID0gY29kZSA9IChjb2RlICsgYmxfY291bnRbYml0cy0xXSkgPDwgMTtcbiAgfVxuICAvKiBDaGVjayB0aGF0IHRoZSBiaXQgY291bnRzIGluIGJsX2NvdW50IGFyZSBjb25zaXN0ZW50LiBUaGUgbGFzdCBjb2RlXG4gICAqIG11c3QgYmUgYWxsIG9uZXMuXG4gICAqL1xuICAvL0Fzc2VydCAoY29kZSArIGJsX2NvdW50W01BWF9CSVRTXS0xID09ICgxPDxNQVhfQklUUyktMSxcbiAgLy8gICAgICAgIFwiaW5jb25zaXN0ZW50IGJpdCBjb3VudHNcIik7XG4gIC8vVHJhY2V2KChzdGRlcnIsXCJcXG5nZW5fY29kZXM6IG1heF9jb2RlICVkIFwiLCBtYXhfY29kZSkpO1xuXG4gIGZvciAobiA9IDA7ICBuIDw9IG1heF9jb2RlOyBuKyspIHtcbiAgICB2YXIgbGVuID0gdHJlZVtuKjIgKyAxXS8qLkxlbiovO1xuICAgIGlmIChsZW4gPT09IDApIHsgY29udGludWU7IH1cbiAgICAvKiBOb3cgcmV2ZXJzZSB0aGUgYml0cyAqL1xuICAgIHRyZWVbbioyXS8qLkNvZGUqLyA9IGJpX3JldmVyc2UobmV4dF9jb2RlW2xlbl0rKywgbGVuKTtcblxuICAgIC8vVHJhY2Vjdih0cmVlICE9IHN0YXRpY19sdHJlZSwgKHN0ZGVycixcIlxcbm4gJTNkICVjIGwgJTJkIGMgJTR4ICgleCkgXCIsXG4gICAgLy8gICAgIG4sIChpc2dyYXBoKG4pID8gbiA6ICcgJyksIGxlbiwgdHJlZVtuXS5Db2RlLCBuZXh0X2NvZGVbbGVuXS0xKSk7XG4gIH1cbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEluaXRpYWxpemUgdGhlIHZhcmlvdXMgJ2NvbnN0YW50JyB0YWJsZXMuXG4gKi9cbmZ1bmN0aW9uIHRyX3N0YXRpY19pbml0KCkge1xuICB2YXIgbjsgICAgICAgIC8qIGl0ZXJhdGVzIG92ZXIgdHJlZSBlbGVtZW50cyAqL1xuICB2YXIgYml0czsgICAgIC8qIGJpdCBjb3VudGVyICovXG4gIHZhciBsZW5ndGg7ICAgLyogbGVuZ3RoIHZhbHVlICovXG4gIHZhciBjb2RlOyAgICAgLyogY29kZSB2YWx1ZSAqL1xuICB2YXIgZGlzdDsgICAgIC8qIGRpc3RhbmNlIGluZGV4ICovXG4gIHZhciBibF9jb3VudCA9IG5ldyBBcnJheShNQVhfQklUUysxKTtcbiAgLyogbnVtYmVyIG9mIGNvZGVzIGF0IGVhY2ggYml0IGxlbmd0aCBmb3IgYW4gb3B0aW1hbCB0cmVlICovXG5cbiAgLy8gZG8gY2hlY2sgaW4gX3RyX2luaXQoKVxuICAvL2lmIChzdGF0aWNfaW5pdF9kb25lKSByZXR1cm47XG5cbiAgLyogRm9yIHNvbWUgZW1iZWRkZWQgdGFyZ2V0cywgZ2xvYmFsIHZhcmlhYmxlcyBhcmUgbm90IGluaXRpYWxpemVkOiAqL1xuLyojaWZkZWYgTk9fSU5JVF9HTE9CQUxfUE9JTlRFUlNcbiAgc3RhdGljX2xfZGVzYy5zdGF0aWNfdHJlZSA9IHN0YXRpY19sdHJlZTtcbiAgc3RhdGljX2xfZGVzYy5leHRyYV9iaXRzID0gZXh0cmFfbGJpdHM7XG4gIHN0YXRpY19kX2Rlc2Muc3RhdGljX3RyZWUgPSBzdGF0aWNfZHRyZWU7XG4gIHN0YXRpY19kX2Rlc2MuZXh0cmFfYml0cyA9IGV4dHJhX2RiaXRzO1xuICBzdGF0aWNfYmxfZGVzYy5leHRyYV9iaXRzID0gZXh0cmFfYmxiaXRzO1xuI2VuZGlmKi9cblxuICAvKiBJbml0aWFsaXplIHRoZSBtYXBwaW5nIGxlbmd0aCAoMC4uMjU1KSAtPiBsZW5ndGggY29kZSAoMC4uMjgpICovXG4gIGxlbmd0aCA9IDA7XG4gIGZvciAoY29kZSA9IDA7IGNvZGUgPCBMRU5HVEhfQ09ERVMtMTsgY29kZSsrKSB7XG4gICAgYmFzZV9sZW5ndGhbY29kZV0gPSBsZW5ndGg7XG4gICAgZm9yIChuID0gMDsgbiA8ICgxPDxleHRyYV9sYml0c1tjb2RlXSk7IG4rKykge1xuICAgICAgX2xlbmd0aF9jb2RlW2xlbmd0aCsrXSA9IGNvZGU7XG4gICAgfVxuICB9XG4gIC8vQXNzZXJ0IChsZW5ndGggPT0gMjU2LCBcInRyX3N0YXRpY19pbml0OiBsZW5ndGggIT0gMjU2XCIpO1xuICAvKiBOb3RlIHRoYXQgdGhlIGxlbmd0aCAyNTUgKG1hdGNoIGxlbmd0aCAyNTgpIGNhbiBiZSByZXByZXNlbnRlZFxuICAgKiBpbiB0d28gZGlmZmVyZW50IHdheXM6IGNvZGUgMjg0ICsgNSBiaXRzIG9yIGNvZGUgMjg1LCBzbyB3ZVxuICAgKiBvdmVyd3JpdGUgbGVuZ3RoX2NvZGVbMjU1XSB0byB1c2UgdGhlIGJlc3QgZW5jb2Rpbmc6XG4gICAqL1xuICBfbGVuZ3RoX2NvZGVbbGVuZ3RoLTFdID0gY29kZTtcblxuICAvKiBJbml0aWFsaXplIHRoZSBtYXBwaW5nIGRpc3QgKDAuLjMySykgLT4gZGlzdCBjb2RlICgwLi4yOSkgKi9cbiAgZGlzdCA9IDA7XG4gIGZvciAoY29kZSA9IDAgOyBjb2RlIDwgMTY7IGNvZGUrKykge1xuICAgIGJhc2VfZGlzdFtjb2RlXSA9IGRpc3Q7XG4gICAgZm9yIChuID0gMDsgbiA8ICgxPDxleHRyYV9kYml0c1tjb2RlXSk7IG4rKykge1xuICAgICAgX2Rpc3RfY29kZVtkaXN0KytdID0gY29kZTtcbiAgICB9XG4gIH1cbiAgLy9Bc3NlcnQgKGRpc3QgPT0gMjU2LCBcInRyX3N0YXRpY19pbml0OiBkaXN0ICE9IDI1NlwiKTtcbiAgZGlzdCA+Pj0gNzsgLyogZnJvbSBub3cgb24sIGFsbCBkaXN0YW5jZXMgYXJlIGRpdmlkZWQgYnkgMTI4ICovXG4gIGZvciAoIDsgY29kZSA8IERfQ09ERVM7IGNvZGUrKykge1xuICAgIGJhc2VfZGlzdFtjb2RlXSA9IGRpc3QgPDwgNztcbiAgICBmb3IgKG4gPSAwOyBuIDwgKDE8PChleHRyYV9kYml0c1tjb2RlXS03KSk7IG4rKykge1xuICAgICAgX2Rpc3RfY29kZVsyNTYgKyBkaXN0KytdID0gY29kZTtcbiAgICB9XG4gIH1cbiAgLy9Bc3NlcnQgKGRpc3QgPT0gMjU2LCBcInRyX3N0YXRpY19pbml0OiAyNTYrZGlzdCAhPSA1MTJcIik7XG5cbiAgLyogQ29uc3RydWN0IHRoZSBjb2RlcyBvZiB0aGUgc3RhdGljIGxpdGVyYWwgdHJlZSAqL1xuICBmb3IgKGJpdHMgPSAwOyBiaXRzIDw9IE1BWF9CSVRTOyBiaXRzKyspIHtcbiAgICBibF9jb3VudFtiaXRzXSA9IDA7XG4gIH1cblxuICBuID0gMDtcbiAgd2hpbGUgKG4gPD0gMTQzKSB7XG4gICAgc3RhdGljX2x0cmVlW24qMiArIDFdLyouTGVuKi8gPSA4O1xuICAgIG4rKztcbiAgICBibF9jb3VudFs4XSsrO1xuICB9XG4gIHdoaWxlIChuIDw9IDI1NSkge1xuICAgIHN0YXRpY19sdHJlZVtuKjIgKyAxXS8qLkxlbiovID0gOTtcbiAgICBuKys7XG4gICAgYmxfY291bnRbOV0rKztcbiAgfVxuICB3aGlsZSAobiA8PSAyNzkpIHtcbiAgICBzdGF0aWNfbHRyZWVbbioyICsgMV0vKi5MZW4qLyA9IDc7XG4gICAgbisrO1xuICAgIGJsX2NvdW50WzddKys7XG4gIH1cbiAgd2hpbGUgKG4gPD0gMjg3KSB7XG4gICAgc3RhdGljX2x0cmVlW24qMiArIDFdLyouTGVuKi8gPSA4O1xuICAgIG4rKztcbiAgICBibF9jb3VudFs4XSsrO1xuICB9XG4gIC8qIENvZGVzIDI4NiBhbmQgMjg3IGRvIG5vdCBleGlzdCwgYnV0IHdlIG11c3QgaW5jbHVkZSB0aGVtIGluIHRoZVxuICAgKiB0cmVlIGNvbnN0cnVjdGlvbiB0byBnZXQgYSBjYW5vbmljYWwgSHVmZm1hbiB0cmVlIChsb25nZXN0IGNvZGVcbiAgICogYWxsIG9uZXMpXG4gICAqL1xuICBnZW5fY29kZXMoc3RhdGljX2x0cmVlLCBMX0NPREVTKzEsIGJsX2NvdW50KTtcblxuICAvKiBUaGUgc3RhdGljIGRpc3RhbmNlIHRyZWUgaXMgdHJpdmlhbDogKi9cbiAgZm9yIChuID0gMDsgbiA8IERfQ09ERVM7IG4rKykge1xuICAgIHN0YXRpY19kdHJlZVtuKjIgKyAxXS8qLkxlbiovID0gNTtcbiAgICBzdGF0aWNfZHRyZWVbbioyXS8qLkNvZGUqLyA9IGJpX3JldmVyc2UobiwgNSk7XG4gIH1cblxuICAvLyBOb3cgZGF0YSByZWFkeSBhbmQgd2UgY2FuIGluaXQgc3RhdGljIHRyZWVzXG4gIHN0YXRpY19sX2Rlc2MgPSBuZXcgU3RhdGljVHJlZURlc2Moc3RhdGljX2x0cmVlLCBleHRyYV9sYml0cywgTElURVJBTFMrMSwgTF9DT0RFUywgTUFYX0JJVFMpO1xuICBzdGF0aWNfZF9kZXNjID0gbmV3IFN0YXRpY1RyZWVEZXNjKHN0YXRpY19kdHJlZSwgZXh0cmFfZGJpdHMsIDAsICAgICAgICAgIERfQ09ERVMsIE1BWF9CSVRTKTtcbiAgc3RhdGljX2JsX2Rlc2MgPW5ldyBTdGF0aWNUcmVlRGVzYyhuZXcgQXJyYXkoMCksIGV4dHJhX2JsYml0cywgMCwgICAgICAgICBCTF9DT0RFUywgTUFYX0JMX0JJVFMpO1xuXG4gIC8vc3RhdGljX2luaXRfZG9uZSA9IHRydWU7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBJbml0aWFsaXplIGEgbmV3IGJsb2NrLlxuICovXG5mdW5jdGlvbiBpbml0X2Jsb2NrKHMpIHtcbiAgdmFyIG47IC8qIGl0ZXJhdGVzIG92ZXIgdHJlZSBlbGVtZW50cyAqL1xuXG4gIC8qIEluaXRpYWxpemUgdGhlIHRyZWVzLiAqL1xuICBmb3IgKG4gPSAwOyBuIDwgTF9DT0RFUzsgIG4rKykgeyBzLmR5bl9sdHJlZVtuKjJdLyouRnJlcSovID0gMDsgfVxuICBmb3IgKG4gPSAwOyBuIDwgRF9DT0RFUzsgIG4rKykgeyBzLmR5bl9kdHJlZVtuKjJdLyouRnJlcSovID0gMDsgfVxuICBmb3IgKG4gPSAwOyBuIDwgQkxfQ09ERVM7IG4rKykgeyBzLmJsX3RyZWVbbioyXS8qLkZyZXEqLyA9IDA7IH1cblxuICBzLmR5bl9sdHJlZVtFTkRfQkxPQ0sqMl0vKi5GcmVxKi8gPSAxO1xuICBzLm9wdF9sZW4gPSBzLnN0YXRpY19sZW4gPSAwO1xuICBzLmxhc3RfbGl0ID0gcy5tYXRjaGVzID0gMDtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEZsdXNoIHRoZSBiaXQgYnVmZmVyIGFuZCBhbGlnbiB0aGUgb3V0cHV0IG9uIGEgYnl0ZSBib3VuZGFyeVxuICovXG5mdW5jdGlvbiBiaV93aW5kdXAocylcbntcbiAgaWYgKHMuYmlfdmFsaWQgPiA4KSB7XG4gICAgcHV0X3Nob3J0KHMsIHMuYmlfYnVmKTtcbiAgfSBlbHNlIGlmIChzLmJpX3ZhbGlkID4gMCkge1xuICAgIC8vcHV0X2J5dGUocywgKEJ5dGUpcy0+YmlfYnVmKTtcbiAgICBzLnBlbmRpbmdfYnVmW3MucGVuZGluZysrXSA9IHMuYmlfYnVmO1xuICB9XG4gIHMuYmlfYnVmID0gMDtcbiAgcy5iaV92YWxpZCA9IDA7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogQ29weSBhIHN0b3JlZCBibG9jaywgc3RvcmluZyBmaXJzdCB0aGUgbGVuZ3RoIGFuZCBpdHNcbiAqIG9uZSdzIGNvbXBsZW1lbnQgaWYgcmVxdWVzdGVkLlxuICovXG5mdW5jdGlvbiBjb3B5X2Jsb2NrKHMsIGJ1ZiwgbGVuLCBoZWFkZXIpXG4vL0RlZmxhdGVTdGF0ZSAqcztcbi8vY2hhcmYgICAgKmJ1ZjsgICAgLyogdGhlIGlucHV0IGRhdGEgKi9cbi8vdW5zaWduZWQgbGVuOyAgICAgLyogaXRzIGxlbmd0aCAqL1xuLy9pbnQgICAgICBoZWFkZXI7ICAvKiB0cnVlIGlmIGJsb2NrIGhlYWRlciBtdXN0IGJlIHdyaXR0ZW4gKi9cbntcbiAgYmlfd2luZHVwKHMpOyAgICAgICAgLyogYWxpZ24gb24gYnl0ZSBib3VuZGFyeSAqL1xuXG4gIGlmIChoZWFkZXIpIHtcbiAgICBwdXRfc2hvcnQocywgbGVuKTtcbiAgICBwdXRfc2hvcnQocywgfmxlbik7XG4gIH1cbi8vICB3aGlsZSAobGVuLS0pIHtcbi8vICAgIHB1dF9ieXRlKHMsICpidWYrKyk7XG4vLyAgfVxuICB1dGlscy5hcnJheVNldChzLnBlbmRpbmdfYnVmLCBzLndpbmRvdywgYnVmLCBsZW4sIHMucGVuZGluZyk7XG4gIHMucGVuZGluZyArPSBsZW47XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogQ29tcGFyZXMgdG8gc3VidHJlZXMsIHVzaW5nIHRoZSB0cmVlIGRlcHRoIGFzIHRpZSBicmVha2VyIHdoZW5cbiAqIHRoZSBzdWJ0cmVlcyBoYXZlIGVxdWFsIGZyZXF1ZW5jeS4gVGhpcyBtaW5pbWl6ZXMgdGhlIHdvcnN0IGNhc2UgbGVuZ3RoLlxuICovXG5mdW5jdGlvbiBzbWFsbGVyKHRyZWUsIG4sIG0sIGRlcHRoKSB7XG4gIHZhciBfbjIgPSBuKjI7XG4gIHZhciBfbTIgPSBtKjI7XG4gIHJldHVybiAodHJlZVtfbjJdLyouRnJlcSovIDwgdHJlZVtfbTJdLyouRnJlcSovIHx8XG4gICAgICAgICAodHJlZVtfbjJdLyouRnJlcSovID09PSB0cmVlW19tMl0vKi5GcmVxKi8gJiYgZGVwdGhbbl0gPD0gZGVwdGhbbV0pKTtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBSZXN0b3JlIHRoZSBoZWFwIHByb3BlcnR5IGJ5IG1vdmluZyBkb3duIHRoZSB0cmVlIHN0YXJ0aW5nIGF0IG5vZGUgayxcbiAqIGV4Y2hhbmdpbmcgYSBub2RlIHdpdGggdGhlIHNtYWxsZXN0IG9mIGl0cyB0d28gc29ucyBpZiBuZWNlc3NhcnksIHN0b3BwaW5nXG4gKiB3aGVuIHRoZSBoZWFwIHByb3BlcnR5IGlzIHJlLWVzdGFibGlzaGVkIChlYWNoIGZhdGhlciBzbWFsbGVyIHRoYW4gaXRzXG4gKiB0d28gc29ucykuXG4gKi9cbmZ1bmN0aW9uIHBxZG93bmhlYXAocywgdHJlZSwgaylcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICBjdF9kYXRhICp0cmVlOyAgLyogdGhlIHRyZWUgdG8gcmVzdG9yZSAqL1xuLy8gICAgaW50IGs7ICAgICAgICAgICAgICAgLyogbm9kZSB0byBtb3ZlIGRvd24gKi9cbntcbiAgdmFyIHYgPSBzLmhlYXBba107XG4gIHZhciBqID0gayA8PCAxOyAgLyogbGVmdCBzb24gb2YgayAqL1xuICB3aGlsZSAoaiA8PSBzLmhlYXBfbGVuKSB7XG4gICAgLyogU2V0IGogdG8gdGhlIHNtYWxsZXN0IG9mIHRoZSB0d28gc29uczogKi9cbiAgICBpZiAoaiA8IHMuaGVhcF9sZW4gJiZcbiAgICAgIHNtYWxsZXIodHJlZSwgcy5oZWFwW2orMV0sIHMuaGVhcFtqXSwgcy5kZXB0aCkpIHtcbiAgICAgIGorKztcbiAgICB9XG4gICAgLyogRXhpdCBpZiB2IGlzIHNtYWxsZXIgdGhhbiBib3RoIHNvbnMgKi9cbiAgICBpZiAoc21hbGxlcih0cmVlLCB2LCBzLmhlYXBbal0sIHMuZGVwdGgpKSB7IGJyZWFrOyB9XG5cbiAgICAvKiBFeGNoYW5nZSB2IHdpdGggdGhlIHNtYWxsZXN0IHNvbiAqL1xuICAgIHMuaGVhcFtrXSA9IHMuaGVhcFtqXTtcbiAgICBrID0gajtcblxuICAgIC8qIEFuZCBjb250aW51ZSBkb3duIHRoZSB0cmVlLCBzZXR0aW5nIGogdG8gdGhlIGxlZnQgc29uIG9mIGsgKi9cbiAgICBqIDw8PSAxO1xuICB9XG4gIHMuaGVhcFtrXSA9IHY7XG59XG5cblxuLy8gaW5saW5lZCBtYW51YWxseVxuLy8gdmFyIFNNQUxMRVNUID0gMTtcblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTZW5kIHRoZSBibG9jayBkYXRhIGNvbXByZXNzZWQgdXNpbmcgdGhlIGdpdmVuIEh1ZmZtYW4gdHJlZXNcbiAqL1xuZnVuY3Rpb24gY29tcHJlc3NfYmxvY2socywgbHRyZWUsIGR0cmVlKVxuLy8gICAgZGVmbGF0ZV9zdGF0ZSAqcztcbi8vICAgIGNvbnN0IGN0X2RhdGEgKmx0cmVlOyAvKiBsaXRlcmFsIHRyZWUgKi9cbi8vICAgIGNvbnN0IGN0X2RhdGEgKmR0cmVlOyAvKiBkaXN0YW5jZSB0cmVlICovXG57XG4gIHZhciBkaXN0OyAgICAgICAgICAgLyogZGlzdGFuY2Ugb2YgbWF0Y2hlZCBzdHJpbmcgKi9cbiAgdmFyIGxjOyAgICAgICAgICAgICAvKiBtYXRjaCBsZW5ndGggb3IgdW5tYXRjaGVkIGNoYXIgKGlmIGRpc3QgPT0gMCkgKi9cbiAgdmFyIGx4ID0gMDsgICAgICAgICAvKiBydW5uaW5nIGluZGV4IGluIGxfYnVmICovXG4gIHZhciBjb2RlOyAgICAgICAgICAgLyogdGhlIGNvZGUgdG8gc2VuZCAqL1xuICB2YXIgZXh0cmE7ICAgICAgICAgIC8qIG51bWJlciBvZiBleHRyYSBiaXRzIHRvIHNlbmQgKi9cblxuICBpZiAocy5sYXN0X2xpdCAhPT0gMCkge1xuICAgIGRvIHtcbiAgICAgIGRpc3QgPSAocy5wZW5kaW5nX2J1ZltzLmRfYnVmICsgbHgqMl0gPDwgOCkgfCAocy5wZW5kaW5nX2J1ZltzLmRfYnVmICsgbHgqMiArIDFdKTtcbiAgICAgIGxjID0gcy5wZW5kaW5nX2J1ZltzLmxfYnVmICsgbHhdO1xuICAgICAgbHgrKztcblxuICAgICAgaWYgKGRpc3QgPT09IDApIHtcbiAgICAgICAgc2VuZF9jb2RlKHMsIGxjLCBsdHJlZSk7IC8qIHNlbmQgYSBsaXRlcmFsIGJ5dGUgKi9cbiAgICAgICAgLy9UcmFjZWN2KGlzZ3JhcGgobGMpLCAoc3RkZXJyLFwiICclYycgXCIsIGxjKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvKiBIZXJlLCBsYyBpcyB0aGUgbWF0Y2ggbGVuZ3RoIC0gTUlOX01BVENIICovXG4gICAgICAgIGNvZGUgPSBfbGVuZ3RoX2NvZGVbbGNdO1xuICAgICAgICBzZW5kX2NvZGUocywgY29kZStMSVRFUkFMUysxLCBsdHJlZSk7IC8qIHNlbmQgdGhlIGxlbmd0aCBjb2RlICovXG4gICAgICAgIGV4dHJhID0gZXh0cmFfbGJpdHNbY29kZV07XG4gICAgICAgIGlmIChleHRyYSAhPT0gMCkge1xuICAgICAgICAgIGxjIC09IGJhc2VfbGVuZ3RoW2NvZGVdO1xuICAgICAgICAgIHNlbmRfYml0cyhzLCBsYywgZXh0cmEpOyAgICAgICAvKiBzZW5kIHRoZSBleHRyYSBsZW5ndGggYml0cyAqL1xuICAgICAgICB9XG4gICAgICAgIGRpc3QtLTsgLyogZGlzdCBpcyBub3cgdGhlIG1hdGNoIGRpc3RhbmNlIC0gMSAqL1xuICAgICAgICBjb2RlID0gZF9jb2RlKGRpc3QpO1xuICAgICAgICAvL0Fzc2VydCAoY29kZSA8IERfQ09ERVMsIFwiYmFkIGRfY29kZVwiKTtcblxuICAgICAgICBzZW5kX2NvZGUocywgY29kZSwgZHRyZWUpOyAgICAgICAvKiBzZW5kIHRoZSBkaXN0YW5jZSBjb2RlICovXG4gICAgICAgIGV4dHJhID0gZXh0cmFfZGJpdHNbY29kZV07XG4gICAgICAgIGlmIChleHRyYSAhPT0gMCkge1xuICAgICAgICAgIGRpc3QgLT0gYmFzZV9kaXN0W2NvZGVdO1xuICAgICAgICAgIHNlbmRfYml0cyhzLCBkaXN0LCBleHRyYSk7ICAgLyogc2VuZCB0aGUgZXh0cmEgZGlzdGFuY2UgYml0cyAqL1xuICAgICAgICB9XG4gICAgICB9IC8qIGxpdGVyYWwgb3IgbWF0Y2ggcGFpciA/ICovXG5cbiAgICAgIC8qIENoZWNrIHRoYXQgdGhlIG92ZXJsYXkgYmV0d2VlbiBwZW5kaW5nX2J1ZiBhbmQgZF9idWYrbF9idWYgaXMgb2s6ICovXG4gICAgICAvL0Fzc2VydCgodUludCkocy0+cGVuZGluZykgPCBzLT5saXRfYnVmc2l6ZSArIDIqbHgsXG4gICAgICAvLyAgICAgICBcInBlbmRpbmdCdWYgb3ZlcmZsb3dcIik7XG5cbiAgICB9IHdoaWxlIChseCA8IHMubGFzdF9saXQpO1xuICB9XG5cbiAgc2VuZF9jb2RlKHMsIEVORF9CTE9DSywgbHRyZWUpO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogQ29uc3RydWN0IG9uZSBIdWZmbWFuIHRyZWUgYW5kIGFzc2lnbnMgdGhlIGNvZGUgYml0IHN0cmluZ3MgYW5kIGxlbmd0aHMuXG4gKiBVcGRhdGUgdGhlIHRvdGFsIGJpdCBsZW5ndGggZm9yIHRoZSBjdXJyZW50IGJsb2NrLlxuICogSU4gYXNzZXJ0aW9uOiB0aGUgZmllbGQgZnJlcSBpcyBzZXQgZm9yIGFsbCB0cmVlIGVsZW1lbnRzLlxuICogT1VUIGFzc2VydGlvbnM6IHRoZSBmaWVsZHMgbGVuIGFuZCBjb2RlIGFyZSBzZXQgdG8gdGhlIG9wdGltYWwgYml0IGxlbmd0aFxuICogICAgIGFuZCBjb3JyZXNwb25kaW5nIGNvZGUuIFRoZSBsZW5ndGggb3B0X2xlbiBpcyB1cGRhdGVkOyBzdGF0aWNfbGVuIGlzXG4gKiAgICAgYWxzbyB1cGRhdGVkIGlmIHN0cmVlIGlzIG5vdCBudWxsLiBUaGUgZmllbGQgbWF4X2NvZGUgaXMgc2V0LlxuICovXG5mdW5jdGlvbiBidWlsZF90cmVlKHMsIGRlc2MpXG4vLyAgICBkZWZsYXRlX3N0YXRlICpzO1xuLy8gICAgdHJlZV9kZXNjICpkZXNjOyAvKiB0aGUgdHJlZSBkZXNjcmlwdG9yICovXG57XG4gIHZhciB0cmVlICAgICA9IGRlc2MuZHluX3RyZWU7XG4gIHZhciBzdHJlZSAgICA9IGRlc2Muc3RhdF9kZXNjLnN0YXRpY190cmVlO1xuICB2YXIgaGFzX3N0cmVlID0gZGVzYy5zdGF0X2Rlc2MuaGFzX3N0cmVlO1xuICB2YXIgZWxlbXMgICAgPSBkZXNjLnN0YXRfZGVzYy5lbGVtcztcbiAgdmFyIG4sIG07ICAgICAgICAgIC8qIGl0ZXJhdGUgb3ZlciBoZWFwIGVsZW1lbnRzICovXG4gIHZhciBtYXhfY29kZSA9IC0xOyAvKiBsYXJnZXN0IGNvZGUgd2l0aCBub24gemVybyBmcmVxdWVuY3kgKi9cbiAgdmFyIG5vZGU7ICAgICAgICAgIC8qIG5ldyBub2RlIGJlaW5nIGNyZWF0ZWQgKi9cblxuICAvKiBDb25zdHJ1Y3QgdGhlIGluaXRpYWwgaGVhcCwgd2l0aCBsZWFzdCBmcmVxdWVudCBlbGVtZW50IGluXG4gICAqIGhlYXBbU01BTExFU1RdLiBUaGUgc29ucyBvZiBoZWFwW25dIGFyZSBoZWFwWzIqbl0gYW5kIGhlYXBbMipuKzFdLlxuICAgKiBoZWFwWzBdIGlzIG5vdCB1c2VkLlxuICAgKi9cbiAgcy5oZWFwX2xlbiA9IDA7XG4gIHMuaGVhcF9tYXggPSBIRUFQX1NJWkU7XG5cbiAgZm9yIChuID0gMDsgbiA8IGVsZW1zOyBuKyspIHtcbiAgICBpZiAodHJlZVtuICogMl0vKi5GcmVxKi8gIT09IDApIHtcbiAgICAgIHMuaGVhcFsrK3MuaGVhcF9sZW5dID0gbWF4X2NvZGUgPSBuO1xuICAgICAgcy5kZXB0aFtuXSA9IDA7XG5cbiAgICB9IGVsc2Uge1xuICAgICAgdHJlZVtuKjIgKyAxXS8qLkxlbiovID0gMDtcbiAgICB9XG4gIH1cblxuICAvKiBUaGUgcGt6aXAgZm9ybWF0IHJlcXVpcmVzIHRoYXQgYXQgbGVhc3Qgb25lIGRpc3RhbmNlIGNvZGUgZXhpc3RzLFxuICAgKiBhbmQgdGhhdCBhdCBsZWFzdCBvbmUgYml0IHNob3VsZCBiZSBzZW50IGV2ZW4gaWYgdGhlcmUgaXMgb25seSBvbmVcbiAgICogcG9zc2libGUgY29kZS4gU28gdG8gYXZvaWQgc3BlY2lhbCBjaGVja3MgbGF0ZXIgb24gd2UgZm9yY2UgYXQgbGVhc3RcbiAgICogdHdvIGNvZGVzIG9mIG5vbiB6ZXJvIGZyZXF1ZW5jeS5cbiAgICovXG4gIHdoaWxlIChzLmhlYXBfbGVuIDwgMikge1xuICAgIG5vZGUgPSBzLmhlYXBbKytzLmhlYXBfbGVuXSA9IChtYXhfY29kZSA8IDIgPyArK21heF9jb2RlIDogMCk7XG4gICAgdHJlZVtub2RlICogMl0vKi5GcmVxKi8gPSAxO1xuICAgIHMuZGVwdGhbbm9kZV0gPSAwO1xuICAgIHMub3B0X2xlbi0tO1xuXG4gICAgaWYgKGhhc19zdHJlZSkge1xuICAgICAgcy5zdGF0aWNfbGVuIC09IHN0cmVlW25vZGUqMiArIDFdLyouTGVuKi87XG4gICAgfVxuICAgIC8qIG5vZGUgaXMgMCBvciAxIHNvIGl0IGRvZXMgbm90IGhhdmUgZXh0cmEgYml0cyAqL1xuICB9XG4gIGRlc2MubWF4X2NvZGUgPSBtYXhfY29kZTtcblxuICAvKiBUaGUgZWxlbWVudHMgaGVhcFtoZWFwX2xlbi8yKzEgLi4gaGVhcF9sZW5dIGFyZSBsZWF2ZXMgb2YgdGhlIHRyZWUsXG4gICAqIGVzdGFibGlzaCBzdWItaGVhcHMgb2YgaW5jcmVhc2luZyBsZW5ndGhzOlxuICAgKi9cbiAgZm9yIChuID0gKHMuaGVhcF9sZW4gPj4gMS8qaW50IC8yKi8pOyBuID49IDE7IG4tLSkgeyBwcWRvd25oZWFwKHMsIHRyZWUsIG4pOyB9XG5cbiAgLyogQ29uc3RydWN0IHRoZSBIdWZmbWFuIHRyZWUgYnkgcmVwZWF0ZWRseSBjb21iaW5pbmcgdGhlIGxlYXN0IHR3b1xuICAgKiBmcmVxdWVudCBub2Rlcy5cbiAgICovXG4gIG5vZGUgPSBlbGVtczsgICAgICAgICAgICAgIC8qIG5leHQgaW50ZXJuYWwgbm9kZSBvZiB0aGUgdHJlZSAqL1xuICBkbyB7XG4gICAgLy9wcXJlbW92ZShzLCB0cmVlLCBuKTsgIC8qIG4gPSBub2RlIG9mIGxlYXN0IGZyZXF1ZW5jeSAqL1xuICAgIC8qKiogcHFyZW1vdmUgKioqL1xuICAgIG4gPSBzLmhlYXBbMS8qU01BTExFU1QqL107XG4gICAgcy5oZWFwWzEvKlNNQUxMRVNUKi9dID0gcy5oZWFwW3MuaGVhcF9sZW4tLV07XG4gICAgcHFkb3duaGVhcChzLCB0cmVlLCAxLypTTUFMTEVTVCovKTtcbiAgICAvKioqL1xuXG4gICAgbSA9IHMuaGVhcFsxLypTTUFMTEVTVCovXTsgLyogbSA9IG5vZGUgb2YgbmV4dCBsZWFzdCBmcmVxdWVuY3kgKi9cblxuICAgIHMuaGVhcFstLXMuaGVhcF9tYXhdID0gbjsgLyoga2VlcCB0aGUgbm9kZXMgc29ydGVkIGJ5IGZyZXF1ZW5jeSAqL1xuICAgIHMuaGVhcFstLXMuaGVhcF9tYXhdID0gbTtcblxuICAgIC8qIENyZWF0ZSBhIG5ldyBub2RlIGZhdGhlciBvZiBuIGFuZCBtICovXG4gICAgdHJlZVtub2RlICogMl0vKi5GcmVxKi8gPSB0cmVlW24gKiAyXS8qLkZyZXEqLyArIHRyZWVbbSAqIDJdLyouRnJlcSovO1xuICAgIHMuZGVwdGhbbm9kZV0gPSAocy5kZXB0aFtuXSA+PSBzLmRlcHRoW21dID8gcy5kZXB0aFtuXSA6IHMuZGVwdGhbbV0pICsgMTtcbiAgICB0cmVlW24qMiArIDFdLyouRGFkKi8gPSB0cmVlW20qMiArIDFdLyouRGFkKi8gPSBub2RlO1xuXG4gICAgLyogYW5kIGluc2VydCB0aGUgbmV3IG5vZGUgaW4gdGhlIGhlYXAgKi9cbiAgICBzLmhlYXBbMS8qU01BTExFU1QqL10gPSBub2RlKys7XG4gICAgcHFkb3duaGVhcChzLCB0cmVlLCAxLypTTUFMTEVTVCovKTtcblxuICB9IHdoaWxlIChzLmhlYXBfbGVuID49IDIpO1xuXG4gIHMuaGVhcFstLXMuaGVhcF9tYXhdID0gcy5oZWFwWzEvKlNNQUxMRVNUKi9dO1xuXG4gIC8qIEF0IHRoaXMgcG9pbnQsIHRoZSBmaWVsZHMgZnJlcSBhbmQgZGFkIGFyZSBzZXQuIFdlIGNhbiBub3dcbiAgICogZ2VuZXJhdGUgdGhlIGJpdCBsZW5ndGhzLlxuICAgKi9cbiAgZ2VuX2JpdGxlbihzLCBkZXNjKTtcblxuICAvKiBUaGUgZmllbGQgbGVuIGlzIG5vdyBzZXQsIHdlIGNhbiBnZW5lcmF0ZSB0aGUgYml0IGNvZGVzICovXG4gIGdlbl9jb2Rlcyh0cmVlLCBtYXhfY29kZSwgcy5ibF9jb3VudCk7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTY2FuIGEgbGl0ZXJhbCBvciBkaXN0YW5jZSB0cmVlIHRvIGRldGVybWluZSB0aGUgZnJlcXVlbmNpZXMgb2YgdGhlIGNvZGVzXG4gKiBpbiB0aGUgYml0IGxlbmd0aCB0cmVlLlxuICovXG5mdW5jdGlvbiBzY2FuX3RyZWUocywgdHJlZSwgbWF4X2NvZGUpXG4vLyAgICBkZWZsYXRlX3N0YXRlICpzO1xuLy8gICAgY3RfZGF0YSAqdHJlZTsgICAvKiB0aGUgdHJlZSB0byBiZSBzY2FubmVkICovXG4vLyAgICBpbnQgbWF4X2NvZGU7ICAgIC8qIGFuZCBpdHMgbGFyZ2VzdCBjb2RlIG9mIG5vbiB6ZXJvIGZyZXF1ZW5jeSAqL1xue1xuICB2YXIgbjsgICAgICAgICAgICAgICAgICAgICAvKiBpdGVyYXRlcyBvdmVyIGFsbCB0cmVlIGVsZW1lbnRzICovXG4gIHZhciBwcmV2bGVuID0gLTE7ICAgICAgICAgIC8qIGxhc3QgZW1pdHRlZCBsZW5ndGggKi9cbiAgdmFyIGN1cmxlbjsgICAgICAgICAgICAgICAgLyogbGVuZ3RoIG9mIGN1cnJlbnQgY29kZSAqL1xuXG4gIHZhciBuZXh0bGVuID0gdHJlZVswKjIgKyAxXS8qLkxlbiovOyAvKiBsZW5ndGggb2YgbmV4dCBjb2RlICovXG5cbiAgdmFyIGNvdW50ID0gMDsgICAgICAgICAgICAgLyogcmVwZWF0IGNvdW50IG9mIHRoZSBjdXJyZW50IGNvZGUgKi9cbiAgdmFyIG1heF9jb3VudCA9IDc7ICAgICAgICAgLyogbWF4IHJlcGVhdCBjb3VudCAqL1xuICB2YXIgbWluX2NvdW50ID0gNDsgICAgICAgICAvKiBtaW4gcmVwZWF0IGNvdW50ICovXG5cbiAgaWYgKG5leHRsZW4gPT09IDApIHtcbiAgICBtYXhfY291bnQgPSAxMzg7XG4gICAgbWluX2NvdW50ID0gMztcbiAgfVxuICB0cmVlWyhtYXhfY29kZSsxKSoyICsgMV0vKi5MZW4qLyA9IDB4ZmZmZjsgLyogZ3VhcmQgKi9cblxuICBmb3IgKG4gPSAwOyBuIDw9IG1heF9jb2RlOyBuKyspIHtcbiAgICBjdXJsZW4gPSBuZXh0bGVuO1xuICAgIG5leHRsZW4gPSB0cmVlWyhuKzEpKjIgKyAxXS8qLkxlbiovO1xuXG4gICAgaWYgKCsrY291bnQgPCBtYXhfY291bnQgJiYgY3VybGVuID09PSBuZXh0bGVuKSB7XG4gICAgICBjb250aW51ZTtcblxuICAgIH0gZWxzZSBpZiAoY291bnQgPCBtaW5fY291bnQpIHtcbiAgICAgIHMuYmxfdHJlZVtjdXJsZW4gKiAyXS8qLkZyZXEqLyArPSBjb3VudDtcblxuICAgIH0gZWxzZSBpZiAoY3VybGVuICE9PSAwKSB7XG5cbiAgICAgIGlmIChjdXJsZW4gIT09IHByZXZsZW4pIHsgcy5ibF90cmVlW2N1cmxlbiAqIDJdLyouRnJlcSovKys7IH1cbiAgICAgIHMuYmxfdHJlZVtSRVBfM182KjJdLyouRnJlcSovKys7XG5cbiAgICB9IGVsc2UgaWYgKGNvdW50IDw9IDEwKSB7XG4gICAgICBzLmJsX3RyZWVbUkVQWl8zXzEwKjJdLyouRnJlcSovKys7XG5cbiAgICB9IGVsc2Uge1xuICAgICAgcy5ibF90cmVlW1JFUFpfMTFfMTM4KjJdLyouRnJlcSovKys7XG4gICAgfVxuXG4gICAgY291bnQgPSAwO1xuICAgIHByZXZsZW4gPSBjdXJsZW47XG5cbiAgICBpZiAobmV4dGxlbiA9PT0gMCkge1xuICAgICAgbWF4X2NvdW50ID0gMTM4O1xuICAgICAgbWluX2NvdW50ID0gMztcblxuICAgIH0gZWxzZSBpZiAoY3VybGVuID09PSBuZXh0bGVuKSB7XG4gICAgICBtYXhfY291bnQgPSA2O1xuICAgICAgbWluX2NvdW50ID0gMztcblxuICAgIH0gZWxzZSB7XG4gICAgICBtYXhfY291bnQgPSA3O1xuICAgICAgbWluX2NvdW50ID0gNDtcbiAgICB9XG4gIH1cbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNlbmQgYSBsaXRlcmFsIG9yIGRpc3RhbmNlIHRyZWUgaW4gY29tcHJlc3NlZCBmb3JtLCB1c2luZyB0aGUgY29kZXMgaW5cbiAqIGJsX3RyZWUuXG4gKi9cbmZ1bmN0aW9uIHNlbmRfdHJlZShzLCB0cmVlLCBtYXhfY29kZSlcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICBjdF9kYXRhICp0cmVlOyAvKiB0aGUgdHJlZSB0byBiZSBzY2FubmVkICovXG4vLyAgICBpbnQgbWF4X2NvZGU7ICAgICAgIC8qIGFuZCBpdHMgbGFyZ2VzdCBjb2RlIG9mIG5vbiB6ZXJvIGZyZXF1ZW5jeSAqL1xue1xuICB2YXIgbjsgICAgICAgICAgICAgICAgICAgICAvKiBpdGVyYXRlcyBvdmVyIGFsbCB0cmVlIGVsZW1lbnRzICovXG4gIHZhciBwcmV2bGVuID0gLTE7ICAgICAgICAgIC8qIGxhc3QgZW1pdHRlZCBsZW5ndGggKi9cbiAgdmFyIGN1cmxlbjsgICAgICAgICAgICAgICAgLyogbGVuZ3RoIG9mIGN1cnJlbnQgY29kZSAqL1xuXG4gIHZhciBuZXh0bGVuID0gdHJlZVswKjIgKyAxXS8qLkxlbiovOyAvKiBsZW5ndGggb2YgbmV4dCBjb2RlICovXG5cbiAgdmFyIGNvdW50ID0gMDsgICAgICAgICAgICAgLyogcmVwZWF0IGNvdW50IG9mIHRoZSBjdXJyZW50IGNvZGUgKi9cbiAgdmFyIG1heF9jb3VudCA9IDc7ICAgICAgICAgLyogbWF4IHJlcGVhdCBjb3VudCAqL1xuICB2YXIgbWluX2NvdW50ID0gNDsgICAgICAgICAvKiBtaW4gcmVwZWF0IGNvdW50ICovXG5cbiAgLyogdHJlZVttYXhfY29kZSsxXS5MZW4gPSAtMTsgKi8gIC8qIGd1YXJkIGFscmVhZHkgc2V0ICovXG4gIGlmIChuZXh0bGVuID09PSAwKSB7XG4gICAgbWF4X2NvdW50ID0gMTM4O1xuICAgIG1pbl9jb3VudCA9IDM7XG4gIH1cblxuICBmb3IgKG4gPSAwOyBuIDw9IG1heF9jb2RlOyBuKyspIHtcbiAgICBjdXJsZW4gPSBuZXh0bGVuO1xuICAgIG5leHRsZW4gPSB0cmVlWyhuKzEpKjIgKyAxXS8qLkxlbiovO1xuXG4gICAgaWYgKCsrY291bnQgPCBtYXhfY291bnQgJiYgY3VybGVuID09PSBuZXh0bGVuKSB7XG4gICAgICBjb250aW51ZTtcblxuICAgIH0gZWxzZSBpZiAoY291bnQgPCBtaW5fY291bnQpIHtcbiAgICAgIGRvIHsgc2VuZF9jb2RlKHMsIGN1cmxlbiwgcy5ibF90cmVlKTsgfSB3aGlsZSAoLS1jb3VudCAhPT0gMCk7XG5cbiAgICB9IGVsc2UgaWYgKGN1cmxlbiAhPT0gMCkge1xuICAgICAgaWYgKGN1cmxlbiAhPT0gcHJldmxlbikge1xuICAgICAgICBzZW5kX2NvZGUocywgY3VybGVuLCBzLmJsX3RyZWUpO1xuICAgICAgICBjb3VudC0tO1xuICAgICAgfVxuICAgICAgLy9Bc3NlcnQoY291bnQgPj0gMyAmJiBjb3VudCA8PSA2LCBcIiAzXzY/XCIpO1xuICAgICAgc2VuZF9jb2RlKHMsIFJFUF8zXzYsIHMuYmxfdHJlZSk7XG4gICAgICBzZW5kX2JpdHMocywgY291bnQtMywgMik7XG5cbiAgICB9IGVsc2UgaWYgKGNvdW50IDw9IDEwKSB7XG4gICAgICBzZW5kX2NvZGUocywgUkVQWl8zXzEwLCBzLmJsX3RyZWUpO1xuICAgICAgc2VuZF9iaXRzKHMsIGNvdW50LTMsIDMpO1xuXG4gICAgfSBlbHNlIHtcbiAgICAgIHNlbmRfY29kZShzLCBSRVBaXzExXzEzOCwgcy5ibF90cmVlKTtcbiAgICAgIHNlbmRfYml0cyhzLCBjb3VudC0xMSwgNyk7XG4gICAgfVxuXG4gICAgY291bnQgPSAwO1xuICAgIHByZXZsZW4gPSBjdXJsZW47XG4gICAgaWYgKG5leHRsZW4gPT09IDApIHtcbiAgICAgIG1heF9jb3VudCA9IDEzODtcbiAgICAgIG1pbl9jb3VudCA9IDM7XG5cbiAgICB9IGVsc2UgaWYgKGN1cmxlbiA9PT0gbmV4dGxlbikge1xuICAgICAgbWF4X2NvdW50ID0gNjtcbiAgICAgIG1pbl9jb3VudCA9IDM7XG5cbiAgICB9IGVsc2Uge1xuICAgICAgbWF4X2NvdW50ID0gNztcbiAgICAgIG1pbl9jb3VudCA9IDQ7XG4gICAgfVxuICB9XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb25zdHJ1Y3QgdGhlIEh1ZmZtYW4gdHJlZSBmb3IgdGhlIGJpdCBsZW5ndGhzIGFuZCByZXR1cm4gdGhlIGluZGV4IGluXG4gKiBibF9vcmRlciBvZiB0aGUgbGFzdCBiaXQgbGVuZ3RoIGNvZGUgdG8gc2VuZC5cbiAqL1xuZnVuY3Rpb24gYnVpbGRfYmxfdHJlZShzKSB7XG4gIHZhciBtYXhfYmxpbmRleDsgIC8qIGluZGV4IG9mIGxhc3QgYml0IGxlbmd0aCBjb2RlIG9mIG5vbiB6ZXJvIGZyZXEgKi9cblxuICAvKiBEZXRlcm1pbmUgdGhlIGJpdCBsZW5ndGggZnJlcXVlbmNpZXMgZm9yIGxpdGVyYWwgYW5kIGRpc3RhbmNlIHRyZWVzICovXG4gIHNjYW5fdHJlZShzLCBzLmR5bl9sdHJlZSwgcy5sX2Rlc2MubWF4X2NvZGUpO1xuICBzY2FuX3RyZWUocywgcy5keW5fZHRyZWUsIHMuZF9kZXNjLm1heF9jb2RlKTtcblxuICAvKiBCdWlsZCB0aGUgYml0IGxlbmd0aCB0cmVlOiAqL1xuICBidWlsZF90cmVlKHMsIHMuYmxfZGVzYyk7XG4gIC8qIG9wdF9sZW4gbm93IGluY2x1ZGVzIHRoZSBsZW5ndGggb2YgdGhlIHRyZWUgcmVwcmVzZW50YXRpb25zLCBleGNlcHRcbiAgICogdGhlIGxlbmd0aHMgb2YgdGhlIGJpdCBsZW5ndGhzIGNvZGVzIGFuZCB0aGUgNSs1KzQgYml0cyBmb3IgdGhlIGNvdW50cy5cbiAgICovXG5cbiAgLyogRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgYml0IGxlbmd0aCBjb2RlcyB0byBzZW5kLiBUaGUgcGt6aXAgZm9ybWF0XG4gICAqIHJlcXVpcmVzIHRoYXQgYXQgbGVhc3QgNCBiaXQgbGVuZ3RoIGNvZGVzIGJlIHNlbnQuIChhcHBub3RlLnR4dCBzYXlzXG4gICAqIDMgYnV0IHRoZSBhY3R1YWwgdmFsdWUgdXNlZCBpcyA0LilcbiAgICovXG4gIGZvciAobWF4X2JsaW5kZXggPSBCTF9DT0RFUy0xOyBtYXhfYmxpbmRleCA+PSAzOyBtYXhfYmxpbmRleC0tKSB7XG4gICAgaWYgKHMuYmxfdHJlZVtibF9vcmRlclttYXhfYmxpbmRleF0qMiArIDFdLyouTGVuKi8gIT09IDApIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICAvKiBVcGRhdGUgb3B0X2xlbiB0byBpbmNsdWRlIHRoZSBiaXQgbGVuZ3RoIHRyZWUgYW5kIGNvdW50cyAqL1xuICBzLm9wdF9sZW4gKz0gMyoobWF4X2JsaW5kZXgrMSkgKyA1KzUrNDtcbiAgLy9UcmFjZXYoKHN0ZGVyciwgXCJcXG5keW4gdHJlZXM6IGR5biAlbGQsIHN0YXQgJWxkXCIsXG4gIC8vICAgICAgICBzLT5vcHRfbGVuLCBzLT5zdGF0aWNfbGVuKSk7XG5cbiAgcmV0dXJuIG1heF9ibGluZGV4O1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2VuZCB0aGUgaGVhZGVyIGZvciBhIGJsb2NrIHVzaW5nIGR5bmFtaWMgSHVmZm1hbiB0cmVlczogdGhlIGNvdW50cywgdGhlXG4gKiBsZW5ndGhzIG9mIHRoZSBiaXQgbGVuZ3RoIGNvZGVzLCB0aGUgbGl0ZXJhbCB0cmVlIGFuZCB0aGUgZGlzdGFuY2UgdHJlZS5cbiAqIElOIGFzc2VydGlvbjogbGNvZGVzID49IDI1NywgZGNvZGVzID49IDEsIGJsY29kZXMgPj0gNC5cbiAqL1xuZnVuY3Rpb24gc2VuZF9hbGxfdHJlZXMocywgbGNvZGVzLCBkY29kZXMsIGJsY29kZXMpXG4vLyAgICBkZWZsYXRlX3N0YXRlICpzO1xuLy8gICAgaW50IGxjb2RlcywgZGNvZGVzLCBibGNvZGVzOyAvKiBudW1iZXIgb2YgY29kZXMgZm9yIGVhY2ggdHJlZSAqL1xue1xuICB2YXIgcmFuazsgICAgICAgICAgICAgICAgICAgIC8qIGluZGV4IGluIGJsX29yZGVyICovXG5cbiAgLy9Bc3NlcnQgKGxjb2RlcyA+PSAyNTcgJiYgZGNvZGVzID49IDEgJiYgYmxjb2RlcyA+PSA0LCBcIm5vdCBlbm91Z2ggY29kZXNcIik7XG4gIC8vQXNzZXJ0IChsY29kZXMgPD0gTF9DT0RFUyAmJiBkY29kZXMgPD0gRF9DT0RFUyAmJiBibGNvZGVzIDw9IEJMX0NPREVTLFxuICAvLyAgICAgICAgXCJ0b28gbWFueSBjb2Rlc1wiKTtcbiAgLy9UcmFjZXYoKHN0ZGVyciwgXCJcXG5ibCBjb3VudHM6IFwiKSk7XG4gIHNlbmRfYml0cyhzLCBsY29kZXMtMjU3LCA1KTsgLyogbm90ICsyNTUgYXMgc3RhdGVkIGluIGFwcG5vdGUudHh0ICovXG4gIHNlbmRfYml0cyhzLCBkY29kZXMtMSwgICA1KTtcbiAgc2VuZF9iaXRzKHMsIGJsY29kZXMtNCwgIDQpOyAvKiBub3QgLTMgYXMgc3RhdGVkIGluIGFwcG5vdGUudHh0ICovXG4gIGZvciAocmFuayA9IDA7IHJhbmsgPCBibGNvZGVzOyByYW5rKyspIHtcbiAgICAvL1RyYWNldigoc3RkZXJyLCBcIlxcbmJsIGNvZGUgJTJkIFwiLCBibF9vcmRlcltyYW5rXSkpO1xuICAgIHNlbmRfYml0cyhzLCBzLmJsX3RyZWVbYmxfb3JkZXJbcmFua10qMiArIDFdLyouTGVuKi8sIDMpO1xuICB9XG4gIC8vVHJhY2V2KChzdGRlcnIsIFwiXFxuYmwgdHJlZTogc2VudCAlbGRcIiwgcy0+Yml0c19zZW50KSk7XG5cbiAgc2VuZF90cmVlKHMsIHMuZHluX2x0cmVlLCBsY29kZXMtMSk7IC8qIGxpdGVyYWwgdHJlZSAqL1xuICAvL1RyYWNldigoc3RkZXJyLCBcIlxcbmxpdCB0cmVlOiBzZW50ICVsZFwiLCBzLT5iaXRzX3NlbnQpKTtcblxuICBzZW5kX3RyZWUocywgcy5keW5fZHRyZWUsIGRjb2Rlcy0xKTsgLyogZGlzdGFuY2UgdHJlZSAqL1xuICAvL1RyYWNldigoc3RkZXJyLCBcIlxcbmRpc3QgdHJlZTogc2VudCAlbGRcIiwgcy0+Yml0c19zZW50KSk7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDaGVjayBpZiB0aGUgZGF0YSB0eXBlIGlzIFRFWFQgb3IgQklOQVJZLCB1c2luZyB0aGUgZm9sbG93aW5nIGFsZ29yaXRobTpcbiAqIC0gVEVYVCBpZiB0aGUgdHdvIGNvbmRpdGlvbnMgYmVsb3cgYXJlIHNhdGlzZmllZDpcbiAqICAgIGEpIFRoZXJlIGFyZSBubyBub24tcG9ydGFibGUgY29udHJvbCBjaGFyYWN0ZXJzIGJlbG9uZ2luZyB0byB0aGVcbiAqICAgICAgIFwiYmxhY2sgbGlzdFwiICgwLi42LCAxNC4uMjUsIDI4Li4zMSkuXG4gKiAgICBiKSBUaGVyZSBpcyBhdCBsZWFzdCBvbmUgcHJpbnRhYmxlIGNoYXJhY3RlciBiZWxvbmdpbmcgdG8gdGhlXG4gKiAgICAgICBcIndoaXRlIGxpc3RcIiAoOSB7VEFCfSwgMTAge0xGfSwgMTMge0NSfSwgMzIuLjI1NSkuXG4gKiAtIEJJTkFSWSBvdGhlcndpc2UuXG4gKiAtIFRoZSBmb2xsb3dpbmcgcGFydGlhbGx5LXBvcnRhYmxlIGNvbnRyb2wgY2hhcmFjdGVycyBmb3JtIGFcbiAqICAgXCJncmF5IGxpc3RcIiB0aGF0IGlzIGlnbm9yZWQgaW4gdGhpcyBkZXRlY3Rpb24gYWxnb3JpdGhtOlxuICogICAoNyB7QkVMfSwgOCB7QlN9LCAxMSB7VlR9LCAxMiB7RkZ9LCAyNiB7U1VCfSwgMjcge0VTQ30pLlxuICogSU4gYXNzZXJ0aW9uOiB0aGUgZmllbGRzIEZyZXEgb2YgZHluX2x0cmVlIGFyZSBzZXQuXG4gKi9cbmZ1bmN0aW9uIGRldGVjdF9kYXRhX3R5cGUocykge1xuICAvKiBibGFja19tYXNrIGlzIHRoZSBiaXQgbWFzayBvZiBibGFjay1saXN0ZWQgYnl0ZXNcbiAgICogc2V0IGJpdHMgMC4uNiwgMTQuLjI1LCBhbmQgMjguLjMxXG4gICAqIDB4ZjNmZmMwN2YgPSBiaW5hcnkgMTExMTAwMTExMTExMTExMTExMDAwMDAwMDExMTExMTFcbiAgICovXG4gIHZhciBibGFja19tYXNrID0gMHhmM2ZmYzA3ZjtcbiAgdmFyIG47XG5cbiAgLyogQ2hlY2sgZm9yIG5vbi10ZXh0dWFsIChcImJsYWNrLWxpc3RlZFwiKSBieXRlcy4gKi9cbiAgZm9yIChuID0gMDsgbiA8PSAzMTsgbisrLCBibGFja19tYXNrID4+Pj0gMSkge1xuICAgIGlmICgoYmxhY2tfbWFzayAmIDEpICYmIChzLmR5bl9sdHJlZVtuKjJdLyouRnJlcSovICE9PSAwKSkge1xuICAgICAgcmV0dXJuIFpfQklOQVJZO1xuICAgIH1cbiAgfVxuXG4gIC8qIENoZWNrIGZvciB0ZXh0dWFsIChcIndoaXRlLWxpc3RlZFwiKSBieXRlcy4gKi9cbiAgaWYgKHMuZHluX2x0cmVlWzkgKiAyXS8qLkZyZXEqLyAhPT0gMCB8fCBzLmR5bl9sdHJlZVsxMCAqIDJdLyouRnJlcSovICE9PSAwIHx8XG4gICAgICBzLmR5bl9sdHJlZVsxMyAqIDJdLyouRnJlcSovICE9PSAwKSB7XG4gICAgcmV0dXJuIFpfVEVYVDtcbiAgfVxuICBmb3IgKG4gPSAzMjsgbiA8IExJVEVSQUxTOyBuKyspIHtcbiAgICBpZiAocy5keW5fbHRyZWVbbiAqIDJdLyouRnJlcSovICE9PSAwKSB7XG4gICAgICByZXR1cm4gWl9URVhUO1xuICAgIH1cbiAgfVxuXG4gIC8qIFRoZXJlIGFyZSBubyBcImJsYWNrLWxpc3RlZFwiIG9yIFwid2hpdGUtbGlzdGVkXCIgYnl0ZXM6XG4gICAqIHRoaXMgc3RyZWFtIGVpdGhlciBpcyBlbXB0eSBvciBoYXMgdG9sZXJhdGVkIChcImdyYXktbGlzdGVkXCIpIGJ5dGVzIG9ubHkuXG4gICAqL1xuICByZXR1cm4gWl9CSU5BUlk7XG59XG5cblxudmFyIHN0YXRpY19pbml0X2RvbmUgPSBmYWxzZTtcblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBJbml0aWFsaXplIHRoZSB0cmVlIGRhdGEgc3RydWN0dXJlcyBmb3IgYSBuZXcgemxpYiBzdHJlYW0uXG4gKi9cbmZ1bmN0aW9uIF90cl9pbml0KHMpXG57XG5cbiAgaWYgKCFzdGF0aWNfaW5pdF9kb25lKSB7XG4gICAgdHJfc3RhdGljX2luaXQoKTtcbiAgICBzdGF0aWNfaW5pdF9kb25lID0gdHJ1ZTtcbiAgfVxuXG4gIHMubF9kZXNjICA9IG5ldyBUcmVlRGVzYyhzLmR5bl9sdHJlZSwgc3RhdGljX2xfZGVzYyk7XG4gIHMuZF9kZXNjICA9IG5ldyBUcmVlRGVzYyhzLmR5bl9kdHJlZSwgc3RhdGljX2RfZGVzYyk7XG4gIHMuYmxfZGVzYyA9IG5ldyBUcmVlRGVzYyhzLmJsX3RyZWUsIHN0YXRpY19ibF9kZXNjKTtcblxuICBzLmJpX2J1ZiA9IDA7XG4gIHMuYmlfdmFsaWQgPSAwO1xuXG4gIC8qIEluaXRpYWxpemUgdGhlIGZpcnN0IGJsb2NrIG9mIHRoZSBmaXJzdCBmaWxlOiAqL1xuICBpbml0X2Jsb2NrKHMpO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2VuZCBhIHN0b3JlZCBibG9ja1xuICovXG5mdW5jdGlvbiBfdHJfc3RvcmVkX2Jsb2NrKHMsIGJ1Ziwgc3RvcmVkX2xlbiwgbGFzdClcbi8vRGVmbGF0ZVN0YXRlICpzO1xuLy9jaGFyZiAqYnVmOyAgICAgICAvKiBpbnB1dCBibG9jayAqL1xuLy91bGcgc3RvcmVkX2xlbjsgICAvKiBsZW5ndGggb2YgaW5wdXQgYmxvY2sgKi9cbi8vaW50IGxhc3Q7ICAgICAgICAgLyogb25lIGlmIHRoaXMgaXMgdGhlIGxhc3QgYmxvY2sgZm9yIGEgZmlsZSAqL1xue1xuICBzZW5kX2JpdHMocywgKFNUT1JFRF9CTE9DSzw8MSkrKGxhc3QgPyAxIDogMCksIDMpOyAgICAvKiBzZW5kIGJsb2NrIHR5cGUgKi9cbiAgY29weV9ibG9jayhzLCBidWYsIHN0b3JlZF9sZW4sIHRydWUpOyAvKiB3aXRoIGhlYWRlciAqL1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2VuZCBvbmUgZW1wdHkgc3RhdGljIGJsb2NrIHRvIGdpdmUgZW5vdWdoIGxvb2thaGVhZCBmb3IgaW5mbGF0ZS5cbiAqIFRoaXMgdGFrZXMgMTAgYml0cywgb2Ygd2hpY2ggNyBtYXkgcmVtYWluIGluIHRoZSBiaXQgYnVmZmVyLlxuICovXG5mdW5jdGlvbiBfdHJfYWxpZ24ocykge1xuICBzZW5kX2JpdHMocywgU1RBVElDX1RSRUVTPDwxLCAzKTtcbiAgc2VuZF9jb2RlKHMsIEVORF9CTE9DSywgc3RhdGljX2x0cmVlKTtcbiAgYmlfZmx1c2gocyk7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBEZXRlcm1pbmUgdGhlIGJlc3QgZW5jb2RpbmcgZm9yIHRoZSBjdXJyZW50IGJsb2NrOiBkeW5hbWljIHRyZWVzLCBzdGF0aWNcbiAqIHRyZWVzIG9yIHN0b3JlLCBhbmQgb3V0cHV0IHRoZSBlbmNvZGVkIGJsb2NrIHRvIHRoZSB6aXAgZmlsZS5cbiAqL1xuZnVuY3Rpb24gX3RyX2ZsdXNoX2Jsb2NrKHMsIGJ1Ziwgc3RvcmVkX2xlbiwgbGFzdClcbi8vRGVmbGF0ZVN0YXRlICpzO1xuLy9jaGFyZiAqYnVmOyAgICAgICAvKiBpbnB1dCBibG9jaywgb3IgTlVMTCBpZiB0b28gb2xkICovXG4vL3VsZyBzdG9yZWRfbGVuOyAgIC8qIGxlbmd0aCBvZiBpbnB1dCBibG9jayAqL1xuLy9pbnQgbGFzdDsgICAgICAgICAvKiBvbmUgaWYgdGhpcyBpcyB0aGUgbGFzdCBibG9jayBmb3IgYSBmaWxlICovXG57XG4gIHZhciBvcHRfbGVuYiwgc3RhdGljX2xlbmI7ICAvKiBvcHRfbGVuIGFuZCBzdGF0aWNfbGVuIGluIGJ5dGVzICovXG4gIHZhciBtYXhfYmxpbmRleCA9IDA7ICAgICAgICAvKiBpbmRleCBvZiBsYXN0IGJpdCBsZW5ndGggY29kZSBvZiBub24gemVybyBmcmVxICovXG5cbiAgLyogQnVpbGQgdGhlIEh1ZmZtYW4gdHJlZXMgdW5sZXNzIGEgc3RvcmVkIGJsb2NrIGlzIGZvcmNlZCAqL1xuICBpZiAocy5sZXZlbCA+IDApIHtcblxuICAgIC8qIENoZWNrIGlmIHRoZSBmaWxlIGlzIGJpbmFyeSBvciB0ZXh0ICovXG4gICAgaWYgKHMuc3RybS5kYXRhX3R5cGUgPT09IFpfVU5LTk9XTikge1xuICAgICAgcy5zdHJtLmRhdGFfdHlwZSA9IGRldGVjdF9kYXRhX3R5cGUocyk7XG4gICAgfVxuXG4gICAgLyogQ29uc3RydWN0IHRoZSBsaXRlcmFsIGFuZCBkaXN0YW5jZSB0cmVlcyAqL1xuICAgIGJ1aWxkX3RyZWUocywgcy5sX2Rlc2MpO1xuICAgIC8vIFRyYWNldigoc3RkZXJyLCBcIlxcbmxpdCBkYXRhOiBkeW4gJWxkLCBzdGF0ICVsZFwiLCBzLT5vcHRfbGVuLFxuICAgIC8vICAgICAgICBzLT5zdGF0aWNfbGVuKSk7XG5cbiAgICBidWlsZF90cmVlKHMsIHMuZF9kZXNjKTtcbiAgICAvLyBUcmFjZXYoKHN0ZGVyciwgXCJcXG5kaXN0IGRhdGE6IGR5biAlbGQsIHN0YXQgJWxkXCIsIHMtPm9wdF9sZW4sXG4gICAgLy8gICAgICAgIHMtPnN0YXRpY19sZW4pKTtcbiAgICAvKiBBdCB0aGlzIHBvaW50LCBvcHRfbGVuIGFuZCBzdGF0aWNfbGVuIGFyZSB0aGUgdG90YWwgYml0IGxlbmd0aHMgb2ZcbiAgICAgKiB0aGUgY29tcHJlc3NlZCBibG9jayBkYXRhLCBleGNsdWRpbmcgdGhlIHRyZWUgcmVwcmVzZW50YXRpb25zLlxuICAgICAqL1xuXG4gICAgLyogQnVpbGQgdGhlIGJpdCBsZW5ndGggdHJlZSBmb3IgdGhlIGFib3ZlIHR3byB0cmVlcywgYW5kIGdldCB0aGUgaW5kZXhcbiAgICAgKiBpbiBibF9vcmRlciBvZiB0aGUgbGFzdCBiaXQgbGVuZ3RoIGNvZGUgdG8gc2VuZC5cbiAgICAgKi9cbiAgICBtYXhfYmxpbmRleCA9IGJ1aWxkX2JsX3RyZWUocyk7XG5cbiAgICAvKiBEZXRlcm1pbmUgdGhlIGJlc3QgZW5jb2RpbmcuIENvbXB1dGUgdGhlIGJsb2NrIGxlbmd0aHMgaW4gYnl0ZXMuICovXG4gICAgb3B0X2xlbmIgPSAocy5vcHRfbGVuKzMrNykgPj4+IDM7XG4gICAgc3RhdGljX2xlbmIgPSAocy5zdGF0aWNfbGVuKzMrNykgPj4+IDM7XG5cbiAgICAvLyBUcmFjZXYoKHN0ZGVyciwgXCJcXG5vcHQgJWx1KCVsdSkgc3RhdCAlbHUoJWx1KSBzdG9yZWQgJWx1IGxpdCAldSBcIixcbiAgICAvLyAgICAgICAgb3B0X2xlbmIsIHMtPm9wdF9sZW4sIHN0YXRpY19sZW5iLCBzLT5zdGF0aWNfbGVuLCBzdG9yZWRfbGVuLFxuICAgIC8vICAgICAgICBzLT5sYXN0X2xpdCkpO1xuXG4gICAgaWYgKHN0YXRpY19sZW5iIDw9IG9wdF9sZW5iKSB7IG9wdF9sZW5iID0gc3RhdGljX2xlbmI7IH1cblxuICB9IGVsc2Uge1xuICAgIC8vIEFzc2VydChidWYgIT0gKGNoYXIqKTAsIFwibG9zdCBidWZcIik7XG4gICAgb3B0X2xlbmIgPSBzdGF0aWNfbGVuYiA9IHN0b3JlZF9sZW4gKyA1OyAvKiBmb3JjZSBhIHN0b3JlZCBibG9jayAqL1xuICB9XG5cbiAgaWYgKChzdG9yZWRfbGVuKzQgPD0gb3B0X2xlbmIpICYmIChidWYgIT09IC0xKSkge1xuICAgIC8qIDQ6IHR3byB3b3JkcyBmb3IgdGhlIGxlbmd0aHMgKi9cblxuICAgIC8qIFRoZSB0ZXN0IGJ1ZiAhPSBOVUxMIGlzIG9ubHkgbmVjZXNzYXJ5IGlmIExJVF9CVUZTSVpFID4gV1NJWkUuXG4gICAgICogT3RoZXJ3aXNlIHdlIGNhbid0IGhhdmUgcHJvY2Vzc2VkIG1vcmUgdGhhbiBXU0laRSBpbnB1dCBieXRlcyBzaW5jZVxuICAgICAqIHRoZSBsYXN0IGJsb2NrIGZsdXNoLCBiZWNhdXNlIGNvbXByZXNzaW9uIHdvdWxkIGhhdmUgYmVlblxuICAgICAqIHN1Y2Nlc3NmdWwuIElmIExJVF9CVUZTSVpFIDw9IFdTSVpFLCBpdCBpcyBuZXZlciB0b28gbGF0ZSB0b1xuICAgICAqIHRyYW5zZm9ybSBhIGJsb2NrIGludG8gYSBzdG9yZWQgYmxvY2suXG4gICAgICovXG4gICAgX3RyX3N0b3JlZF9ibG9jayhzLCBidWYsIHN0b3JlZF9sZW4sIGxhc3QpO1xuXG4gIH0gZWxzZSBpZiAocy5zdHJhdGVneSA9PT0gWl9GSVhFRCB8fCBzdGF0aWNfbGVuYiA9PT0gb3B0X2xlbmIpIHtcblxuICAgIHNlbmRfYml0cyhzLCAoU1RBVElDX1RSRUVTPDwxKSArIChsYXN0ID8gMSA6IDApLCAzKTtcbiAgICBjb21wcmVzc19ibG9jayhzLCBzdGF0aWNfbHRyZWUsIHN0YXRpY19kdHJlZSk7XG5cbiAgfSBlbHNlIHtcbiAgICBzZW5kX2JpdHMocywgKERZTl9UUkVFUzw8MSkgKyAobGFzdCA/IDEgOiAwKSwgMyk7XG4gICAgc2VuZF9hbGxfdHJlZXMocywgcy5sX2Rlc2MubWF4X2NvZGUrMSwgcy5kX2Rlc2MubWF4X2NvZGUrMSwgbWF4X2JsaW5kZXgrMSk7XG4gICAgY29tcHJlc3NfYmxvY2socywgcy5keW5fbHRyZWUsIHMuZHluX2R0cmVlKTtcbiAgfVxuICAvLyBBc3NlcnQgKHMtPmNvbXByZXNzZWRfbGVuID09IHMtPmJpdHNfc2VudCwgXCJiYWQgY29tcHJlc3NlZCBzaXplXCIpO1xuICAvKiBUaGUgYWJvdmUgY2hlY2sgaXMgbWFkZSBtb2QgMl4zMiwgZm9yIGZpbGVzIGxhcmdlciB0aGFuIDUxMiBNQlxuICAgKiBhbmQgdUxvbmcgaW1wbGVtZW50ZWQgb24gMzIgYml0cy5cbiAgICovXG4gIGluaXRfYmxvY2socyk7XG5cbiAgaWYgKGxhc3QpIHtcbiAgICBiaV93aW5kdXAocyk7XG4gIH1cbiAgLy8gVHJhY2V2KChzdGRlcnIsXCJcXG5jb21wcmxlbiAlbHUoJWx1KSBcIiwgcy0+Y29tcHJlc3NlZF9sZW4+PjMsXG4gIC8vICAgICAgIHMtPmNvbXByZXNzZWRfbGVuLTcqbGFzdCkpO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNhdmUgdGhlIG1hdGNoIGluZm8gYW5kIHRhbGx5IHRoZSBmcmVxdWVuY3kgY291bnRzLiBSZXR1cm4gdHJ1ZSBpZlxuICogdGhlIGN1cnJlbnQgYmxvY2sgbXVzdCBiZSBmbHVzaGVkLlxuICovXG5mdW5jdGlvbiBfdHJfdGFsbHkocywgZGlzdCwgbGMpXG4vLyAgICBkZWZsYXRlX3N0YXRlICpzO1xuLy8gICAgdW5zaWduZWQgZGlzdDsgIC8qIGRpc3RhbmNlIG9mIG1hdGNoZWQgc3RyaW5nICovXG4vLyAgICB1bnNpZ25lZCBsYzsgICAgLyogbWF0Y2ggbGVuZ3RoLU1JTl9NQVRDSCBvciB1bm1hdGNoZWQgY2hhciAoaWYgZGlzdD09MCkgKi9cbntcbiAgLy92YXIgb3V0X2xlbmd0aCwgaW5fbGVuZ3RoLCBkY29kZTtcblxuICBzLnBlbmRpbmdfYnVmW3MuZF9idWYgKyBzLmxhc3RfbGl0ICogMl0gICAgID0gKGRpc3QgPj4+IDgpICYgMHhmZjtcbiAgcy5wZW5kaW5nX2J1ZltzLmRfYnVmICsgcy5sYXN0X2xpdCAqIDIgKyAxXSA9IGRpc3QgJiAweGZmO1xuXG4gIHMucGVuZGluZ19idWZbcy5sX2J1ZiArIHMubGFzdF9saXRdID0gbGMgJiAweGZmO1xuICBzLmxhc3RfbGl0Kys7XG5cbiAgaWYgKGRpc3QgPT09IDApIHtcbiAgICAvKiBsYyBpcyB0aGUgdW5tYXRjaGVkIGNoYXIgKi9cbiAgICBzLmR5bl9sdHJlZVtsYyoyXS8qLkZyZXEqLysrO1xuICB9IGVsc2Uge1xuICAgIHMubWF0Y2hlcysrO1xuICAgIC8qIEhlcmUsIGxjIGlzIHRoZSBtYXRjaCBsZW5ndGggLSBNSU5fTUFUQ0ggKi9cbiAgICBkaXN0LS07ICAgICAgICAgICAgIC8qIGRpc3QgPSBtYXRjaCBkaXN0YW5jZSAtIDEgKi9cbiAgICAvL0Fzc2VydCgodXNoKWRpc3QgPCAodXNoKU1BWF9ESVNUKHMpICYmXG4gICAgLy8gICAgICAgKHVzaClsYyA8PSAodXNoKShNQVhfTUFUQ0gtTUlOX01BVENIKSAmJlxuICAgIC8vICAgICAgICh1c2gpZF9jb2RlKGRpc3QpIDwgKHVzaClEX0NPREVTLCAgXCJfdHJfdGFsbHk6IGJhZCBtYXRjaFwiKTtcblxuICAgIHMuZHluX2x0cmVlWyhfbGVuZ3RoX2NvZGVbbGNdK0xJVEVSQUxTKzEpICogMl0vKi5GcmVxKi8rKztcbiAgICBzLmR5bl9kdHJlZVtkX2NvZGUoZGlzdCkgKiAyXS8qLkZyZXEqLysrO1xuICB9XG5cbi8vICghKSBUaGlzIGJsb2NrIGlzIGRpc2FibGVkIGluIHpsaWIgZGVmYWlsdHMsXG4vLyBkb24ndCBlbmFibGUgaXQgZm9yIGJpbmFyeSBjb21wYXRpYmlsaXR5XG5cbi8vI2lmZGVmIFRSVU5DQVRFX0JMT0NLXG4vLyAgLyogVHJ5IHRvIGd1ZXNzIGlmIGl0IGlzIHByb2ZpdGFibGUgdG8gc3RvcCB0aGUgY3VycmVudCBibG9jayBoZXJlICovXG4vLyAgaWYgKChzLmxhc3RfbGl0ICYgMHgxZmZmKSA9PT0gMCAmJiBzLmxldmVsID4gMikge1xuLy8gICAgLyogQ29tcHV0ZSBhbiB1cHBlciBib3VuZCBmb3IgdGhlIGNvbXByZXNzZWQgbGVuZ3RoICovXG4vLyAgICBvdXRfbGVuZ3RoID0gcy5sYXN0X2xpdCo4O1xuLy8gICAgaW5fbGVuZ3RoID0gcy5zdHJzdGFydCAtIHMuYmxvY2tfc3RhcnQ7XG4vL1xuLy8gICAgZm9yIChkY29kZSA9IDA7IGRjb2RlIDwgRF9DT0RFUzsgZGNvZGUrKykge1xuLy8gICAgICBvdXRfbGVuZ3RoICs9IHMuZHluX2R0cmVlW2Rjb2RlKjJdLyouRnJlcSovICogKDUgKyBleHRyYV9kYml0c1tkY29kZV0pO1xuLy8gICAgfVxuLy8gICAgb3V0X2xlbmd0aCA+Pj49IDM7XG4vLyAgICAvL1RyYWNldigoc3RkZXJyLFwiXFxubGFzdF9saXQgJXUsIGluICVsZCwgb3V0IH4lbGQoJWxkJSUpIFwiLFxuLy8gICAgLy8gICAgICAgcy0+bGFzdF9saXQsIGluX2xlbmd0aCwgb3V0X2xlbmd0aCxcbi8vICAgIC8vICAgICAgIDEwMEwgLSBvdXRfbGVuZ3RoKjEwMEwvaW5fbGVuZ3RoKSk7XG4vLyAgICBpZiAocy5tYXRjaGVzIDwgKHMubGFzdF9saXQ+PjEpLyppbnQgLzIqLyAmJiBvdXRfbGVuZ3RoIDwgKGluX2xlbmd0aD4+MSkvKmludCAvMiovKSB7XG4vLyAgICAgIHJldHVybiB0cnVlO1xuLy8gICAgfVxuLy8gIH1cbi8vI2VuZGlmXG5cbiAgcmV0dXJuIChzLmxhc3RfbGl0ID09PSBzLmxpdF9idWZzaXplLTEpO1xuICAvKiBXZSBhdm9pZCBlcXVhbGl0eSB3aXRoIGxpdF9idWZzaXplIGJlY2F1c2Ugb2Ygd3JhcGFyb3VuZCBhdCA2NEtcbiAgICogb24gMTYgYml0IG1hY2hpbmVzIGFuZCBiZWNhdXNlIHN0b3JlZCBibG9ja3MgYXJlIHJlc3RyaWN0ZWQgdG9cbiAgICogNjRLLTEgYnl0ZXMuXG4gICAqL1xufVxuXG5leHBvcnRzLl90cl9pbml0ICA9IF90cl9pbml0O1xuZXhwb3J0cy5fdHJfc3RvcmVkX2Jsb2NrID0gX3RyX3N0b3JlZF9ibG9jaztcbmV4cG9ydHMuX3RyX2ZsdXNoX2Jsb2NrICA9IF90cl9mbHVzaF9ibG9jaztcbmV4cG9ydHMuX3RyX3RhbGx5ID0gX3RyX3RhbGx5O1xuZXhwb3J0cy5fdHJfYWxpZ24gPSBfdHJfYWxpZ247IiwiJ3VzZSBzdHJpY3QnO1xuXG5cbmZ1bmN0aW9uIFpTdHJlYW0oKSB7XG4gIC8qIG5leHQgaW5wdXQgYnl0ZSAqL1xuICB0aGlzLmlucHV0ID0gbnVsbDsgLy8gSlMgc3BlY2lmaWMsIGJlY2F1c2Ugd2UgaGF2ZSBubyBwb2ludGVyc1xuICB0aGlzLm5leHRfaW4gPSAwO1xuICAvKiBudW1iZXIgb2YgYnl0ZXMgYXZhaWxhYmxlIGF0IGlucHV0ICovXG4gIHRoaXMuYXZhaWxfaW4gPSAwO1xuICAvKiB0b3RhbCBudW1iZXIgb2YgaW5wdXQgYnl0ZXMgcmVhZCBzbyBmYXIgKi9cbiAgdGhpcy50b3RhbF9pbiA9IDA7XG4gIC8qIG5leHQgb3V0cHV0IGJ5dGUgc2hvdWxkIGJlIHB1dCB0aGVyZSAqL1xuICB0aGlzLm91dHB1dCA9IG51bGw7IC8vIEpTIHNwZWNpZmljLCBiZWNhdXNlIHdlIGhhdmUgbm8gcG9pbnRlcnNcbiAgdGhpcy5uZXh0X291dCA9IDA7XG4gIC8qIHJlbWFpbmluZyBmcmVlIHNwYWNlIGF0IG91dHB1dCAqL1xuICB0aGlzLmF2YWlsX291dCA9IDA7XG4gIC8qIHRvdGFsIG51bWJlciBvZiBieXRlcyBvdXRwdXQgc28gZmFyICovXG4gIHRoaXMudG90YWxfb3V0ID0gMDtcbiAgLyogbGFzdCBlcnJvciBtZXNzYWdlLCBOVUxMIGlmIG5vIGVycm9yICovXG4gIHRoaXMubXNnID0gJycvKlpfTlVMTCovO1xuICAvKiBub3QgdmlzaWJsZSBieSBhcHBsaWNhdGlvbnMgKi9cbiAgdGhpcy5zdGF0ZSA9IG51bGw7XG4gIC8qIGJlc3QgZ3Vlc3MgYWJvdXQgdGhlIGRhdGEgdHlwZTogYmluYXJ5IG9yIHRleHQgKi9cbiAgdGhpcy5kYXRhX3R5cGUgPSAyLypaX1VOS05PV04qLztcbiAgLyogYWRsZXIzMiB2YWx1ZSBvZiB0aGUgdW5jb21wcmVzc2VkIGRhdGEgKi9cbiAgdGhpcy5hZGxlciA9IDA7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gWlN0cmVhbTsiLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGFycmF5RXF1YWxzKGFycmF5KSB7XG4gICAgLy8gaWYgdGhlIG90aGVyIGFycmF5IGlzIGEgZmFsc3kgdmFsdWUsIHJldHVyblxuICAgIGlmICghYXJyYXkpXG4gICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgIC8vIGNvbXBhcmUgbGVuZ3RocyAtIGNhbiBzYXZlIGEgbG90IG9mIHRpbWVcbiAgICBpZiAodGhpcy5sZW5ndGggIT0gYXJyYXkubGVuZ3RoKVxuICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICBmb3IgKHZhciBpID0gMCwgbCA9IHRoaXMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgICBpZiAodGhpc1tpXSBpbnN0YW5jZW9mIEFycmF5ICYmIGFycmF5W2ldIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICAgICAgICAgIC8vIHJlY3Vyc2UgaW50byB0aGUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgICAgaWYgKCFhcnJheUVxdWFscy5hcHBseSh0aGlzW2ldLCBbYXJyYXlbaV1dKSlcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH0gZWxzZSBpZiAodGhpc1tpXSAhPSBhcnJheVtpXSkge1xuICAgICAgICAgICAgLy8gV2FybmluZyAtIHR3byBkaWZmZXJlbnQgb2JqZWN0IGluc3RhbmNlcyB3aWxsIG5ldmVyIGJlIGVxdWFsOlxuICAgICAgICAgICAgLy8ge3g6MjB9ICE9IHt4OjIwfVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuXG4iLCJleHBvcnRzLkludGVyb3AgPSByZXF1aXJlKCcuL2ludGVyb3AnKTtcbiIsInZhciB0cmFuc2Zvcm0gPSByZXF1aXJlKCcuL3RyYW5zZm9ybScpO1xudmFyIGFycmF5RXF1YWxzID0gcmVxdWlyZSgnLi9hcnJheS1lcXVhbHMnKTtcblxuZnVuY3Rpb24gSW50ZXJvcCgpIHsgfVxubW9kdWxlLmV4cG9ydHMgPSBJbnRlcm9wO1xuXG4vKipcbiAqIFRoaXMgbWFwIGhvbGRzIHRoZSBtb3N0IHJlY2VudCBQbGFuIEEgb2ZmZXIvYW5zd2VyIFNEUCB0aGF0IHdhcyBjb252ZXJ0ZWRcbiAqIHRvIFBsYW4gQiwgd2l0aCB0aGUgU0RQIHR5cGUgKCdvZmZlcicgb3IgJ2Fuc3dlcicpIGFzIGtleXMgYW5kIHRoZSBTRFBcbiAqIHN0cmluZyBhcyB2YWx1ZXMuXG4gKlxuICogQHR5cGUge3t9fVxuICovXG52YXIgY2FjaGUgPSB7fTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCB0cmFuc2Zvcm1zIGEgUGxhbiBBIFNEUCB0byBhbiBlcXVpdmFsZW50IFBsYW4gQiBTRFAuIEFcbiAqIFBlZXJDb25uZWN0aW9uIHdyYXBwZXIgdHJhbnNmb3JtcyB0aGUgU0RQIHRvIFBsYW4gQiBiZWZvcmUgcGFzc2luZyBpdCB0byB0aGVcbiAqIGFwcGxpY2F0aW9uLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuSW50ZXJvcC5wcm90b3R5cGUudG9QbGFuQiA9IGZ1bmN0aW9uKGRlc2MpIHtcblxuICAgIC8vI3JlZ2lvbiBQcmVsaW1pbmFyeSBpbnB1dCB2YWxpZGF0aW9uLlxuXG4gICAgaWYgKHR5cGVvZiBkZXNjICE9PSAnb2JqZWN0JyB8fCBkZXNjID09PSBudWxsIHx8XG4gICAgICAgIHR5cGVvZiBkZXNjLnNkcCAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdBbiBlbXB0eSBkZXNjcmlwdGlvbiB3YXMgcGFzc2VkIGFzIGFuIGFyZ3VtZW50LicpO1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICAvLyBPYmplY3RpZnkgdGhlIFNEUCBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbi5cbiAgICB2YXIgc2Vzc2lvbiA9IHRyYW5zZm9ybS5wYXJzZShkZXNjLnNkcCk7XG5cbiAgICAvLyBJZiB0aGUgU0RQIGNvbnRhaW5zIG5vIG1lZGlhLCB0aGVyZSdzIG5vdGhpbmcgdG8gdHJhbnNmb3JtLlxuICAgIGlmICh0eXBlb2Ygc2Vzc2lvbi5tZWRpYSA9PT0gJ3VuZGVmaW5lZCcgfHxcbiAgICAgICAgIUFycmF5LmlzQXJyYXkoc2Vzc2lvbi5tZWRpYSkgfHwgc2Vzc2lvbi5tZWRpYS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdUaGUgZGVzY3JpcHRpb24gaGFzIG5vIG1lZGlhLicpO1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICAvLyBUcnkgc29tZSBoZXVyaXN0aWNzIHRvIFwibWFrZSBzdXJlXCIgdGhpcyBpcyBhIFBsYW4gQSBTRFAuIFBsYW4gQiBTRFAgaGFzXG4gICAgLy8gYSB2aWRlbywgYW4gYXVkaW8gYW5kIGEgZGF0YSBcImNoYW5uZWxcIiBhdCBtb3N0LlxuICAgIGlmIChzZXNzaW9uLm1lZGlhLmxlbmd0aCA8PSAzICYmIHNlc3Npb24ubWVkaWEuZXZlcnkoZnVuY3Rpb24obSkge1xuICAgICAgICAgICAgcmV0dXJuIFsndmlkZW8nLCAnYXVkaW8nLCAnZGF0YSddLmluZGV4T2YobS5taWQpICE9PSAtMTtcbiAgICAgICAgfSkpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdUaGlzIGRlc2NyaXB0aW9uIGRvZXMgbm90IGxvb2sgbGlrZSBQbGFuIEEuJyk7XG4gICAgICAgIHJldHVybiBkZXNjO1xuICAgIH1cblxuICAgIC8vI2VuZHJlZ2lvblxuXG4gICAgLy8gUGxhbiBBIFNEUCBpcyBvdXIgXCJwcmVjaW91c1wiLiBDYWNoZSBpdCBmb3IgbGF0ZXIgdXNlIGluIHRoZSBQbGFuIEIgLT5cbiAgICAvLyBQbGFuIEEgdHJhbnNmb3JtYXRpb24uXG4gICAgY2FjaGVbZGVzYy50eXBlXSA9IGRlc2Muc2RwO1xuXG4gICAgLy8jcmVnaW9uIENvbnZlcnQgZnJvbSBQbGFuIEEgdG8gUGxhbiBCLlxuXG4gICAgLy8gV2UgcmVidWlsZCB0aGUgc2Vzc2lvbi5tZWRpYSBhcnJheS5cbiAgICB2YXIgbWVkaWEgPSBzZXNzaW9uLm1lZGlhO1xuICAgIHNlc3Npb24ubWVkaWEgPSBbXTtcblxuICAgIC8vIEFzc29jaWF0aXZlIGFycmF5IHRoYXQgbWFwcyBjaGFubmVsIHR5cGVzIHRvIGNoYW5uZWwgb2JqZWN0cyBmb3IgZmFzdFxuICAgIC8vIGFjY2VzcyB0byBjaGFubmVsIG9iamVjdHMgYnkgdGhlaXIgdHlwZSwgZS5nLiBjaGFubmVsc1snYXVkaW8nXS0+Y2hhbm5lbFxuICAgIC8vIG9iai5cbiAgICB2YXIgY2hhbm5lbHMgPSB7fTtcblxuICAgIC8vIFVzZWQgdG8gYnVpbGQgdGhlIGdyb3VwOkJVTkRMRSB2YWx1ZSBhZnRlciB0aGUgY2hhbm5lbHMgY29uc3RydWN0aW9uXG4gICAgLy8gbG9vcC5cbiAgICB2YXIgdHlwZXMgPSBbXTtcblxuICAgIC8vIEltcGxvZGUgdGhlIFBsYW4gQSBtLWxpbmVzL3RyYWNrcyBpbnRvIFBsYW4gQiBcImNoYW5uZWxzXCIuXG4gICAgbWVkaWEuZm9yRWFjaChmdW5jdGlvbihtTGluZSkge1xuXG4gICAgICAgIC8vIHJ0Y3AtbXV4IGlzIHJlcXVpcmVkIGluIHRoZSBQbGFuIEIgU0RQLlxuICAgICAgICBpZiAodHlwZW9mIG1MaW5lLnJ0Y3BNdXggIT09ICdzdHJpbmcnIHx8XG4gICAgICAgICAgICBtTGluZS5ydGNwTXV4ICE9PSAncnRjcC1tdXgnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBjb252ZXJ0IHRvIFBsYW4gQiBiZWNhdXNlIG0tbGluZXMgJyArXG4gICAgICAgICAgICAgICAgJ3dpdGhvdXQgdGhlIHJ0Y3AtbXV4IGF0dHJpYnV0ZSB3ZXJlIGZvdW5kLicpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgd2UgZG9uJ3QgaGF2ZSBhIGNoYW5uZWwgZm9yIHRoaXMgbUxpbmUudHlwZSwgdGhlbiB1c2UgdGhpcyBtTGluZVxuICAgICAgICAvLyBhcyB0aGUgY2hhbm5lbCBiYXNpcy5cbiAgICAgICAgaWYgKHR5cGVvZiBjaGFubmVsc1ttTGluZS50eXBlXSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIGNoYW5uZWxzW21MaW5lLnR5cGVdID0gbUxpbmU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgc291cmNlcyB0byB0aGUgY2hhbm5lbCBhbmQgaGFuZGxlIGE9bXNpZC5cbiAgICAgICAgaWYgKHR5cGVvZiBtTGluZS5zb3VyY2VzID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgT2JqZWN0LmtleXMobUxpbmUuc291cmNlcykuZm9yRWFjaChmdW5jdGlvbihzc3JjKSB7XG4gICAgICAgICAgICAgICAgLy8gQXNzaWduIHRoZSBzb3VyY2VzIHRvIHRoZSBjaGFubmVsLlxuICAgICAgICAgICAgICAgIGNoYW5uZWxzW21MaW5lLnR5cGVdLnNvdXJjZXNbc3NyY10gPSBtTGluZS5zb3VyY2VzW3NzcmNdO1xuXG4gICAgICAgICAgICAgICAgLy8gSW4gUGxhbiBCIHRoZSBtc2lkIGlzIGFuIFNTUkMgYXR0cmlidXRlLiBBbHNvLCB3ZSBkb24ndCBjYXJlXG4gICAgICAgICAgICAgICAgLy8gYWJvdXQgdGhlIG9ic29sZXRlIGxhYmVsIGFuZCBtc2xhYmVsIGF0dHJpYnV0ZXMuXG4gICAgICAgICAgICAgICAgY2hhbm5lbHNbbUxpbmUudHlwZV0uc291cmNlc1tzc3JjXS5tc2lkID0gbUxpbmUubXNpZDtcblxuICAgICAgICAgICAgICAgIC8vIE5PVEUgc3NyY3MgaW4gc3NyYyBncm91cHMgd2lsbCBzaGFyZSBtc2lkcywgYXNcbiAgICAgICAgICAgICAgICAvLyBkcmFmdC11YmVydGktcnRjd2ViLXBsYW4tMDAgbWFuZGF0ZXMuXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEFkZCBzc3JjIGdyb3VwcyB0byB0aGUgY2hhbm5lbC5cbiAgICAgICAgaWYgKHR5cGVvZiBtTGluZS5zc3JjR3JvdXBzICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAgICAgICAgICAgIEFycmF5LmlzQXJyYXkobUxpbmUuc3NyY0dyb3VwcykpIHtcblxuICAgICAgICAgICAgLy8gQ3JlYXRlIHRoZSBzc3JjR3JvdXBzIGFycmF5LCBpZiBpdCdzIG5vdCBkZWZpbmVkLlxuICAgICAgICAgICAgaWYgKHR5cGVvZiBjaGFubmVsLnNzcmNHcm91cHMgPT09ICd1bmRlZmluZWQnIHx8XG4gICAgICAgICAgICAgICAgICAgICFBcnJheS5pc0FycmF5KGNoYW5uZWwuc3NyY0dyb3VwcykpIHtcbiAgICAgICAgICAgICAgICBjaGFubmVsLnNzcmNHcm91cHMgPSBbXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY2hhbm5lbC5zc3JjR3JvdXBzID0gY2hhbm5lbC5zc3JjR3JvdXBzLmNvbmNhdChtTGluZS5zc3JjR3JvdXBzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGFubmVsc1ttTGluZS50eXBlXSA9PT0gbUxpbmUpIHtcbiAgICAgICAgICAgIC8vIENvcHkgSUNFIHJlbGF0ZWQgc3R1ZmYgZnJvbSB0aGUgcHJpbmNpcGFsIG1lZGlhIGxpbmUuXG4gICAgICAgICAgICBtTGluZS5jYW5kaWRhdGVzID0gbWVkaWFbMF0uY2FuZGlkYXRlcztcbiAgICAgICAgICAgIG1MaW5lLmljZVVmcmFnID0gbWVkaWFbMF0uaWNlVWZyYWc7XG4gICAgICAgICAgICBtTGluZS5pY2VQd2QgPSBtZWRpYVswXS5pY2VQd2Q7XG4gICAgICAgICAgICBtTGluZS5maW5nZXJwcmludCA9IG1lZGlhWzBdLmZpbmdlcnByaW50O1xuXG4gICAgICAgICAgICAvLyBQbGFuIEIgbWlkcyBhcmUgaW4gWydhdWRpbycsICd2aWRlbycsICdkYXRhJ11cbiAgICAgICAgICAgIG1MaW5lLm1pZCA9IG1MaW5lLnR5cGU7XG5cbiAgICAgICAgICAgIC8vIFBsYW4gQiBkb2Vzbid0IHN1cHBvcnQvbmVlZCB0aGUgYnVuZGxlLW9ubHkgYXR0cmlidXRlLlxuICAgICAgICAgICAgZGVsZXRlIG1MaW5lLmJ1bmRsZU9ubHk7XG5cbiAgICAgICAgICAgIC8vIEluIFBsYW4gQiB0aGUgbXNpZCBpcyBhbiBTU1JDIGF0dHJpYnV0ZS5cbiAgICAgICAgICAgIGRlbGV0ZSBtTGluZS5tc2lkO1xuXG4gICAgICAgICAgICAvLyBVc2VkIHRvIGJ1aWxkIHRoZSBncm91cDpCVU5ETEUgdmFsdWUgYWZ0ZXIgdGhpcyBsb29wLlxuICAgICAgICAgICAgdHlwZXMucHVzaChtTGluZS50eXBlKTtcblxuICAgICAgICAgICAgLy8gQWRkIHRoZSBjaGFubmVsIHRvIHRoZSBuZXcgbWVkaWEgYXJyYXkuXG4gICAgICAgICAgICBzZXNzaW9uLm1lZGlhLnB1c2gobUxpbmUpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBXZSByZWdlbmVyYXRlIHRoZSBCVU5ETEUgZ3JvdXAgd2l0aCB0aGUgbmV3IG1pZHMuXG4gICAgc2Vzc2lvbi5ncm91cHMuZXZlcnkoZnVuY3Rpb24oZ3JvdXApIHtcbiAgICAgICAgaWYgKGdyb3VwLnR5cGUgPT09ICdCVU5ETEUnKSB7XG4gICAgICAgICAgICBncm91cC5taWRzID0gdHlwZXMuam9pbignICcpO1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8vIG1zaWQgc2VtYW50aWNcbiAgICBzZXNzaW9uLm1zaWRTZW1hbnRpYyA9IHtcbiAgICAgICAgc2VtYW50aWM6ICdXTVMnLFxuICAgICAgICB0b2tlbjogJyonXG4gICAgfTtcblxuICAgIHZhciByZXNTdHIgPSB0cmFuc2Zvcm0ud3JpdGUoc2Vzc2lvbik7XG5cbiAgICByZXR1cm4gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgc2RwOiByZXNTdHJcbiAgICB9KTtcblxuICAgIC8vI2VuZHJlZ2lvblxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCB0cmFuc2Zvcm1zIGEgUGxhbiBCIFNEUCB0byBhbiBlcXVpdmFsZW50IFBsYW4gQSBTRFAuIEFcbiAqIFBlZXJDb25uZWN0aW9uIHdyYXBwZXIgdHJhbnNmb3JtcyB0aGUgU0RQIHRvIFBsYW4gQSBiZWZvcmUgcGFzc2luZyBpdCB0byBGRi5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cbkludGVyb3AucHJvdG90eXBlLnRvUGxhbkEgPSBmdW5jdGlvbihkZXNjKSB7XG5cbiAgICAvLyNyZWdpb24gUHJlbGltaW5hcnkgaW5wdXQgdmFsaWRhdGlvbi5cblxuICAgIGlmICh0eXBlb2YgZGVzYyAhPT0gJ29iamVjdCcgfHwgZGVzYyA9PT0gbnVsbCB8fFxuICAgICAgICB0eXBlb2YgZGVzYy5zZHAgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignQW4gZW1wdHkgZGVzY3JpcHRpb24gd2FzIHBhc3NlZCBhcyBhbiBhcmd1bWVudC4nKTtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG4gICAgdmFyIHNlc3Npb24gPSB0cmFuc2Zvcm0ucGFyc2UoZGVzYy5zZHApO1xuXG4gICAgLy8gSWYgdGhlIFNEUCBjb250YWlucyBubyBtZWRpYSwgdGhlcmUncyBub3RoaW5nIHRvIHRyYW5zZm9ybS5cbiAgICBpZiAodHlwZW9mIHNlc3Npb24ubWVkaWEgPT09ICd1bmRlZmluZWQnIHx8XG4gICAgICAgICFBcnJheS5pc0FycmF5KHNlc3Npb24ubWVkaWEpIHx8IHNlc3Npb24ubWVkaWEubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignVGhlIGRlc2NyaXB0aW9uIGhhcyBubyBtZWRpYS4nKTtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG4gICAgLy8gVHJ5IHNvbWUgaGV1cmlzdGljcyB0byBcIm1ha2Ugc3VyZVwiIHRoaXMgaXMgYSBQbGFuIEIgU0RQLiBQbGFuIEIgU0RQIGhhc1xuICAgIC8vIGEgdmlkZW8sIGFuIGF1ZGlvIGFuZCBhIGRhdGEgXCJjaGFubmVsXCIgYXQgbW9zdC5cbiAgICBpZiAoc2Vzc2lvbi5tZWRpYS5sZW5ndGggPiAzIHx8ICFzZXNzaW9uLm1lZGlhLmV2ZXJ5KGZ1bmN0aW9uKG0pIHtcbiAgICAgICAgICAgIHJldHVybiBbJ3ZpZGVvJywgJ2F1ZGlvJywgJ2RhdGEnXS5pbmRleE9mKG0ubWlkKSAhPT0gLTE7XG4gICAgICAgIH0pKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignVGhpcyBkZXNjcmlwdGlvbiBkb2VzIG5vdCBsb29rIGxpa2UgUGxhbiBCLicpO1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICAvLyBNYWtlIHN1cmUgdGhpcyBQbGFuIEIgU0RQIGNhbiBiZSBjb252ZXJ0ZWQgdG8gYSBQbGFuIEEgU0RQLlxuICAgIHZhciBtaWRzID0gW107XG4gICAgc2Vzc2lvbi5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKG0pIHtcbiAgICAgICAgbWlkcy5wdXNoKG0ubWlkKTtcbiAgICB9KTtcblxuICAgIHZhciBoYXNCdW5kbGUgPSBmYWxzZTtcbiAgICBpZiAodHlwZW9mIHNlc3Npb24uZ3JvdXBzICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAgICBBcnJheS5pc0FycmF5KHNlc3Npb24uZ3JvdXBzKSkge1xuICAgICAgICBoYXNCdW5kbGUgPSBzZXNzaW9uLmdyb3Vwcy5ldmVyeShmdW5jdGlvbihnKSB7XG4gICAgICAgICAgICByZXR1cm4gZy50eXBlICE9PSAnQlVORExFJyB8fFxuICAgICAgICAgICAgICAgIGFycmF5RXF1YWxzLmFwcGx5KGcubWlkcy5zb3J0KCksIFttaWRzLnNvcnQoKV0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAoIWhhc0J1bmRsZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYW5ub3QgY29udmVydCB0byBQbGFuIEEgYmVjYXVzZSBtLWxpbmVzIHRoYXQgYXJlIFwiICtcbiAgICAgICAgICAgIFwibm90IGJ1bmRsZWQgd2VyZSBmb3VuZC5cIik7XG4gICAgfVxuXG4gICAgLy8jZW5kcmVnaW9uXG5cblxuICAgIC8vI3JlZ2lvbiBDb252ZXJ0IGZyb20gUGxhbiBCIHRvIFBsYW4gQS5cblxuICAgIC8vIFVuZm9ydHVuYXRlbHksIGEgUGxhbiBCIG9mZmVyL2Fuc3dlciBkb2Vzbid0IGhhdmUgZW5vdWdoIGluZm9ybWF0aW9uIHRvXG4gICAgLy8gcmVidWlsZCBhbiBlcXVpdmFsZW50IFBsYW4gQSBvZmZlci9hbnN3ZXIuXG4gICAgLy9cbiAgICAvLyBGb3IgZXhhbXBsZSwgaWYgdGhpcyBpcyBhIGxvY2FsIGFuc3dlciAoaW4gUGxhbiBBIHN0eWxlKSB0aGF0IHdlIGNvbnZlcnRcbiAgICAvLyB0byBQbGFuIEIgcHJpb3IgdG8gaGFuZGluZyBpdCBvdmVyIHRvIHRoZSBhcHBsaWNhdGlvbiAodGhlXG4gICAgLy8gUGVlckNvbm5lY3Rpb24gd3JhcHBlciBjYWxsZWQgdXMsIGZvciBpbnN0YW5jZSwgYWZ0ZXIgYSBzdWNjZXNzZnVsXG4gICAgLy8gY3JlYXRlQW5zd2VyKSwgd2Ugd2FudCB0byByZW1lbWJlciB0aGUgbS1saW5lIGF0IHdoaWNoIHdlJ3ZlIHNlZW4gdGhlXG4gICAgLy8gKGxvY2FsKSBTU1JDLiBUaGF0J3MgYmVjYXVzZSB3aGVuIHRoZSBhcHBsaWNhdGlvbiB3YW50cyB0byBkbyBjYWxsIHRoZVxuICAgIC8vIFNMRCBtZXRob2QsIGZvcmNpbmcgdXMgdG8gZG8gdGhlIGludmVyc2UgdHJhbnNmb3JtYXRpb24gKGZyb20gUGxhbiBCIHRvXG4gICAgLy8gUGxhbiBBKSwgd2UgbmVlZCB0byBrbm93IHRvIHdoaWNoIG0tbGluZSB0byBhc3NpZ24gdGhlIChsb2NhbCkgU1NSQy4gV2VcbiAgICAvLyBhbHNvIG5lZWQgdG8ga25vdyBhbGwgdGhlIG90aGVyIG0tbGluZXMgdGhhdCB0aGUgb3JpZ2luYWwgYW5zd2VyIGhhZCBhbmRcbiAgICAvLyBpbmNsdWRlIHRoZW0gaW4gdGhlIHRyYW5zZm9ybWVkIGFuc3dlciBhcyB3ZWxsLlxuICAgIC8vXG4gICAgLy8gQW5vdGhlciBleGFtcGxlIGlzIGlmIHRoaXMgaXMgYSByZW1vdGUgb2ZmZXIgdGhhdCB3ZSBjb252ZXJ0IHRvIFBsYW4gQlxuICAgIC8vIHByaW9yIHRvIGdpdmluZyBpdCB0byB0aGUgYXBwbGljYXRpb24sIHdlIHdhbnQgdG8gcmVtZW1iZXIgdGhlIG1pZCBhdFxuICAgIC8vIHdoaWNoIHdlJ3ZlIHNlZW4gdGhlIChyZW1vdGUpIFNTUkMuXG4gICAgLy9cbiAgICAvLyBJbiB0aGUgaXRlcmF0aW9uIHRoYXQgZm9sbG93cywgd2UgdXNlIHRoZSBjYWNoZWQgUGxhbiBBIChpZiBpdCBleGlzdHMpXG4gICAgLy8gdG8gYXNzaWduIG1pZHMgdG8gc3NyY3MuXG5cbiAgICB2YXIgY2FjaGVkO1xuICAgIGlmICh0eXBlb2YgY2FjaGVbZGVzYy50eXBlXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgY2FjaGVkID0gdHJhbnNmb3JtLnBhcnNlKGNhY2hlW2Rlc2MudHlwZV0pO1xuICAgIH1cblxuICAgIC8vIEEgaGVscGVyIG1hcCB0aGF0IHNlbmRzIG1pZHMgdG8gbS1saW5lIG9iamVjdHMuIFdlIHVzZSBpdCBsYXRlciB0b1xuICAgIC8vIHJlYnVpbGQgdGhlIFBsYW4gQSBzdHlsZSBzZXNzaW9uLm1lZGlhIGFycmF5LlxuICAgIHZhciBtZWRpYSA9IHt9O1xuICAgIHNlc3Npb24ubWVkaWEuZm9yRWFjaChmdW5jdGlvbihjaGFubmVsKSB7XG4gICAgICAgIGlmICh0eXBlb2YgY2hhbm5lbC5ydGNwTXV4ICE9PSAnc3RyaW5nJyB8fFxuICAgICAgICAgICAgY2hhbm5lbC5ydGNwTXV4ICE9PSAncnRjcC1tdXgnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYW5ub3QgY29udmVydCB0byBQbGFuIEEgYmVjYXVzZSBtLWxpbmVzIFwiICtcbiAgICAgICAgICAgICAgICBcIndpdGhvdXQgdGhlIHJ0Y3AtbXV4IGF0dHJpYnV0ZSB3ZXJlIGZvdW5kLlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFdpdGggcnRjcC1tdXggYW5kIGJ1bmRsZSBhbGwgdGhlIGNoYW5uZWxzIHNob3VsZCBoYXZlIHRoZSBzYW1lIElDRVxuICAgICAgICAvLyBzdHVmZi5cbiAgICAgICAgdmFyIHNvdXJjZXMgPSBjaGFubmVsLnNvdXJjZXM7XG4gICAgICAgIHZhciBzc3JjR3JvdXBzID0gY2hhbm5lbC5zc3JjR3JvdXBzO1xuICAgICAgICB2YXIgY2FuZGlkYXRlcyA9IGNoYW5uZWwuY2FuZGlkYXRlcztcbiAgICAgICAgdmFyIGljZVVmcmFnID0gY2hhbm5lbC5pY2VVZnJhZztcbiAgICAgICAgdmFyIGljZVB3ZCA9IGNoYW5uZWwuaWNlUHdkO1xuICAgICAgICB2YXIgZmluZ2VycHJpbnQgPSBjaGFubmVsLmZpbmdlcnByaW50O1xuICAgICAgICB2YXIgcG9ydCA9IGNoYW5uZWwucG9ydDtcblxuICAgICAgICAvLyBXZSdsbCB1c2UgdGhlIFwiY2hhbm5lbFwiIG9iamVjdCBhcyBhIHByb3RvdHlwZSBmb3IgZWFjaCBuZXcgXCJtTGluZVwiXG4gICAgICAgIC8vIHRoYXQgd2UgY3JlYXRlLCBidXQgZmlyc3Qgd2UgbmVlZCB0byBjbGVhbiBpdCB1cCBhIGJpdC5cbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuc291cmNlcztcbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuc3NyY0dyb3VwcztcbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuY2FuZGlkYXRlcztcbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuaWNlVWZyYWc7XG4gICAgICAgIGRlbGV0ZSBjaGFubmVsLmljZVB3ZDtcbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuZmluZ2VycHJpbnQ7XG4gICAgICAgIGRlbGV0ZSBjaGFubmVsLnBvcnQ7XG4gICAgICAgIGRlbGV0ZSBjaGFubmVsLm1pZDtcblxuICAgICAgICAvLyBpbnZlcnRlZCBzc3JjIGdyb3VwIG1hcFxuICAgICAgICB2YXIgaW52ZXJ0ZWRHcm91cHMgPSB7fTtcbiAgICAgICAgaWYgKHR5cGVvZiBzc3JjR3JvdXBzICE9PSAndW5kZWZpbmVkJyAmJiBBcnJheS5pc0FycmF5KHNzcmNHcm91cHMpKSB7XG4gICAgICAgICAgICBzc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKHNzcmNHcm91cCkge1xuXG4gICAgICAgICAgICAgICAgLy8gVE9ETyhncCkgZmluZCBvdXQgaG93IHRvIHJlY2VpdmUgc2ltdWxjYXN0IHdpdGggRkYuIEZvciB0aGVcbiAgICAgICAgICAgICAgICAvLyB0aW1lIGJlaW5nLCBoaWRlIGl0LlxuICAgICAgICAgICAgICAgIGlmIChzc3JjR3JvdXAuc2VtYW50aWNzID09PSAnU0lNJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBzc3JjR3JvdXAuc3NyY3MgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICAgICAgICAgIEFycmF5LmlzQXJyYXkoc3NyY0dyb3VwLnNzcmNzKSkge1xuICAgICAgICAgICAgICAgICAgICBzc3JjR3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpbnZlcnRlZEdyb3Vwc1tzc3JjXSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnZlcnRlZEdyb3Vwc1tzc3JjXSA9IFtdO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnZlcnRlZEdyb3Vwc1tzc3JjXS5wdXNoKHNzcmNHcm91cCk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gc3NyYyB0byBtLWxpbmUgaW5kZXguXG4gICAgICAgIHZhciBtTGluZXMgPSB7fTtcblxuICAgICAgICBpZiAodHlwZW9mIHNvdXJjZXMgPT09ICdvYmplY3QnKSB7XG5cbiAgICAgICAgICAgIC8vIEV4cGxvZGUgdGhlIFBsYW4gQiBjaGFubmVsIHNvdXJjZXMgd2l0aCBvbmUgbS1saW5lIHBlciBzb3VyY2UuXG4gICAgICAgICAgICBPYmplY3Qua2V5cyhzb3VyY2VzKS5mb3JFYWNoKGZ1bmN0aW9uKHNzcmMpIHtcblxuICAgICAgICAgICAgICAgIHZhciBtTGluZTtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGludmVydGVkR3JvdXBzW3NzcmNdICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAgICAgICAgICAgICAgICBBcnJheS5pc0FycmF5KGludmVydGVkR3JvdXBzW3NzcmNdKSkge1xuICAgICAgICAgICAgICAgICAgICBpbnZlcnRlZEdyb3Vwc1tzc3JjXS5ldmVyeShmdW5jdGlvbiAoc3NyY0dyb3VwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBzc3JjR3JvdXAuc3NyY3MgKmlzKiBhbiBBcnJheSwgbm8gbmVlZCB0byBjaGVja1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYWdhaW4gaGVyZS5cbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBzc3JjR3JvdXAuc3NyY3MuZXZlcnkoZnVuY3Rpb24gKHJlbGF0ZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIG1MaW5lc1tyZWxhdGVkXSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbUxpbmUgPSBtTGluZXNbcmVsYXRlZF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBtTGluZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIG0tbGluZSBhbHJlYWR5IGV4aXN0cy4gSnVzdCBhZGQgdGhlIHNvdXJjZS5cbiAgICAgICAgICAgICAgICAgICAgbUxpbmUuc291cmNlc1tzc3JjXSA9IHNvdXJjZXNbc3NyY107XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzb3VyY2VzW3NzcmNdLm1zaWQ7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBVc2UgdGhlIFwiY2hhbm5lbFwiIGFzIGEgcHJvdG90eXBlIGZvciB0aGUgXCJtTGluZVwiLlxuICAgICAgICAgICAgICAgIG1MaW5lID0gT2JqZWN0LmNyZWF0ZShjaGFubmVsKTtcbiAgICAgICAgICAgICAgICBtTGluZXNbc3NyY10gPSBtTGluZTtcblxuICAgICAgICAgICAgICAgIC8vIEFzc2lnbiB0aGUgbXNpZCBvZiB0aGUgc291cmNlIHRvIHRoZSBtLWxpbmUuXG4gICAgICAgICAgICAgICAgbUxpbmUubXNpZCA9IHNvdXJjZXNbc3NyY10ubXNpZDtcbiAgICAgICAgICAgICAgICBkZWxldGUgc291cmNlc1tzc3JjXS5tc2lkO1xuXG4gICAgICAgICAgICAgICAgLy8gV2UgYXNzaWduIG9uZSBTU1JDIHBlciBtZWRpYSBsaW5lLlxuICAgICAgICAgICAgICAgIG1MaW5lLnNvdXJjZXMgPSB7fTtcbiAgICAgICAgICAgICAgICBtTGluZS5zb3VyY2VzW3NzcmNdID0gc291cmNlc1tzc3JjXTtcbiAgICAgICAgICAgICAgICBtTGluZS5zc3JjR3JvdXBzID0gaW52ZXJ0ZWRHcm91cHNbc3NyY107XG5cbiAgICAgICAgICAgICAgICAvLyBVc2UgdGhlIGNhY2hlZCBQbGFuIEEgU0RQIChpZiBpdCBleGlzdHMpIHRvIGFzc2lnbiBTU1JDcyB0b1xuICAgICAgICAgICAgICAgIC8vIG1pZHMuXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBjYWNoZWQgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICAgICAgICAgIHR5cGVvZiBjYWNoZWQubWVkaWEgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICAgICAgICAgIEFycmF5LmlzQXJyYXkoY2FjaGVkLm1lZGlhKSkge1xuXG4gICAgICAgICAgICAgICAgICAgIGNhY2hlZC5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKG0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgbS5zb3VyY2VzID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9iamVjdC5rZXlzKG0uc291cmNlcykuZm9yRWFjaChmdW5jdGlvbihzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzID09PSBzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtTGluZS5taWQgPSBtLm1pZDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIG1MaW5lLm1pZCA9PT0gJ3VuZGVmaW5lZCcpIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB0aGlzIGlzIGFuIFNTUkMgdGhhdCB3ZSBzZWUgZm9yIHRoZSBmaXJzdCB0aW1lIGFzc2lnblxuICAgICAgICAgICAgICAgICAgICAvLyBpdCBhIG5ldyBtaWQuIFRoaXMgaXMgdHlwaWNhbGx5IHRoZSBjYXNlIHdoZW4gdGhpc1xuICAgICAgICAgICAgICAgICAgICAvLyBtZXRob2QgaXMgY2FsbGVkIHRvIHRyYW5zZm9ybSBhIHJlbW90ZSBkZXNjcmlwdGlvbiBmb3JcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGZpcnN0IHRpbWUgb3Igd2hlbiB0aGVyZSBpcyBhIG5ldyBTU1JDIGluIHRoZSByZW1vdGVcbiAgICAgICAgICAgICAgICAgICAgLy8gZGVzY3JpcHRpb24gYmVjYXVzZSBhIG5ldyBwZWVyIGhhcyBqb2luZWQgdGhlXG4gICAgICAgICAgICAgICAgICAgIC8vIGNvbmZlcmVuY2UuIExvY2FsIFNTUkNzIHNob3VsZCBoYXZlIGFscmVhZHkgYmVlbiBhZGRlZFxuICAgICAgICAgICAgICAgICAgICAvLyB0byB0aGUgbWFwIGluIHRoZSB0b1BsYW5CIG1ldGhvZC5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gQmVjYXVzZSBGRiBnZW5lcmF0ZXMgYW5zd2VycyBpbiBQbGFuIEEgc3R5bGUsIHdlIE1VU1RcbiAgICAgICAgICAgICAgICAgICAgLy8gYWxyZWFkeSBoYXZlIGEgY2FjaGVkIGFuc3dlciB3aXRoIGFsbCB0aGUgbG9jYWwgU1NSQ3NcbiAgICAgICAgICAgICAgICAgICAgLy8gbWFwcGVkIHRvIHNvbWUgbUxpbmUvbWlkLlxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChkZXNjLnR5cGUgPT09ICdhbnN3ZXInKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBbiB1bm1hcHBlZCBTU1JDIHdhcyBmb3VuZC5cIik7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBtTGluZS5taWQgPSBbY2hhbm5lbC50eXBlLCAnLScsIHNzcmNdLmpvaW4oJycpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIEluY2x1ZGUgdGhlIGNhbmRpZGF0ZXMgaW4gdGhlIDFzdCBtZWRpYSBsaW5lLlxuICAgICAgICAgICAgICAgIG1MaW5lLmNhbmRpZGF0ZXMgPSBjYW5kaWRhdGVzO1xuICAgICAgICAgICAgICAgIG1MaW5lLmljZVVmcmFnID0gaWNlVWZyYWc7XG4gICAgICAgICAgICAgICAgbUxpbmUuaWNlUHdkID0gaWNlUHdkO1xuICAgICAgICAgICAgICAgIG1MaW5lLmZpbmdlcnByaW50ID0gZmluZ2VycHJpbnQ7XG4gICAgICAgICAgICAgICAgbUxpbmUucG9ydCA9IHBvcnQ7XG5cbiAgICAgICAgICAgICAgICBtZWRpYVttTGluZS5taWRdID0gbUxpbmU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8vIFJlYnVpbGQgdGhlIG1lZGlhIGFycmF5IGluIHRoZSByaWdodCBvcmRlciBhbmQgYWRkIHRoZSBtaXNzaW5nIG1MaW5lc1xuICAgIC8vIChtaXNzaW5nIGZyb20gdGhlIFBsYW4gQiBTRFApLlxuICAgIHNlc3Npb24ubWVkaWEgPSBbXTtcbiAgICBtaWRzID0gW107IC8vIHJldXNlXG5cbiAgICBpZiAoZGVzYy50eXBlID09PSAnYW5zd2VyJykge1xuXG4gICAgICAgIC8vIFRoZSBtZWRpYSBsaW5lcyBpbiB0aGUgYW5zd2VyIG11c3QgbWF0Y2ggdGhlIG1lZGlhIGxpbmVzIGluIHRoZVxuICAgICAgICAvLyBvZmZlci4gVGhlIG9yZGVyIGlzIGltcG9ydGFudCB0b28uIEhlcmUgd2UgdXNlIHRoZSBjYWNoZWQgb2ZmZXIgdG9cbiAgICAgICAgLy8gZmluZCB0aGUgbS1saW5lcyB0aGF0IGFyZSBtaXNzaW5nIChmcm9tIHRoZSBjb252ZXJ0ZWQgYW5zd2VyKSwgYW5kXG4gICAgICAgIC8vIHVzZSB0aGUgY2FjaGVkIGFuc3dlciB0byBjb21wbGV0ZSB0aGUgY29udmVydGVkIGFuc3dlci5cblxuICAgICAgICBpZiAodHlwZW9mIGNhY2hlWydvZmZlciddID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQW4gYW5zd2VyIGlzIGJlaW5nIHByb2Nlc3NlZCBidXQgd2UgY291bGRuJ3QgXCIgK1xuICAgICAgICAgICAgICAgICAgICBcImZpbmQgYSBjYWNoZWQgb2ZmZXIuXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGNhY2hlZE9mZmVyID0gdHJhbnNmb3JtLnBhcnNlKGNhY2hlWydvZmZlciddKTtcblxuICAgICAgICBpZiAodHlwZW9mIGNhY2hlZE9mZmVyID09PSAndW5kZWZpbmVkJyB8fFxuICAgICAgICAgICAgdHlwZW9mIGNhY2hlZE9mZmVyLm1lZGlhID09PSAndW5kZWZpbmVkJyB8fFxuICAgICAgICAgICAgIUFycmF5LmlzQXJyYXkoY2FjaGVkT2ZmZXIubWVkaWEpKSB7XG4gICAgICAgICAgICAgICAgLy8gRklYTUUoZ3ApIGlzIHRoaXMgcmVhbGx5IGEgcHJvYmxlbSBpbiB0aGUgZ2VuZXJhbCBjYXNlP1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlRoZSBjYWNoZWQgb2ZmZXIgaGFzIG5vIG1lZGlhLlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhY2hlZE9mZmVyLm1lZGlhLmZvckVhY2goZnVuY3Rpb24obW8pIHtcblxuICAgICAgICAgICAgdmFyIG1MaW5lO1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBtZWRpYVttby5taWRdID09PSAndW5kZWZpbmVkJykge1xuXG4gICAgICAgICAgICAgICAgLy8gVGhpcyBpcyBwcm9iYWJseSBhbiBtLWxpbmUgY29udGFpbmluZyBhIHJlbW90ZSB0cmFjayBvbmx5LlxuICAgICAgICAgICAgICAgIC8vIEl0IE1VU1QgZXhpc3QgaW4gdGhlIGNhY2hlZCBhbnN3ZXIgYXMgYSByZW1vdGUgdHJhY2sgb25seVxuICAgICAgICAgICAgICAgIC8vIG1MaW5lLlxuXG4gICAgICAgICAgICAgICAgY2FjaGVkLm1lZGlhLmV2ZXJ5KGZ1bmN0aW9uKG1hKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChtby5taWQgPT0gbWEubWlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtTGluZSA9IG1hO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbUxpbmUgPSBtZWRpYVttby5taWRdO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodHlwZW9mIG1MaW5lID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlRoZSBjYWNoZWQgb2ZmZXIgY29udGFpbnMgYW4gbS1saW5lIHRoYXQgXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJkb2Vzbid0IGV4aXN0IG5laXRoZXIgaW4gdGhlIGNhY2hlZCBhbnN3ZXIgbm9yIGluIFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidGhlIGNvbnZlcnRlZCBhbnN3ZXIuXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXNzaW9uLm1lZGlhLnB1c2gobUxpbmUpO1xuICAgICAgICAgICAgbWlkcy5wdXNoKG1MaW5lLm1pZCk7XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG5cbiAgICAgICAgLy8gU0RQIG9mZmVyL2Fuc3dlciAoYW5kIHRoZSBKU0VQIHNwZWMpIGZvcmJpZHMgcmVtb3ZpbmcgYW4gbS1zZWN0aW9uXG4gICAgICAgIC8vIHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzLiBJZiB3ZSBhcmUgbm8gbG9uZ2VyIGludGVyZXN0ZWQgaW4gc2VuZGluZyBhXG4gICAgICAgIC8vIHRyYWNrLCB3ZSBqdXN0IHJlbW92ZSB0aGUgbXNpZCBhbmQgc3NyYyBhdHRyaWJ1dGVzIGFuZCBzZXQgaXQgdG9cbiAgICAgICAgLy8gZWl0aGVyIGE9cmVjdm9ubHkgKGFzIHRoZSByZW9mZmVyZXIsIHdlIG11c3QgdXNlIHJlY3Zvbmx5IGlmIHRoZVxuICAgICAgICAvLyBvdGhlciBzaWRlIHdhcyBwcmV2aW91c2x5IHNlbmRpbmcgb24gdGhlIG0tc2VjdGlvbiwgYnV0IHdlIGNhbiBhbHNvXG4gICAgICAgIC8vIGxlYXZlIHRoZSBwb3NzaWJpbGl0eSBvcGVuIGlmIGl0IHdhc24ndCBwcmV2aW91c2x5IGluIHVzZSksIG9yXG4gICAgICAgIC8vIGE9aW5hY2l2ZS5cblxuICAgICAgICBpZiAodHlwZW9mIGNhY2hlZCAhPT0gJ3VuZGVmaW5lZCcgJiZcbiAgICAgICAgICAgIHR5cGVvZiBjYWNoZWQubWVkaWEgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICBBcnJheS5pc0FycmF5KGNhY2hlZC5tZWRpYSkpIHtcbiAgICAgICAgICAgIGNhY2hlZC5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKHBtKSB7XG4gICAgICAgICAgICAgICAgbWlkcy5wdXNoKHBtLm1pZCk7XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBtZWRpYVtwbS5taWRdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgICAgICBzZXNzaW9uLm1lZGlhLnB1c2gobWVkaWFbcG0ubWlkXSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHBtLm1zaWQ7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBwbS5zb3VyY2VzO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgcG0uc3NyY0dyb3VwcztcbiAgICAgICAgICAgICAgICAgICAgcG0uZGlyZWN0aW9uID0gJ3JlY3Zvbmx5JztcbiAgICAgICAgICAgICAgICAgICAgc2Vzc2lvbi5tZWRpYS5wdXNoKHBtKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEFkZCBhbGwgdGhlIHJlbWFpbmluZyAobmV3KSBtLWxpbmVzIG9mIHRoZSB0cmFuc2Zvcm1lZCBTRFAuXG4gICAgICAgIE9iamVjdC5rZXlzKG1lZGlhKS5mb3JFYWNoKGZ1bmN0aW9uKG1pZCkge1xuICAgICAgICAgICAgaWYgKG1pZHMuaW5kZXhPZihtaWQpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIG1pZHMucHVzaChtaWQpO1xuICAgICAgICAgICAgICAgIHNlc3Npb24ubWVkaWEucHVzaChtZWRpYVttaWRdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gV2UgcmVnZW5lcmF0ZSB0aGUgQlVORExFIGdyb3VwIChzaW5jZSB3ZSByZWdlbmVyYXRlZCB0aGUgbWlkcylcbiAgICBzZXNzaW9uLmdyb3Vwcy5ldmVyeShmdW5jdGlvbihncm91cCkge1xuICAgICAgICBpZiAoZ3JvdXAudHlwZSA9PT0gJ0JVTkRMRScpIHtcbiAgICAgICAgICAgIGdyb3VwLm1pZHMgPSBtaWRzLmpvaW4oJyAnKTtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBtc2lkIHNlbWFudGljXG4gICAgc2Vzc2lvbi5tc2lkU2VtYW50aWMgPSB7XG4gICAgICAgIHNlbWFudGljOiAnV01TJyxcbiAgICAgICAgdG9rZW46ICcqJ1xuICAgIH07XG5cbiAgICB2YXIgcmVzU3RyID0gdHJhbnNmb3JtLndyaXRlKHNlc3Npb24pO1xuXG4gICAgLy8gQ2FjaGUgdGhlIHRyYW5zZm9ybWVkIFNEUCAoUGxhbiBBKSBmb3IgbGF0ZXIgcmUtdXNlIGluIHRoaXMgZnVuY3Rpb24uXG4gICAgY2FjaGVbZGVzYy50eXBlXSA9IHJlc1N0cjtcblxuICAgIHJldHVybiBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICBzZHA6IHJlc1N0clxuICAgIH0pO1xuXG4gICAgLy8jZW5kcmVnaW9uXG59O1xuIiwidmFyIHRyYW5zZm9ybSA9IHJlcXVpcmUoJ3NkcC10cmFuc2Zvcm0nKTtcblxuZXhwb3J0cy53cml0ZSA9IGZ1bmN0aW9uKHNlc3Npb24sIG9wdHMpIHtcblxuICBpZiAodHlwZW9mIHNlc3Npb24gIT09ICd1bmRlZmluZWQnICYmXG4gICAgICB0eXBlb2Ygc2Vzc2lvbi5tZWRpYSAhPT0gJ3VuZGVmaW5lZCcgJiZcbiAgICAgIEFycmF5LmlzQXJyYXkoc2Vzc2lvbi5tZWRpYSkpIHtcblxuICAgIHNlc3Npb24ubWVkaWEuZm9yRWFjaChmdW5jdGlvbiAobUxpbmUpIHtcbiAgICAgIC8vIGV4cGFuZCBzb3VyY2VzIHRvIHNzcmNzXG4gICAgICBpZiAodHlwZW9mIG1MaW5lLnNvdXJjZXMgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgIE9iamVjdC5rZXlzKG1MaW5lLnNvdXJjZXMpLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgIG1MaW5lLnNzcmNzID0gW107XG4gICAgICAgICAgT2JqZWN0LmtleXMobUxpbmUuc291cmNlcykuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgdmFyIHNvdXJjZSA9IG1MaW5lLnNvdXJjZXNbc3NyY107XG4gICAgICAgICAgICBPYmplY3Qua2V5cyhzb3VyY2UpLmZvckVhY2goZnVuY3Rpb24gKGF0dHJpYnV0ZSkge1xuICAgICAgICAgICAgICBtTGluZS5zc3Jjcy5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogc3NyYyxcbiAgICAgICAgICAgICAgICBhdHRyaWJ1dGU6IGF0dHJpYnV0ZSxcbiAgICAgICAgICAgICAgICB2YWx1ZTogc291cmNlW2F0dHJpYnV0ZV1cbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBkZWxldGUgbUxpbmUuc291cmNlcztcbiAgICAgICAgfVxuXG4gICAgICAvLyBqb2luIHNzcmNzIGluIHNzcmMgZ3JvdXBzXG4gICAgICBpZiAodHlwZW9mIG1MaW5lLnNzcmNHcm91cHMgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgIEFycmF5LmlzQXJyYXkobUxpbmUuc3NyY0dyb3VwcykpIHtcbiAgICAgICAgICBtTGluZS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKHNzcmNHcm91cCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBzc3JjR3JvdXAuc3NyY3MgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICAgICAgQXJyYXkuaXNBcnJheShzc3JjR3JvdXAuc3NyY3MpKSB7XG4gICAgICAgICAgICAgIHNzcmNHcm91cC5zc3JjcyA9IHNzcmNHcm91cC5zc3Jjcy5qb2luKCcgJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8vIGpvaW4gZ3JvdXAgbWlkc1xuICBpZiAodHlwZW9mIHNlc3Npb24gIT09ICd1bmRlZmluZWQnICYmXG4gICAgICB0eXBlb2Ygc2Vzc2lvbi5ncm91cHMgIT09ICd1bmRlZmluZWQnICYmIEFycmF5LmlzQXJyYXkoc2Vzc2lvbi5ncm91cHMpKSB7XG5cbiAgICBzZXNzaW9uLmdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChnKSB7XG4gICAgICBpZiAodHlwZW9mIGcubWlkcyAhPT0gJ3VuZGVmaW5lZCcgJiYgQXJyYXkuaXNBcnJheShnLm1pZHMpKSB7XG4gICAgICAgIGcubWlkcyA9IGcubWlkcy5qb2luKCcgJyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gdHJhbnNmb3JtLndyaXRlKHNlc3Npb24sIG9wdHMpO1xufTtcblxuZXhwb3J0cy5wYXJzZSA9IGZ1bmN0aW9uKHNkcCkge1xuICB2YXIgc2Vzc2lvbiA9IHRyYW5zZm9ybS5wYXJzZShzZHApO1xuXG4gIGlmICh0eXBlb2Ygc2Vzc2lvbiAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIHNlc3Npb24ubWVkaWEgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICBBcnJheS5pc0FycmF5KHNlc3Npb24ubWVkaWEpKSB7XG5cbiAgICBzZXNzaW9uLm1lZGlhLmZvckVhY2goZnVuY3Rpb24gKG1MaW5lKSB7XG4gICAgICAvLyBncm91cCBzb3VyY2VzIGF0dHJpYnV0ZXMgYnkgc3NyY1xuICAgICAgaWYgKHR5cGVvZiBtTGluZS5zc3JjcyAhPT0gJ3VuZGVmaW5lZCcgJiYgQXJyYXkuaXNBcnJheShtTGluZS5zc3JjcykpIHtcbiAgICAgICAgbUxpbmUuc291cmNlcyA9IHt9O1xuICAgICAgICBtTGluZS5zc3Jjcy5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgaWYgKCFtTGluZS5zb3VyY2VzW3NzcmMuaWRdKVxuICAgICAgICAgIG1MaW5lLnNvdXJjZXNbc3NyYy5pZF0gPSB7fTtcbiAgICAgICAgbUxpbmUuc291cmNlc1tzc3JjLmlkXVtzc3JjLmF0dHJpYnV0ZV0gPSBzc3JjLnZhbHVlO1xuICAgICAgICB9KTtcblxuICAgICAgICBkZWxldGUgbUxpbmUuc3NyY3M7XG4gICAgICB9XG5cbiAgICAgIC8vIHNwbGl0IHNzcmNzIGluIHNzcmMgZ3JvdXBzXG4gICAgICBpZiAodHlwZW9mIG1MaW5lLnNzcmNHcm91cHMgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgIEFycmF5LmlzQXJyYXkobUxpbmUuc3NyY0dyb3VwcykpIHtcbiAgICAgICAgICBtTGluZS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKHNzcmNHcm91cCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBzc3JjR3JvdXAuc3NyY3MgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgIHNzcmNHcm91cC5zc3JjcyA9IHNzcmNHcm91cC5zc3Jjcy5zcGxpdCgnICcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfSk7XG4gIH1cbiAgLy8gc3BsaXQgZ3JvdXAgbWlkc1xuICBpZiAodHlwZW9mIHNlc3Npb24gIT09ICd1bmRlZmluZWQnICYmXG4gICAgICB0eXBlb2Ygc2Vzc2lvbi5ncm91cHMgIT09ICd1bmRlZmluZWQnICYmIEFycmF5LmlzQXJyYXkoc2Vzc2lvbi5ncm91cHMpKSB7XG5cbiAgICBzZXNzaW9uLmdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChnKSB7XG4gICAgICBpZiAodHlwZW9mIGcubWlkcyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgZy5taWRzID0gZy5taWRzLnNwbGl0KCcgJyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gc2Vzc2lvbjtcbn07XG5cbiIsInZhciBncmFtbWFyID0gbW9kdWxlLmV4cG9ydHMgPSB7XG4gIHY6IFt7XG4gICAgICBuYW1lOiAndmVyc2lvbicsXG4gICAgICByZWc6IC9eKFxcZCopJC9cbiAgfV0sXG4gIG86IFt7IC8vbz0tIDIwNTE4IDAgSU4gSVA0IDIwMy4wLjExMy4xXG4gICAgLy8gTkI6IHNlc3Npb25JZCB3aWxsIGJlIGEgU3RyaW5nIGluIG1vc3QgY2FzZXMgYmVjYXVzZSBpdCBpcyBodWdlXG4gICAgbmFtZTogJ29yaWdpbicsXG4gICAgcmVnOiAvXihcXFMqKSAoXFxkKikgKFxcZCopIChcXFMqKSBJUChcXGQpIChcXFMqKS8sXG4gICAgbmFtZXM6IFsndXNlcm5hbWUnLCAnc2Vzc2lvbklkJywgJ3Nlc3Npb25WZXJzaW9uJywgJ25ldFR5cGUnLCAnaXBWZXInLCAnYWRkcmVzcyddLFxuICAgIGZvcm1hdDogXCIlcyAlcyAlZCAlcyBJUCVkICVzXCJcbiAgfV0sXG4gIC8vIGRlZmF1bHQgcGFyc2luZyBvZiB0aGVzZSBvbmx5ICh0aG91Z2ggc29tZSBvZiB0aGVzZSBmZWVsIG91dGRhdGVkKVxuICBzOiBbeyBuYW1lOiAnbmFtZScgfV0sXG4gIGk6IFt7IG5hbWU6ICdkZXNjcmlwdGlvbicgfV0sXG4gIHU6IFt7IG5hbWU6ICd1cmknIH1dLFxuICBlOiBbeyBuYW1lOiAnZW1haWwnIH1dLFxuICBwOiBbeyBuYW1lOiAncGhvbmUnIH1dLFxuICB6OiBbeyBuYW1lOiAndGltZXpvbmVzJyB9XSwgLy8gVE9ETzogdGhpcyBvbmUgY2FuIGFjdHVhbGx5IGJlIHBhcnNlZCBwcm9wZXJseS4uXG4gIHI6IFt7IG5hbWU6ICdyZXBlYXRzJyB9XSwgICAvLyBUT0RPOiB0aGlzIG9uZSBjYW4gYWxzbyBiZSBwYXJzZWQgcHJvcGVybHlcbiAgLy9rOiBbe31dLCAvLyBvdXRkYXRlZCB0aGluZyBpZ25vcmVkXG4gIHQ6IFt7IC8vdD0wIDBcbiAgICBuYW1lOiAndGltaW5nJyxcbiAgICByZWc6IC9eKFxcZCopIChcXGQqKS8sXG4gICAgbmFtZXM6IFsnc3RhcnQnLCAnc3RvcCddLFxuICAgIGZvcm1hdDogXCIlZCAlZFwiXG4gIH1dLFxuICBjOiBbeyAvL2M9SU4gSVA0IDEwLjQ3LjE5Ny4yNlxuICAgICAgbmFtZTogJ2Nvbm5lY3Rpb24nLFxuICAgICAgcmVnOiAvXklOIElQKFxcZCkgKFxcUyopLyxcbiAgICAgIG5hbWVzOiBbJ3ZlcnNpb24nLCAnaXAnXSxcbiAgICAgIGZvcm1hdDogXCJJTiBJUCVkICVzXCJcbiAgfV0sXG4gIGI6IFt7IC8vYj1BUzo0MDAwXG4gICAgICBwdXNoOiAnYmFuZHdpZHRoJyxcbiAgICAgIHJlZzogL14oVElBU3xBU3xDVHxSUnxSUyk6KFxcZCopLyxcbiAgICAgIG5hbWVzOiBbJ3R5cGUnLCAnbGltaXQnXSxcbiAgICAgIGZvcm1hdDogXCIlczolc1wiXG4gIH1dLFxuICBtOiBbeyAvL209dmlkZW8gNTE3NDQgUlRQL0FWUCAxMjYgOTcgOTggMzQgMzFcbiAgICAgIC8vIE5COiBzcGVjaWFsIC0gcHVzaGVzIHRvIHNlc3Npb25cbiAgICAgIC8vIFRPRE86IHJ0cC9mbXRwIHNob3VsZCBiZSBmaWx0ZXJlZCBieSB0aGUgcGF5bG9hZHMgZm91bmQgaGVyZT9cbiAgICAgIHJlZzogL14oXFx3KikgKFxcZCopIChbXFx3XFwvXSopKD86ICguKikpPy8sXG4gICAgICBuYW1lczogWyd0eXBlJywgJ3BvcnQnLCAncHJvdG9jb2wnLCAncGF5bG9hZHMnXSxcbiAgICAgIGZvcm1hdDogXCIlcyAlZCAlcyAlc1wiXG4gIH1dLFxuICBhOiBbXG4gICAgeyAvL2E9cnRwbWFwOjExMCBvcHVzLzQ4MDAwLzJcbiAgICAgIHB1c2g6ICdydHAnLFxuICAgICAgcmVnOiAvXnJ0cG1hcDooXFxkKikgKFtcXHdcXC1dKilcXC8oXFxkKikoPzpcXHMqXFwvKFxcUyopKT8vLFxuICAgICAgbmFtZXM6IFsncGF5bG9hZCcsICdjb2RlYycsICdyYXRlJywgJ2VuY29kaW5nJ10sXG4gICAgICBmb3JtYXQ6IGZ1bmN0aW9uIChvKSB7XG4gICAgICAgIHJldHVybiAoby5lbmNvZGluZykgP1xuICAgICAgICAgIFwicnRwbWFwOiVkICVzLyVzLyVzXCI6XG4gICAgICAgICAgXCJydHBtYXA6JWQgJXMvJXNcIjtcbiAgICAgIH1cbiAgICB9LFxuICAgIHsgLy9hPWZtdHA6MTA4IHByb2ZpbGUtbGV2ZWwtaWQ9MjQ7b2JqZWN0PTIzO2JpdHJhdGU9NjQwMDBcbiAgICAgIHB1c2g6ICdmbXRwJyxcbiAgICAgIHJlZzogL15mbXRwOihcXGQqKSAoXFxTKikvLFxuICAgICAgbmFtZXM6IFsncGF5bG9hZCcsICdjb25maWcnXSxcbiAgICAgIGZvcm1hdDogXCJmbXRwOiVkICVzXCJcbiAgICB9LFxuICAgIHsgLy9hPWNvbnRyb2w6c3RyZWFtaWQ9MFxuICAgICAgICBuYW1lOiAnY29udHJvbCcsXG4gICAgICAgIHJlZzogL15jb250cm9sOiguKikvLFxuICAgICAgICBmb3JtYXQ6IFwiY29udHJvbDolc1wiXG4gICAgfSxcbiAgICB7IC8vYT1ydGNwOjY1MTc5IElOIElQNCAxOTMuODQuNzcuMTk0XG4gICAgICBuYW1lOiAncnRjcCcsXG4gICAgICByZWc6IC9ecnRjcDooXFxkKikoPzogKFxcUyopIElQKFxcZCkgKFxcUyopKT8vLFxuICAgICAgbmFtZXM6IFsncG9ydCcsICduZXRUeXBlJywgJ2lwVmVyJywgJ2FkZHJlc3MnXSxcbiAgICAgIGZvcm1hdDogZnVuY3Rpb24gKG8pIHtcbiAgICAgICAgcmV0dXJuIChvLmFkZHJlc3MgIT0gbnVsbCkgP1xuICAgICAgICAgIFwicnRjcDolZCAlcyBJUCVkICVzXCI6XG4gICAgICAgICAgXCJydGNwOiVkXCI7XG4gICAgICB9XG4gICAgfSxcbiAgICB7IC8vYT1ydGNwLWZiOjk4IHRyci1pbnQgMTAwXG4gICAgICBwdXNoOiAncnRjcEZiVHJySW50JyxcbiAgICAgIHJlZzogL15ydGNwLWZiOihcXCp8XFxkKikgdHJyLWludCAoXFxkKikvLFxuICAgICAgbmFtZXM6IFsncGF5bG9hZCcsICd2YWx1ZSddLFxuICAgICAgZm9ybWF0OiBcInJ0Y3AtZmI6JWQgdHJyLWludCAlZFwiXG4gICAgfSxcbiAgICB7IC8vYT1ydGNwLWZiOjk4IG5hY2sgcnBzaVxuICAgICAgcHVzaDogJ3J0Y3BGYicsXG4gICAgICByZWc6IC9ecnRjcC1mYjooXFwqfFxcZCopIChbXFx3LV9dKikoPzogKFtcXHctX10qKSk/LyxcbiAgICAgIG5hbWVzOiBbJ3BheWxvYWQnLCAndHlwZScsICdzdWJ0eXBlJ10sXG4gICAgICBmb3JtYXQ6IGZ1bmN0aW9uIChvKSB7XG4gICAgICAgIHJldHVybiAoby5zdWJ0eXBlICE9IG51bGwpID9cbiAgICAgICAgICBcInJ0Y3AtZmI6JXMgJXMgJXNcIjpcbiAgICAgICAgICBcInJ0Y3AtZmI6JXMgJXNcIjtcbiAgICAgIH1cbiAgICB9LFxuICAgIHsgLy9hPWV4dG1hcDoyIHVybjppZXRmOnBhcmFtczpydHAtaGRyZXh0OnRvZmZzZXRcbiAgICAgIC8vYT1leHRtYXA6MS9yZWN2b25seSBVUkktZ3BzLXN0cmluZ1xuICAgICAgcHVzaDogJ2V4dCcsXG4gICAgICByZWc6IC9eZXh0bWFwOihbXFx3X1xcL10qKSAoXFxTKikoPzogKFxcUyopKT8vLFxuICAgICAgbmFtZXM6IFsndmFsdWUnLCAndXJpJywgJ2NvbmZpZyddLCAvLyB2YWx1ZSBtYXkgaW5jbHVkZSBcIi9kaXJlY3Rpb25cIiBzdWZmaXhcbiAgICAgIGZvcm1hdDogZnVuY3Rpb24gKG8pIHtcbiAgICAgICAgcmV0dXJuIChvLmNvbmZpZyAhPSBudWxsKSA/XG4gICAgICAgICAgXCJleHRtYXA6JXMgJXMgJXNcIjpcbiAgICAgICAgICBcImV4dG1hcDolcyAlc1wiO1xuICAgICAgfVxuICAgIH0sXG4gICAge1xuICAgICAgLy9hPWNyeXB0bzoxIEFFU19DTV8xMjhfSE1BQ19TSEExXzgwIGlubGluZTpQUzF1UUNWZWVDRkNhblZtY2prcFB5d2pOV2hjWUQwbVhYdHhhVkJSfDJeMjB8MTozMlxuICAgICAgcHVzaDogJ2NyeXB0bycsXG4gICAgICByZWc6IC9eY3J5cHRvOihcXGQqKSAoW1xcd19dKikgKFxcUyopKD86IChcXFMqKSk/LyxcbiAgICAgIG5hbWVzOiBbJ2lkJywgJ3N1aXRlJywgJ2NvbmZpZycsICdzZXNzaW9uQ29uZmlnJ10sXG4gICAgICBmb3JtYXQ6IGZ1bmN0aW9uIChvKSB7XG4gICAgICAgIHJldHVybiAoby5zZXNzaW9uQ29uZmlnICE9IG51bGwpID9cbiAgICAgICAgICBcImNyeXB0bzolZCAlcyAlcyAlc1wiOlxuICAgICAgICAgIFwiY3J5cHRvOiVkICVzICVzXCI7XG4gICAgICB9XG4gICAgfSxcbiAgICB7IC8vYT1zZXR1cDphY3RwYXNzXG4gICAgICBuYW1lOiAnc2V0dXAnLFxuICAgICAgcmVnOiAvXnNldHVwOihcXHcqKS8sXG4gICAgICBmb3JtYXQ6IFwic2V0dXA6JXNcIlxuICAgIH0sXG4gICAgeyAvL2E9bWlkOjFcbiAgICAgIG5hbWU6ICdtaWQnLFxuICAgICAgcmVnOiAvXm1pZDooW15cXHNdKikvLFxuICAgICAgZm9ybWF0OiBcIm1pZDolc1wiXG4gICAgfSxcbiAgICB7IC8vYT1tc2lkOjBjOGIwNjRkLWQ4MDctNDNiNC1iNDM0LWY5MmE4ODlkODU4NyA5ODE3ODY4NS1kNDA5LTQ2ZTAtOGUxNi03ZWYwZGIwZGI2NGFcbiAgICAgIG5hbWU6ICdtc2lkJyxcbiAgICAgIHJlZzogL15tc2lkOiguKikvLFxuICAgICAgZm9ybWF0OiBcIm1zaWQ6JXNcIlxuICAgIH0sXG4gICAgeyAvL2E9cHRpbWU6MjBcbiAgICAgIG5hbWU6ICdwdGltZScsXG4gICAgICByZWc6IC9ecHRpbWU6KFxcZCopLyxcbiAgICAgIGZvcm1hdDogXCJwdGltZTolZFwiXG4gICAgfSxcbiAgICB7IC8vYT1tYXhwdGltZTo2MFxuICAgICAgbmFtZTogJ21heHB0aW1lJyxcbiAgICAgIHJlZzogL15tYXhwdGltZTooXFxkKikvLFxuICAgICAgZm9ybWF0OiBcIm1heHB0aW1lOiVkXCJcbiAgICB9LFxuICAgIHsgLy9hPXNlbmRyZWN2XG4gICAgICBuYW1lOiAnZGlyZWN0aW9uJyxcbiAgICAgIHJlZzogL14oc2VuZHJlY3Z8cmVjdm9ubHl8c2VuZG9ubHl8aW5hY3RpdmUpL1xuICAgIH0sXG4gICAgeyAvL2E9aWNlLWxpdGVcbiAgICAgIG5hbWU6ICdpY2VsaXRlJyxcbiAgICAgIHJlZzogL14oaWNlLWxpdGUpL1xuICAgIH0sXG4gICAgeyAvL2E9aWNlLXVmcmFnOkY3Z0lcbiAgICAgIG5hbWU6ICdpY2VVZnJhZycsXG4gICAgICByZWc6IC9eaWNlLXVmcmFnOihcXFMqKS8sXG4gICAgICBmb3JtYXQ6IFwiaWNlLXVmcmFnOiVzXCJcbiAgICB9LFxuICAgIHsgLy9hPWljZS1wd2Q6eDljbWwvWXppY2hWMitYbGhpTXU4Z1xuICAgICAgbmFtZTogJ2ljZVB3ZCcsXG4gICAgICByZWc6IC9eaWNlLXB3ZDooXFxTKikvLFxuICAgICAgZm9ybWF0OiBcImljZS1wd2Q6JXNcIlxuICAgIH0sXG4gICAgeyAvL2E9ZmluZ2VycHJpbnQ6U0hBLTEgMDA6MTE6MjI6MzM6NDQ6NTU6NjY6Nzc6ODg6OTk6QUE6QkI6Q0M6REQ6RUU6RkY6MDA6MTE6MjI6MzNcbiAgICAgIG5hbWU6ICdmaW5nZXJwcmludCcsXG4gICAgICByZWc6IC9eZmluZ2VycHJpbnQ6KFxcUyopIChcXFMqKS8sXG4gICAgICBuYW1lczogWyd0eXBlJywgJ2hhc2gnXSxcbiAgICAgIGZvcm1hdDogXCJmaW5nZXJwcmludDolcyAlc1wiXG4gICAgfSxcbiAgICB7XG4gICAgICAvL2E9Y2FuZGlkYXRlOjAgMSBVRFAgMjExMzY2NzMyNyAyMDMuMC4xMTMuMSA1NDQwMCB0eXAgaG9zdFxuICAgICAgLy9hPWNhbmRpZGF0ZToxMTYyODc1MDgxIDEgdWRwIDIxMTM5MzcxNTEgMTkyLjE2OC4zNC43NSA2MDAxNyB0eXAgaG9zdCBnZW5lcmF0aW9uIDBcbiAgICAgIC8vYT1jYW5kaWRhdGU6MzI4OTkxMjk1NyAyIHVkcCAxODQ1NTAxNjk1IDE5My44NC43Ny4xOTQgNjAwMTcgdHlwIHNyZmx4IHJhZGRyIDE5Mi4xNjguMzQuNzUgcnBvcnQgNjAwMTcgZ2VuZXJhdGlvbiAwXG4gICAgICBwdXNoOidjYW5kaWRhdGVzJyxcbiAgICAgIHJlZzogL15jYW5kaWRhdGU6KFxcUyopIChcXGQqKSAoXFxTKikgKFxcZCopIChcXFMqKSAoXFxkKikgdHlwIChcXFMqKSg/OiByYWRkciAoXFxTKikgcnBvcnQgKFxcZCopKT8oPzogZ2VuZXJhdGlvbiAoXFxkKikpPy8sXG4gICAgICBuYW1lczogWydmb3VuZGF0aW9uJywgJ2NvbXBvbmVudCcsICd0cmFuc3BvcnQnLCAncHJpb3JpdHknLCAnaXAnLCAncG9ydCcsICd0eXBlJywgJ3JhZGRyJywgJ3Jwb3J0JywgJ2dlbmVyYXRpb24nXSxcbiAgICAgIGZvcm1hdDogZnVuY3Rpb24gKG8pIHtcbiAgICAgICAgdmFyIHN0ciA9IFwiY2FuZGlkYXRlOiVzICVkICVzICVkICVzICVkIHR5cCAlc1wiO1xuICAgICAgICAvLyBOQjogY2FuZGlkYXRlIGhhcyB0d28gb3B0aW9uYWwgY2h1bmtzLCBzbyAldm9pZCBtaWRkbGUgb25lIGlmIGl0J3MgbWlzc2luZ1xuICAgICAgICBzdHIgKz0gKG8ucmFkZHIgIT0gbnVsbCkgPyBcIiByYWRkciAlcyBycG9ydCAlZFwiIDogXCIldiV2XCI7XG4gICAgICAgIGlmIChvLmdlbmVyYXRpb24gIT0gbnVsbCkge1xuICAgICAgICAgIHN0ciArPSBcIiBnZW5lcmF0aW9uICVkXCI7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHN0cjtcbiAgICAgIH1cbiAgICB9LFxuICAgIHsgLy9hPWVuZC1vZi1jYW5kaWRhdGVzIChrZWVwIGFmdGVyIHRoZSBjYW5kaWRhdGVzIGxpbmUgZm9yIHJlYWRhYmlsaXR5KVxuICAgICAgbmFtZTogJ2VuZE9mQ2FuZGlkYXRlcycsXG4gICAgICByZWc6IC9eKGVuZC1vZi1jYW5kaWRhdGVzKS9cbiAgICB9LFxuICAgIHsgLy9hPXJlbW90ZS1jYW5kaWRhdGVzOjEgMjAzLjAuMTEzLjEgNTQ0MDAgMiAyMDMuMC4xMTMuMSA1NDQwMSAuLi5cbiAgICAgIG5hbWU6ICdyZW1vdGVDYW5kaWRhdGVzJyxcbiAgICAgIHJlZzogL15yZW1vdGUtY2FuZGlkYXRlczooLiopLyxcbiAgICAgIGZvcm1hdDogXCJyZW1vdGUtY2FuZGlkYXRlczolc1wiXG4gICAgfSxcbiAgICB7IC8vYT1pY2Utb3B0aW9uczpnb29nbGUtaWNlXG4gICAgICBuYW1lOiAnaWNlT3B0aW9ucycsXG4gICAgICByZWc6IC9eaWNlLW9wdGlvbnM6KFxcUyopLyxcbiAgICAgIGZvcm1hdDogXCJpY2Utb3B0aW9uczolc1wiXG4gICAgfSxcbiAgICB7IC8vYT1zc3JjOjI1NjYxMDc1NjkgY25hbWU6dDlZVThNMVV4VEY4WTFBMVxuICAgICAgcHVzaDogXCJzc3Jjc1wiLFxuICAgICAgcmVnOiAvXnNzcmM6KFxcZCopIChbXFx3X10qKTooLiopLyxcbiAgICAgIG5hbWVzOiBbJ2lkJywgJ2F0dHJpYnV0ZScsICd2YWx1ZSddLFxuICAgICAgZm9ybWF0OiBcInNzcmM6JWQgJXM6JXNcIlxuICAgIH0sXG4gICAgeyAvL2E9c3NyYy1ncm91cDpGRUMgMSAyXG4gICAgICBwdXNoOiBcInNzcmNHcm91cHNcIixcbiAgICAgIHJlZzogL15zc3JjLWdyb3VwOihcXHcqKSAoLiopLyxcbiAgICAgIG5hbWVzOiBbJ3NlbWFudGljcycsICdzc3JjcyddLFxuICAgICAgZm9ybWF0OiBcInNzcmMtZ3JvdXA6JXMgJXNcIlxuICAgIH0sXG4gICAgeyAvL2E9bXNpZC1zZW1hbnRpYzogV01TIEp2bGFtNVgzU1gxT1A2cG4yMHpXb2d2YUtKejVIamY5T25sVlxuICAgICAgbmFtZTogXCJtc2lkU2VtYW50aWNcIixcbiAgICAgIHJlZzogL15tc2lkLXNlbWFudGljOlxccz8oXFx3KikgKFxcUyopLyxcbiAgICAgIG5hbWVzOiBbJ3NlbWFudGljJywgJ3Rva2VuJ10sXG4gICAgICBmb3JtYXQ6IFwibXNpZC1zZW1hbnRpYzogJXMgJXNcIiAvLyBzcGFjZSBhZnRlciBcIjpcIiBpcyBub3QgYWNjaWRlbnRhbFxuICAgIH0sXG4gICAgeyAvL2E9Z3JvdXA6QlVORExFIGF1ZGlvIHZpZGVvXG4gICAgICBwdXNoOiAnZ3JvdXBzJyxcbiAgICAgIHJlZzogL15ncm91cDooXFx3KikgKC4qKS8sXG4gICAgICBuYW1lczogWyd0eXBlJywgJ21pZHMnXSxcbiAgICAgIGZvcm1hdDogXCJncm91cDolcyAlc1wiXG4gICAgfSxcbiAgICB7IC8vYT1ydGNwLW11eFxuICAgICAgbmFtZTogJ3J0Y3BNdXgnLFxuICAgICAgcmVnOiAvXihydGNwLW11eCkvXG4gICAgfSxcbiAgICB7IC8vIGFueSBhPSB0aGF0IHdlIGRvbid0IHVuZGVyc3RhbmQgaXMga2VwdHMgdmVyYmF0aW0gb24gbWVkaWEuaW52YWxpZFxuICAgICAgcHVzaDogJ2ludmFsaWQnLFxuICAgICAgbmFtZXM6IFtcInZhbHVlXCJdXG4gICAgfVxuICBdXG59O1xuXG4vLyBzZXQgc2Vuc2libGUgZGVmYXVsdHMgdG8gYXZvaWQgcG9sbHV0aW5nIHRoZSBncmFtbWFyIHdpdGggYm9yaW5nIGRldGFpbHNcbk9iamVjdC5rZXlzKGdyYW1tYXIpLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICB2YXIgb2JqcyA9IGdyYW1tYXJba2V5XTtcbiAgb2Jqcy5mb3JFYWNoKGZ1bmN0aW9uIChvYmopIHtcbiAgICBpZiAoIW9iai5yZWcpIHtcbiAgICAgIG9iai5yZWcgPSAvKC4qKS87XG4gICAgfVxuICAgIGlmICghb2JqLmZvcm1hdCkge1xuICAgICAgb2JqLmZvcm1hdCA9IFwiJXNcIjtcbiAgICB9XG4gIH0pO1xufSk7XG4iLCJ2YXIgcGFyc2VyID0gcmVxdWlyZSgnLi9wYXJzZXInKTtcbnZhciB3cml0ZXIgPSByZXF1aXJlKCcuL3dyaXRlcicpO1xuXG5leHBvcnRzLndyaXRlID0gd3JpdGVyO1xuZXhwb3J0cy5wYXJzZSA9IHBhcnNlci5wYXJzZTtcbmV4cG9ydHMucGFyc2VGbXRwQ29uZmlnID0gcGFyc2VyLnBhcnNlRm10cENvbmZpZztcbmV4cG9ydHMucGFyc2VQYXlsb2FkcyA9IHBhcnNlci5wYXJzZVBheWxvYWRzO1xuZXhwb3J0cy5wYXJzZVJlbW90ZUNhbmRpZGF0ZXMgPSBwYXJzZXIucGFyc2VSZW1vdGVDYW5kaWRhdGVzO1xuIiwidmFyIHRvSW50SWZJbnQgPSBmdW5jdGlvbiAodikge1xuICByZXR1cm4gU3RyaW5nKE51bWJlcih2KSkgPT09IHYgPyBOdW1iZXIodikgOiB2O1xufTtcblxudmFyIGF0dGFjaFByb3BlcnRpZXMgPSBmdW5jdGlvbiAobWF0Y2gsIGxvY2F0aW9uLCBuYW1lcywgcmF3TmFtZSkge1xuICBpZiAocmF3TmFtZSAmJiAhbmFtZXMpIHtcbiAgICBsb2NhdGlvbltyYXdOYW1lXSA9IHRvSW50SWZJbnQobWF0Y2hbMV0pO1xuICB9XG4gIGVsc2Uge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbmFtZXMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgIGlmIChtYXRjaFtpKzFdICE9IG51bGwpIHtcbiAgICAgICAgbG9jYXRpb25bbmFtZXNbaV1dID0gdG9JbnRJZkludChtYXRjaFtpKzFdKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn07XG5cbnZhciBwYXJzZVJlZyA9IGZ1bmN0aW9uIChvYmosIGxvY2F0aW9uLCBjb250ZW50KSB7XG4gIHZhciBuZWVkc0JsYW5rID0gb2JqLm5hbWUgJiYgb2JqLm5hbWVzO1xuICBpZiAob2JqLnB1c2ggJiYgIWxvY2F0aW9uW29iai5wdXNoXSkge1xuICAgIGxvY2F0aW9uW29iai5wdXNoXSA9IFtdO1xuICB9XG4gIGVsc2UgaWYgKG5lZWRzQmxhbmsgJiYgIWxvY2F0aW9uW29iai5uYW1lXSkge1xuICAgIGxvY2F0aW9uW29iai5uYW1lXSA9IHt9O1xuICB9XG4gIHZhciBrZXlMb2NhdGlvbiA9IG9iai5wdXNoID9cbiAgICB7fSA6ICAvLyBibGFuayBvYmplY3QgdGhhdCB3aWxsIGJlIHB1c2hlZFxuICAgIG5lZWRzQmxhbmsgPyBsb2NhdGlvbltvYmoubmFtZV0gOiBsb2NhdGlvbjsgLy8gb3RoZXJ3aXNlLCBuYW1lZCBsb2NhdGlvbiBvciByb290XG5cbiAgYXR0YWNoUHJvcGVydGllcyhjb250ZW50Lm1hdGNoKG9iai5yZWcpLCBrZXlMb2NhdGlvbiwgb2JqLm5hbWVzLCBvYmoubmFtZSk7XG5cbiAgaWYgKG9iai5wdXNoKSB7XG4gICAgbG9jYXRpb25bb2JqLnB1c2hdLnB1c2goa2V5TG9jYXRpb24pO1xuICB9XG59O1xuXG52YXIgZ3JhbW1hciA9IHJlcXVpcmUoJy4vZ3JhbW1hcicpO1xudmFyIHZhbGlkTGluZSA9IFJlZ0V4cC5wcm90b3R5cGUudGVzdC5iaW5kKC9eKFthLXpdKT0oLiopLyk7XG5cbmV4cG9ydHMucGFyc2UgPSBmdW5jdGlvbiAoc2RwKSB7XG4gIHZhciBzZXNzaW9uID0ge31cbiAgICAsIG1lZGlhID0gW11cbiAgICAsIGxvY2F0aW9uID0gc2Vzc2lvbjsgLy8gcG9pbnRzIGF0IHdoZXJlIHByb3BlcnRpZXMgZ28gdW5kZXIgKG9uZSBvZiB0aGUgYWJvdmUpXG5cbiAgLy8gcGFyc2UgbGluZXMgd2UgdW5kZXJzdGFuZFxuICBzZHAuc3BsaXQoLyhcXHJcXG58XFxyfFxcbikvKS5maWx0ZXIodmFsaWRMaW5lKS5mb3JFYWNoKGZ1bmN0aW9uIChsKSB7XG4gICAgdmFyIHR5cGUgPSBsWzBdO1xuICAgIHZhciBjb250ZW50ID0gbC5zbGljZSgyKTtcbiAgICBpZiAodHlwZSA9PT0gJ20nKSB7XG4gICAgICBtZWRpYS5wdXNoKHtydHA6IFtdLCBmbXRwOiBbXX0pO1xuICAgICAgbG9jYXRpb24gPSBtZWRpYVttZWRpYS5sZW5ndGgtMV07IC8vIHBvaW50IGF0IGxhdGVzdCBtZWRpYSBsaW5lXG4gICAgfVxuXG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCAoZ3JhbW1hclt0eXBlXSB8fCBbXSkubGVuZ3RoOyBqICs9IDEpIHtcbiAgICAgIHZhciBvYmogPSBncmFtbWFyW3R5cGVdW2pdO1xuICAgICAgaWYgKG9iai5yZWcudGVzdChjb250ZW50KSkge1xuICAgICAgICByZXR1cm4gcGFyc2VSZWcob2JqLCBsb2NhdGlvbiwgY29udGVudCk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICBzZXNzaW9uLm1lZGlhID0gbWVkaWE7IC8vIGxpbmsgaXQgdXBcbiAgcmV0dXJuIHNlc3Npb247XG59O1xuXG52YXIgZm10cFJlZHVjZXIgPSBmdW5jdGlvbiAoYWNjLCBleHByKSB7XG4gIHZhciBzID0gZXhwci5zcGxpdCgnPScpO1xuICBpZiAocy5sZW5ndGggPT09IDIpIHtcbiAgICBhY2Nbc1swXV0gPSB0b0ludElmSW50KHNbMV0pO1xuICB9XG4gIHJldHVybiBhY2M7XG59O1xuXG5leHBvcnRzLnBhcnNlRm10cENvbmZpZyA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgcmV0dXJuIHN0ci5zcGxpdCgnOycpLnJlZHVjZShmbXRwUmVkdWNlciwge30pO1xufTtcblxuZXhwb3J0cy5wYXJzZVBheWxvYWRzID0gZnVuY3Rpb24gKHN0cikge1xuICByZXR1cm4gc3RyLnNwbGl0KCcgJykubWFwKE51bWJlcik7XG59O1xuXG5leHBvcnRzLnBhcnNlUmVtb3RlQ2FuZGlkYXRlcyA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgdmFyIGNhbmRpZGF0ZXMgPSBbXTtcbiAgdmFyIHBhcnRzID0gc3RyLnNwbGl0KCcgJykubWFwKHRvSW50SWZJbnQpO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSArPSAzKSB7XG4gICAgY2FuZGlkYXRlcy5wdXNoKHtcbiAgICAgIGNvbXBvbmVudDogcGFydHNbaV0sXG4gICAgICBpcDogcGFydHNbaSArIDFdLFxuICAgICAgcG9ydDogcGFydHNbaSArIDJdXG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGNhbmRpZGF0ZXM7XG59O1xuIiwidmFyIGdyYW1tYXIgPSByZXF1aXJlKCcuL2dyYW1tYXInKTtcblxuLy8gY3VzdG9taXplZCB1dGlsLmZvcm1hdCAtIGRpc2NhcmRzIGV4Y2VzcyBhcmd1bWVudHMgYW5kIGNhbiB2b2lkIG1pZGRsZSBvbmVzXG52YXIgZm9ybWF0UmVnRXhwID0gLyVbc2R2JV0vZztcbnZhciBmb3JtYXQgPSBmdW5jdGlvbiAoZm9ybWF0U3RyKSB7XG4gIHZhciBpID0gMTtcbiAgdmFyIGFyZ3MgPSBhcmd1bWVudHM7XG4gIHZhciBsZW4gPSBhcmdzLmxlbmd0aDtcbiAgcmV0dXJuIGZvcm1hdFN0ci5yZXBsYWNlKGZvcm1hdFJlZ0V4cCwgZnVuY3Rpb24gKHgpIHtcbiAgICBpZiAoaSA+PSBsZW4pIHtcbiAgICAgIHJldHVybiB4OyAvLyBtaXNzaW5nIGFyZ3VtZW50XG4gICAgfVxuICAgIHZhciBhcmcgPSBhcmdzW2ldO1xuICAgIGkgKz0gMTtcbiAgICBzd2l0Y2ggKHgpIHtcbiAgICAgIGNhc2UgJyUlJzpcbiAgICAgICAgcmV0dXJuICclJztcbiAgICAgIGNhc2UgJyVzJzpcbiAgICAgICAgcmV0dXJuIFN0cmluZyhhcmcpO1xuICAgICAgY2FzZSAnJWQnOlxuICAgICAgICByZXR1cm4gTnVtYmVyKGFyZyk7XG4gICAgICBjYXNlICcldic6XG4gICAgICAgIHJldHVybiAnJztcbiAgICB9XG4gIH0pO1xuICAvLyBOQjogd2UgZGlzY2FyZCBleGNlc3MgYXJndW1lbnRzIC0gdGhleSBhcmUgdHlwaWNhbGx5IHVuZGVmaW5lZCBmcm9tIG1ha2VMaW5lXG59O1xuXG52YXIgbWFrZUxpbmUgPSBmdW5jdGlvbiAodHlwZSwgb2JqLCBsb2NhdGlvbikge1xuICB2YXIgc3RyID0gb2JqLmZvcm1hdCBpbnN0YW5jZW9mIEZ1bmN0aW9uID9cbiAgICAob2JqLmZvcm1hdChvYmoucHVzaCA/IGxvY2F0aW9uIDogbG9jYXRpb25bb2JqLm5hbWVdKSkgOlxuICAgIG9iai5mb3JtYXQ7XG5cbiAgdmFyIGFyZ3MgPSBbdHlwZSArICc9JyArIHN0cl07XG4gIGlmIChvYmoubmFtZXMpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9iai5uYW1lcy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgdmFyIG4gPSBvYmoubmFtZXNbaV07XG4gICAgICBpZiAob2JqLm5hbWUpIHtcbiAgICAgICAgYXJncy5wdXNoKGxvY2F0aW9uW29iai5uYW1lXVtuXSk7XG4gICAgICB9XG4gICAgICBlbHNlIHsgLy8gZm9yIG1MaW5lIGFuZCBwdXNoIGF0dHJpYnV0ZXNcbiAgICAgICAgYXJncy5wdXNoKGxvY2F0aW9uW29iai5uYW1lc1tpXV0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBlbHNlIHtcbiAgICBhcmdzLnB1c2gobG9jYXRpb25bb2JqLm5hbWVdKTtcbiAgfVxuICByZXR1cm4gZm9ybWF0LmFwcGx5KG51bGwsIGFyZ3MpO1xufTtcblxuLy8gUkZDIHNwZWNpZmllZCBvcmRlclxuLy8gVE9ETzogZXh0ZW5kIHRoaXMgd2l0aCBhbGwgdGhlIHJlc3RcbnZhciBkZWZhdWx0T3V0ZXJPcmRlciA9IFtcbiAgJ3YnLCAnbycsICdzJywgJ2knLFxuICAndScsICdlJywgJ3AnLCAnYycsXG4gICdiJywgJ3QnLCAncicsICd6JywgJ2EnXG5dO1xudmFyIGRlZmF1bHRJbm5lck9yZGVyID0gWydpJywgJ2MnLCAnYicsICdhJ107XG5cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoc2Vzc2lvbiwgb3B0cykge1xuICBvcHRzID0gb3B0cyB8fCB7fTtcbiAgLy8gZW5zdXJlIGNlcnRhaW4gcHJvcGVydGllcyBleGlzdFxuICBpZiAoc2Vzc2lvbi52ZXJzaW9uID09IG51bGwpIHtcbiAgICBzZXNzaW9uLnZlcnNpb24gPSAwOyAvLyBcInY9MFwiIG11c3QgYmUgdGhlcmUgKG9ubHkgZGVmaW5lZCB2ZXJzaW9uIGF0bSlcbiAgfVxuICBpZiAoc2Vzc2lvbi5uYW1lID09IG51bGwpIHtcbiAgICBzZXNzaW9uLm5hbWUgPSBcIiBcIjsgLy8gXCJzPSBcIiBtdXN0IGJlIHRoZXJlIGlmIG5vIG1lYW5pbmdmdWwgbmFtZSBzZXRcbiAgfVxuICBzZXNzaW9uLm1lZGlhLmZvckVhY2goZnVuY3Rpb24gKG1MaW5lKSB7XG4gICAgaWYgKG1MaW5lLnBheWxvYWRzID09IG51bGwpIHtcbiAgICAgIG1MaW5lLnBheWxvYWRzID0gXCJcIjtcbiAgICB9XG4gIH0pO1xuXG4gIHZhciBvdXRlck9yZGVyID0gb3B0cy5vdXRlck9yZGVyIHx8IGRlZmF1bHRPdXRlck9yZGVyO1xuICB2YXIgaW5uZXJPcmRlciA9IG9wdHMuaW5uZXJPcmRlciB8fCBkZWZhdWx0SW5uZXJPcmRlcjtcbiAgdmFyIHNkcCA9IFtdO1xuXG4gIC8vIGxvb3AgdGhyb3VnaCBvdXRlck9yZGVyIGZvciBtYXRjaGluZyBwcm9wZXJ0aWVzIG9uIHNlc3Npb25cbiAgb3V0ZXJPcmRlci5mb3JFYWNoKGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgZ3JhbW1hclt0eXBlXS5mb3JFYWNoKGZ1bmN0aW9uIChvYmopIHtcbiAgICAgIGlmIChvYmoubmFtZSBpbiBzZXNzaW9uICYmIHNlc3Npb25bb2JqLm5hbWVdICE9IG51bGwpIHtcbiAgICAgICAgc2RwLnB1c2gobWFrZUxpbmUodHlwZSwgb2JqLCBzZXNzaW9uKSk7XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChvYmoucHVzaCBpbiBzZXNzaW9uICYmIHNlc3Npb25bb2JqLnB1c2hdICE9IG51bGwpIHtcbiAgICAgICAgc2Vzc2lvbltvYmoucHVzaF0uZm9yRWFjaChmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgICBzZHAucHVzaChtYWtlTGluZSh0eXBlLCBvYmosIGVsKSk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcblxuICAvLyB0aGVuIGZvciBlYWNoIG1lZGlhIGxpbmUsIGZvbGxvdyB0aGUgaW5uZXJPcmRlclxuICBzZXNzaW9uLm1lZGlhLmZvckVhY2goZnVuY3Rpb24gKG1MaW5lKSB7XG4gICAgc2RwLnB1c2gobWFrZUxpbmUoJ20nLCBncmFtbWFyLm1bMF0sIG1MaW5lKSk7XG5cbiAgICBpbm5lck9yZGVyLmZvckVhY2goZnVuY3Rpb24gKHR5cGUpIHtcbiAgICAgIGdyYW1tYXJbdHlwZV0uZm9yRWFjaChmdW5jdGlvbiAob2JqKSB7XG4gICAgICAgIGlmIChvYmoubmFtZSBpbiBtTGluZSAmJiBtTGluZVtvYmoubmFtZV0gIT0gbnVsbCkge1xuICAgICAgICAgIHNkcC5wdXNoKG1ha2VMaW5lKHR5cGUsIG9iaiwgbUxpbmUpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChvYmoucHVzaCBpbiBtTGluZSAmJiBtTGluZVtvYmoucHVzaF0gIT0gbnVsbCkge1xuICAgICAgICAgIG1MaW5lW29iai5wdXNoXS5mb3JFYWNoKGZ1bmN0aW9uIChlbCkge1xuICAgICAgICAgICAgc2RwLnB1c2gobWFrZUxpbmUodHlwZSwgb2JqLCBlbCkpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgcmV0dXJuIHNkcC5qb2luKCdcXHJcXG4nKSArICdcXHJcXG4nO1xufTtcbiIsInZhciBNZWRpYVN0cmVhbVR5cGUgPSB7XG4gICAgVklERU9fVFlQRTogXCJWaWRlb1wiLFxuXG4gICAgQVVESU9fVFlQRTogXCJBdWRpb1wiXG59O1xubW9kdWxlLmV4cG9ydHMgPSBNZWRpYVN0cmVhbVR5cGU7IiwidmFyIFJUQ0Jyb3dzZXJUeXBlID0ge1xuICAgIFJUQ19CUk9XU0VSX0NIUk9NRTogXCJydGNfYnJvd3Nlci5jaHJvbWVcIixcblxuICAgIFJUQ19CUk9XU0VSX0ZJUkVGT1g6IFwicnRjX2Jyb3dzZXIuZmlyZWZveFwiXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJUQ0Jyb3dzZXJUeXBlOyIsInZhciBSVENFdmVudHMgPSB7XG4gICAgTEFTVE5fQ0hBTkdFRDogXCJydGMubGFzdG5fY2hhbmdlZFwiLFxuICAgIERPTUlOQU5UU1BFQUtFUl9DSEFOR0VEOiBcInJ0Yy5kb21pbmFudHNwZWFrZXJfY2hhbmdlZFwiLFxuICAgIExBU1ROX0VORFBPSU5UX0NIQU5HRUQ6IFwicnRjLmxhc3RuX2VuZHBvaW50X2NoYW5nZWRcIixcbiAgICBTSU1VTENBU1RfTEFZRVJfQ0hBTkdFRDogXCJydGMuc2ltdWxjYXN0X2xheWVyX2NoYW5nZWRcIixcbiAgICBTSU1VTENBU1RfTEFZRVJfQ0hBTkdJTkc6IFwicnRjLnNpbXVsY2FzdF9sYXllcl9jaGFuZ2luZ1wiLFxuICAgIFNJTVVMQ0FTVF9TVEFSVDogXCJydGMuc2ltbGNhc3Rfc3RhcnRcIixcbiAgICBTSU1VTENBU1RfU1RPUDogXCJydGMuc2ltbGNhc3Rfc3RvcFwiLFxuICAgIEFWQUlMQUJMRV9ERVZJQ0VTX0NIQU5HRUQ6IFwicnRjLmF2YWlsYWJsZV9kZXZpY2VzX2NoYW5nZWRcIlxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBSVENFdmVudHM7IiwidmFyIFJlc29sdXRpb25zID0ge1xuICAgIFwiMTA4MFwiOiB7XG4gICAgICAgIHdpZHRoOiAxOTIwLFxuICAgICAgICBoZWlnaHQ6IDEwODAsXG4gICAgICAgIG9yZGVyOiA3XG4gICAgfSxcbiAgICBcImZ1bGxoZFwiOiB7XG4gICAgICAgIHdpZHRoOiAxOTIwLFxuICAgICAgICBoZWlnaHQ6IDEwODAsXG4gICAgICAgIG9yZGVyOiA3XG4gICAgfSxcbiAgICBcIjcyMFwiOiB7XG4gICAgICAgIHdpZHRoOiAxMjgwLFxuICAgICAgICBoZWlnaHQ6IDcyMCxcbiAgICAgICAgb3JkZXI6IDZcbiAgICB9LFxuICAgIFwiaGRcIjoge1xuICAgICAgICB3aWR0aDogMTI4MCxcbiAgICAgICAgaGVpZ2h0OiA3MjAsXG4gICAgICAgIG9yZGVyOiA2XG4gICAgfSxcbiAgICBcIjk2MFwiOiB7XG4gICAgICAgIHdpZHRoOiA5NjAsXG4gICAgICAgIGhlaWdodDogNzIwLFxuICAgICAgICBvcmRlcjogNVxuICAgIH0sXG4gICAgXCI2NDBcIjoge1xuICAgICAgICB3aWR0aDogNjQwLFxuICAgICAgICBoZWlnaHQ6IDQ4MCxcbiAgICAgICAgb3JkZXI6IDRcbiAgICB9LFxuICAgIFwidmdhXCI6IHtcbiAgICAgICAgd2lkdGg6IDY0MCxcbiAgICAgICAgaGVpZ2h0OiA0ODAsXG4gICAgICAgIG9yZGVyOiA0XG4gICAgfSxcbiAgICBcIjM2MFwiOiB7XG4gICAgICAgIHdpZHRoOiA2NDAsXG4gICAgICAgIGhlaWdodDogMzYwLFxuICAgICAgICBvcmRlcjogM1xuICAgIH0sXG4gICAgXCIzMjBcIjoge1xuICAgICAgICB3aWR0aDogMzIwLFxuICAgICAgICBoZWlnaHQ6IDI0MCxcbiAgICAgICAgb3JkZXI6IDJcbiAgICB9LFxuICAgIFwiMTgwXCI6IHtcbiAgICAgICAgd2lkdGg6IDMyMCxcbiAgICAgICAgaGVpZ2h0OiAxODAsXG4gICAgICAgIG9yZGVyOiAxXG4gICAgfVxufTtcbm1vZHVsZS5leHBvcnRzID0gUmVzb2x1dGlvbnM7IiwidmFyIFN0cmVhbUV2ZW50VHlwZXMgPSB7XG4gICAgRVZFTlRfVFlQRV9MT0NBTF9DUkVBVEVEOiBcInN0cmVhbS5sb2NhbF9jcmVhdGVkXCIsXG5cbiAgICBFVkVOVF9UWVBFX0xPQ0FMX0NIQU5HRUQ6IFwic3RyZWFtLmxvY2FsX2NoYW5nZWRcIixcblxuICAgIEVWRU5UX1RZUEVfTE9DQUxfRU5ERUQ6IFwic3RyZWFtLmxvY2FsX2VuZGVkXCIsXG5cbiAgICBFVkVOVF9UWVBFX1JFTU9URV9DUkVBVEVEOiBcInN0cmVhbS5yZW1vdGVfY3JlYXRlZFwiLFxuXG4gICAgRVZFTlRfVFlQRV9SRU1PVEVfRU5ERUQ6IFwic3RyZWFtLnJlbW90ZV9lbmRlZFwiLFxuXG4gICAgRVZFTlRfVFlQRV9SRU1PVEVfQ0hBTkdFRDogXCJzdHJlYW0uY2hhbmdlZFwiXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmVhbUV2ZW50VHlwZXM7IiwidmFyIFVJRXZlbnRzID0ge1xuICAgIE5JQ0tOQU1FX0NIQU5HRUQ6IFwiVUkubmlja25hbWVfY2hhbmdlZFwiLFxuICAgIFNFTEVDVEVEX0VORFBPSU5UOiBcIlVJLnNlbGVjdGVkX2VuZHBvaW50XCIsXG4gICAgUElOTkVEX0VORFBPSU5UOiBcIlVJLnBpbm5lZF9lbmRwb2ludFwiXG59O1xubW9kdWxlLmV4cG9ydHMgPSBVSUV2ZW50czsiLCJ2YXIgQXV0aGVudGljYXRpb25FdmVudHMgPSB7XG4gICAgLyoqXG4gICAgICogRXZlbnQgY2FsbGJhY2sgYXJndW1lbnRzOlxuICAgICAqIGZ1bmN0aW9uKGF1dGhlbnRpY2F0aW9uRW5hYmxlZCwgdXNlcklkZW50aXR5KVxuICAgICAqIGF1dGhlbnRpY2F0aW9uRW5hYmxlZCAtIGluZGljYXRlcyB3aGV0aGVyIGF1dGhlbnRpY2F0aW9uIGhhcyBiZWVuIGVuYWJsZWRcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICBpbiB0aGlzIHNlc3Npb25cbiAgICAgKiB1c2VySWRlbnRpdHkgLSBpZiB1c2VyIGhhcyBiZWVuIGxvZ2dlZCBpbiB0aGVuIGl0IGNvbnRhaW5zIHVzZXIgbmFtZS4gSWZcbiAgICAgKiAgICAgICAgICAgICAgICBjb250YWlucyAnbnVsbCcgb3IgJ3VuZGVmaW5lZCcgdGhlbiB1c2VyIGlzIG5vdCBsb2dnZWQgaW4uXG4gICAgICovXG4gICAgSURFTlRJVFlfVVBEQVRFRDogXCJhdXRoZW50aWNhdGlvbi5pZGVudGl0eV91cGRhdGVkXCJcbn07XG5tb2R1bGUuZXhwb3J0cyA9IEF1dGhlbnRpY2F0aW9uRXZlbnRzO1xuIiwidmFyIENRRXZlbnRzID0ge1xuICAgIExPQ0FMU1RBVFNfVVBEQVRFRDogXCJjcS5sb2NhbHN0YXRzX3VwZGF0ZWRcIixcbiAgICBSRU1PVEVTVEFUU19VUERBVEVEOiBcImNxLnJlbW90ZXN0YXRzX3VwZGF0ZWRcIixcbiAgICBTVE9QOiBcImNxLnN0b3BcIlxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDUUV2ZW50czsiLCJ2YXIgRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzID0ge1xuICAgIElOSVQ6IFwiZHMuaW5pdFwiLFxuXG4gICAgU1dJVENISU5HX0RPTkU6IFwiZHMuc3dpdGNoaW5nX2RvbmVcIixcblxuICAgIE5FV19TVFJFQU1fQ1JFQVRFRDogXCJkcy5uZXdfc3RyZWFtX2NyZWF0ZWRcIlxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXM7IiwibW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgZ2V0TGFuZ3VhZ2VzIDogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgbGFuZ3VhZ2VzID0gW107XG4gICAgICAgIGZvcih2YXIgbGFuZyBpbiB0aGlzKVxuICAgICAgICB7XG4gICAgICAgICAgICBpZih0eXBlb2YgdGhpc1tsYW5nXSA9PT0gXCJzdHJpbmdcIilcbiAgICAgICAgICAgICAgICBsYW5ndWFnZXMucHVzaCh0aGlzW2xhbmddKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbGFuZ3VhZ2VzO1xuICAgIH0sXG4gICAgRU46IFwiZW5cIixcbiAgICBCRzogXCJiZ1wiLFxuICAgIERFOiBcImRlXCIsXG4gICAgVFI6IFwidHJcIlxufSIsInZhciBYTVBQRXZlbnRzID0ge1xuICAgIENPTkZFUkVOQ0VfQ0VSQVRFRDogXCJ4bXBwLmNvbmZlcmVuY2VDcmVhdGVkLmppbmdsZVwiLFxuICAgIENBTExfVEVSTUlOQVRFRDogXCJ4bXBwLmNhbGx0ZXJtaW5hdGVkLmppbmdsZVwiLFxuICAgIENBTExfSU5DT01JTkc6IFwieG1wcC5jYWxsaW5jb21pbmcuamluZ2xlXCIsXG4gICAgRElTUE9TRV9DT05GRVJFTkNFOiBcInhtcHAuZGlzcG9jZV9jb25mZXJuY2VcIixcbiAgICBHUkFDRUZVTF9TSFVURE9XTjogXCJ4bXBwLmdyYWNlZnVsX3NodXRkb3duXCIsXG4gICAgS0lDS0VEOiBcInhtcHAua2lja2VkXCIsXG4gICAgQlJJREdFX0RPV046IFwieG1wcC5icmlkZ2VfZG93blwiLFxuICAgIFVTRVJfSURfQ0hBTkdFRDogXCJ4bXBwLnVzZXJfaWRfY2hhbmdlZFwiLFxuICAgIENIQU5HRURfU1RSRUFNUzogXCJ4bXBwLmNoYW5nZWRfc3RyZWFtc1wiLFxuICAgIE1VQ19KT0lORUQ6IFwieG1wcC5tdWNfam9pbmVkXCIsXG4gICAgTVVDX0VOVEVSOiBcInhtcHAubXVjX2VudGVyXCIsXG4gICAgTVVDX1JPTEVfQ0hBTkdFRDogXCJ4bXBwLm11Y19yb2xlX2NoYW5nZWRcIixcbiAgICBNVUNfTEVGVDogXCJ4bXBwLm11Y19sZWZ0XCIsXG4gICAgTVVDX0RFU1RST1lFRDogXCJ4bXBwLm11Y19kZXN0cm95ZWRcIixcbiAgICBESVNQTEFZX05BTUVfQ0hBTkdFRDogXCJ4bXBwLmRpc3BsYXlfbmFtZV9jaGFuZ2VkXCIsXG4gICAgUkVNT1RFX1NUQVRTOiBcInhtcHAucmVtb3RlX3N0YXRzXCIsXG4gICAgTE9DQUxST0xFX0NIQU5HRUQ6IFwieG1wcC5sb2NhbHJvbGVfY2hhbmdlZFwiLFxuICAgIFBSRVNFTkNFX1NUQVRVUzogXCJ4bXBwLnByZXNlbmNlX3N0YXR1c1wiLFxuICAgIFJFU0VSVkFUSU9OX0VSUk9SOiBcInhtcHAucm9vbV9yZXNlcnZhdGlvbl9lcnJvclwiLFxuICAgIFNVQkpFQ1RfQ0hBTkdFRDogXCJ4bXBwLnN1YmplY3RfY2hhbmdlZFwiLFxuICAgIE1FU1NBR0VfUkVDRUlWRUQ6IFwieG1wcC5tZXNzYWdlX3JlY2VpdmVkXCIsXG4gICAgU0VORElOR19DSEFUX01FU1NBR0U6IFwieG1wcC5zZW5kaW5nX2NoYXRfbWVzc2FnZVwiLFxuICAgIFBBU1NXT1JEX1JFUVVJUkVEOiBcInhtcHAucGFzc3dvcmRfcmVxdWlyZWRcIixcbiAgICBBVVRIRU5USUNBVElPTl9SRVFVSVJFRDogXCJ4bXBwLmF1dGhlbnRpY2F0aW9uX3JlcXVpcmVkXCIsXG4gICAgQ0hBVF9FUlJPUl9SRUNFSVZFRDogXCJ4bXBwLmNoYXRfZXJyb3JfcmVjZWl2ZWRcIixcbiAgICBFVEhFUlBBRDogXCJ4bXBwLmV0aGVycGFkXCIsXG4gICAgREVWSUNFX0FWQUlMQUJMRTogXCJ4bXBwLmRldmljZV9hdmFpbGFibGVcIlxufTtcbm1vZHVsZS5leHBvcnRzID0gWE1QUEV2ZW50czsiLCIvLyBDb3B5cmlnaHQgSm95ZW50LCBJbmMuIGFuZCBvdGhlciBOb2RlIGNvbnRyaWJ1dG9ycy5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYVxuLy8gY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZVxuLy8gXCJTb2Z0d2FyZVwiKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nXG4vLyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsXG4vLyBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0XG4vLyBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGVcbi8vIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkXG4vLyBpbiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbi8vXG4vLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTXG4vLyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GXG4vLyBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOXG4vLyBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSxcbi8vIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUlxuLy8gT1RIRVJXSVNFLCBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRVxuLy8gVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS5cblxuZnVuY3Rpb24gRXZlbnRFbWl0dGVyKCkge1xuICB0aGlzLl9ldmVudHMgPSB0aGlzLl9ldmVudHMgfHwge307XG4gIHRoaXMuX21heExpc3RlbmVycyA9IHRoaXMuX21heExpc3RlbmVycyB8fCB1bmRlZmluZWQ7XG59XG5tb2R1bGUuZXhwb3J0cyA9IEV2ZW50RW1pdHRlcjtcblxuLy8gQmFja3dhcmRzLWNvbXBhdCB3aXRoIG5vZGUgMC4xMC54XG5FdmVudEVtaXR0ZXIuRXZlbnRFbWl0dGVyID0gRXZlbnRFbWl0dGVyO1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLl9ldmVudHMgPSB1bmRlZmluZWQ7XG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLl9tYXhMaXN0ZW5lcnMgPSB1bmRlZmluZWQ7XG5cbi8vIEJ5IGRlZmF1bHQgRXZlbnRFbWl0dGVycyB3aWxsIHByaW50IGEgd2FybmluZyBpZiBtb3JlIHRoYW4gMTAgbGlzdGVuZXJzIGFyZVxuLy8gYWRkZWQgdG8gaXQuIFRoaXMgaXMgYSB1c2VmdWwgZGVmYXVsdCB3aGljaCBoZWxwcyBmaW5kaW5nIG1lbW9yeSBsZWFrcy5cbkV2ZW50RW1pdHRlci5kZWZhdWx0TWF4TGlzdGVuZXJzID0gMTA7XG5cbi8vIE9idmlvdXNseSBub3QgYWxsIEVtaXR0ZXJzIHNob3VsZCBiZSBsaW1pdGVkIHRvIDEwLiBUaGlzIGZ1bmN0aW9uIGFsbG93c1xuLy8gdGhhdCB0byBiZSBpbmNyZWFzZWQuIFNldCB0byB6ZXJvIGZvciB1bmxpbWl0ZWQuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnNldE1heExpc3RlbmVycyA9IGZ1bmN0aW9uKG4pIHtcbiAgaWYgKCFpc051bWJlcihuKSB8fCBuIDwgMCB8fCBpc05hTihuKSlcbiAgICB0aHJvdyBUeXBlRXJyb3IoJ24gbXVzdCBiZSBhIHBvc2l0aXZlIG51bWJlcicpO1xuICB0aGlzLl9tYXhMaXN0ZW5lcnMgPSBuO1xuICByZXR1cm4gdGhpcztcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuZW1pdCA9IGZ1bmN0aW9uKHR5cGUpIHtcbiAgdmFyIGVyLCBoYW5kbGVyLCBsZW4sIGFyZ3MsIGksIGxpc3RlbmVycztcblxuICBpZiAoIXRoaXMuX2V2ZW50cylcbiAgICB0aGlzLl9ldmVudHMgPSB7fTtcblxuICAvLyBJZiB0aGVyZSBpcyBubyAnZXJyb3InIGV2ZW50IGxpc3RlbmVyIHRoZW4gdGhyb3cuXG4gIGlmICh0eXBlID09PSAnZXJyb3InKSB7XG4gICAgaWYgKCF0aGlzLl9ldmVudHMuZXJyb3IgfHxcbiAgICAgICAgKGlzT2JqZWN0KHRoaXMuX2V2ZW50cy5lcnJvcikgJiYgIXRoaXMuX2V2ZW50cy5lcnJvci5sZW5ndGgpKSB7XG4gICAgICBlciA9IGFyZ3VtZW50c1sxXTtcbiAgICAgIGlmIChlciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICAgIHRocm93IGVyOyAvLyBVbmhhbmRsZWQgJ2Vycm9yJyBldmVudFxuICAgICAgfVxuICAgICAgdGhyb3cgVHlwZUVycm9yKCdVbmNhdWdodCwgdW5zcGVjaWZpZWQgXCJlcnJvclwiIGV2ZW50LicpO1xuICAgIH1cbiAgfVxuXG4gIGhhbmRsZXIgPSB0aGlzLl9ldmVudHNbdHlwZV07XG5cbiAgaWYgKGlzVW5kZWZpbmVkKGhhbmRsZXIpKVxuICAgIHJldHVybiBmYWxzZTtcblxuICBpZiAoaXNGdW5jdGlvbihoYW5kbGVyKSkge1xuICAgIHN3aXRjaCAoYXJndW1lbnRzLmxlbmd0aCkge1xuICAgICAgLy8gZmFzdCBjYXNlc1xuICAgICAgY2FzZSAxOlxuICAgICAgICBoYW5kbGVyLmNhbGwodGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAyOlxuICAgICAgICBoYW5kbGVyLmNhbGwodGhpcywgYXJndW1lbnRzWzFdKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDM6XG4gICAgICAgIGhhbmRsZXIuY2FsbCh0aGlzLCBhcmd1bWVudHNbMV0sIGFyZ3VtZW50c1syXSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgLy8gc2xvd2VyXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBsZW4gPSBhcmd1bWVudHMubGVuZ3RoO1xuICAgICAgICBhcmdzID0gbmV3IEFycmF5KGxlbiAtIDEpO1xuICAgICAgICBmb3IgKGkgPSAxOyBpIDwgbGVuOyBpKyspXG4gICAgICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG4gICAgICAgIGhhbmRsZXIuYXBwbHkodGhpcywgYXJncyk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGlzT2JqZWN0KGhhbmRsZXIpKSB7XG4gICAgbGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICBhcmdzID0gbmV3IEFycmF5KGxlbiAtIDEpO1xuICAgIGZvciAoaSA9IDE7IGkgPCBsZW47IGkrKylcbiAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuXG4gICAgbGlzdGVuZXJzID0gaGFuZGxlci5zbGljZSgpO1xuICAgIGxlbiA9IGxpc3RlbmVycy5sZW5ndGg7XG4gICAgZm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKVxuICAgICAgbGlzdGVuZXJzW2ldLmFwcGx5KHRoaXMsIGFyZ3MpO1xuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmFkZExpc3RlbmVyID0gZnVuY3Rpb24odHlwZSwgbGlzdGVuZXIpIHtcbiAgdmFyIG07XG5cbiAgaWYgKCFpc0Z1bmN0aW9uKGxpc3RlbmVyKSlcbiAgICB0aHJvdyBUeXBlRXJyb3IoJ2xpc3RlbmVyIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzKVxuICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuXG4gIC8vIFRvIGF2b2lkIHJlY3Vyc2lvbiBpbiB0aGUgY2FzZSB0aGF0IHR5cGUgPT09IFwibmV3TGlzdGVuZXJcIiEgQmVmb3JlXG4gIC8vIGFkZGluZyBpdCB0byB0aGUgbGlzdGVuZXJzLCBmaXJzdCBlbWl0IFwibmV3TGlzdGVuZXJcIi5cbiAgaWYgKHRoaXMuX2V2ZW50cy5uZXdMaXN0ZW5lcilcbiAgICB0aGlzLmVtaXQoJ25ld0xpc3RlbmVyJywgdHlwZSxcbiAgICAgICAgICAgICAgaXNGdW5jdGlvbihsaXN0ZW5lci5saXN0ZW5lcikgP1xuICAgICAgICAgICAgICBsaXN0ZW5lci5saXN0ZW5lciA6IGxpc3RlbmVyKTtcblxuICBpZiAoIXRoaXMuX2V2ZW50c1t0eXBlXSlcbiAgICAvLyBPcHRpbWl6ZSB0aGUgY2FzZSBvZiBvbmUgbGlzdGVuZXIuIERvbid0IG5lZWQgdGhlIGV4dHJhIGFycmF5IG9iamVjdC5cbiAgICB0aGlzLl9ldmVudHNbdHlwZV0gPSBsaXN0ZW5lcjtcbiAgZWxzZSBpZiAoaXNPYmplY3QodGhpcy5fZXZlbnRzW3R5cGVdKSlcbiAgICAvLyBJZiB3ZSd2ZSBhbHJlYWR5IGdvdCBhbiBhcnJheSwganVzdCBhcHBlbmQuXG4gICAgdGhpcy5fZXZlbnRzW3R5cGVdLnB1c2gobGlzdGVuZXIpO1xuICBlbHNlXG4gICAgLy8gQWRkaW5nIHRoZSBzZWNvbmQgZWxlbWVudCwgbmVlZCB0byBjaGFuZ2UgdG8gYXJyYXkuXG4gICAgdGhpcy5fZXZlbnRzW3R5cGVdID0gW3RoaXMuX2V2ZW50c1t0eXBlXSwgbGlzdGVuZXJdO1xuXG4gIC8vIENoZWNrIGZvciBsaXN0ZW5lciBsZWFrXG4gIGlmIChpc09iamVjdCh0aGlzLl9ldmVudHNbdHlwZV0pICYmICF0aGlzLl9ldmVudHNbdHlwZV0ud2FybmVkKSB7XG4gICAgdmFyIG07XG4gICAgaWYgKCFpc1VuZGVmaW5lZCh0aGlzLl9tYXhMaXN0ZW5lcnMpKSB7XG4gICAgICBtID0gdGhpcy5fbWF4TGlzdGVuZXJzO1xuICAgIH0gZWxzZSB7XG4gICAgICBtID0gRXZlbnRFbWl0dGVyLmRlZmF1bHRNYXhMaXN0ZW5lcnM7XG4gICAgfVxuXG4gICAgaWYgKG0gJiYgbSA+IDAgJiYgdGhpcy5fZXZlbnRzW3R5cGVdLmxlbmd0aCA+IG0pIHtcbiAgICAgIHRoaXMuX2V2ZW50c1t0eXBlXS53YXJuZWQgPSB0cnVlO1xuICAgICAgY29uc29sZS5lcnJvcignKG5vZGUpIHdhcm5pbmc6IHBvc3NpYmxlIEV2ZW50RW1pdHRlciBtZW1vcnkgJyArXG4gICAgICAgICAgICAgICAgICAgICdsZWFrIGRldGVjdGVkLiAlZCBsaXN0ZW5lcnMgYWRkZWQuICcgK1xuICAgICAgICAgICAgICAgICAgICAnVXNlIGVtaXR0ZXIuc2V0TWF4TGlzdGVuZXJzKCkgdG8gaW5jcmVhc2UgbGltaXQuJyxcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fZXZlbnRzW3R5cGVdLmxlbmd0aCk7XG4gICAgICBpZiAodHlwZW9mIGNvbnNvbGUudHJhY2UgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgLy8gbm90IHN1cHBvcnRlZCBpbiBJRSAxMFxuICAgICAgICBjb25zb2xlLnRyYWNlKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLm9uID0gRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lcjtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbmNlID0gZnVuY3Rpb24odHlwZSwgbGlzdGVuZXIpIHtcbiAgaWYgKCFpc0Z1bmN0aW9uKGxpc3RlbmVyKSlcbiAgICB0aHJvdyBUeXBlRXJyb3IoJ2xpc3RlbmVyIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuXG4gIHZhciBmaXJlZCA9IGZhbHNlO1xuXG4gIGZ1bmN0aW9uIGcoKSB7XG4gICAgdGhpcy5yZW1vdmVMaXN0ZW5lcih0eXBlLCBnKTtcblxuICAgIGlmICghZmlyZWQpIHtcbiAgICAgIGZpcmVkID0gdHJ1ZTtcbiAgICAgIGxpc3RlbmVyLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuICB9XG5cbiAgZy5saXN0ZW5lciA9IGxpc3RlbmVyO1xuICB0aGlzLm9uKHR5cGUsIGcpO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxuLy8gZW1pdHMgYSAncmVtb3ZlTGlzdGVuZXInIGV2ZW50IGlmZiB0aGUgbGlzdGVuZXIgd2FzIHJlbW92ZWRcbkV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlTGlzdGVuZXIgPSBmdW5jdGlvbih0eXBlLCBsaXN0ZW5lcikge1xuICB2YXIgbGlzdCwgcG9zaXRpb24sIGxlbmd0aCwgaTtcblxuICBpZiAoIWlzRnVuY3Rpb24obGlzdGVuZXIpKVxuICAgIHRocm93IFR5cGVFcnJvcignbGlzdGVuZXIgbXVzdCBiZSBhIGZ1bmN0aW9uJyk7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMgfHwgIXRoaXMuX2V2ZW50c1t0eXBlXSlcbiAgICByZXR1cm4gdGhpcztcblxuICBsaXN0ID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xuICBsZW5ndGggPSBsaXN0Lmxlbmd0aDtcbiAgcG9zaXRpb24gPSAtMTtcblxuICBpZiAobGlzdCA9PT0gbGlzdGVuZXIgfHxcbiAgICAgIChpc0Z1bmN0aW9uKGxpc3QubGlzdGVuZXIpICYmIGxpc3QubGlzdGVuZXIgPT09IGxpc3RlbmVyKSkge1xuICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gICAgaWYgKHRoaXMuX2V2ZW50cy5yZW1vdmVMaXN0ZW5lcilcbiAgICAgIHRoaXMuZW1pdCgncmVtb3ZlTGlzdGVuZXInLCB0eXBlLCBsaXN0ZW5lcik7XG5cbiAgfSBlbHNlIGlmIChpc09iamVjdChsaXN0KSkge1xuICAgIGZvciAoaSA9IGxlbmd0aDsgaS0tID4gMDspIHtcbiAgICAgIGlmIChsaXN0W2ldID09PSBsaXN0ZW5lciB8fFxuICAgICAgICAgIChsaXN0W2ldLmxpc3RlbmVyICYmIGxpc3RbaV0ubGlzdGVuZXIgPT09IGxpc3RlbmVyKSkge1xuICAgICAgICBwb3NpdGlvbiA9IGk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChwb3NpdGlvbiA8IDApXG4gICAgICByZXR1cm4gdGhpcztcblxuICAgIGlmIChsaXN0Lmxlbmd0aCA9PT0gMSkge1xuICAgICAgbGlzdC5sZW5ndGggPSAwO1xuICAgICAgZGVsZXRlIHRoaXMuX2V2ZW50c1t0eXBlXTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGlzdC5zcGxpY2UocG9zaXRpb24sIDEpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLl9ldmVudHMucmVtb3ZlTGlzdGVuZXIpXG4gICAgICB0aGlzLmVtaXQoJ3JlbW92ZUxpc3RlbmVyJywgdHlwZSwgbGlzdGVuZXIpO1xuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUFsbExpc3RlbmVycyA9IGZ1bmN0aW9uKHR5cGUpIHtcbiAgdmFyIGtleSwgbGlzdGVuZXJzO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzKVxuICAgIHJldHVybiB0aGlzO1xuXG4gIC8vIG5vdCBsaXN0ZW5pbmcgZm9yIHJlbW92ZUxpc3RlbmVyLCBubyBuZWVkIHRvIGVtaXRcbiAgaWYgKCF0aGlzLl9ldmVudHMucmVtb3ZlTGlzdGVuZXIpIHtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMClcbiAgICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuICAgIGVsc2UgaWYgKHRoaXMuX2V2ZW50c1t0eXBlXSlcbiAgICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvLyBlbWl0IHJlbW92ZUxpc3RlbmVyIGZvciBhbGwgbGlzdGVuZXJzIG9uIGFsbCBldmVudHNcbiAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICBmb3IgKGtleSBpbiB0aGlzLl9ldmVudHMpIHtcbiAgICAgIGlmIChrZXkgPT09ICdyZW1vdmVMaXN0ZW5lcicpIGNvbnRpbnVlO1xuICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoa2V5KTtcbiAgICB9XG4gICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3JlbW92ZUxpc3RlbmVyJyk7XG4gICAgdGhpcy5fZXZlbnRzID0ge307XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XG5cbiAgaWYgKGlzRnVuY3Rpb24obGlzdGVuZXJzKSkge1xuICAgIHRoaXMucmVtb3ZlTGlzdGVuZXIodHlwZSwgbGlzdGVuZXJzKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBMSUZPIG9yZGVyXG4gICAgd2hpbGUgKGxpc3RlbmVycy5sZW5ndGgpXG4gICAgICB0aGlzLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVyc1tsaXN0ZW5lcnMubGVuZ3RoIC0gMV0pO1xuICB9XG4gIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmxpc3RlbmVycyA9IGZ1bmN0aW9uKHR5cGUpIHtcbiAgdmFyIHJldDtcbiAgaWYgKCF0aGlzLl9ldmVudHMgfHwgIXRoaXMuX2V2ZW50c1t0eXBlXSlcbiAgICByZXQgPSBbXTtcbiAgZWxzZSBpZiAoaXNGdW5jdGlvbih0aGlzLl9ldmVudHNbdHlwZV0pKVxuICAgIHJldCA9IFt0aGlzLl9ldmVudHNbdHlwZV1dO1xuICBlbHNlXG4gICAgcmV0ID0gdGhpcy5fZXZlbnRzW3R5cGVdLnNsaWNlKCk7XG4gIHJldHVybiByZXQ7XG59O1xuXG5FdmVudEVtaXR0ZXIubGlzdGVuZXJDb3VudCA9IGZ1bmN0aW9uKGVtaXR0ZXIsIHR5cGUpIHtcbiAgdmFyIHJldDtcbiAgaWYgKCFlbWl0dGVyLl9ldmVudHMgfHwgIWVtaXR0ZXIuX2V2ZW50c1t0eXBlXSlcbiAgICByZXQgPSAwO1xuICBlbHNlIGlmIChpc0Z1bmN0aW9uKGVtaXR0ZXIuX2V2ZW50c1t0eXBlXSkpXG4gICAgcmV0ID0gMTtcbiAgZWxzZVxuICAgIHJldCA9IGVtaXR0ZXIuX2V2ZW50c1t0eXBlXS5sZW5ndGg7XG4gIHJldHVybiByZXQ7XG59O1xuXG5mdW5jdGlvbiBpc0Z1bmN0aW9uKGFyZykge1xuICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ2Z1bmN0aW9uJztcbn1cblxuZnVuY3Rpb24gaXNOdW1iZXIoYXJnKSB7XG4gIHJldHVybiB0eXBlb2YgYXJnID09PSAnbnVtYmVyJztcbn1cblxuZnVuY3Rpb24gaXNPYmplY3QoYXJnKSB7XG4gIHJldHVybiB0eXBlb2YgYXJnID09PSAnb2JqZWN0JyAmJiBhcmcgIT09IG51bGw7XG59XG5cbmZ1bmN0aW9uIGlzVW5kZWZpbmVkKGFyZykge1xuICByZXR1cm4gYXJnID09PSB2b2lkIDA7XG59XG4iXX0=