!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 -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":86}],3:[function(require,module,exports){ /* global Strophe, updateLargeVideo, 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().userJid; // 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') { 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":80}],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) { var tracks = this.getTracks(); for (var idx = 0; idx < tracks.length; idx++) { tracks[idx].enabled = mute; } }; LocalStream.prototype.isMuted = function () { var tracks = []; if(this.type == "audio") { tracks = this.stream.getAudioTracks(); } else { 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":81}],5:[function(require,module,exports){ ////These lines should be uncommented when require works in app.js var RTCBrowserType = require("../../service/RTC/RTCBrowserType.js"); var MediaStreamType = require("../../service/RTC/MediaStreamTypes"); /** * 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) { // 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; if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX) { if (!this.getVideoTracks) this.getVideoTracks = function () { return []; }; if (!this.getAudioTracks) this.getAudioTracks = function () { return []; }; } } MediaStream.prototype.getOriginalStream = function() { return this.stream; } MediaStream.prototype.setMute = function (value) { this.stream.muted = value; this.muted = value; } module.exports = MediaStream; },{"../../service/RTC/MediaStreamTypes":78,"../../service/RTC/RTCBrowserType.js":79}],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 XMPPEvents = require("../../service/xmpp/XMPPEvents"); var UIEvents = require("../../service/UI/UIEvents"); var eventEmitter = new EventEmitter(); var RTC = { rtcUtils: null, 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) { var localStream = new LocalStream(stream, type, eventEmitter); //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()); 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); console.debug("ADD remote stream ", remoteStream.type, " ", jid, " ", thessrc); 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.videoType = 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 false; 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"); this.localVideo = this.createLocalStream(stream, "video", true, type); // Stop the stream to trigger onended event for old stream oldStream.stop(); APP.xmpp.switchStreams(stream, oldStream,callback); }, /** * 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() && APP.xmpp.myResource() === 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; } }; module.exports = RTC; },{"../../service/RTC/MediaStreamTypes":78,"../../service/RTC/StreamEventTypes.js":81,"../../service/UI/UIEvents":82,"../../service/desktopsharing/DesktopSharingEventTypes":84,"../../service/xmpp/XMPPEvents":86,"./DataChannels":3,"./LocalStream.js":4,"./MediaStream.js":5,"./RTCUtils.js":7,"events":87}],7:[function(require,module,exports){ var RTCBrowserType = require("../../service/RTC/RTCBrowserType.js"); function setResolutionConstraints(constraints, resolution, isAndroid) { if (resolution && !constraints.video || isAndroid) { constraints.video = { mandatory: {}, optional: [] };// same behaviour as true } // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions switch (resolution) { // 16:9 first case '1080': case 'fullhd': constraints.video.mandatory.minWidth = 1920; constraints.video.mandatory.minHeight = 1080; break; case '720': case 'hd': constraints.video.mandatory.minWidth = 1280; constraints.video.mandatory.minHeight = 720; break; case '360': constraints.video.mandatory.minWidth = 640; constraints.video.mandatory.minHeight = 360; break; case '180': constraints.video.mandatory.minWidth = 320; constraints.video.mandatory.minHeight = 180; break; // 4:3 case '960': constraints.video.mandatory.minWidth = 960; constraints.video.mandatory.minHeight = 720; break; case '640': case 'vga': constraints.video.mandatory.minWidth = 640; constraints.video.mandatory.minHeight = 480; break; case '320': constraints.video.mandatory.minWidth = 320; constraints.video.mandatory.minHeight = 240; break; default: if (isAndroid) { constraints.video.mandatory.minWidth = 320; constraints.video.mandatory.minHeight = 240; constraints.video.mandatory.maxFrameRate = 15; } break; } 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 >= 22) { this.peerconnection = mozRTCPeerConnection; this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX; this.getUserMedia = navigator.mozGetUserMedia.bind(navigator); this.pc_constraints = {}; this.attachMediaStream = function (element, stream) { 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 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 = 'webrtcrequired.html'; return; } if (this.browser !== RTCBrowserType.RTC_BROWSER_CHROME && config.enableFirefoxSupport !== true) { window.location.href = 'chromeonly.html'; return; } } RTCUtils.prototype.getUserMediaWithConstraints = function( um, success_callback, failure_callback, resolution,bandwidth, fps, desktopStream) { // 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; 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'); success_callback(stream); }, function (error) { console.warn('Failed to get access to local media. Error ', error); if (failure_callback) { failure_callback(error); } }); } else { this.getUserMedia(constraints, function (stream) { console.log('onUserMediaSuccess'); success_callback(stream); }, function (error) { 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); } } }; /** * We ask for audio and video combined stream in order to get permissions and * not to ask twice. */ RTCUtils.prototype.obtainAudioAndVideoPermissions = function() { var self = this; // Get AV var cb = function (stream) { console.log('got', stream, stream.getAudioTracks().length, stream.getVideoTracks().length); self.handleLocalStream(stream); }; var self = this; this.getUserMediaWithConstraints( ['audio', 'video'], cb, function (error) { console.error('failed to obtain audio/video stream - trying audio only', error); self.getUserMediaWithConstraints( ['audio'], cb, function (error) { console.error('failed to obtain audio/video stream - stop', error); APP.UI.messageHandler.showError("Error", "Failed to obtain permissions to use the local microphone" + "and/or camera."); } ); }, config.resolution || '360'); } RTCUtils.prototype.handleLocalStream = function(stream) { if(window.webkitMediaStream) { var audioStream = new webkitMediaStream(); var videoStream = new webkitMediaStream(); var audioTracks = stream.getAudioTracks(); var videoTracks = stream.getVideoTracks(); for (var i = 0; i < audioTracks.length; i++) { audioStream.addTrack(audioTracks[i]); } this.service.createLocalStream(audioStream, "audio"); for (i = 0; i < videoTracks.length; i++) { videoStream.addTrack(videoTracks[i]); } this.service.createLocalStream(videoStream, "video"); } else {//firefox this.service.createLocalStream(stream, "stream"); } }; module.exports = RTCUtils; },{"../../service/RTC/RTCBrowserType.js":79}],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("./side_pannels/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.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); }); VideoLayout.init(eventEmitter); 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.KICKED, function () { messageHandler.openMessageDialog("Session Terminated", "Ouch! You have been kicked out of the meet!"); }); APP.xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () { messageHandler.showError("Error", "Jitsi Videobridge is currently unavailable. Please try again later!"); }); 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); } /** * Mutes/unmutes the local video. * * @param mute true to mute the local video; otherwise, false * @param options an object which specifies optional arguments such as the * boolean key byUser with default value true which * specifies whether the method was initiated in response to a user command (in * contrast to an automatic decision taken by the application logic) */ function setVideoMute(mute, options) { APP.xmpp.setVideoMute( mute, function (mute) { var video = $('#video'); var communicativeClass = "icon-camera"; var muteClass = "icon-camera icon-camera-disabled"; if (mute) { video.removeClass(communicativeClass); video.addClass(muteClass); } else { video.removeClass(muteClass); video.addClass(communicativeClass); } }, options); } 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}); 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(); 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); document.getElementById('localNick').appendChild( document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)') ); 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(); // Show authenticate button if needed Toolbar.showAuthenticateButton( APP.xmpp.isExternalAuthEnabled() && !APP.xmpp.isModerator()); var displayName = !config.displayJids ? info.displayName : Strophe.getResourceFromJid(jid); if (displayName) onDisplayNameChanged('localVideoContainer', displayName + ' (me)'); } 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 || 'Somebody', 'disconnected', '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, isExternalAuthEnabled) { console.info("My role changed, new role: " + info.role); onModeratorStatusChanged(isModerator); VideoLayout.showModeratorIndicator(); Toolbar.showAuthenticateButton( isExternalAuthEnabled && !isModerator); if (isModerator) { Authentication.closeAuthenticationWindow(); messageHandler.notify( 'Me', 'connected', 'Moderator rights granted !'); } } 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(); messageHandler.openTwoButtonDialog(null, '

Password required

' + '', true, "Ok", function (e, v, m, f) {}, function (event) { document.getElementById('lockKey').focus(); }, function (e, v, m, f) { if (v) { var lockKey = document.getElementById('lockKey'); if (lockKey.value !== null) { Toolbar.setSharedKey(lockKey.value); callback(lockKey.value); } } } ); } function onMucEntered(jid, id, displayName) { messageHandler.notify(displayName || 'Somebody', 'connected', '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 displayName = displayName; if (!displayName) { displayName = 'Somebody'; } messageHandler.notify( displayName, 'connected', 'Moderator rights granted to ' + displayName + '!'); } } 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.getCredentials = function () { var settings = this.getSettings(); return { bosh: document.getElementById('boshURL').value, password: document.getElementById('password').value, jid: document.getElementById('jid').value, email: settings.email, displayName: settings.displayName, uid: settings.uid }; }; UI.disableConnect = function () { document.getElementById('connect').disabled = true; }; UI.showLoginPopup = function(callback) { console.log('password is required'); UI.messageHandler.openTwoButtonDialog(null, '

Password required

' + '' + '', true, "Ok", function (e, v, m, f) { if (v) { var username = document.getElementById('passwordrequired.username'); var password = document.getElementById('passwordrequired.password'); if (username.value !== null && password.value != null) { callback(username.value, password.value); } } }, function (event) { document.getElementById('passwordrequired.username').focus(); } ); } UI.checkForNicknameAndJoin = function () { Authentication.closeAuthenticationDialog(); Authentication.stopInterval(); var nick = null; if (config.useNicks) { nick = window.prompt('Your nickname (optional)'); } 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 () { UIUtil.buttonClick("#video", "icon-camera icon-camera-disabled"); 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); } module.exports = UI; },{"../../service/RTC/RTCEvents":80,"../../service/RTC/StreamEventTypes":81,"../../service/connectionquality/CQEvents":83,"../../service/desktopsharing/DesktopSharingEventTypes":84,"../../service/xmpp/XMPPEvents":86,"./audio_levels/AudioLevels.js":9,"./authentication/Authentication":11,"./avatar/Avatar":12,"./etherpad/Etherpad.js":13,"./prezi/Prezi.js":14,"./side_pannels/SidePanelToggler":16,"./side_pannels/chat/Chat.js":17,"./side_pannels/contactlist/ContactList":21,"./side_pannels/settings/Settings":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":87}],9:[function(require,module,exports){ var CanvasUtil = require("./CanvasUtils"); /** * The audio Levels plugin. */ var AudioLevels = (function(my) { var audioLevelCanvasCache = {}; my.LOCAL_LEVEL = 'local'; /** * 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) { AudioLevels.updateActiveSpeakerAudioLevel(audioLevel); } }; my.updateActiveSpeakerAudioLevel = function(audioLevel) { var drawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d'); var r = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2; var center = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + r) / 2; // Save the previous state of the context. drawContext.save(); drawContext.clearRect(0, 0, 300, 300); // Draw a circle. drawContext.arc(center, center, r, 0, 2 * Math.PI); // Add a shadow around the circle drawContext.shadowColor = interfaceConfig.SHADOW_COLOR; drawContext.shadowBlur = getShadowLevel(audioLevel); drawContext.shadowOffsetX = 0; drawContext.shadowOffsetY = 0; // Fill the shape. drawContext.fill(); drawContext.save(); drawContext.restore(); drawContext.arc(center, center, r, 0, 2 * Math.PI); drawContext.clip(); drawContext.clearRect(0, 0, 277, 200); // Restore the previous context state. drawContext.restore(); }; /** * 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){ /* Initial "authentication required" dialog */ var authDialog = null; /* Loop retry ID that wits for other user to create the room */ var authRetryId = null; var authenticationWindow = null; var Authentication = { openAuthenticationDialog: function (roomName, intervalCallback, callback) { // This is the loop that will wait for the room to be created by // someone else. 'auth_required.moderator' will bring us back here. authRetryId = window.setTimeout(intervalCallback , 5000); // Show prompt only if it's not open if (authDialog !== null) { return; } // extract room name from 'room@muc.server.net' var room = roomName.substr(0, roomName.indexOf('@')); authDialog = messageHandler.openDialog( 'Stop', 'Authentication is required to create room:
' + room + '
You can either authenticate to create the room or ' + 'just wait for someone else to do so.', true, { Authenticate: 'authNow' }, function (onSubmitEvent, submitValue) { // Do not close the dialog yet onSubmitEvent.preventDefault(); // Open login popup if (submitValue === 'authNow') { callback(); } } ); }, closeAuthenticationWindow:function () { if (authenticationWindow) { authenticationWindow.close(); authenticationWindow = null; } }, focusAuthenticationWindow: function () { // If auth window exists just bring it to the front if (authenticationWindow) { authenticationWindow.focus(); return; } }, closeAuthenticationDialog: function () { // Close authentication dialog if opened if (authDialog) { APP.UI.messageHandler.closeDialog(); authDialog = null; } }, createAuthenticationWindow: function (callback, url) { authenticationWindow = messageHandler.openCenteredPopup( url, 910, 660, // On closed function () { // Close authentication dialog if opened if (authDialog) { messageHandler.closeDialog(); authDialog = null; } callback(); authenticationWindow = null; }); return authenticationWindow; }, stopInterval: function () { // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice if (authRetryId) { window.clearTimeout(authRetryId); authRetryId = null; } } }; module.exports = Authentication; },{}],12:[function(require,module,exports){ var Settings = require("../side_pannels/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":78,"../side_pannels/settings/Settings":22,"../videolayout/VideoLayout":32}],13:[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":14,"../util/UIUtil":30,"../videolayout/VideoLayout":32}],14:[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 true if the presentation is visible, false - * 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("Remove Prezi", "Are you sure you would like to remove your Prezi?", false, "Remove", function(e,v,m,f) { if(v) { APP.xmpp.removePreziFromPresence(); } } ); } else if (preziPlayer != null) { messageHandler.openTwoButtonDialog("Share a Prezi", "Another participant is already sharing a Prezi." + "This conference allows only one Prezi at a time.", false, "Ok", function(e,v,m,f) { $.prompt.close(); } ); } else { var openPreziState = { state0: { html: '

Share a Prezi

' + '', persistent: false, buttons: { "Share": true , "Cancel": false}, defaultButton: 1, submit: function(e,v,m,f){ e.preventDefault(); if(v) { var preziUrl = document.getElementById('preziUrl'); if (preziUrl.value) { var urlValue = encodeURI(UIUtil.escapeHtml(preziUrl.value)); 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: '

Share a Prezi

' + 'Please provide a correct prezi link.', persistent: false, buttons: { "Back": true, "Cancel": false }, defaultButton: 1, submit:function(e,v,m,f) { e.preventDefault(); if(v==0) $.prompt.close(); else $.prompt.goToState('state0'); } } }; var focusPreziUrl = function(e) { document.getElementById('preziUrl').focus(); }; messageHandler.openDialogWithStates(openPreziState, focusPreziUrl, focusPreziUrl); } } }; /** * 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":15}],15:[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 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; },{}],16:[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,"./chat/Chat":17,"./contactlist/ContactList":21,"./settings/Settings":22,"./settings/SettingsMenu":23}],17:[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 & var escMessage = message.replace(//g, '>').replace(/\n/g, '
'); var escDisplayName = UIUtil.escapeHtml(displayName); message = Replacement.processReplacements(escMessage); var messageContainer = '
'+ '' + '
' + escDisplayName + '
' + '
' + getCurrentTime() + '
' + '
' + message + '
' + '
'; $('#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( '
Error: ' + 'Your message' + (originalText? (' \"'+ originalText + '\"') : "") + ' was not sent.' + (errorMessage? (' Reason: ' + errorMessage) : '') + '
'); $('#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":82,"../../toolbars/ToolbarToggler":26,"../../util/NicknameHandler":29,"../../util/UIUtil":30,"../SidePanelToggler":16,"./Commands":18,"./Replacement":19,"./smileys.json":20}],18:[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}],19:[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 */ 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, '$1'); //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$2'); //Change email addresses to mailto:: links. replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim; replacedText = replacedText.replace(replacePattern3, '$1'); 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], ''); } } return body; } module.exports = { processReplacements: processReplacements, linkify: linkify }; },{"./smileys.json":20}],20:[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|<3|&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 } } },{}],21:[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(displayName) { var p = document.createElement('p'); p.innerText = displayName; 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 true if the chat is currently visible, false - * 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; },{}],22:[function(require,module,exports){ var email = ''; var displayName = ''; var userId; 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 || ''; } 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 }; } }; module.exports = Settings; },{}],23:[function(require,module,exports){ var Avatar = require("../../avatar/Avatar"); var Settings = require("./Settings"); var UIUtil = require("../../util/UIUtil"); var languages = require("../../../../service/translation/languages"); function generateLanguagesSelectBox() { var currentLang = APP.translation.getCurrentLanguage(); var html = ""; } var SettingsMenu = { init: function () { $("#updateSettings").before(generateLanguagesSelectBox()); $('#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); } APP.translation.setLanguage($("#languages_selectbox").val()); 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":85,"../../avatar/Avatar":12,"../../util/UIUtil":30,"./Settings":22}],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":16}],25:[function(require,module,exports){ /* global $, buttonClick, config, lockRoom, setSharedKey, Util */ var messageHandler = require("../util/MessageHandler"); var BottomToolbar = require("./BottomToolbar"); var Prezi = require("../prezi/Prezi"); var Etherpad = require("../etherpad/Etherpad"); var PanelToggler = require("../side_pannels/SidePanelToggler"); var Authentication = require("../authentication/Authentication"); var UIUtil = require("../util/UIUtil"); var roomUrl = null; var sharedKey = ''; var 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(); } }; function hangup() { APP.xmpp.disposeConference(); if(config.enableWelcomePage) { setTimeout(function() { window.localStorage.welcomePageDisabled = false; window.location.pathname = "/"; }, 10000); } UI.messageHandler.openDialog( "Session Terminated", "You hung up the call", true, { "Join again": true }, 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) { APP.UI.messageHandler.openTwoButtonDialog(null, '

Enter recording token

' + '', false, "Save", function (e, v, m, f) { if (v) { var token = document.getElementById('recordingToken'); if (token.value) { callback(UIUtil.escapeHtml(token.value)); } } }, function (event) { document.getElementById('recordingToken').focus(); }, function () { } ); }, 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('Lock failed', 'Failed to lock conference.', err); Toolbar.setSharedKey(''); }, function () { console.warn('room passwords not supported'); messageHandler.showError('Warning', 'Room passwords are currently not supported.'); Toolbar.setSharedKey(''); }); }; /** * Invite participants to conference. */ function inviteParticipants() { if (roomUrl === null) return; var sharedKeyText = ""; if (sharedKey && sharedKey.length > 0) { sharedKeyText = "This conference is password protected. Please use the " + "following pin when joining:%0D%0A%0D%0A" + sharedKey + "%0D%0A%0D%0A"; } var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1); var subject = "Invitation to a " + interfaceConfig.APP_NAME + " (" + conferenceName + ")"; var body = "Hey there, I%27d like to invite you to a " + interfaceConfig.APP_NAME + " conference I%27ve just set up.%0D%0A%0D%0A" + "Please click on the following link in order" + " to join the conference.%0D%0A%0D%0A" + roomUrl + "%0D%0A%0D%0A" + sharedKeyText + "Note that " + interfaceConfig.APP_NAME + " is currently" + " only supported by Chromium," + " Google Chrome and Opera, so you need" + " to be using one of these browsers.%0D%0A%0D%0A" + "Talk to you in a sec!"; 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 : ''; messageHandler.openTwoButtonDialog(null, '

Enter SIP number

' + '', false, "Dial", function (e, v, m, f) { if (v) { var numberInput = document.getElementById('sipNumber'); if (numberInput.value) { APP.xmpp.dial(numberInput.value, 'fromnumber', UI.getRoomName(), sharedKey); } } }, function (event) { document.getElementById('sipNumber').focus(); } ); } var Toolbar = (function (my) { my.init = function (ui) { for(var k in buttonHandlers) $("#" + k).click(buttonHandlers[k]); UI = ui; } /** * Sets shared key * @param sKey the shared key */ my.setSharedKey = function (sKey) { sharedKey = sKey; }; my.authenticateClicked = function () { Authentication.focusAuthenticationWindow(); // Get authentication URL APP.xmpp.getAuthUrl(APP.UI.getRoomName(), function (url) { // Open popup with authentication URL var authenticationWindow = Authentication.createAuthenticationWindow(function () { // On popup closed - retry room allocation APP.xmpp.allocateConferenceFocus(APP.UI.getRoomName(), APP.UI.checkForNicknameAndJoin); }, url); if (!authenticationWindow) { Toolbar.showAuthenticateButton(true); messageHandler.openMessageDialog( null, "Your browser is blocking popup windows from this site." + " Please enable popups in your browser security settings" + " and try again."); } }); }; /** * 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, "This conversation is currently protected by" + " a password. Only the owner of the conference" + " could set a password.", false, "Password"); } else { messageHandler.openMessageDialog(null, "This conversation isn't currently protected by" + " a password. Only the owner of the conference" + " could set a password.", false, "Password"); } } else { if (sharedKey) { messageHandler.openTwoButtonDialog(null, "Are you sure you would like to remove your password?", false, "Remove", function (e, v) { if (v) { Toolbar.setSharedKey(''); lockRoom(false); } }); } else { messageHandler.openTwoButtonDialog(null, '

Set a password to lock your room

' + '', false, "Save", function (e, v) { if (v) { var lockKey = document.getElementById('lockKey'); if (lockKey.value) { Toolbar.setSharedKey(UIUtil.escapeHtml(lockKey.value)); lockRoom(true); } } }, function () { document.getElementById('lockKey').focus(); } ); } } }; /** * Opens the invite link dialog. */ my.openLinkDialog = function () { var inviteLink; if (roomUrl === null) { inviteLink = "Your conference is currently being created..."; } else { inviteLink = encodeURI(roomUrl); } messageHandler.openTwoButtonDialog( "Share this link with everyone you want to invite", '', false, "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. */ my.openSettingsDialog = function () { messageHandler.openTwoButtonDialog( '

Configure your conference

' + '' + 'Participants join muted
' + '' + 'Require nicknames

' + 'Set a password to lock your room:' + '', null, false, "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 true to show or false 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"}); } else { $('#sipCallButton').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; },{"../authentication/Authentication":11,"../etherpad/Etherpad":13,"../prezi/Prezi":14,"../side_pannels/SidePanelToggler":16,"../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 = '
' + '
'; 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 $, jQuery */ 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(titleString, messageString) { $.prompt(messageString, { title: titleString, 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 */ my.openTwoButtonDialog = function(titleString, msgString, persistent, leftButton, submitFunction, loadedFunction, closeFunction) { var buttons = {}; buttons[leftButton] = true; buttons.Cancel = false; $.prompt(msgString, { title: titleString, persistent: false, buttons: buttons, defaultButton: 1, 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 $.prompt(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 * @param loadedFunction function to be called after the prompt is fully loaded * @param stateChangedFunction function to be called when the state of the dialog is changed */ my.openDialogWithStates = function(statesObject, loadedFunction, stateChangedFunction) { var myPrompt = $.prompt(statesObject); myPrompt.on('impromptu:loaded', loadedFunction); myPrompt.on('impromptu:statechanged', stateChangedFunction); }; /** * Opens new popup window for given url 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(titleString, msgString, error) { my.openMessageDialog(titleString, msgString); 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(title, message) { if(!(title || message)) { title = title || "Oops!"; message = message || "There was some kind of error"; } messageHandler.openMessageDialog(title, message); }; my.notify = function(displayName, cls, message) { toastr.info( '' + displayName + '
' + '' + message + ''); }; 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":82}],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 $('
').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":16}],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 = "" + (this.packetLoss.download !== null? this.packetLoss.download : "N/A") + "% " + (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 = "" + "" + "" + "" + "" + "" + "" + "" + "" + "
" + translate("connectionindicator.bitrate") + "" + downloadBitrate + " " + uploadBitrate + "
" + translate("connectionindicator.packetloss") + "" + packetLoss + "
" + translate("connectionindicator.resolution") + "" + resolution + "
"; if(this.videoContainer.id == "localVideoContainer") { result += "
" + translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) + "

"; } 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 = "" + "" + translate("connectionindicator.address") + "" + " N/A"; } 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." + (data.localIP.length > 1? "localaddresses" : "localaddress"); var remote_address_key = "connectionindicator." + (data.remoteIP.length > 1? "remoteaddresses" : "remoteaddress"); var localTransport = "" + translate(local_address_key) + " " + ConnectionIndicator.getStringFromArray(data.localIP) + ""; transport = "" + translate(remote_address_key) + " " + ConnectionIndicator.getStringFromArray(data.remoteIP) + ""; var key_remote = "connectionindicator.", key_local = "connectionindicator."; if(this.transport.length > 1) { key_remote += "remoteports"; key_local += "localports"; } else { key_remote += "remoteport"; key_local += "localport"; } transport += "" + "" + "" + translate(key_remote) + ""; localTransport += "" + "" + "" + translate(key_local) + ""; transport += ConnectionIndicator.getStringFromArray(data.remotePort); localTransport += ConnectionIndicator.getStringFromArray(data.localPort); transport += ""; transport += localTransport + ""; transport +="" + "" + translate("connectionindicator.transport") + "" + "" + this.transport[0].type + ""; } result += "" + "" + ""; result += transport + "
" + "" + translate("connectionindicator.bandwidth") + "" + "" + "" + downloadBandwidth + " " + uploadBandwidth + "
"; } 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: "
" + APP.translation.translateString("connectionindicator.na") + "
", 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( "
" + this.generateText() + "
"); 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) { var nameSpan = $('#' + videoSpanId + '>span.displayname'); var defaultLocalDisplayName = 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) $('#localDisplayName').html(displayName + ' (me)'); else $('#localDisplayName').text(defaultLocalDisplayName); } else { if (displayName && displayName.length > 0) $('#' + videoSpanId + '_name').html(displayName); 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(); nameSpan.innerText = defaultLocalDisplayName; } else { nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME; } if (displayName && displayName.length > 0) { nameSpan.innerText = displayName; } 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.substring(0, displayName.indexOf(' (me)')); } editableText.setAttribute('style', 'display:none;'); editableText.setAttribute('placeholder', 'ex. Jane Pink'); $('#' + 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 userJid * @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 jid in the * given parentElement. * * @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); // 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 = ""; if (!mutedAudios[jid]) { muteLinkItem.innerHTML = mutedIndicator + "
"; muteLinkItem.className = 'mutelink'; } else { muteLinkItem.innerHTML = mutedIndicator + "
"; 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 + "
"; this.className = 'mutelink disabled'; } else { this.innerHTML = mutedIndicator + "
"; this.className = 'mutelink'; } }; muteMenuItem.appendChild(muteLinkItem); popupmenuElement.appendChild(muteMenuItem); var ejectIndicator = ""; var ejectMenuItem = document.createElement('li'); var ejectLinkItem = document.createElement('a'); var ejectText = "
 
"; 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 videoElementId. * * @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 = ''; 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); }; /** * 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(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 true if visible, false - 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 true if the peer container exists, * false - 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 true if given stream 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()) $('#localDisplayName').text(NicknameHandler.getNickname() + " (me)"); else $('#localDisplayName') .text(interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME); $('#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 + " > 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 = ""; 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(!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; // Update the current dominant speaker. if (resourceJid !== currentDominantSpeaker) { var oldSpeakerVideoSpanId = "participant_" + currentDominantSpeaker, newSpeakerVideoSpanId = "participant_" + resourceJid; if($("#" + oldSpeakerVideoSpanId + ">span.displayname").text() === interfaceConfig.DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME) { setDisplayName(oldSpeakerVideoSpanId, null); } if($("#" + newSpeakerVideoSpanId + ">span.displayname").text() === interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME) { setDisplayName(newSpeakerVideoSpanId, 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 = $(''); // 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; } } return my; }(VideoLayout || {})); module.exports = VideoLayout; },{"../../../service/RTC/MediaStreamTypes":78,"../../../service/UI/UIEvents":82,"../audio_levels/AudioLevels":9,"../avatar/Avatar":12,"../etherpad/Etherpad":13,"../prezi/Prezi":14,"../side_pannels/chat/Chat":17,"../side_pannels/contactlist/ContactList":21,"../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":83,"../../service/xmpp/XMPPEvents":86,"events":87}],36:[function(require,module,exports){ /* global $, alert, 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; /** * Flag used to cache desktop sharing enabled state. Do not use directly as it can be null. * @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('Error', 'Error when trying to detect desktopsharing extension.'); return true; } } function checkExtInstalled(isInstalledCallback) { if (!chrome.runtime) { // No API, so no extension for sure isInstalledCallback(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); isInstalledCallback(false); } else { // Check installed extension version var extVersion = response.version; console.log('Extension version is: ' + extVersion); var updateRequired = isUpdateRequired(config.minChromeExtVersion, extVersion); if (updateRequired) { alert( 'Jitsi Desktop Streamer requires update. ' + 'Changes will take effect after next Chrome restart.'); } isInstalledCallback(!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) { checkExtInstalled( function (isInstalled) { if (isInstalled) { doGetStreamFromExtension(streamCallback, failCallback); } else { 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('Error', 'Failed to install desktop sharing extension'); } ); } } ); } /** * 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 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 getSwitchStreamFailed(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} true 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(); } 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) { toggleScreenSharing(); } } ); newStreamCreated(stream); }, getSwitchStreamFailed); } else { // Disable screen stream APP.RTC.getUserMediaWithConstraints( ['video'], function (stream) { // We are now using camera stream isUsingScreenStream = false; newStreamCreated(stream); }, getSwitchStreamFailed, config.resolution || '360' ); } } }; },{"../../service/desktopsharing/DesktopSharingEventTypes":84,"events":87}],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){ /** * * @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; },{}],39:[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":78,"./SimulcastLogger":38,"./SimulcastUtils":41}],40:[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":38,"./SimulcastUtils":41}],41:[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":38}],42:[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":80,"./SimulcastReceiver":39,"./SimulcastSender":40,"./SimulcastUtils":41}],43:[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)); } /** * LocalStatsCollector 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; },{}],44:[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 ssrc 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 ssrc 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 ssrc 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 ssrc 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] = Math.min(Math.max(audioLevel, 0), 1); }; /** * Array with the transport information. * @type {Array} */ PeerStats.transport = []; /** * StatsCollector registers for stats updates of given * peerconnection in given interval. On each update particular * stats are extracted and put in {@link PeerStats} objects. Once the processing * is done audioLevelsUpdateCallback is called with this * 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 = 10000; /** * 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; clearInterval(this.statsIntervalId); this.statsIntervalId = null; clearInterval(this.logStatsIntervalId); this.logStatsIntervalId = null; clearInterval(this.gatherStatsIntervalId); this.gatherStatsIntervalId = null; } }; /** * Callback passed to getStats method. * @param error an error that occurred on getStats 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); } }; /** * 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) { var stat = self.statsToBeLogged.stats[report.id]; if (!stat) { stat = self.statsToBeLogged.stats[report.id] = {}; } stat.type = report.type; report.names().map(function (name) { 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":79}],45:[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(), 100, 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":81,"../../service/xmpp/XMPPEvents":86,"./LocalStatsCollector.js":43,"./RTPStatsCollector.js":44,"events":87}],46:[function(require,module,exports){ var i18n = require("i18next-client"); var languages = require("../../service/translation/languages"); var DEFAULT_LANG = languages.EN; var initialized = false; var waitingForInit = []; 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, useDataAttrOptions: true, app: interfaceConfig.APP_NAME, getAsync: true, 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 }); initialized = false; 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) { initialized = true; $("[data-i18n]").i18n(); for(var i = 0; i < waitingForInit.length; i++) { var obj = waitingForInit[i]; obj.callback(i18n.t(obj.key)); } waitingForInit = []; } module.exports = { init: function (lang) { initialized = false; var options = defaultOptions; if(lang) options.lng = lang; i18n.init(options, initCompleted); }, translateString: function (key, cb) { if(!cb) return i18n.t(key); if(initialized) { cb(i18n.t(key)); } else { waitingForInit.push({"callback": cb, "key": key}); } }, setLanguage: function (lang) { initialized = false; if(!lang) lang = DEFAULT_LANG; i18n.setLng(lang, defaultOptions, initCompleted); }, getCurrentLanguage: function () { return i18n.lng(); }, translateElement: function (selector) { selector.i18n(); } }; },{"../../service/translation/languages":85,"i18next-client":61}],47:[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 = ''; tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() { var semantics = this.getAttribute('semantics'); var ssrcs = $(this).find('>source').map(function () { return this.getAttribute('ssrc'); }).get(); if (ssrcs.length != 0) { lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n'; } }); tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source tmp.each(function () { var ssrc = $(this).attr('ssrc'); if(mySdp.containsSSRC(ssrc)){ /** * This happens when multiple participants change their streams at the same time and * ColibriFocus.modifySources have to wait for stable state. In the meantime multiple * addssrc are scheduled for update IQ. See */ console.warn("Got add stream request for my own ssrc: "+ssrc); return; } $(this).find('>parameter').each(function () { lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name'); if ($(this).attr('value') && $(this).attr('value').length) lines += ':' + $(this).attr('value'); lines += '\r\n'; }); }); sdp.media.forEach(function(media, idx) { if (!SDPUtil.find_line(media, 'a=mid:' + name)) return; sdp.media[idx] += lines; if (!self.addssrc[idx]) self.addssrc[idx] = ''; self.addssrc[idx] += lines; }); sdp.raw = sdp.session + sdp.media.join(''); }); this.modifySources(); }; JingleSession.prototype.removeSource = function (elem, fromJid) { var self = this; // FIXME: dirty waiting if (!this.peerconnection.localDescription) { console.warn("removeSource - localDescription not ready yet") setTimeout(function() { self.removeSource(elem, fromJid); }, 200 ); return; } console.log('removessrc', new Date().getTime()); console.log('ice', this.peerconnection.iceConnectionState); var sdp = new SDP(this.peerconnection.remoteDescription.sdp); var mySdp = new SDP(this.peerconnection.localDescription.sdp); $(elem).each(function (idx, content) { var name = $(content).attr('name'); var lines = ''; tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() { var semantics = this.getAttribute('semantics'); var ssrcs = $(this).find('>source').map(function () { return this.getAttribute('ssrc'); }).get(); if (ssrcs.length != 0) { lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n'; } }); tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source tmp.each(function () { var ssrc = $(this).attr('ssrc'); // This should never happen, but can be useful for bug detection if(mySdp.containsSSRC(ssrc)){ console.error("Got remove stream request for my own ssrc: "+ssrc); return; } $(this).find('>parameter').each(function () { lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name'); if ($(this).attr('value') && $(this).attr('value').length) lines += ':' + $(this).attr('value'); lines += '\r\n'; }); }); sdp.media.forEach(function(media, idx) { if (!SDPUtil.find_line(media, 'a=mid:' + name)) return; sdp.media[idx] += lines; if (!self.removessrc[idx]) self.removessrc[idx] = ''; self.removessrc[idx] += lines; }); sdp.raw = sdp.session + sdp.media.join(''); }); this.modifySources(); }; JingleSession.prototype.modifySources = function (successCallback) { var self = this; if (this.peerconnection.signalingState == 'closed') return; if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){ // There is nothing to do since scheduled job might have been executed by another succeeding call this.setLocalDescription(); if(successCallback){ successCallback(); } return; } // FIXME: this is a big hack // https://code.google.com/p/webrtc/issues/detail?id=2688 // ^ has been fixed. if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) { console.warn('modifySources not yet', this.peerconnection.signalingState, this.peerconnection.iceConnectionState); this.wait = true; window.setTimeout(function() { self.modifySources(successCallback); }, 250); return; } if (this.wait) { window.setTimeout(function() { self.modifySources(successCallback); }, 2500); this.wait = false; return; } // Reset switch streams flag this.switchstreams = false; var sdp = new SDP(this.peerconnection.remoteDescription.sdp); // add sources this.addssrc.forEach(function(lines, idx) { sdp.media[idx] += lines; }); this.addssrc = []; // remove sources this.removessrc.forEach(function(lines, idx) { lines = lines.split('\r\n'); lines.pop(); // remove empty last element; lines.forEach(function(line) { sdp.media[idx] = sdp.media[idx].replace(line + '\r\n', ''); }); }); this.removessrc = []; // FIXME: // this was a hack for the situation when only one peer exists // in the conference. // check if still required and remove if (sdp.media[0]) sdp.media[0] = sdp.media[0].replace('a=recvonly', 'a=sendrecv'); if (sdp.media[1]) sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv'); sdp.raw = sdp.session + sdp.media.join(''); this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}), function() { if(self.signalingState == 'closed') { console.error("createAnswer attempt on closed state"); return; } self.peerconnection.createAnswer( function(modifiedAnswer) { // change video direction, see https://github.com/jitsi/jitmeet/issues/41 if (self.pendingop !== null) { var sdp = new SDP(modifiedAnswer.sdp); if (sdp.media.length > 1) { switch(self.pendingop) { case 'mute': sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly'); break; case 'unmute': sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv'); break; } sdp.raw = sdp.session + sdp.media.join(''); modifiedAnswer.sdp = sdp.raw; } self.pendingop = null; } // FIXME: pushing down an answer while ice connection state // is still checking is bad... //console.log(self.peerconnection.iceConnectionState); // trying to work around another chrome bug //modifiedAnswer.sdp = modifiedAnswer.sdp.replace(/a=setup:active/g, 'a=setup:actpass'); self.peerconnection.setLocalDescription(modifiedAnswer, function() { //console.log('modified setLocalDescription ok'); self.setLocalDescription(); if(successCallback){ successCallback(); } }, function(error) { console.error('modified setLocalDescription failed', error); } ); }, function(error) { console.error('modified answer failed', error); } ); }, function(error) { console.error('modify failed', error); } ); }; /** * Switches video streams. * @param new_stream new stream that will be used as video of this session. * @param oldStream old video stream of this session. * @param success_callback callback executed after successful stream switch. */ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) { var self = this; // Remember SDP to figure out added/removed SSRCs var oldSdp = null; if(self.peerconnection) { if(self.peerconnection.localDescription) { oldSdp = new SDP(self.peerconnection.localDescription.sdp); } self.peerconnection.removeStream(oldStream, true); self.peerconnection.addStream(new_stream); } 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'); } }; /** * Determines whether the (local) video is mute i.e. all video tracks are * disabled. * * @return true if the (local) video is mute i.e. all video tracks are * disabled; otherwise, false */ JingleSession.prototype.isVideoMute = function () { var tracks = APP.RTC.localVideo.getVideoTracks(); var mute = true; for (var i = 0; i < tracks.length; ++i) { if (tracks[i].enabled) { mute = false; break; } } return mute; }; /** * Mutes/unmutes the (local) video i.e. enables/disables all video tracks. * * @param mute true to mute the (local) video i.e. to disable all video * tracks; otherwise, false * @param callback a function to be invoked with mute after all video * tracks have been enabled/disabled. The function may, optionally, return * another function which is to be invoked after the whole mute/unmute operation * has completed successfully. * @param options an object which specifies optional arguments such as the * boolean key byUser with default value true which * specifies whether the method was initiated in response to a user command (in * contrast to an automatic decision made by the application logic) */ JingleSession.prototype.setVideoMute = function (mute, callback, options) { var byUser; if (options) { byUser = options.byUser; if (typeof byUser === 'undefined') { byUser = true; } } else { byUser = true; } // The user's command to mute the (local) video takes precedence over any // automatic decision made by the application logic. if (byUser) { this.videoMuteByUser = mute; } else if (this.videoMuteByUser) { return; } var self = this; var localCallback = function (mute) { self.connection.emuc.addVideoInfoToPresence(mute); self.connection.emuc.sendPresence(); return callback(mute) }; if (mute == APP.RTC.localVideo.isMuted()) { // Even if no change occurs, the specified callback is to be executed. // The specified callback may, optionally, return a successCallback // which is to be executed as well. var successCallback = localCallback(mute); if (successCallback) { successCallback(); } } else { APP.RTC.localVideo.setMute(!mute); this.hardMuteVideo(mute); this.modifySources(localCallback(mute)); } }; // SDP-based mute by going recvonly/sendrecv // FIXME: should probably black out the screen as well JingleSession.prototype.toggleVideoMute = function (callback) { this.service.setVideoMute(APP.RTC.localVideo.isMuted(), callback); }; JingleSession.prototype.hardMuteVideo = function (muted) { this.pendingop = muted ? 'mute' : 'unmute'; }; JingleSession.prototype.sendMute = function (muted, content) { var info = $iq({to: this.peerjid, type: 'set'}) .c('jingle', {xmlns: 'urn:xmpp:jingle:1', action: 'session-info', initiator: this.initiator, sid: this.sid }); info.c(muted ? 'mute' : 'unmute', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'}); info.attrs({'creator': this.me == this.initiator ? 'creator' : 'responder'}); if (content) { info.attrs({'name': content}); } this.connection.send(info); }; JingleSession.prototype.sendRinging = function () { var info = $iq({to: this.peerjid, type: 'set'}) .c('jingle', {xmlns: 'urn:xmpp:jingle:1', action: 'session-info', initiator: this.initiator, sid: this.sid }); info.c('ringing', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'}); this.connection.send(info); }; JingleSession.prototype.getStats = function (interval) { var self = this; var recv = {audio: 0, video: 0}; var lost = {audio: 0, video: 0}; var lastrecv = {audio: 0, video: 0}; var lastlost = {audio: 0, video: 0}; var loss = {audio: 0, video: 0}; var delta = {audio: 0, video: 0}; this.statsinterval = window.setInterval(function () { if (self && self.peerconnection && self.peerconnection.getStats) { self.peerconnection.getStats(function (stats) { var results = stats.result(); // TODO: there are so much statistics you can get from this.. for (var i = 0; i < results.length; ++i) { if (results[i].type == 'ssrc') { var packetsrecv = results[i].stat('packetsReceived'); var packetslost = results[i].stat('packetsLost'); if (packetsrecv && packetslost) { packetsrecv = parseInt(packetsrecv, 10); packetslost = parseInt(packetslost, 10); if (results[i].stat('googFrameRateReceived')) { lastlost.video = lost.video; lastrecv.video = recv.video; recv.video = packetsrecv; lost.video = packetslost; } else { lastlost.audio = lost.audio; lastrecv.audio = recv.audio; recv.audio = packetsrecv; lost.audio = packetslost; } } } } delta.audio = recv.audio - lastrecv.audio; delta.video = recv.video - lastrecv.video; loss.audio = (delta.audio > 0) ? Math.ceil(100 * (lost.audio - lastlost.audio) / delta.audio) : 0; loss.video = (delta.video > 0) ? Math.ceil(100 * (lost.video - lastlost.video) / delta.video) : 0; $(document).trigger('packetloss.jingle', [self.sid, loss]); }); } }, interval || 3000); return this.statsinterval; }; JingleSession.onJingleError = function (session, error) { console.error("Jingle error", error); } JingleSession.onJingleFatalError = function (session, error) { this.service.sessionTerminated = true; this.connection.emuc.doLeave(); APP.UI.messageHandler.showError( "Sorry", "Internal application error[setRemoteDescription]"); } JingleSession.prototype.setLocalDescription = function () { // put our ssrcs into presence so other clients can identify our stream var newssrcs = []; var media = 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) { 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]) { // TODO(gp) limit wait duration to 1 sec. setTimeout(function(d) { return function() { self.remoteStreamAdded(d); } }(data), 250); return; } // ok to overwrite the one from focus? might save work in colibri.js console.log('associated jid', ssrc2jid[thessrc], data.peerjid); if (ssrc2jid[thessrc]) { data.peerjid = ssrc2jid[thessrc]; } } } //TODO: this code should be removed when firefox implement multistream support if(APP.RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX) { if((JingleSession.notReceivedSSRCs.length == 0) || !ssrc2jid[JingleSession.notReceivedSSRCs[JingleSession.notReceivedSSRCs.length - 1]]) { // TODO(gp) limit wait duration to 1 sec. setTimeout(function(d) { return function() { self.remoteStreamAdded(d); } }(data), 250); return; } thessrc = JingleSession.notReceivedSSRCs.pop(); 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":79,"./SDP":48,"./SDPDiffer":49,"./SDPUtil":50,"./TraceablePeerConnection":51}],48:[function(require,module,exports){ /* jshint -W117 */ var SDPUtil = require("./SDPUtil"); // SDP STUFF function SDP(sdp) { this.media = sdp.split('\r\nm='); for (var i = 1; i < this.media.length; i++) { this.media[i] = 'm=' + this.media[i]; if (i != this.media.length - 1) { this.media[i] += '\r\n'; } } this.session = this.media.shift() + '\r\n'; this.raw = this.session + this.media.join(''); } /** * Returns map of MediaChannel mapped per channel idx. */ SDP.prototype.getMediaSsrcMap = function() { var self = this; var media_ssrcs = {}; var tmp; for (var mediaindex = 0; mediaindex < self.media.length; mediaindex++) { tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:'); var mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:')); var media = { mediaindex: mediaindex, mid: mid, ssrcs: {}, ssrcGroups: [] }; media_ssrcs[mediaindex] = media; tmp.forEach(function (line) { var linessrc = line.substring(7).split(' ')[0]; // allocate new ChannelSsrc if(!media.ssrcs[linessrc]) { media.ssrcs[linessrc] = { ssrc: linessrc, lines: [] }; } media.ssrcs[linessrc].lines.push(line); }); tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc-group:'); tmp.forEach(function(line){ var semantics = line.substr(0, idx).substr(13); var ssrcs = line.substr(14 + semantics.length).split(' '); if (ssrcs.length != 0) { media.ssrcGroups.push({ semantics: semantics, ssrcs: ssrcs }); } }); } return media_ssrcs; }; /** * Returns true if this SDP contains given SSRC. * @param ssrc the ssrc to check. * @returns {boolean} true if this SDP contains given SSRC. */ SDP.prototype.containsSSRC = function(ssrc) { var medias = this.getMediaSsrcMap(); var contains = false; Object.keys(medias).forEach(function(mediaindex){ var media = medias[mediaindex]; //console.log("Check", channel, ssrc); if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){ contains = true; } }); return contains; }; // remove iSAC and CN from SDP SDP.prototype.mangle = function () { var i, j, mline, lines, rtpmap, newdesc; for (i = 0; i < this.media.length; i++) { lines = this.media[i].split('\r\n'); lines.pop(); // remove empty last element mline = SDPUtil.parse_mline(lines.shift()); if (mline.media != 'audio') continue; newdesc = ''; mline.fmt.length = 0; for (j = 0; j < lines.length; j++) { if (lines[j].substr(0, 9) == 'a=rtpmap:') { rtpmap = SDPUtil.parse_rtpmap(lines[j]); if (rtpmap.name == 'CN' || rtpmap.name == 'ISAC') continue; mline.fmt.push(rtpmap.id); newdesc += lines[j] + '\r\n'; } else { newdesc += lines[j] + '\r\n'; } } this.media[i] = SDPUtil.build_mline(mline) + '\r\n'; this.media[i] += newdesc; } this.raw = this.session + this.media.join(''); }; // remove lines matching prefix from session section SDP.prototype.removeSessionLines = function(prefix) { var self = this; var lines = SDPUtil.find_lines(this.session, prefix); lines.forEach(function(line) { self.session = self.session.replace(line + '\r\n', ''); }); this.raw = this.session + this.media.join(''); return lines; } // remove lines matching prefix from a media section specified by mediaindex // TODO: non-numeric mediaindex could match mid SDP.prototype.removeMediaLines = function(mediaindex, prefix) { var self = this; var lines = SDPUtil.find_lines(this.media[mediaindex], prefix); lines.forEach(function(line) { self.media[mediaindex] = self.media[mediaindex].replace(line + '\r\n', ''); }); this.raw = this.session + this.media.join(''); return lines; } // add content's to a jingle element SDP.prototype.toJingle = function (elem, thecreator, ssrcs) { // console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]); var i, j, k, mline, ssrc, rtpmap, tmp, line, lines; var self = this; // new bundle plan if (SDPUtil.find_line(this.session, 'a=group:')) { lines = SDPUtil.find_lines(this.session, 'a=group:'); for (i = 0; i < lines.length; i++) { tmp = lines[i].split(' '); var semantics = tmp.shift().substr(8); elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics:semantics}); for (j = 0; j < tmp.length; j++) { elem.c('content', {name: tmp[j]}).up(); } elem.up(); } } for (i = 0; i < this.media.length; i++) { mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]); if (!(mline.media === 'audio' || mline.media === 'video' || mline.media === 'application')) { continue; } if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) { ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first } else { if(ssrcs && ssrcs[mline.media]) { ssrc = ssrcs[mline.media]; } else ssrc = false; } elem.c('content', {creator: thecreator, name: mline.media}); if (SDPUtil.find_line(this.media[i], 'a=mid:')) { // prefer identifier from a=mid if present var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:')); elem.attrs({ name: mid }); } if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length) { elem.c('description', {xmlns: 'urn:xmpp:jingle:apps:rtp:1', media: mline.media }); if (ssrc) { elem.attrs({ssrc: ssrc}); } for (j = 0; j < mline.fmt.length; j++) { rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]); elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap)); // put any 'a=fmtp:' + mline.fmt[j] lines into if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) { tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])); for (k = 0; k < tmp.length; k++) { elem.c('parameter', tmp[k]).up(); } } this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb elem.up(); } if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) { elem.c('encryption', {required: 1}); var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session); crypto.forEach(function(line) { elem.c('crypto', SDPUtil.parse_crypto(line)).up(); }); elem.up(); // end of encryption } if (ssrc) { // new style mapping elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); // FIXME: group by ssrc and support multiple different ssrcs var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:'); if(ssrclines.length > 0) { ssrclines.forEach(function (line) { idx = line.indexOf(' '); var linessrc = line.substr(0, idx).substr(7); if (linessrc != ssrc) { elem.up(); ssrc = linessrc; elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); } var kv = line.substr(idx + 1); elem.c('parameter'); if (kv.indexOf(':') == -1) { elem.attrs({ name: kv }); } else { elem.attrs({ name: kv.split(':', 2)[0] }); elem.attrs({ value: kv.split(':', 2)[1] }); } elem.up(); }); elem.up(); } else { elem.up(); elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); elem.c('parameter'); elem.attrs({name: "cname", value:Math.random().toString(36).substring(7)}); elem.up(); var msid = null; if(mline.media == "audio") { msid = 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'; // // 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":50}],49:[function(require,module,exports){ function SDPDiffer(mySDP, otherSDP) { this.mySDP = mySDP; this.otherSDP = otherSDP; } /** * Returns map of MediaChannel that contains only media not contained in otherSdp. Mapped by channel idx. * @param otherSdp the other SDP to check ssrc with. */ SDPDiffer.prototype.getNewMedia = function() { // this could be useful in Array.prototype. function arrayEquals(array) { // if the other array is a falsy value, return if (!array) return false; // compare lengths - can save a lot of time if (this.length != array.length) return false; for (var i = 0, l=this.length; i < l; i++) { // Check if we have nested arrays if (this[i] instanceof Array && array[i] instanceof Array) { // recurse into the nested arrays if (!this[i].equals(array[i])) return false; } else if (this[i] != array[i]) { // Warning - two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; } var myMedias = this.mySDP.getMediaSsrcMap(); var othersMedias = this.otherSDP.getMediaSsrcMap(); var newMedia = {}; Object.keys(othersMedias).forEach(function(othersMediaIdx) { var myMedia = myMedias[othersMediaIdx]; var othersMedia = othersMedias[othersMediaIdx]; if(!myMedia && othersMedia) { // Add whole channel newMedia[othersMediaIdx] = othersMedia; return; } // Look for new ssrcs accross the channel Object.keys(othersMedia.ssrcs).forEach(function(ssrc) { if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) { // Allocate channel if we've found ssrc that doesn't exist in our channel if(!newMedia[othersMediaIdx]){ newMedia[othersMediaIdx] = { mediaindex: othersMedia.mediaindex, mid: othersMedia.mid, ssrcs: {}, ssrcGroups: [] }; } newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc]; } }); // Look for new ssrc groups across the channels othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){ // try to match the other ssrc-group with an ssrc-group of ours var matched = false; for (var i = 0; i < myMedia.ssrcGroups.length; i++) { var mySsrcGroup = myMedia.ssrcGroups[i]; if (otherSsrcGroup.semantics == mySsrcGroup.semantics && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) { matched = true; break; } } if (!matched) { // Allocate channel if we've found an ssrc-group that doesn't // exist in our channel if(!newMedia[othersMediaIdx]){ newMedia[othersMediaIdx] = { mediaindex: othersMedia.mediaindex, mid: othersMedia.mid, ssrcs: {}, ssrcGroups: [] }; } newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup); } }); }); return newMedia; }; /** * Sends SSRC update IQ. * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove. * @param sid session identifier that will be put into the IQ. * @param initiator initiator identifier. * @param toJid destination Jid * @param isAdd indicates if this is remove or add operation. */ SDPDiffer.prototype.toJingle = function(modify) { var sdpMediaSsrcs = this.getNewMedia(); var self = this; // FIXME: only announce video ssrcs since we mix audio and dont need // the audio ssrcs therefore var modified = false; Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){ modified = true; var media = sdpMediaSsrcs[mediaindex]; modify.c('content', {name: media.mid}); modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid}); // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly // generate sources from lines Object.keys(media.ssrcs).forEach(function(ssrcNum) { var mediaSsrc = media.ssrcs[ssrcNum]; modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); modify.attrs({ssrc: mediaSsrc.ssrc}); // iterate over ssrc lines mediaSsrc.lines.forEach(function (line) { var idx = line.indexOf(' '); var kv = line.substr(idx + 1); modify.c('parameter'); if (kv.indexOf(':') == -1) { modify.attrs({ name: kv }); } else { modify.attrs({ name: kv.split(':', 2)[0] }); modify.attrs({ value: kv.split(':', 2)[1] }); } modify.up(); // end of parameter }); modify.up(); // end of source }); // generate source groups from lines media.ssrcGroups.forEach(function(ssrcGroup) { if (ssrcGroup.ssrcs.length != 0) { modify.c('ssrc-group', { semantics: ssrcGroup.semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); ssrcGroup.ssrcs.forEach(function (ssrc) { modify.c('source', { ssrc: ssrc }) .up(); // end of source }); modify.up(); // end of ssrc-group } }); modify.up(); // end of description modify.up(); // end of content }); return modified; }; module.exports = SDPDiffer; },{}],50:[function(require,module,exports){ SDPUtil = { iceparams: function (mediadesc, sessiondesc) { var data = null; if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) && SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) { data = { ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)), pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) }; } return data; }, parse_iceufrag: function (line) { return line.substring(12); }, build_iceufrag: function (frag) { return 'a=ice-ufrag:' + frag; }, parse_icepwd: function (line) { return line.substring(10); }, build_icepwd: function (pwd) { return 'a=ice-pwd:' + pwd; }, parse_mid: function (line) { return line.substring(6); }, parse_mline: function (line) { var parts = line.substring(2).split(' '), data = {}; data.media = parts.shift(); data.port = parts.shift(); data.proto = parts.shift(); if (parts[parts.length - 1] === '') { // trailing whitespace parts.pop(); } data.fmt = parts; return data; }, build_mline: function (mline) { return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' '); }, parse_rtpmap: function (line) { var parts = line.substring(9).split(' '), data = {}; data.id = parts.shift(); parts = parts[0].split('/'); data.name = parts.shift(); data.clockrate = parts.shift(); data.channels = parts.length ? parts.shift() : '1'; return data; }, /** * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it. * @param line eg. "a=sctpmap:5000 webrtc-datachannel" * @returns [SCTP port number, protocol, streams] */ parse_sctpmap: function (line) { var parts = line.substring(10).split(' '); var sctpPort = parts[0]; var protocol = parts[1]; // Stream count is optional var streamCount = parts.length > 2 ? parts[2] : null; return [sctpPort, protocol, streamCount];// SCTP port }, build_rtpmap: function (el) { var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate'); if (el.getAttribute('channels') && el.getAttribute('channels') != '1') { line += '/' + el.getAttribute('channels'); } return line; }, parse_crypto: function (line) { var parts = line.substring(9).split(' '), data = {}; data.tag = parts.shift(); data['crypto-suite'] = parts.shift(); data['key-params'] = parts.shift(); if (parts.length) { data['session-params'] = parts.join(' '); } return data; }, parse_fingerprint: function (line) { // RFC 4572 var parts = line.substring(14).split(' '), data = {}; data.hash = parts.shift(); data.fingerprint = parts.shift(); // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ? return data; }, parse_fmtp: function (line) { var parts = line.split(' '), i, key, value, data = []; parts.shift(); parts = parts.join(' ').split(';'); for (i = 0; i < parts.length; i++) { key = parts[i].split('=')[0]; while (key.length && key[0] == ' ') { key = key.substring(1); } value = parts[i].split('=')[1]; if (key && value) { data.push({name: key, value: value}); } else if (key) { // rfc 4733 (DTMF) style stuff data.push({name: '', value: key}); } } return data; }, parse_icecandidate: function (line) { var candidate = {}, elems = line.split(' '); candidate.foundation = elems[0].substring(12); candidate.component = elems[1]; candidate.protocol = elems[2].toLowerCase(); candidate.priority = elems[3]; candidate.ip = elems[4]; candidate.port = elems[5]; // elems[6] => "typ" candidate.type = elems[7]; candidate.generation = 0; // default value, may be overwritten below for (var i = 8; i < elems.length; i += 2) { switch (elems[i]) { case 'raddr': candidate['rel-addr'] = elems[i + 1]; break; case 'rport': candidate['rel-port'] = elems[i + 1]; break; case 'generation': candidate.generation = elems[i + 1]; break; case 'tcptype': candidate.tcptype = elems[i + 1]; break; default: // TODO console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"'); } } candidate.network = '1'; candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random return candidate; }, build_icecandidate: function (cand) { var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' '); line += ' '; switch (cand.type) { case 'srflx': case 'prflx': case 'relay': if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) { line += 'raddr'; line += ' '; line += cand['rel-addr']; line += ' '; line += 'rport'; line += ' '; line += cand['rel-port']; line += ' '; } break; } if (cand.hasOwnAttribute('tcptype')) { line += 'tcptype'; line += ' '; line += cand.tcptype; line += ' '; } line += 'generation'; line += ' '; line += cand.hasOwnAttribute('generation') ? cand.generation : '0'; return line; }, parse_ssrc: function (desc) { // proprietary mapping of a=ssrc lines // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs // and parse according to that var lines = desc.split('\r\n'), data = {}; for (var i = 0; i < lines.length; i++) { if (lines[i].substring(0, 7) == 'a=ssrc:') { var idx = lines[i].indexOf(' '); data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1]; } } return data; }, parse_rtcpfb: function (line) { var parts = line.substr(10).split(' '); var data = {}; data.pt = parts.shift(); data.type = parts.shift(); data.params = parts; return data; }, parse_extmap: function (line) { var parts = line.substr(9).split(' '); var data = {}; data.value = parts.shift(); if (data.value.indexOf('/') != -1) { data.direction = data.value.substr(data.value.indexOf('/') + 1); data.value = data.value.substr(0, data.value.indexOf('/')); } else { data.direction = 'both'; } data.uri = parts.shift(); data.params = parts; return data; }, find_line: function (haystack, needle, sessionpart) { var lines = haystack.split('\r\n'); for (var i = 0; i < lines.length; i++) { if (lines[i].substring(0, needle.length) == needle) { return lines[i]; } } if (!sessionpart) { return false; } // search session part lines = sessionpart.split('\r\n'); for (var j = 0; j < lines.length; j++) { if (lines[j].substring(0, needle.length) == needle) { return lines[j]; } } return false; }, find_lines: function (haystack, needle, sessionpart) { var lines = haystack.split('\r\n'), needles = []; for (var i = 0; i < lines.length; i++) { if (lines[i].substring(0, needle.length) == needle) needles.push(lines[i]); } if (needles.length || !sessionpart) { return needles; } // search session part lines = sessionpart.split('\r\n'); for (var j = 0; j < lines.length; j++) { if (lines[j].substring(0, needle.length) == needle) { needles.push(lines[j]); } } return needles; }, candidateToJingle: function (line) { // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0 // if (line.indexOf('candidate:') === 0) { line = 'a=' + line; } else if (line.substring(0, 12) != 'a=candidate:') { console.log('parseCandidate called with a line that is not a candidate line'); console.log(line); return null; } if (line.substring(line.length - 2) == '\r\n') // chomp it line = line.substring(0, line.length - 2); var candidate = {}, elems = line.split(' '), i; if (elems[6] != 'typ') { console.log('did not find typ in the right place'); console.log(line); return null; } candidate.foundation = elems[0].substring(12); candidate.component = elems[1]; candidate.protocol = elems[2].toLowerCase(); candidate.priority = elems[3]; candidate.ip = elems[4]; candidate.port = elems[5]; // elems[6] => "typ" candidate.type = elems[7]; candidate.generation = '0'; // default, may be overwritten below for (i = 8; i < elems.length; i += 2) { switch (elems[i]) { case 'raddr': candidate['rel-addr'] = elems[i + 1]; break; case 'rport': candidate['rel-port'] = elems[i + 1]; break; case 'generation': candidate.generation = elems[i + 1]; break; case 'tcptype': candidate.tcptype = elems[i + 1]; break; default: // TODO console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"'); } } candidate.network = '1'; candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random return candidate; }, candidateFromJingle: function (cand) { var line = 'a=candidate:'; line += cand.getAttribute('foundation'); line += ' '; line += cand.getAttribute('component'); line += ' '; line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this line += ' '; line += cand.getAttribute('priority'); line += ' '; line += cand.getAttribute('ip'); line += ' '; line += cand.getAttribute('port'); line += ' '; line += 'typ'; line += ' ' + cand.getAttribute('type'); line += ' '; switch (cand.getAttribute('type')) { case 'srflx': case 'prflx': case 'relay': if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) { line += 'raddr'; line += ' '; line += cand.getAttribute('rel-addr'); line += ' '; line += 'rport'; line += ' '; line += cand.getAttribute('rel-port'); line += ' '; } break; } if (cand.getAttribute('protocol').toLowerCase() == 'tcp') { line += 'tcptype'; line += ' '; line += cand.getAttribute('tcptype'); line += ' '; } line += 'generation'; line += ' '; line += cand.getAttribute('generation') || '0'; return line + '\r\n'; } }; module.exports = SDPUtil; },{}],51:[function(require,module,exports){ function TraceablePeerConnection(ice_config, constraints) { var self = this; var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection; this.peerconnection = new RTCPeerconnection(ice_config, constraints); this.updateLog = []; this.stats = {}; this.statsinterval = null; this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable // override as desired this.trace = function (what, info) { //console.warn('WTRACE', what, info); self.updateLog.push({ time: new Date(), type: what, value: info || "" }); }; this.onicecandidate = null; this.peerconnection.onicecandidate = function (event) { self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' ')); if (self.onicecandidate !== null) { self.onicecandidate(event); } }; this.onaddstream = null; this.peerconnection.onaddstream = function (event) { self.trace('onaddstream', event.stream.id); if (self.onaddstream !== null) { self.onaddstream(event); } }; this.onremovestream = null; this.peerconnection.onremovestream = function (event) { self.trace('onremovestream', event.stream.id); if (self.onremovestream !== null) { self.onremovestream(event); } }; this.onsignalingstatechange = null; this.peerconnection.onsignalingstatechange = function (event) { self.trace('onsignalingstatechange', self.signalingState); if (self.onsignalingstatechange !== null) { self.onsignalingstatechange(event); } }; this.oniceconnectionstatechange = null; this.peerconnection.oniceconnectionstatechange = function (event) { self.trace('oniceconnectionstatechange', self.iceConnectionState); if (self.oniceconnectionstatechange !== null) { self.oniceconnectionstatechange(event); } }; this.onnegotiationneeded = null; this.peerconnection.onnegotiationneeded = function (event) { self.trace('onnegotiationneeded'); if (self.onnegotiationneeded !== null) { self.onnegotiationneeded(event); } }; self.ondatachannel = null; this.peerconnection.ondatachannel = function (event) { self.trace('ondatachannel', event); if (self.ondatachannel !== null) { self.ondatachannel(event); } }; if (!navigator.mozGetUserMedia && this.maxstats) { this.statsinterval = window.setInterval(function() { self.peerconnection.getStats(function(stats) { var results = stats.result(); for (var i = 0; i < results.length; ++i) { //console.log(results[i].type, results[i].id, results[i].names()) var now = new Date(); results[i].names().forEach(function (name) { var id = results[i].id + '-' + name; if (!self.stats[id]) { self.stats[id] = { startTime: now, endTime: now, values: [], times: [] }; } self.stats[id].values.push(results[i].stat(name)); self.stats[id].times.push(now.getTime()); if (self.stats[id].values.length > self.maxstats) { self.stats[id].values.shift(); self.stats[id].times.shift(); } self.stats[id].endTime = now; }); } }); }, 1000); } }; dumpSDP = function(description) { return 'type: ' + description.type + '\r\n' + description.sdp; } if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) { TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; }); TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; }); TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() { var publicLocalDescription = APP.simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription); return publicLocalDescription; }); TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { var publicRemoteDescription = APP.simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription); return publicRemoteDescription; }); } 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(); }); } this.peerconnection.removeStream(stream); }; TraceablePeerConnection.prototype.createDataChannel = function (label, opts) { this.trace('createDataChannel', label, opts); return this.peerconnection.createDataChannel(label, opts); }; TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) { var self = this; description = APP.simulcast.transformLocalDescription(description); this.trace('setLocalDescription', dumpSDP(description)); this.peerconnection.setLocalDescription(description, function () { self.trace('setLocalDescriptionOnSuccess'); successCallback(); }, function (err) { self.trace('setLocalDescriptionOnFailure', err); failureCallback(err); } ); /* if (this.statsinterval === null && this.maxstats > 0) { // start gathering stats } */ }; TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) { var self = this; description = APP.simulcast.transformRemoteDescription(description); this.trace('setRemoteDescription', dumpSDP(description)); this.peerconnection.setRemoteDescription(description, function () { self.trace('setRemoteDescriptionOnSuccess'); successCallback(); }, function (err) { self.trace('setRemoteDescriptionOnFailure', err); failureCallback(err); } ); /* if (this.statsinterval === null && this.maxstats > 0) { // start gathering stats } */ }; TraceablePeerConnection.prototype.close = function () { this.trace('stop'); if (this.statsinterval !== null) { window.clearInterval(this.statsinterval); this.statsinterval = null; } this.peerconnection.close(); }; TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) { var self = this; this.trace('createOffer', JSON.stringify(constraints, null, ' ')); this.peerconnection.createOffer( function (offer) { self.trace('createOfferOnSuccess', dumpSDP(offer)); successCallback(offer); }, function(err) { self.trace('createOfferOnFailure', err); failureCallback(err); }, constraints ); }; TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) { var self = this; this.trace('createAnswer', JSON.stringify(constraints, null, ' ')); this.peerconnection.createAnswer( function (answer) { answer = APP.simulcast.transformAnswer(answer); self.trace('createAnswerOnSuccess', dumpSDP(answer)); successCallback(answer); }, function(err) { self.trace('createAnswerOnFailure', err); failureCallback(err); }, constraints ); }; TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) { var self = this; this.trace('addIceCandidate', JSON.stringify(candidate, null, ' ')); this.peerconnection.addIceCandidate(candidate); /* maybe later this.peerconnection.addIceCandidate(candidate, function () { self.trace('addIceCandidateOnSuccess'); successCallback(); }, function (err) { self.trace('addIceCandidateOnFailure', err); failureCallback(err); } ); */ }; TraceablePeerConnection.prototype.getStats = function(callback, errback) { if (navigator.mozGetUserMedia) { // ignore for now... if(!errback) errback = function () { } this.peerconnection.getStats(null,callback,errback); } else { this.peerconnection.getStats(callback); } }; module.exports = TraceablePeerConnection; },{}],52:[function(require,module,exports){ /* global $, $iq, config, connection, UI, messageHandler, roomName, sessionTerminated, Strophe, Util */ var XMPPEvents = require("../../service/xmpp/XMPPEvents"); /** * 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; }, onMucLeft: function (jid) { console.info("Someone left is it focus ? " + jid); var resource = Strophe.getResourceFromJid(jid); if (resource === 'focus' && !this.xmppService.sessionTerminated) { console.info( "Focus has left the room - leaving conference"); //hangUp(); // We'd rather reload to have everything re-initialized // FIXME: show some message before reload location.reload(); } }, setFocusUserJid: function (focusJid) { if (!focusUserJid) { focusUserJid = focusJid; console.info("Focus jid set to: " + focusUserJid); } }, getFocusUserJid: function () { return focusUserJid; }, getFocusComponent: function () { // Get focus component address var focusComponent = config.hosts.focus; // If not specified use default: 'focus.domain' if (!focusComponent) { focusComponent = 'focus.' + config.hosts.domain; } return focusComponent; }, createConferenceIq: function (roomName) { // Generate create conference IQ var elem = $iq({to: Moderator.getFocusComponent(), type: 'set'}); elem.c('conference', { xmlns: 'http://jitsi.org/protocol/focus', room: roomName }); if (config.hosts.bridge !== undefined) { elem.c( 'property', { name: 'bridge', value: config.hosts.bridge}) .up(); } // Tell the focus we have Jigasi configured if (config.hosts.call_control !== undefined) { elem.c( 'property', { name: 'call_control', value: config.hosts.call_control}) .up(); } if (config.channelLastN !== undefined) { elem.c( 'property', { name: 'channelLastN', value: config.channelLastN}) .up(); } if (config.adaptiveLastN !== undefined) { elem.c( 'property', { name: 'adaptiveLastN', value: config.adaptiveLastN}) .up(); } if (config.adaptiveSimulcast !== undefined) { elem.c( 'property', { name: 'adaptiveSimulcast', value: config.adaptiveSimulcast}) .up(); } if (config.openSctp !== undefined) { elem.c( 'property', { name: 'openSctp', value: config.openSctp}) .up(); } if (config.enableFirefoxSupport !== undefined) { elem.c( 'property', { name: 'enableFirefoxHacks', value: config.enableFirefoxSupport}) .up(); } elem.up(); return elem; }, parseConfigOptions: function (resultIq) { Moderator.setFocusUserJid( $(resultIq).find('conference').attr('focusjid')); var extAuthParam = $(resultIq).find('>conference>property[name=\'externalAuth\']'); if (extAuthParam.length) { externalAuthEnabled = extAuthParam.attr('value') === 'true'; } console.info("External authentication enabled: " + externalAuthEnabled); // Check if focus has auto-detected Jigasi component(this will be also // included if we have passed our host from the config) if ($(resultIq).find( '>conference>property[name=\'sipGatewayEnabled\']').length) { sipGatewayEnabled = true; } console.info("Sip gateway enabled: " + sipGatewayEnabled); }, // FIXME: we need to show the fact that we're waiting for the focus // to the user(or that focus is not available) allocateConferenceFocus: function (roomName, callback) { // Try to use focus user JID from the config Moderator.setFocusUserJid(config.focusUserJid); // Send create conference IQ var iq = Moderator.createConferenceIq(roomName); var self = this; connection.sendIQ( iq, function (result) { if ('true' === $(result).find('conference').attr('ready')) { // Reset both timers getNextTimeout(true); getNextErrorTimeout(true); // Setup config options Moderator.parseConfigOptions(result); // Exec callback callback(); } else { var waitMs = getNextTimeout(); console.info("Waiting for the focus... " + waitMs); // Reset error timeout getNextErrorTimeout(true); window.setTimeout( function () { Moderator.allocateConferenceFocus( roomName, callback); }, waitMs); } }, function (error) { // Not authorized to create new room if ($(error).find('>error>not-authorized').length) { console.warn("Unauthorized to start the conference"); var toDomain = Strophe.getDomainFromJid(error.getAttribute('to')); if (toDomain === config.hosts.anonymousdomain) { // we are connected with anonymous domain and // only non anonymous users can create rooms // we must authorize the user self.xmppService.promptLogin(); } else { eventEmitter.emit(XMPPEvents.AUTHENTICATION_REQUIRED, // External authentication mode function () { Moderator.allocateConferenceFocus( roomName, callback); }); } return; } var waitMs = getNextErrorTimeout(); console.error("Focus error, retry after " + waitMs, error); // Show message APP.UI.messageHandler.notify( 'Conference focus', 'disconnected', Moderator.getFocusComponent() + ' not available - retry in ' + (waitMs / 1000) + ' sec'); // Reset response timeout getNextTimeout(true); window.setTimeout( function () { Moderator.allocateConferenceFocus(roomName, callback); }, waitMs); } ); }, getAuthUrl: function (roomName, urlCallback) { var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'}); iq.c('auth-url', { xmlns: 'http://jitsi.org/protocol/focus', room: roomName }); connection.sendIQ( iq, function (result) { var url = $(result).find('auth-url').attr('url'); if (url) { console.info("Got auth url: " + url); urlCallback(url); } else { console.error( "Failed to get auth url fro mthe focus", result); } }, function (error) { console.error("Get auth url error", error); } ); } }; module.exports = Moderator; },{"../../service/xmpp/XMPPEvents":86}],53:[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(); }); 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":52}],54:[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 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(), Moderator.isExternalAuthEnabled()); } 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'); // 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, 'Oops ! We couldn`t join the conference.' + ' There might be some problem with security' + ' configuration. Please contact service' + ' administrator.', pres); } else { console.warn('onPresError ', pres); APP.UI.messageHandler.openReportDialog(null, 'Oops! Something went wrong and we couldn`t connect to the conference.', pres); } } else { console.warn('onPresError ', pres); APP.UI.messageHandler.openReportDialog(null, 'Oops! Something went wrong and we couldn`t connect to the conference.', pres); } return true; }, sendMessage: function (body, nickname) { var msg = $msg({to: this.roomjid, type: 'groupchat'}); msg.c('body', body).up(); if (nickname) { msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up(); } this.connection.send(msg); 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['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; }, 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":86,"./JingleSession":47,"./moderator":52}],55:[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":86,"./JingleSession":47}],56:[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]); } }); }; },{}],57:[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); } }); } },{}],58:[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; } ); } } ); }; },{}],59:[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"; } }; }; },{}],60:[function(require,module,exports){ var Moderator = require("./moderator"); var EventEmitter = require("events"); var Recording = require("./recording"); var SDP = require("./SDP"); var Pako = require("pako"); var StreamEventTypes = require("../../service/RTC/StreamEventTypes"); 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, uiCredentials) { var bosh = (uiCredentials && uiCredentials.bosh? uiCredentials.bosh : null) || config.bosh || '/http-bind'; connection = new Strophe.Connection(bosh); Moderator.setConnection(connection); if(uiCredentials) { var email = uiCredentials.email; var displayName = uiCredentials.displayName; if (email) { connection.emuc.addEmailToPresence(email); } else { connection.emuc.addUserIdToPresence(uiCredentials.uid); } if (displayName) { connection.emuc.addDisplayNameToPresence(displayName); } } 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}); } if(!password) password = uiCredentials.password; var anonymousConnectionFailed = false; connection.connect(jid, password, function (status, msg) { console.log('Strophe status changed to', Strophe.getStatusString(status)); if (status === Strophe.Status.CONNECTED) { if (config.useStunTurn) { connection.jingle.getStunAndTurnCredentials(); } APP.UI.disableConnect(); console.info("My Jabber ID: " + connection.jid); if(password) authenticatedUser = true; maybeDoJoin(); } else if (status === Strophe.Status.CONNFAIL) { if(msg === 'x-strophe-bad-non-anon-jid') { anonymousConnectionFailed = true; } } else if (status === Strophe.Status.DISCONNECTED) { if(anonymousConnectionFailed) { // prompt user for username and password XMPP.promptLogin(); } } else if (status === Strophe.Status.AUTHFAIL) { // wrong password or username, prompt user XMPP.promptLogin(); } }); } function maybeDoJoin() { if (connection && connection.connected && Strophe.getResourceFromJid(connection.jid) && (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.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: "" + "" + "", success: function (data) { console.log('signed out'); console.log(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { console.log('signout error', textStatus + ' (' + errorThrown + ')'); } }); } XMPP.disposeConference(true); }); } var XMPP = { sessionTerminated: false, /** * Remembers if we were muted by the focus. * @type {boolean} */ forceMuted: false, start: function (uiCredentials) { setupEvents(); initStrophePlugins(); registerListeners(); Moderator.init(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 = uiCredentials.jid || configDomain || window.location.hostname; connect(jid, null, uiCredentials); }, promptLogin: function () { 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); }, 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.error("No conference handler"); APP.UI.messageHandler.showError('Error', 'Unable to switch video stream.'); callback(); } }, setVideoMute: function (mute, callback, options) { if(connection && APP.RTC.localVideo && connection.jingle.activecall) { connection.jingle.activecall.setVideoMute(mute, callback, options); } }, 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('Error', 'Oops! Something went wrong and we failed to ' + 'mute! (SLD Failure)'); } ); }, function (error) { console.log(error); APP.UI.messageHandler.showError(); } ); }, function (error) { console.log('muteVideo SRD error'); APP.UI.messageHandler.showError('Error', 'Oops! Something went wrong and we failed to stop video!' + '(SRD Failure)'); } ); }, toggleRecording: function (tokenEmptyCallback, startingCallback, startedCallback) { Recording.toggleRecording(tokenEmptyCallback, startingCallback, startedCallback, 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); default : console.log("Unknown tag for presence."); return; } if(!dontSend) connection.emuc.sendPresence(); }, sendLogs: function (data) { if(!connection.emuc.focusMucJid) return; 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); }, populateData: function () { var data = {}; if (connection.jingle) { data = connection.jingle.populateData(); } return data; }, getLogger: function () { if(connection.logger) return connection.logger.log; return null; }, getPrezi: function () { return connection.emuc.getPrezi(this.myJid()); }, removePreziFromPresence: function () { connection.emuc.removePreziFromPresence(); connection.emuc.sendPresence(); }, sendChatMessage: function (message, nickname) { connection.emuc.sendMessage(message, nickname); }, setSubject: function (topic) { connection.emuc.setSubject(topic); }, lockRoom: function (key, onSuccess, onError, onNotSupported) { connection.emuc.lockRoom(key, onSuccess, onError, onNotSupported); }, dial: function (to, from, roomName,roomPass) { connection.rayo.dial(to, from, roomName,roomPass); }, setMute: function (jid, mute) { connection.moderate.setMute(jid, mute); }, eject: function (jid) { connection.moderate.eject(jid); }, findJidFromResource: function (resource) { 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; } }; module.exports = XMPP; },{"../../service/RTC/StreamEventTypes":81,"../../service/UI/UIEvents":82,"../../service/xmpp/XMPPEvents":86,"./SDP":48,"./moderator":52,"./recording":53,"./strophe.emuc":54,"./strophe.jingle":55,"./strophe.logger":56,"./strophe.moderate":57,"./strophe.rayo":58,"./strophe.util":59,"events":87,"pako":62}],61:[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 = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' }; 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 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 -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"}],62:[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":63,"./lib/inflate":64,"./lib/utils/common":65,"./lib/zlib/constants":68}],63:[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'); /* 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|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 { 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":65,"./utils/strings":66,"./zlib/deflate.js":70,"./zlib/messages":75,"./zlib/zstream":77}],64:[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'); /** * 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|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 { 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":65,"./utils/strings":66,"./zlib/constants":68,"./zlib/gzheader":71,"./zlib/inflate.js":73,"./zlib/messages":75,"./zlib/zstream":77}],65:[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= 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 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":65}],67:[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; },{}],68:[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 }; },{}],69:[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; },{}],70:[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":65,"./adler32":67,"./crc32":69,"./messages":75,"./trees":76}],71:[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; },{}],72:[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; }; },{}],73:[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":65,"./adler32":67,"./crc32":69,"./inffast":72,"./inftrees":74}],74:[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":65}],75:[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) */ }; },{}],76:[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< length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 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":65}],77:[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; },{}],78:[function(require,module,exports){ var MediaStreamType = { VIDEO_TYPE: "Video", AUDIO_TYPE: "Audio" }; module.exports = MediaStreamType; },{}],79:[function(require,module,exports){ var RTCBrowserType = { RTC_BROWSER_CHROME: "rtc_browser.chrome", RTC_BROWSER_FIREFOX: "rtc_browser.firefox" }; module.exports = RTCBrowserType; },{}],80:[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" }; module.exports = RTCEvents; },{}],81:[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" }; module.exports = StreamEventTypes; },{}],82:[function(require,module,exports){ var UIEvents = { NICKNAME_CHANGED: "UI.nickname_changed", SELECTED_ENDPOINT: "UI.selected_endpoint", PINNED_ENDPOINT: "UI.pinned_endpoint" }; module.exports = UIEvents; },{}],83:[function(require,module,exports){ var CQEvents = { LOCALSTATS_UPDATED: "cq.localstats_updated", REMOTESTATS_UPDATED: "cq.remotestats_updated", STOP: "cq.stop" }; module.exports = CQEvents; },{}],84:[function(require,module,exports){ var DesktopSharingEventTypes = { INIT: "ds.init", SWITCHING_DONE: "ds.switching_done", NEW_STREAM_CREATED: "ds.new_stream_created" }; module.exports = DesktopSharingEventTypes; },{}],85:[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" } },{}],86:[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", 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", DISPLAY_NAME_CHANGED: "xmpp.display_name_changed", REMOTE_STATS: "xmpp.remote_stats", LOCALROLE_CHANGED: "xmpp.localrole_changed", PRESENCE_STATUS: "xmpp.presence_status", 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" }; module.exports = XMPPEvents; },{}],87:[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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Vzci9sb2NhbC9saWIvbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImFwcC5qcyIsIm1vZHVsZXMvQVBJL0FQSS5qcyIsIm1vZHVsZXMvUlRDL0RhdGFDaGFubmVscy5qcyIsIm1vZHVsZXMvUlRDL0xvY2FsU3RyZWFtLmpzIiwibW9kdWxlcy9SVEMvTWVkaWFTdHJlYW0uanMiLCJtb2R1bGVzL1JUQy9SVEMuanMiLCJtb2R1bGVzL1JUQy9SVENVdGlscy5qcyIsIm1vZHVsZXMvVUkvVUkuanMiLCJtb2R1bGVzL1VJL2F1ZGlvX2xldmVscy9BdWRpb0xldmVscy5qcyIsIm1vZHVsZXMvVUkvYXVkaW9fbGV2ZWxzL0NhbnZhc1V0aWxzLmpzIiwibW9kdWxlcy9VSS9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvbi5qcyIsIm1vZHVsZXMvVUkvYXZhdGFyL0F2YXRhci5qcyIsIm1vZHVsZXMvVUkvZXRoZXJwYWQvRXRoZXJwYWQuanMiLCJtb2R1bGVzL1VJL3ByZXppL1ByZXppLmpzIiwibW9kdWxlcy9VSS9wcmV6aS9QcmV6aVBsYXllci5qcyIsIm1vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXIuanMiLCJtb2R1bGVzL1VJL3NpZGVfcGFubmVscy9jaGF0L0NoYXQuanMiLCJtb2R1bGVzL1VJL3NpZGVfcGFubmVscy9jaGF0L0NvbW1hbmRzLmpzIiwibW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY2hhdC9SZXBsYWNlbWVudC5qcyIsIm1vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL2NoYXQvc21pbGV5cy5qc29uIiwibW9kdWxlcy9VSS9zaWRlX3Bhbm5lbHMvY29udGFjdGxpc3QvQ29udGFjdExpc3QuanMiLCJtb2R1bGVzL1VJL3NpZGVfcGFubmVscy9zZXR0aW5ncy9TZXR0aW5ncy5qcyIsIm1vZHVsZXMvVUkvc2lkZV9wYW5uZWxzL3NldHRpbmdzL1NldHRpbmdzTWVudS5qcyIsIm1vZHVsZXMvVUkvdG9vbGJhcnMvQm90dG9tVG9vbGJhci5qcyIsIm1vZHVsZXMvVUkvdG9vbGJhcnMvVG9vbGJhci5qcyIsIm1vZHVsZXMvVUkvdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXIuanMiLCJtb2R1bGVzL1VJL3V0aWwvSml0c2lQb3BvdmVyLmpzIiwibW9kdWxlcy9VSS91dGlsL01lc3NhZ2VIYW5kbGVyLmpzIiwibW9kdWxlcy9VSS91dGlsL05pY2tuYW1lSGFuZGxlci5qcyIsIm1vZHVsZXMvVUkvdXRpbC9VSVV0aWwuanMiLCJtb2R1bGVzL1VJL3ZpZGVvbGF5b3V0L0Nvbm5lY3Rpb25JbmRpY2F0b3IuanMiLCJtb2R1bGVzL1VJL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0LmpzIiwibW9kdWxlcy9VSS93ZWxjb21lX3BhZ2UvUm9vbW5hbWVHZW5lcmF0b3IuanMiLCJtb2R1bGVzL1VJL3dlbGNvbWVfcGFnZS9XZWxjb21lUGFnZS5qcyIsIm1vZHVsZXMvY29ubmVjdGlvbnF1YWxpdHkvY29ubmVjdGlvbnF1YWxpdHkuanMiLCJtb2R1bGVzL2Rlc2t0b3BzaGFyaW5nL2Rlc2t0b3BzaGFyaW5nLmpzIiwibW9kdWxlcy9rZXlib2FyZHNob3J0Y3V0L2tleWJvYXJkc2hvcnRjdXQuanMiLCJtb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RMb2dnZXIuanMiLCJtb2R1bGVzL3NpbXVsY2FzdC9TaW11bGNhc3RSZWNlaXZlci5qcyIsIm1vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFNlbmRlci5qcyIsIm1vZHVsZXMvc2ltdWxjYXN0L1NpbXVsY2FzdFV0aWxzLmpzIiwibW9kdWxlcy9zaW11bGNhc3Qvc2ltdWxjYXN0LmpzIiwibW9kdWxlcy9zdGF0aXN0aWNzL0xvY2FsU3RhdHNDb2xsZWN0b3IuanMiLCJtb2R1bGVzL3N0YXRpc3RpY3MvUlRQU3RhdHNDb2xsZWN0b3IuanMiLCJtb2R1bGVzL3N0YXRpc3RpY3Mvc3RhdGlzdGljcy5qcyIsIm1vZHVsZXMvdHJhbnNsYXRpb24vdHJhbnNsYXRpb24uanMiLCJtb2R1bGVzL3htcHAvSmluZ2xlU2Vzc2lvbi5qcyIsIm1vZHVsZXMveG1wcC9TRFAuanMiLCJtb2R1bGVzL3htcHAvU0RQRGlmZmVyLmpzIiwibW9kdWxlcy94bXBwL1NEUFV0aWwuanMiLCJtb2R1bGVzL3htcHAvVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24uanMiLCJtb2R1bGVzL3htcHAvbW9kZXJhdG9yLmpzIiwibW9kdWxlcy94bXBwL3JlY29yZGluZy5qcyIsIm1vZHVsZXMveG1wcC9zdHJvcGhlLmVtdWMuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5qaW5nbGUuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5sb2dnZXIuanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS5tb2RlcmF0ZS5qcyIsIm1vZHVsZXMveG1wcC9zdHJvcGhlLnJheW8uanMiLCJtb2R1bGVzL3htcHAvc3Ryb3BoZS51dGlsLmpzIiwibW9kdWxlcy94bXBwL3htcHAuanMiLCJub2RlX21vZHVsZXMvaTE4bmV4dC1jbGllbnQvaTE4bmV4dC5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL2RlZmxhdGUuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvaW5mbGF0ZS5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi91dGlscy9jb21tb24uanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvdXRpbHMvc3RyaW5ncy5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2FkbGVyMzIuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9jb25zdGFudHMuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9jcmMzMi5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2RlZmxhdGUuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9nemhlYWRlci5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL2luZmZhc3QuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9pbmZsYXRlLmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL3psaWIvaW5mdHJlZXMuanMiLCJub2RlX21vZHVsZXMvcGFrby9saWIvemxpYi9tZXNzYWdlcy5qcyIsIm5vZGVfbW9kdWxlcy9wYWtvL2xpYi96bGliL3RyZWVzLmpzIiwibm9kZV9tb2R1bGVzL3Bha28vbGliL3psaWIvenN0cmVhbS5qcyIsInNlcnZpY2UvUlRDL01lZGlhU3RyZWFtVHlwZXMuanMiLCJzZXJ2aWNlL1JUQy9SVENCcm93c2VyVHlwZS5qcyIsInNlcnZpY2UvUlRDL1JUQ0V2ZW50cy5qcyIsInNlcnZpY2UvUlRDL1N0cmVhbUV2ZW50VHlwZXMuanMiLCJzZXJ2aWNlL1VJL1VJRXZlbnRzLmpzIiwic2VydmljZS9jb25uZWN0aW9ucXVhbGl0eS9DUUV2ZW50cy5qcyIsInNlcnZpY2UvZGVza3RvcHNoYXJpbmcvRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzLmpzIiwic2VydmljZS90cmFuc2xhdGlvbi9sYW5ndWFnZXMuanMiLCJzZXJ2aWNlL3htcHAvWE1QUEV2ZW50cy5qcyIsIi4uLy4uLy4uLy4uLy4uL3Vzci9sb2NhbC9saWIvbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2V2ZW50cy9ldmVudHMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5VUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9zQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZRQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvUEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2S0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6cEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNySUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25VQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzUUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDek1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hyQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25JQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsM0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1bUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNVZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxUUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZtQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzViQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6a0VBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDYkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeFdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwdURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JVQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOTlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JVQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlxQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0xBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ05BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0xBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ05BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvKiBqc2hpbnQgLVcxMTcgKi9cbi8qIGFwcGxpY2F0aW9uIHNwZWNpZmljIGxvZ2ljICovXG5cbnZhciBBUFAgPVxue1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdGhpcy5VSSA9IHJlcXVpcmUoXCIuL21vZHVsZXMvVUkvVUlcIik7XG4gICAgICAgIHRoaXMuQVBJID0gcmVxdWlyZShcIi4vbW9kdWxlcy9BUEkvQVBJXCIpO1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb25xdWFsaXR5ID0gcmVxdWlyZShcIi4vbW9kdWxlcy9jb25uZWN0aW9ucXVhbGl0eS9jb25uZWN0aW9ucXVhbGl0eVwiKTtcbiAgICAgICAgdGhpcy5zdGF0aXN0aWNzID0gcmVxdWlyZShcIi4vbW9kdWxlcy9zdGF0aXN0aWNzL3N0YXRpc3RpY3NcIik7XG4gICAgICAgIHRoaXMuUlRDID0gcmVxdWlyZShcIi4vbW9kdWxlcy9SVEMvUlRDXCIpO1xuICAgICAgICB0aGlzLnNpbXVsY2FzdCA9IHJlcXVpcmUoXCIuL21vZHVsZXMvc2ltdWxjYXN0L3NpbXVsY2FzdFwiKTtcbiAgICAgICAgdGhpcy5kZXNrdG9wc2hhcmluZyA9IHJlcXVpcmUoXCIuL21vZHVsZXMvZGVza3RvcHNoYXJpbmcvZGVza3RvcHNoYXJpbmdcIik7XG4gICAgICAgIHRoaXMueG1wcCA9IHJlcXVpcmUoXCIuL21vZHVsZXMveG1wcC94bXBwXCIpO1xuICAgICAgICB0aGlzLmtleWJvYXJkc2hvcnRjdXQgPSByZXF1aXJlKFwiLi9tb2R1bGVzL2tleWJvYXJkc2hvcnRjdXQva2V5Ym9hcmRzaG9ydGN1dFwiKTtcbiAgICAgICAgdGhpcy50cmFuc2xhdGlvbiA9IHJlcXVpcmUoXCIuL21vZHVsZXMvdHJhbnNsYXRpb24vdHJhbnNsYXRpb25cIik7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gaW5pdCgpIHtcblxuICAgIEFQUC5SVEMuc3RhcnQoKTtcbiAgICBBUFAueG1wcC5zdGFydChBUFAuVUkuZ2V0Q3JlZGVudGlhbHMoKSk7XG4gICAgQVBQLnN0YXRpc3RpY3Muc3RhcnQoKTtcbiAgICBBUFAuY29ubmVjdGlvbnF1YWxpdHkuaW5pdCgpO1xuXG4gICAgLy8gU2V0IGRlZmF1bHQgZGVza3RvcCBzaGFyaW5nIG1ldGhvZFxuICAgIEFQUC5kZXNrdG9wc2hhcmluZy5pbml0KCk7XG5cbiAgICBBUFAua2V5Ym9hcmRzaG9ydGN1dC5pbml0KCk7XG59XG5cblxuJChkb2N1bWVudCkucmVhZHkoZnVuY3Rpb24gKCkge1xuXG4gICAgQVBQLmluaXQoKTtcblxuICAgIEFQUC50cmFuc2xhdGlvbi5pbml0KCk7XG5cbiAgICBpZihBUFAuQVBJLmlzRW5hYmxlZCgpKVxuICAgICAgICBBUFAuQVBJLmluaXQoKTtcblxuICAgIEFQUC5VSS5zdGFydChpbml0KTtcblxufSk7XG5cbiQod2luZG93KS5iaW5kKCdiZWZvcmV1bmxvYWQnLCBmdW5jdGlvbiAoKSB7XG4gICAgaWYoQVBQLkFQSS5pc0VuYWJsZWQoKSlcbiAgICAgICAgQVBQLkFQSS5kaXNwb3NlKCk7XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBBUFA7XG5cbiIsIi8qKlxuICogSW1wbGVtZW50cyBBUEkgY2xhc3MgdGhhdCBjb21tdW5pY2F0ZXMgd2l0aCBleHRlcm5hbCBhcGkgY2xhc3NcbiAqIGFuZCBwcm92aWRlcyBpbnRlcmZhY2UgdG8gYWNjZXNzIEppdHNpIE1lZXQgZmVhdHVyZXMgYnkgZXh0ZXJuYWxcbiAqIGFwcGxpY2F0aW9ucyB0aGF0IGVtYmVkIEppdHNpIE1lZXRcbiAqL1xuXG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcblxuLyoqXG4gKiBMaXN0IG9mIHRoZSBhdmFpbGFibGUgY29tbWFuZHMuXG4gKiBAdHlwZSB7e1xuICogICAgICAgICAgICAgIGRpc3BsYXlOYW1lOiBpbnB1dERpc3BsYXlOYW1lSGFuZGxlcixcbiAqICAgICAgICAgICAgICBtdXRlQXVkaW86IHRvZ2dsZUF1ZGlvLFxuICogICAgICAgICAgICAgIG11dGVWaWRlbzogdG9nZ2xlVmlkZW8sXG4gKiAgICAgICAgICAgICAgZmlsbVN0cmlwOiB0b2dnbGVGaWxtU3RyaXBcbiAqICAgICAgICAgIH19XG4gKi9cbnZhciBjb21tYW5kcyA9XG57XG4gICAgZGlzcGxheU5hbWU6IEFQUC5VSS5pbnB1dERpc3BsYXlOYW1lSGFuZGxlcixcbiAgICBtdXRlQXVkaW86IEFQUC5VSS50b2dnbGVBdWRpbyxcbiAgICBtdXRlVmlkZW86IEFQUC5VSS50b2dnbGVWaWRlbyxcbiAgICB0b2dnbGVGaWxtU3RyaXA6IEFQUC5VSS50b2dnbGVGaWxtU3RyaXAsXG4gICAgdG9nZ2xlQ2hhdDogQVBQLlVJLnRvZ2dsZUNoYXQsXG4gICAgdG9nZ2xlQ29udGFjdExpc3Q6IEFQUC5VSS50b2dnbGVDb250YWN0TGlzdFxufTtcblxuXG4vKipcbiAqIE1hcHMgdGhlIHN1cHBvcnRlZCBldmVudHMgYW5kIHRoZWlyIHN0YXR1c1xuICogKHRydWUgaXQgdGhlIGV2ZW50IGlzIGVuYWJsZWQgYW5kIGZhbHNlIGlmIGl0IGlzIGRpc2FibGVkKVxuICogQHR5cGUge3tcbiAqICAgICAgICAgICAgICBpbmNvbWluZ01lc3NhZ2U6IGJvb2xlYW4sXG4gKiAgICAgICAgICAgICAgb3V0Z29pbmdNZXNzYWdlOiBib29sZWFuLFxuICogICAgICAgICAgICAgIGRpc3BsYXlOYW1lQ2hhbmdlOiBib29sZWFuLFxuICogICAgICAgICAgICAgIHBhcnRpY2lwYW50Sm9pbmVkOiBib29sZWFuLFxuICogICAgICAgICAgICAgIHBhcnRpY2lwYW50TGVmdDogYm9vbGVhblxuICogICAgICB9fVxuICovXG52YXIgZXZlbnRzID1cbntcbiAgICBpbmNvbWluZ01lc3NhZ2U6IGZhbHNlLFxuICAgIG91dGdvaW5nTWVzc2FnZTpmYWxzZSxcbiAgICBkaXNwbGF5TmFtZUNoYW5nZTogZmFsc2UsXG4gICAgcGFydGljaXBhbnRKb2luZWQ6IGZhbHNlLFxuICAgIHBhcnRpY2lwYW50TGVmdDogZmFsc2Vcbn07XG5cbnZhciBkaXNwbGF5TmFtZSA9IHt9O1xuXG4vKipcbiAqIFByb2Nlc3NlcyBjb21tYW5kcyBmcm9tIGV4dGVybmFsIGFwcGxpY2FpdG9uLlxuICogQHBhcmFtIG1lc3NhZ2UgdGhlIG9iamVjdCB3aXRoIHRoZSBjb21tYW5kXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NDb21tYW5kKG1lc3NhZ2UpXG57XG4gICAgaWYobWVzc2FnZS5hY3Rpb24gIT0gXCJleGVjdXRlXCIpXG4gICAge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5rbm93biBhY3Rpb24gb2YgdGhlIG1lc3NhZ2VcIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZm9yKHZhciBrZXkgaW4gbWVzc2FnZSlcbiAgICB7XG4gICAgICAgIGlmKGNvbW1hbmRzW2tleV0pXG4gICAgICAgICAgICBjb21tYW5kc1trZXldLmFwcGx5KG51bGwsIG1lc3NhZ2Vba2V5XSk7XG4gICAgfVxufVxuXG4vKipcbiAqIFByb2Nlc3NlcyBldmVudHMgb2JqZWN0cyBmcm9tIGV4dGVybmFsIGFwcGxpY2F0aW9uc1xuICogQHBhcmFtIGV2ZW50IHRoZSBldmVudFxuICovXG5mdW5jdGlvbiBwcm9jZXNzRXZlbnQoZXZlbnQpIHtcbiAgICBpZighZXZlbnQuYWN0aW9uKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkV2ZW50IHdpdGggbm8gYWN0aW9uIGlzIHJlY2VpdmVkLlwiKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBpID0gMDtcbiAgICBzd2l0Y2goZXZlbnQuYWN0aW9uKVxuICAgIHtcbiAgICAgICAgY2FzZSBcImFkZFwiOlxuICAgICAgICAgICAgZm9yKDsgaSA8IGV2ZW50LmV2ZW50cy5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBldmVudHNbZXZlbnQuZXZlbnRzW2ldXSA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInJlbW92ZVwiOlxuICAgICAgICAgICAgZm9yKDsgaSA8IGV2ZW50LmV2ZW50cy5sZW5ndGg7IGkrKylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBldmVudHNbZXZlbnQuZXZlbnRzW2ldXSA9IGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5rbm93biBhY3Rpb24gZm9yIGV2ZW50LlwiKTtcbiAgICB9XG5cbn1cblxuLyoqXG4gKiBTZW5kcyBtZXNzYWdlIHRvIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvbi5cbiAqIEBwYXJhbSBvYmplY3RcbiAqL1xuZnVuY3Rpb24gc2VuZE1lc3NhZ2Uob2JqZWN0KSB7XG4gICAgd2luZG93LnBhcmVudC5wb3N0TWVzc2FnZShKU09OLnN0cmluZ2lmeShvYmplY3QpLCBcIipcIik7XG59XG5cbi8qKlxuICogUHJvY2Vzc2VzIGEgbWVzc2FnZSBldmVudCBmcm9tIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvblxuICogQHBhcmFtIGV2ZW50IHRoZSBtZXNzYWdlIGV2ZW50XG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NNZXNzYWdlKGV2ZW50KVxue1xuICAgIHZhciBtZXNzYWdlO1xuICAgIHRyeSB7XG4gICAgICAgIG1lc3NhZ2UgPSBKU09OLnBhcnNlKGV2ZW50LmRhdGEpO1xuICAgIH0gY2F0Y2ggKGUpIHt9XG5cbiAgICBpZighbWVzc2FnZS50eXBlKVxuICAgICAgICByZXR1cm47XG4gICAgc3dpdGNoIChtZXNzYWdlLnR5cGUpXG4gICAge1xuICAgICAgICBjYXNlIFwiY29tbWFuZFwiOlxuICAgICAgICAgICAgcHJvY2Vzc0NvbW1hbmQobWVzc2FnZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImV2ZW50XCI6XG4gICAgICAgICAgICBwcm9jZXNzRXZlbnQobWVzc2FnZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJVbmtub3duIHR5cGUgb2YgdGhlIG1lc3NhZ2VcIik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgfVxuXG59XG5cbmZ1bmN0aW9uIHNldHVwTGlzdGVuZXJzKCkge1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0VOVEVSLCBmdW5jdGlvbiAoZnJvbSkge1xuICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwicGFydGljaXBhbnRKb2luZWRcIiwge2ppZDogZnJvbX0pO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTUVTU0FHRV9SRUNFSVZFRCwgZnVuY3Rpb24gKGZyb20sIG5pY2ssIHR4dCwgbXlqaWQpIHtcbiAgICAgICAgaWYgKGZyb20gIT0gbXlqaWQpXG4gICAgICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwiaW5jb21pbmdNZXNzYWdlXCIsXG4gICAgICAgICAgICAgICAge1wiZnJvbVwiOiBmcm9tLCBcIm5pY2tcIjogbmljaywgXCJtZXNzYWdlXCI6IHR4dH0pO1xuICAgIH0pO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0xFRlQsIGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgQVBJLnRyaWdnZXJFdmVudChcInBhcnRpY2lwYW50TGVmdFwiLCB7amlkOiBqaWR9KTtcbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkRJU1BMQVlfTkFNRV9DSEFOR0VELCBmdW5jdGlvbiAoamlkLCBuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICBuYW1lID0gZGlzcGxheU5hbWVbamlkXTtcbiAgICAgICAgaWYoIW5hbWUgfHwgbmFtZSAhPSBuZXdEaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgQVBJLnRyaWdnZXJFdmVudChcImRpc3BsYXlOYW1lQ2hhbmdlXCIsIHtqaWQ6IGppZCwgZGlzcGxheW5hbWU6IG5ld0Rpc3BsYXlOYW1lfSk7XG4gICAgICAgICAgICBkaXNwbGF5TmFtZVtqaWRdID0gbmV3RGlzcGxheU5hbWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLlNFTkRJTkdfQ0hBVF9NRVNTQUdFLCBmdW5jdGlvbiAoYm9keSkge1xuICAgICAgICBBUFAuQVBJLnRyaWdnZXJFdmVudChcIm91dGdvaW5nTWVzc2FnZVwiLCB7XCJtZXNzYWdlXCI6IGJvZHl9KTtcbiAgICB9KTtcbn1cblxudmFyIEFQSSA9IHtcbiAgICAvKipcbiAgICAgKiBDaGVjayB3aGV0aGVyIHRoZSBBUEkgc2hvdWxkIGJlIGVuYWJsZWQgb3Igbm90LlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAqL1xuICAgIGlzRW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaGFzaCA9IGxvY2F0aW9uLmhhc2g7XG4gICAgICAgIGlmKGhhc2ggJiYgaGFzaC5pbmRleE9mKFwiZXh0ZXJuYWxcIikgPiAtMSAmJiB3aW5kb3cucG9zdE1lc3NhZ2UpXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZXMgdGhlIEFQSUNvbm5lY3Rvci4gU2V0dXBzIG1lc3NhZ2UgZXZlbnQgbGlzdGVuZXJzIHRoYXQgd2lsbFxuICAgICAqIHJlY2VpdmUgaW5mb3JtYXRpb24gZnJvbSBleHRlcm5hbCBhcHBsaWNhdGlvbnMgdGhhdCBlbWJlZCBKaXRzaSBNZWV0LlxuICAgICAqIEl0IGFsc28gc2VuZHMgYSBtZXNzYWdlIHRvIHRoZSBleHRlcm5hbCBhcHBsaWNhdGlvbiB0aGF0IEFQSUNvbm5lY3RvclxuICAgICAqIGlzIGluaXRpYWxpemVkLlxuICAgICAqL1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsXG4gICAgICAgICAgICAgICAgcHJvY2Vzc01lc3NhZ2UsIGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5hdHRhY2hFdmVudCgnb25tZXNzYWdlJywgcHJvY2Vzc01lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICAgIHNlbmRNZXNzYWdlKHt0eXBlOiBcInN5c3RlbVwiLCBsb2FkZWQ6IHRydWV9KTtcbiAgICAgICAgc2V0dXBMaXN0ZW5lcnMoKTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIENoZWNrcyB3aGV0aGVyIHRoZSBldmVudCBpcyBlbmFibGVkIG90IG5vdC5cbiAgICAgKiBAcGFyYW0gbmFtZSB0aGUgbmFtZSBvZiB0aGUgZXZlbnQuXG4gICAgICogQHJldHVybnMgeyp9XG4gICAgICovXG4gICAgaXNFdmVudEVuYWJsZWQ6IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgIHJldHVybiBldmVudHNbbmFtZV07XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFNlbmRzIGV2ZW50IG9iamVjdCB0byB0aGUgZXh0ZXJuYWwgYXBwbGljYXRpb24gdGhhdCBoYXMgYmVlbiBzdWJzY3JpYmVkXG4gICAgICogZm9yIHRoYXQgZXZlbnQuXG4gICAgICogQHBhcmFtIG5hbWUgdGhlIG5hbWUgZXZlbnRcbiAgICAgKiBAcGFyYW0gb2JqZWN0IGRhdGEgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudFxuICAgICAqL1xuICAgIHRyaWdnZXJFdmVudDogZnVuY3Rpb24gKG5hbWUsIG9iamVjdCkge1xuICAgICAgICBpZih0aGlzLmlzRW5hYmxlZCgpICYmIHRoaXMuaXNFdmVudEVuYWJsZWQobmFtZSkpXG4gICAgICAgICAgICBzZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgdHlwZTogXCJldmVudFwiLCBhY3Rpb246IFwicmVzdWx0XCIsIGV2ZW50OiBuYW1lLCByZXN1bHQ6IG9iamVjdH0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIHRoZSBsaXN0ZW5lcnMuXG4gICAgICovXG4gICAgZGlzcG9zZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZih3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcilcbiAgICAgICAge1xuICAgICAgICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsXG4gICAgICAgICAgICAgICAgcHJvY2Vzc01lc3NhZ2UsIGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5kZXRhY2hFdmVudCgnb25tZXNzYWdlJywgcHJvY2Vzc01lc3NhZ2UpO1xuICAgICAgICB9XG5cbiAgICB9XG5cblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBBUEk7IiwiLyogZ2xvYmFsIFN0cm9waGUsIHVwZGF0ZUxhcmdlVmlkZW8sIGZvY3VzZWRWaWRlb1NyYyovXG5cbi8vIGNhY2hlIGRhdGFjaGFubmVscyB0byBhdm9pZCBnYXJiYWdlIGNvbGxlY3Rpb25cbi8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD00MDU1NDVcbnZhciBSVENFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvUlRDRXZlbnRzXCIpO1xuXG52YXIgX2RhdGFDaGFubmVscyA9IFtdO1xudmFyIGV2ZW50RW1pdHRlciA9IG51bGw7XG5cblxuXG5cbnZhciBEYXRhQ2hhbm5lbHMgPVxue1xuXG4gICAgLyoqXG4gICAgICogQ2FsbGJhY2sgdHJpZ2dlcmVkIGJ5IFBlZXJDb25uZWN0aW9uIHdoZW4gbmV3IGRhdGEgY2hhbm5lbCBpcyBvcGVuZWRcbiAgICAgKiBvbiB0aGUgYnJpZGdlLlxuICAgICAqIEBwYXJhbSBldmVudCB0aGUgZXZlbnQgaW5mbyBvYmplY3QuXG4gICAgICovXG5cbiAgICBvbkRhdGFDaGFubmVsOiBmdW5jdGlvbiAoZXZlbnQpXG4gICAge1xuICAgICAgICB2YXIgZGF0YUNoYW5uZWwgPSBldmVudC5jaGFubmVsO1xuXG4gICAgICAgIGRhdGFDaGFubmVsLm9ub3BlbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkRhdGEgY2hhbm5lbCBvcGVuZWQgYnkgdGhlIFZpZGVvYnJpZGdlIVwiLCBkYXRhQ2hhbm5lbCk7XG5cbiAgICAgICAgICAgIC8vIENvZGUgc2FtcGxlIGZvciBzZW5kaW5nIHN0cmluZyBhbmQvb3IgYmluYXJ5IGRhdGFcbiAgICAgICAgICAgIC8vIFNlbmRzIFN0cmluZyBtZXNzYWdlIHRvIHRoZSBicmlkZ2VcbiAgICAgICAgICAgIC8vZGF0YUNoYW5uZWwuc2VuZChcIkhlbGxvIGJyaWRnZSFcIik7XG4gICAgICAgICAgICAvLyBTZW5kcyAxMiBieXRlcyBiaW5hcnkgbWVzc2FnZSB0byB0aGUgYnJpZGdlXG4gICAgICAgICAgICAvL2RhdGFDaGFubmVsLnNlbmQobmV3IEFycmF5QnVmZmVyKDEyKSk7XG5cbiAgICAgICAgICAgIC8vIHdoZW4gdGhlIGRhdGEgY2hhbm5lbCBiZWNvbWVzIGF2YWlsYWJsZSwgdGVsbCB0aGUgYnJpZGdlIGFib3V0IHZpZGVvXG4gICAgICAgICAgICAvLyBzZWxlY3Rpb25zIHNvIHRoYXQgaXQgY2FuIGRvIGFkYXB0aXZlIHNpbXVsY2FzdCxcbiAgICAgICAgICAgIC8vIHdlIHdhbnQgdGhlIG5vdGlmaWNhdGlvbiB0byB0cmlnZ2VyIGV2ZW4gaWYgdXNlckppZCBpcyB1bmRlZmluZWQsXG4gICAgICAgICAgICAvLyBvciBudWxsLlxuICAgICAgICAgICAgdmFyIHVzZXJKaWQgPSBBUFAuVUkuZ2V0TGFyZ2VWaWRlb1N0YXRlKCkudXNlckppZDtcbiAgICAgICAgICAgIC8vIHdlIHdhbnQgdGhlIG5vdGlmaWNhdGlvbiB0byB0cmlnZ2VyIGV2ZW4gaWYgdXNlckppZCBpcyB1bmRlZmluZWQsXG4gICAgICAgICAgICAvLyBvciBudWxsLlxuICAgICAgICAgICAgb25TZWxlY3RlZEVuZHBvaW50Q2hhbmdlZCh1c2VySmlkKTtcbiAgICAgICAgfTtcblxuICAgICAgICBkYXRhQ2hhbm5lbC5vbmVycm9yID0gZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRGF0YSBDaGFubmVsIEVycm9yOlwiLCBlcnJvciwgZGF0YUNoYW5uZWwpO1xuICAgICAgICB9O1xuXG4gICAgICAgIGRhdGFDaGFubmVsLm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgdmFyIGRhdGEgPSBldmVudC5kYXRhO1xuICAgICAgICAgICAgLy8gSlNPTlxuICAgICAgICAgICAgdmFyIG9iajtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBvYmogPSBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgICAgICBcIkZhaWxlZCB0byBwYXJzZSBkYXRhIGNoYW5uZWwgbWVzc2FnZSBhcyBKU09OOiBcIixcbiAgICAgICAgICAgICAgICAgICAgZGF0YSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YUNoYW5uZWwpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCgndW5kZWZpbmVkJyAhPT0gdHlwZW9mKG9iaikpICYmIChudWxsICE9PSBvYmopKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNvbGlicmlDbGFzcyA9IG9iai5jb2xpYnJpQ2xhc3M7XG5cbiAgICAgICAgICAgICAgICBpZiAoXCJEb21pbmFudFNwZWFrZXJFbmRwb2ludENoYW5nZUV2ZW50XCIgPT09IGNvbGlicmlDbGFzcykge1xuICAgICAgICAgICAgICAgICAgICAvLyBFbmRwb2ludCBJRCBmcm9tIHRoZSBWaWRlb2JyaWRnZS5cbiAgICAgICAgICAgICAgICAgICAgdmFyIGRvbWluYW50U3BlYWtlckVuZHBvaW50ID0gb2JqLmRvbWluYW50U3BlYWtlckVuZHBvaW50O1xuXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiRGF0YSBjaGFubmVsIG5ldyBkb21pbmFudCBzcGVha2VyIGV2ZW50OiBcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvbWluYW50U3BlYWtlckVuZHBvaW50KTtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLkRPTUlOQU5UU1BFQUtFUl9DSEFOR0VELCBkb21pbmFudFNwZWFrZXJFbmRwb2ludCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKFwiSW5MYXN0TkNoYW5nZUV2ZW50XCIgPT09IGNvbGlicmlDbGFzcylcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBvbGRWYWx1ZSA9IG9iai5vbGRWYWx1ZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5ld1ZhbHVlID0gb2JqLm5ld1ZhbHVlO1xuICAgICAgICAgICAgICAgICAgICAvLyBNYWtlIHN1cmUgdGhhdCBvbGRWYWx1ZSBhbmQgbmV3VmFsdWUgYXJlIG9mIHR5cGUgYm9vbGVhbi5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHR5cGU7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCh0eXBlID0gdHlwZW9mIG9sZFZhbHVlKSAhPT0gJ2Jvb2xlYW4nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbGRWYWx1ZSA9IChvbGRWYWx1ZSA9PSBcInRydWVcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZFZhbHVlID0gbmV3IEJvb2xlYW4ob2xkVmFsdWUpLnZhbHVlT2YoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoKHR5cGUgPSB0eXBlb2YgbmV3VmFsdWUpICE9PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0eXBlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld1ZhbHVlID0gKG5ld1ZhbHVlID09IFwidHJ1ZVwiKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3VmFsdWUgPSBuZXcgQm9vbGVhbihuZXdWYWx1ZSkudmFsdWVPZigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLkxBU1ROX0NIQU5HRUQsIG9sZFZhbHVlLCBuZXdWYWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKFwiTGFzdE5FbmRwb2ludHNDaGFuZ2VFdmVudFwiID09PSBjb2xpYnJpQ2xhc3MpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAvLyBUaGUgbmV3L2xhdGVzdCBsaXN0IG9mIGxhc3QtbiBlbmRwb2ludCBJRHMuXG4gICAgICAgICAgICAgICAgICAgIHZhciBsYXN0TkVuZHBvaW50cyA9IG9iai5sYXN0TkVuZHBvaW50cztcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlIGxpc3Qgb2YgZW5kcG9pbnQgSURzIHdoaWNoIGFyZSBlbnRlcmluZyB0aGUgbGlzdCBvZlxuICAgICAgICAgICAgICAgICAgICAvLyBsYXN0LW4gYXQgdGhpcyB0aW1lIGkuZS4gd2VyZSBub3QgaW4gdGhlIG9sZCBsaXN0IG9mIGxhc3QtblxuICAgICAgICAgICAgICAgICAgICAvLyBlbmRwb2ludCBJRHMuXG4gICAgICAgICAgICAgICAgICAgIHZhciBlbmRwb2ludHNFbnRlcmluZ0xhc3ROID0gb2JqLmVuZHBvaW50c0VudGVyaW5nTGFzdE47XG4gICAgICAgICAgICAgICAgICAgIHZhciBzdHJlYW0gPSBvYmouc3RyZWFtO1xuXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJEYXRhIGNoYW5uZWwgbmV3IGxhc3QtbiBldmVudDogXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0TkVuZHBvaW50cywgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Tiwgb2JqKTtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLkxBU1ROX0VORFBPSU5UX0NIQU5HRUQsXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0TkVuZHBvaW50cywgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Tiwgb2JqKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoXCJTaW11bGNhc3RMYXllcnNDaGFuZ2VkRXZlbnRcIiA9PT0gY29saWJyaUNsYXNzKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoUlRDRXZlbnRzLlNJTVVMQ0FTVF9MQVlFUl9DSEFOR0VELFxuICAgICAgICAgICAgICAgICAgICAgICAgb2JqLmVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoXCJTaW11bGNhc3RMYXllcnNDaGFuZ2luZ0V2ZW50XCIgPT09IGNvbGlicmlDbGFzcylcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFJUQ0V2ZW50cy5TSU1VTENBU1RfTEFZRVJfQ0hBTkdJTkcsXG4gICAgICAgICAgICAgICAgICAgICAgICBvYmouZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChcIlN0YXJ0U2ltdWxjYXN0TGF5ZXJFdmVudFwiID09PSBjb2xpYnJpQ2xhc3MpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChSVENFdmVudHMuU0lNVUxDQVNUX1NUQVJULCBvYmouc2ltdWxjYXN0TGF5ZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChcIlN0b3BTaW11bGNhc3RMYXllckV2ZW50XCIgPT09IGNvbGlicmlDbGFzcylcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFJUQ0V2ZW50cy5TSU1VTENBU1RfU1RPUCwgb2JqLnNpbXVsY2FzdExheWVyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIkRhdGEgY2hhbm5lbCBKU09OLWZvcm1hdHRlZCBtZXNzYWdlOiBcIiwgb2JqKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgZGF0YUNoYW5uZWwub25jbG9zZSA9IGZ1bmN0aW9uICgpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIlRoZSBEYXRhIENoYW5uZWwgY2xvc2VkXCIsIGRhdGFDaGFubmVsKTtcbiAgICAgICAgICAgIHZhciBpZHggPSBfZGF0YUNoYW5uZWxzLmluZGV4T2YoZGF0YUNoYW5uZWwpO1xuICAgICAgICAgICAgaWYgKGlkeCA+IC0xKVxuICAgICAgICAgICAgICAgIF9kYXRhQ2hhbm5lbHMgPSBfZGF0YUNoYW5uZWxzLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB9O1xuICAgICAgICBfZGF0YUNoYW5uZWxzLnB1c2goZGF0YUNoYW5uZWwpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBCaW5kcyBcIm9uZGF0YWNoYW5uZWxcIiBldmVudCBsaXN0ZW5lciB0byBnaXZlbiBQZWVyQ29ubmVjdGlvbiBpbnN0YW5jZS5cbiAgICAgKiBAcGFyYW0gcGVlckNvbm5lY3Rpb24gV2ViUlRDIHBlZXIgY29ubmVjdGlvbiBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBpbml0OiBmdW5jdGlvbiAocGVlckNvbm5lY3Rpb24sIGVtaXR0ZXIpIHtcbiAgICAgICAgaWYoIWNvbmZpZy5vcGVuU2N0cClcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBwZWVyQ29ubmVjdGlvbi5vbmRhdGFjaGFubmVsID0gdGhpcy5vbkRhdGFDaGFubmVsO1xuICAgICAgICBldmVudEVtaXR0ZXIgPSBlbWl0dGVyO1xuXG4gICAgICAgIC8vIFNhbXBsZSBjb2RlIGZvciBvcGVuaW5nIG5ldyBkYXRhIGNoYW5uZWwgZnJvbSBKaXRzaSBNZWV0IHRvIHRoZSBicmlkZ2UuXG4gICAgICAgIC8vIEFsdGhvdWdoIGl0J3Mgbm90IGEgcmVxdWlyZW1lbnQgdG8gb3BlbiBzZXBhcmF0ZSBjaGFubmVscyBmcm9tIGJvdGggYnJpZGdlXG4gICAgICAgIC8vIGFuZCBwZWVyIGFzIHNpbmdsZSBjaGFubmVsIGNhbiBiZSB1c2VkIGZvciBzZW5kaW5nIGFuZCByZWNlaXZpbmcgZGF0YS5cbiAgICAgICAgLy8gU28gZWl0aGVyIGNoYW5uZWwgb3BlbmVkIGJ5IHRoZSBicmlkZ2Ugb3IgdGhlIG9uZSBvcGVuZWQgaGVyZSBpcyBlbm91Z2hcbiAgICAgICAgLy8gZm9yIGNvbW11bmljYXRpb24gd2l0aCB0aGUgYnJpZGdlLlxuICAgICAgICAvKnZhciBkYXRhQ2hhbm5lbE9wdGlvbnMgPVxuICAgICAgICAge1xuICAgICAgICAgcmVsaWFibGU6IHRydWVcbiAgICAgICAgIH07XG4gICAgICAgICB2YXIgZGF0YUNoYW5uZWxcbiAgICAgICAgID0gcGVlckNvbm5lY3Rpb24uY3JlYXRlRGF0YUNoYW5uZWwoXCJteUNoYW5uZWxcIiwgZGF0YUNoYW5uZWxPcHRpb25zKTtcblxuICAgICAgICAgLy8gQ2FuIGJlIHVzZWQgb25seSB3aGVuIGlzIGluIG9wZW4gc3RhdGVcbiAgICAgICAgIGRhdGFDaGFubmVsLm9ub3BlbiA9IGZ1bmN0aW9uICgpXG4gICAgICAgICB7XG4gICAgICAgICBkYXRhQ2hhbm5lbC5zZW5kKFwiTXkgY2hhbm5lbCAhISFcIik7XG4gICAgICAgICB9O1xuICAgICAgICAgZGF0YUNoYW5uZWwub25tZXNzYWdlID0gZnVuY3Rpb24gKGV2ZW50KVxuICAgICAgICAge1xuICAgICAgICAgdmFyIG1zZ0RhdGEgPSBldmVudC5kYXRhO1xuICAgICAgICAgY29uc29sZS5pbmZvKFwiR290IE15IERhdGEgQ2hhbm5lbCBNZXNzYWdlOlwiLCBtc2dEYXRhLCBkYXRhQ2hhbm5lbCk7XG4gICAgICAgICB9OyovXG4gICAgfSxcbiAgICBoYW5kbGVTZWxlY3RlZEVuZHBvaW50RXZlbnQ6IG9uU2VsZWN0ZWRFbmRwb2ludENoYW5nZWQsXG4gICAgaGFuZGxlUGlubmVkRW5kcG9pbnRFdmVudDogb25QaW5uZWRFbmRwb2ludENoYW5nZWRcblxufTtcblxuZnVuY3Rpb24gb25TZWxlY3RlZEVuZHBvaW50Q2hhbmdlZCh1c2VyUmVzb3VyY2UpXG57XG4gICAgY29uc29sZS5sb2coJ3NlbGVjdGVkIGVuZHBvaW50IGNoYW5nZWQ6ICcsIHVzZXJSZXNvdXJjZSk7XG4gICAgaWYgKF9kYXRhQ2hhbm5lbHMgJiYgX2RhdGFDaGFubmVscy5sZW5ndGggIT0gMClcbiAgICB7XG4gICAgICAgIF9kYXRhQ2hhbm5lbHMuc29tZShmdW5jdGlvbiAoZGF0YUNoYW5uZWwpIHtcbiAgICAgICAgICAgIGlmIChkYXRhQ2hhbm5lbC5yZWFkeVN0YXRlID09ICdvcGVuJylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBkYXRhQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICAgICAgICAgICAgJ2NvbGlicmlDbGFzcyc6ICdTZWxlY3RlZEVuZHBvaW50Q2hhbmdlZEV2ZW50JyxcbiAgICAgICAgICAgICAgICAgICAgJ3NlbGVjdGVkRW5kcG9pbnQnOlxuICAgICAgICAgICAgICAgICAgICAgICAgKCF1c2VyUmVzb3VyY2UgfHwgdXNlclJlc291cmNlID09PSBudWxsKT9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBudWxsIDogdXNlclJlc291cmNlXG4gICAgICAgICAgICAgICAgfSkpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gb25QaW5uZWRFbmRwb2ludENoYW5nZWQodXNlclJlc291cmNlKVxue1xuICAgIGNvbnNvbGUubG9nKCdwaW5uZWQgZW5kcG9pbnQgY2hhbmdlZDogJywgdXNlclJlc291cmNlKTtcbiAgICBpZiAoX2RhdGFDaGFubmVscyAmJiBfZGF0YUNoYW5uZWxzLmxlbmd0aCAhPSAwKVxuICAgIHtcbiAgICAgICAgX2RhdGFDaGFubmVscy5zb21lKGZ1bmN0aW9uIChkYXRhQ2hhbm5lbCkge1xuICAgICAgICAgICAgaWYgKGRhdGFDaGFubmVsLnJlYWR5U3RhdGUgPT0gJ29wZW4nKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGRhdGFDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICAgICAgICAnY29saWJyaUNsYXNzJzogJ1Bpbm5lZEVuZHBvaW50Q2hhbmdlZEV2ZW50JyxcbiAgICAgICAgICAgICAgICAgICAgJ3Bpbm5lZEVuZHBvaW50JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICghdXNlclJlc291cmNlIHx8IHVzZXJSZXNvdXJjZSA9PSBudWxsKT9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBudWxsIDogdXNlclJlc291cmNlXG4gICAgICAgICAgICAgICAgfSkpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBEYXRhQ2hhbm5lbHM7XG5cbiIsInZhciBTdHJlYW1FdmVudFR5cGVzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1N0cmVhbUV2ZW50VHlwZXMuanNcIik7XG5cbmZ1bmN0aW9uIExvY2FsU3RyZWFtKHN0cmVhbSwgdHlwZSwgZXZlbnRFbWl0dGVyLCB2aWRlb1R5cGUpXG57XG4gICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5ldmVudEVtaXR0ZXIgPSBldmVudEVtaXR0ZXI7XG4gICAgdGhpcy50eXBlID0gdHlwZTtcbiAgICB0aGlzLnZpZGVvVHlwZSA9IHZpZGVvVHlwZTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYodHlwZSA9PSBcImF1ZGlvXCIpXG4gICAge1xuICAgICAgICB0aGlzLmdldFRyYWNrcyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLnN0cmVhbS5nZXRBdWRpb1RyYWNrcygpO1xuICAgICAgICB9O1xuICAgIH1cbiAgICBlbHNlXG4gICAge1xuICAgICAgICB0aGlzLmdldFRyYWNrcyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLnN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIHRoaXMuc3RyZWFtLm9uZW5kZWQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICBzZWxmLnN0cmVhbUVuZGVkKCk7XG4gICAgfTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLnN0cmVhbUVuZGVkID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMuZXZlbnRFbWl0dGVyLmVtaXQoU3RyZWFtRXZlbnRUeXBlcy5FVkVOVF9UWVBFX0xPQ0FMX0VOREVELCB0aGlzKTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLmdldE9yaWdpbmFsU3RyZWFtID0gZnVuY3Rpb24oKVxue1xuICAgIHJldHVybiB0aGlzLnN0cmVhbTtcbn1cblxuTG9jYWxTdHJlYW0ucHJvdG90eXBlLmlzQXVkaW9TdHJlYW0gPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuICh0aGlzLnN0cmVhbS5nZXRBdWRpb1RyYWNrcygpICYmIHRoaXMuc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkubGVuZ3RoID4gMCk7XG59O1xuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUubXV0ZSA9IGZ1bmN0aW9uKClcbntcbiAgICB2YXIgaXNtdXRlZCA9IGZhbHNlO1xuICAgIHZhciB0cmFja3MgPSB0aGlzLmdldFRyYWNrcygpO1xuXG4gICAgZm9yICh2YXIgaWR4ID0gMDsgaWR4IDwgdHJhY2tzLmxlbmd0aDsgaWR4KyspIHtcbiAgICAgICAgaXNtdXRlZCA9ICF0cmFja3NbaWR4XS5lbmFibGVkO1xuICAgICAgICB0cmFja3NbaWR4XS5lbmFibGVkID0gaXNtdXRlZDtcbiAgICB9XG4gICAgcmV0dXJuIGlzbXV0ZWQ7XG59O1xuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUuc2V0TXV0ZSA9IGZ1bmN0aW9uKG11dGUpXG57XG4gICAgdmFyIHRyYWNrcyA9IHRoaXMuZ2V0VHJhY2tzKCk7XG5cbiAgICBmb3IgKHZhciBpZHggPSAwOyBpZHggPCB0cmFja3MubGVuZ3RoOyBpZHgrKykge1xuICAgICAgICB0cmFja3NbaWR4XS5lbmFibGVkID0gbXV0ZTtcbiAgICB9XG59O1xuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUuaXNNdXRlZCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgdHJhY2tzID0gW107XG4gICAgaWYodGhpcy50eXBlID09IFwiYXVkaW9cIilcbiAgICB7XG4gICAgICAgIHRyYWNrcyA9IHRoaXMuc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCk7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHRyYWNrcyA9IHRoaXMuc3RyZWFtLmdldFZpZGVvVHJhY2tzKCk7XG4gICAgfVxuICAgIGZvciAodmFyIGlkeCA9IDA7IGlkeCA8IHRyYWNrcy5sZW5ndGg7IGlkeCsrKSB7XG4gICAgICAgIGlmKHRyYWNrc1tpZHhdLmVuYWJsZWQpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuXG5Mb2NhbFN0cmVhbS5wcm90b3R5cGUuZ2V0SWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RyZWFtLmdldFRyYWNrcygpWzBdLmlkO1xufVxuXG5cblxubW9kdWxlLmV4cG9ydHMgPSBMb2NhbFN0cmVhbTtcbiIsIi8vLy9UaGVzZSBsaW5lcyBzaG91bGQgYmUgdW5jb21tZW50ZWQgd2hlbiByZXF1aXJlIHdvcmtzIGluIGFwcC5qc1xudmFyIFJUQ0Jyb3dzZXJUeXBlID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvUlRDL1JUQ0Jyb3dzZXJUeXBlLmpzXCIpO1xudmFyIE1lZGlhU3RyZWFtVHlwZSA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9NZWRpYVN0cmVhbVR5cGVzXCIpO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBNZWRpYVN0cmVhbSBvYmplY3QgZm9yIHRoZSBnaXZlbiBkYXRhLCBzZXNzaW9uIGlkIGFuZCBzc3JjLlxuICogSXQgaXMgYSB3cmFwcGVyIGNsYXNzIGZvciB0aGUgTWVkaWFTdHJlYW0uXG4gKlxuICogQHBhcmFtIGRhdGEgdGhlIGRhdGEgb2JqZWN0IGZyb20gd2hpY2ggd2Ugb2J0YWluIHRoZSBzdHJlYW0sXG4gKiB0aGUgcGVlcmppZCwgZXRjLlxuICogQHBhcmFtIHNpZCB0aGUgc2Vzc2lvbiBpZFxuICogQHBhcmFtIHNzcmMgdGhlIHNzcmMgY29ycmVzcG9uZGluZyB0byB0aGlzIE1lZGlhU3RyZWFtXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIE1lZGlhU3RyZWFtKGRhdGEsIHNpZCwgc3NyYywgYnJvd3Nlcikge1xuXG4gICAgLy8gWFhYKGdwKSB0byBtaW5pbWl6ZSBoZWFkYWNoZXMgaW4gdGhlIGZ1dHVyZSwgd2Ugc2hvdWxkIGJ1aWxkIG91clxuICAgIC8vIGFic3RyYWN0aW9ucyBhcm91bmQgdHJhY2tzIGFuZCBub3Qgc3RyZWFtcy4gT1JUQyBpcyB0cmFjayBiYXNlZCBBUEkuXG4gICAgLy8gTW96aWxsYSBleHBlY3RzIG0tbGluZXMgdG8gcmVwcmVzZW50IG1lZGlhIHRyYWNrcy5cbiAgICAvL1xuICAgIC8vIFByYWN0aWNhbGx5LCB3aGF0IEknbSBzYXlpbmcgaXMgdGhhdCB3ZSBzaG91bGQgaGF2ZSBhIE1lZGlhVHJhY2sgY2xhc3NcbiAgICAvLyBhbmQgbm90IGEgTWVkaWFTdHJlYW0gY2xhc3MuXG4gICAgLy9cbiAgICAvLyBBbHNvLCB3ZSBzaG91bGQgYmUgYWJsZSB0byBhc3NvY2lhdGUgbXVsdGlwbGUgU1NSQ3Mgd2l0aCBhIE1lZGlhVHJhY2sgYXNcbiAgICAvLyBhIHRyYWNrIG1pZ2h0IGhhdmUgYW4gYXNzb2NpYXRlZCBSVFggYW5kIEZFQyBzb3VyY2VzLlxuXG4gICAgdGhpcy5zaWQgPSBzaWQ7XG4gICAgdGhpcy5zdHJlYW0gPSBkYXRhLnN0cmVhbTtcbiAgICB0aGlzLnBlZXJqaWQgPSBkYXRhLnBlZXJqaWQ7XG4gICAgdGhpcy5zc3JjID0gc3NyYztcbiAgICB0aGlzLnR5cGUgPSAodGhpcy5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPiAwKT9cbiAgICAgICAgTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEUgOiBNZWRpYVN0cmVhbVR5cGUuQVVESU9fVFlQRTtcbiAgICB0aGlzLnZpZGVvVHlwZSA9IG51bGw7XG4gICAgdGhpcy5tdXRlZCA9IGZhbHNlO1xuICAgIGlmKGJyb3dzZXIgPT0gUlRDQnJvd3NlclR5cGUuUlRDX0JST1dTRVJfRklSRUZPWClcbiAgICB7XG4gICAgICAgIGlmICghdGhpcy5nZXRWaWRlb1RyYWNrcylcbiAgICAgICAgICAgIHRoaXMuZ2V0VmlkZW9UcmFja3MgPSBmdW5jdGlvbiAoKSB7IHJldHVybiBbXTsgfTtcbiAgICAgICAgaWYgKCF0aGlzLmdldEF1ZGlvVHJhY2tzKVxuICAgICAgICAgICAgdGhpcy5nZXRBdWRpb1RyYWNrcyA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIFtdOyB9O1xuICAgIH1cbn1cblxuXG5NZWRpYVN0cmVhbS5wcm90b3R5cGUuZ2V0T3JpZ2luYWxTdHJlYW0gPSBmdW5jdGlvbigpXG57XG4gICAgcmV0dXJuIHRoaXMuc3RyZWFtO1xufVxuXG5NZWRpYVN0cmVhbS5wcm90b3R5cGUuc2V0TXV0ZSA9IGZ1bmN0aW9uICh2YWx1ZSlcbntcbiAgICB0aGlzLnN0cmVhbS5tdXRlZCA9IHZhbHVlO1xuICAgIHRoaXMubXV0ZWQgPSB2YWx1ZTtcbn1cblxuXG5tb2R1bGUuZXhwb3J0cyA9IE1lZGlhU3RyZWFtO1xuIiwidmFyIEV2ZW50RW1pdHRlciA9IHJlcXVpcmUoXCJldmVudHNcIik7XG52YXIgUlRDVXRpbHMgPSByZXF1aXJlKFwiLi9SVENVdGlscy5qc1wiKTtcbnZhciBMb2NhbFN0cmVhbSA9IHJlcXVpcmUoXCIuL0xvY2FsU3RyZWFtLmpzXCIpO1xudmFyIERhdGFDaGFubmVscyA9IHJlcXVpcmUoXCIuL0RhdGFDaGFubmVsc1wiKTtcbnZhciBNZWRpYVN0cmVhbSA9IHJlcXVpcmUoXCIuL01lZGlhU3RyZWFtLmpzXCIpO1xudmFyIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlc1xuICAgID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvZGVza3RvcHNoYXJpbmcvRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXCIpO1xudmFyIE1lZGlhU3RyZWFtVHlwZSA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9NZWRpYVN0cmVhbVR5cGVzXCIpO1xudmFyIFN0cmVhbUV2ZW50VHlwZXMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvU3RyZWFtRXZlbnRUeXBlcy5qc1wiKTtcbnZhciBYTVBQRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UveG1wcC9YTVBQRXZlbnRzXCIpO1xudmFyIFVJRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvVUkvVUlFdmVudHNcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbnZhciBSVEMgPSB7XG4gICAgcnRjVXRpbHM6IG51bGwsXG4gICAgbG9jYWxTdHJlYW1zOiBbXSxcbiAgICByZW1vdGVTdHJlYW1zOiB7fSxcbiAgICBsb2NhbEF1ZGlvOiBudWxsLFxuICAgIGxvY2FsVmlkZW86IG51bGwsXG4gICAgYWRkU3RyZWFtTGlzdGVuZXI6IGZ1bmN0aW9uIChsaXN0ZW5lciwgZXZlbnRUeXBlKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbihldmVudFR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGFkZExpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIHJlbW92ZVN0cmVhbUxpc3RlbmVyOiBmdW5jdGlvbiAobGlzdGVuZXIsIGV2ZW50VHlwZSkge1xuICAgICAgICBpZighKGV2ZW50VHlwZSBpbnN0YW5jZW9mIFN0cmVhbUV2ZW50VHlwZXMpKVxuICAgICAgICAgICAgdGhyb3cgXCJJbGxlZ2FsIGFyZ3VtZW50XCI7XG5cbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKGV2ZW50VHlwZSwgbGlzdGVuZXIpO1xuICAgIH0sXG4gICAgY3JlYXRlTG9jYWxTdHJlYW06IGZ1bmN0aW9uIChzdHJlYW0sIHR5cGUsIGNoYW5nZSkge1xuXG4gICAgICAgIHZhciBsb2NhbFN0cmVhbSA9ICBuZXcgTG9jYWxTdHJlYW0oc3RyZWFtLCB0eXBlLCBldmVudEVtaXR0ZXIpO1xuICAgICAgICAvL2luIGZpcmVmb3ggd2UgaGF2ZSBvbmx5IG9uZSBzdHJlYW0gb2JqZWN0XG4gICAgICAgIGlmKHRoaXMubG9jYWxTdHJlYW1zLmxlbmd0aCA9PSAwIHx8XG4gICAgICAgICAgICB0aGlzLmxvY2FsU3RyZWFtc1swXS5nZXRPcmlnaW5hbFN0cmVhbSgpICE9IHN0cmVhbSlcbiAgICAgICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnB1c2gobG9jYWxTdHJlYW0pO1xuICAgICAgICBpZih0eXBlID09IFwiYXVkaW9cIilcbiAgICAgICAge1xuICAgICAgICAgICAgdGhpcy5sb2NhbEF1ZGlvID0gbG9jYWxTdHJlYW07XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICB0aGlzLmxvY2FsVmlkZW8gPSBsb2NhbFN0cmVhbTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgZXZlbnRUeXBlID0gU3RyZWFtRXZlbnRUeXBlcy5FVkVOVF9UWVBFX0xPQ0FMX0NSRUFURUQ7XG4gICAgICAgIGlmKGNoYW5nZSlcbiAgICAgICAgICAgIGV2ZW50VHlwZSA9IFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DSEFOR0VEO1xuXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KGV2ZW50VHlwZSwgbG9jYWxTdHJlYW0pO1xuICAgICAgICByZXR1cm4gbG9jYWxTdHJlYW07XG4gICAgfSxcbiAgICByZW1vdmVMb2NhbFN0cmVhbTogZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICBmb3IodmFyIGkgPSAwOyBpIDwgdGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoOyBpKyspXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlmKHRoaXMubG9jYWxTdHJlYW1zW2ldLmdldE9yaWdpbmFsU3RyZWFtKCkgPT09IHN0cmVhbSkge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmxvY2FsU3RyZWFtc1tpXTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGNyZWF0ZVJlbW90ZVN0cmVhbTogZnVuY3Rpb24gKGRhdGEsIHNpZCwgdGhlc3NyYykge1xuICAgICAgICB2YXIgcmVtb3RlU3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKGRhdGEsIHNpZCwgdGhlc3NyYyxcbiAgICAgICAgICAgIHRoaXMuZ2V0QnJvd3NlclR5cGUoKSk7XG4gICAgICAgIHZhciBqaWQgPSBkYXRhLnBlZXJqaWQgfHwgQVBQLnhtcHAubXlKaWQoKTtcbiAgICAgICAgaWYoIXRoaXMucmVtb3RlU3RyZWFtc1tqaWRdKSB7XG4gICAgICAgICAgICB0aGlzLnJlbW90ZVN0cmVhbXNbamlkXSA9IHt9O1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucmVtb3RlU3RyZWFtc1tqaWRdW3JlbW90ZVN0cmVhbS50eXBlXT0gcmVtb3RlU3RyZWFtO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfUkVNT1RFX0NSRUFURUQsIHJlbW90ZVN0cmVhbSk7XG4gICAgICAgIGNvbnNvbGUuZGVidWcoXCJBREQgcmVtb3RlIHN0cmVhbSBcIiwgcmVtb3RlU3RyZWFtLnR5cGUsIFwiIFwiLCBqaWQsIFwiIFwiLCB0aGVzc3JjKTtcbiAgICAgICAgcmV0dXJuIHJlbW90ZVN0cmVhbTtcbiAgICB9LFxuICAgIGdldEJyb3dzZXJUeXBlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ0Y1V0aWxzLmJyb3dzZXI7XG4gICAgfSxcbiAgICBnZXRQQ0NvbnN0cmFpbnRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ0Y1V0aWxzLnBjX2NvbnN0cmFpbnRzO1xuICAgIH0sXG4gICAgZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzOmZ1bmN0aW9uKHVtLCBzdWNjZXNzX2NhbGxiYWNrLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWlsdXJlX2NhbGxiYWNrLCByZXNvbHV0aW9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYW5kd2lkdGgsIGZwcywgZGVza3RvcFN0cmVhbSlcbiAgICB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ0Y1V0aWxzLmdldFVzZXJNZWRpYVdpdGhDb25zdHJhaW50cyh1bSwgc3VjY2Vzc19jYWxsYmFjayxcbiAgICAgICAgICAgIGZhaWx1cmVfY2FsbGJhY2ssIHJlc29sdXRpb24sIGJhbmR3aWR0aCwgZnBzLCBkZXNrdG9wU3RyZWFtKTtcbiAgICB9LFxuICAgIGF0dGFjaE1lZGlhU3RyZWFtOiAgZnVuY3Rpb24gKGVsZW1lbnQsIHN0cmVhbSkge1xuICAgICAgICB0aGlzLnJ0Y1V0aWxzLmF0dGFjaE1lZGlhU3RyZWFtKGVsZW1lbnQsIHN0cmVhbSk7XG4gICAgfSxcbiAgICBnZXRTdHJlYW1JRDogIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucnRjVXRpbHMuZ2V0U3RyZWFtSUQoc3RyZWFtKTtcbiAgICB9LFxuICAgIGdldFZpZGVvU3JjOiBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICByZXR1cm4gdGhpcy5ydGNVdGlscy5nZXRWaWRlb1NyYyhlbGVtZW50KTtcbiAgICB9LFxuICAgIHNldFZpZGVvU3JjOiBmdW5jdGlvbiAoZWxlbWVudCwgc3JjKSB7XG4gICAgICAgIHRoaXMucnRjVXRpbHMuc2V0VmlkZW9TcmMoZWxlbWVudCwgc3JjKTtcbiAgICB9LFxuICAgIGRpc3Bvc2U6IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAodGhpcy5ydGNVdGlscykge1xuICAgICAgICAgICAgdGhpcy5ydGNVdGlscyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHN0b3A6ICBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoaXMuZGlzcG9zZSgpO1xuICAgIH0sXG4gICAgc3RhcnQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICBBUFAuZGVza3RvcHNoYXJpbmcuYWRkTGlzdGVuZXIoXG4gICAgICAgICAgICBmdW5jdGlvbiAoc3RyZWFtLCBpc1VzaW5nU2NyZWVuU3RyZWFtLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHNlbGYuY2hhbmdlTG9jYWxWaWRlbyhzdHJlYW0sIGlzVXNpbmdTY3JlZW5TdHJlYW0sIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5ORVdfU1RSRUFNX0NSRUFURUQpO1xuICAgICAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkNIQU5HRURfU1RSRUFNUywgZnVuY3Rpb24gKGppZCwgY2hhbmdlZFN0cmVhbXMpIHtcbiAgICAgICAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBjaGFuZ2VkU3RyZWFtcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciB0eXBlID0gY2hhbmdlZFN0cmVhbXNbaV0udHlwZTtcbiAgICAgICAgICAgICAgICBpZiAodHlwZSAhPSBcImF1ZGlvXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBlZXJTdHJlYW1zID0gc2VsZi5yZW1vdGVTdHJlYW1zW2ppZF07XG4gICAgICAgICAgICAgICAgICAgIGlmKCFwZWVyU3RyZWFtcylcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBwZWVyU3RyZWFtc1tNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgICAgICAgICAgICAgIGlmKCF2aWRlb1N0cmVhbSlcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB2aWRlb1N0cmVhbS52aWRlb1R5cGUgPSBjaGFuZ2VkU3RyZWFtc1tpXS50eXBlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuQ0FMTF9JTkNPTUlORywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICAgIERhdGFDaGFubmVscy5pbml0KGV2ZW50LnBlZXJjb25uZWN0aW9uLCBldmVudEVtaXR0ZXIpO1xuICAgICAgICB9KTtcbiAgICAgICAgQVBQLlVJLmFkZExpc3RlbmVyKFVJRXZlbnRzLlNFTEVDVEVEX0VORFBPSU5ULFxuICAgICAgICAgICAgRGF0YUNoYW5uZWxzLmhhbmRsZVNlbGVjdGVkRW5kcG9pbnRFdmVudCk7XG4gICAgICAgIEFQUC5VSS5hZGRMaXN0ZW5lcihVSUV2ZW50cy5QSU5ORURfRU5EUE9JTlQsXG4gICAgICAgICAgICBEYXRhQ2hhbm5lbHMuaGFuZGxlUGlubmVkRW5kcG9pbnRFdmVudCk7XG4gICAgICAgIHRoaXMucnRjVXRpbHMgPSBuZXcgUlRDVXRpbHModGhpcyk7XG4gICAgICAgIHRoaXMucnRjVXRpbHMub2J0YWluQXVkaW9BbmRWaWRlb1Blcm1pc3Npb25zKCk7XG4gICAgfSxcbiAgICBtdXRlUmVtb3RlVmlkZW9TdHJlYW06IGZ1bmN0aW9uIChqaWQsIHZhbHVlKSB7XG4gICAgICAgIHZhciBzdHJlYW07XG5cbiAgICAgICAgaWYodGhpcy5yZW1vdGVTdHJlYW1zW2ppZF0gJiZcbiAgICAgICAgICAgIHRoaXMucmVtb3RlU3RyZWFtc1tqaWRdW01lZGlhU3RyZWFtVHlwZS5WSURFT19UWVBFXSlcbiAgICAgICAge1xuICAgICAgICAgICAgc3RyZWFtID0gdGhpcy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoIXN0cmVhbSlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICBpZiAodmFsdWUgIT0gc3RyZWFtLm11dGVkKSB7XG4gICAgICAgICAgICBzdHJlYW0uc2V0TXV0ZSh2YWx1ZSk7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBzd2l0Y2hWaWRlb1N0cmVhbXM6IGZ1bmN0aW9uIChuZXdfc3RyZWFtKSB7XG4gICAgICAgIHRoaXMubG9jYWxWaWRlby5zdHJlYW0gPSBuZXdfc3RyZWFtO1xuXG4gICAgICAgIHRoaXMubG9jYWxTdHJlYW1zID0gW107XG5cbiAgICAgICAgLy9pbiBmaXJlZm94IHdlIGhhdmUgb25seSBvbmUgc3RyZWFtIG9iamVjdFxuICAgICAgICBpZiAodGhpcy5sb2NhbEF1ZGlvLmdldE9yaWdpbmFsU3RyZWFtKCkgIT0gbmV3X3N0cmVhbSlcbiAgICAgICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnB1c2godGhpcy5sb2NhbEF1ZGlvKTtcbiAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXMucHVzaCh0aGlzLmxvY2FsVmlkZW8pO1xuICAgIH0sXG4gICAgY2hhbmdlTG9jYWxWaWRlbzogZnVuY3Rpb24gKHN0cmVhbSwgaXNVc2luZ1NjcmVlblN0cmVhbSwgY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIG9sZFN0cmVhbSA9IHRoaXMubG9jYWxWaWRlby5nZXRPcmlnaW5hbFN0cmVhbSgpO1xuICAgICAgICB2YXIgdHlwZSA9IChpc1VzaW5nU2NyZWVuU3RyZWFtPyBcInNjcmVlblwiIDogXCJ2aWRlb1wiKTtcbiAgICAgICAgdGhpcy5sb2NhbFZpZGVvID0gdGhpcy5jcmVhdGVMb2NhbFN0cmVhbShzdHJlYW0sIFwidmlkZW9cIiwgdHJ1ZSwgdHlwZSk7XG4gICAgICAgIC8vIFN0b3AgdGhlIHN0cmVhbSB0byB0cmlnZ2VyIG9uZW5kZWQgZXZlbnQgZm9yIG9sZCBzdHJlYW1cbiAgICAgICAgb2xkU3RyZWFtLnN0b3AoKTtcbiAgICAgICAgQVBQLnhtcHAuc3dpdGNoU3RyZWFtcyhzdHJlYW0sIG9sZFN0cmVhbSxjYWxsYmFjayk7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgdmlkZW8gaWRlbnRpZmllZCBieSBnaXZlbiBzcmMgaXMgZGVza3RvcCBzdHJlYW0uXG4gICAgICogQHBhcmFtIHZpZGVvU3JjIGVnLlxuICAgICAqIGJsb2I6aHR0cHMlM0EvL3Bhd2VsLmppdHNpLm5ldC85YTQ2ZTBiZC0xMzFlLTRkMTgtOWMxNC1hOTI2NGU4ZGIzOTVcbiAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICAgKi9cbiAgICBpc1ZpZGVvU3JjRGVza3RvcDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZighamlkKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB2YXIgaXNEZXNrdG9wID0gZmFsc2U7XG4gICAgICAgIHZhciBzdHJlYW0gPSBudWxsO1xuICAgICAgICBpZiAoQVBQLnhtcHAubXlKaWQoKSAmJlxuICAgICAgICAgICAgQVBQLnhtcHAubXlSZXNvdXJjZSgpID09PSBqaWQpIHtcbiAgICAgICAgICAgIC8vIGxvY2FsIHZpZGVvXG4gICAgICAgICAgICBzdHJlYW0gPSB0aGlzLmxvY2FsVmlkZW87XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgcGVlclN0cmVhbXMgPSB0aGlzLnJlbW90ZVN0cmVhbXNbamlkXTtcbiAgICAgICAgICAgIGlmKCFwZWVyU3RyZWFtcylcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICBzdHJlYW0gPSBwZWVyU3RyZWFtc1tNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgIH1cblxuICAgICAgICBpZihzdHJlYW0pXG4gICAgICAgICAgICBpc0Rlc2t0b3AgPSAoc3RyZWFtLnZpZGVvVHlwZSA9PT0gXCJzY3JlZW5cIik7XG5cbiAgICAgICAgcmV0dXJuIGlzRGVza3RvcDtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJUQztcbiIsInZhciBSVENCcm93c2VyVHlwZSA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENCcm93c2VyVHlwZS5qc1wiKTtcblxuZnVuY3Rpb24gc2V0UmVzb2x1dGlvbkNvbnN0cmFpbnRzKGNvbnN0cmFpbnRzLCByZXNvbHV0aW9uLCBpc0FuZHJvaWQpXG57XG4gICAgaWYgKHJlc29sdXRpb24gJiYgIWNvbnN0cmFpbnRzLnZpZGVvIHx8IGlzQW5kcm9pZCkge1xuICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IHsgbWFuZGF0b3J5OiB7fSwgb3B0aW9uYWw6IFtdIH07Ly8gc2FtZSBiZWhhdmlvdXIgYXMgdHJ1ZVxuICAgIH1cbiAgICAvLyBzZWUgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTE0MzYzMSNjOSBmb3IgbGlzdCBvZiBzdXBwb3J0ZWQgcmVzb2x1dGlvbnNcbiAgICBzd2l0Y2ggKHJlc29sdXRpb24pIHtcbiAgICAgICAgLy8gMTY6OSBmaXJzdFxuICAgICAgICBjYXNlICcxMDgwJzpcbiAgICAgICAgY2FzZSAnZnVsbGhkJzpcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aCA9IDE5MjA7XG4gICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluSGVpZ2h0ID0gMTA4MDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICc3MjAnOlxuICAgICAgICBjYXNlICdoZCc6XG4gICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluV2lkdGggPSAxMjgwO1xuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IDcyMDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICczNjAnOlxuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbldpZHRoID0gNjQwO1xuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IDM2MDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICcxODAnOlxuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbldpZHRoID0gMzIwO1xuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IDE4MDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAvLyA0OjNcbiAgICAgICAgY2FzZSAnOTYwJzpcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aCA9IDk2MDtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5IZWlnaHQgPSA3MjA7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnNjQwJzpcbiAgICAgICAgY2FzZSAndmdhJzpcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aCA9IDY0MDtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5IZWlnaHQgPSA0ODA7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnMzIwJzpcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aCA9IDMyMDtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5IZWlnaHQgPSAyNDA7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGlmIChpc0FuZHJvaWQpIHtcbiAgICAgICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluV2lkdGggPSAzMjA7XG4gICAgICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodCA9IDI0MDtcbiAgICAgICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWF4RnJhbWVSYXRlID0gMTU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG4gICAgaWYgKGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5taW5XaWR0aClcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1heFdpZHRoID0gY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbldpZHRoO1xuICAgIGlmIChjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWluSGVpZ2h0KVxuICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWF4SGVpZ2h0ID0gY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkhlaWdodDtcbn1cblxuXG5mdW5jdGlvbiBnZXRDb25zdHJhaW50cyh1bSwgcmVzb2x1dGlvbiwgYmFuZHdpZHRoLCBmcHMsIGRlc2t0b3BTdHJlYW0sIGlzQW5kcm9pZClcbntcbiAgICB2YXIgY29uc3RyYWludHMgPSB7YXVkaW86IGZhbHNlLCB2aWRlbzogZmFsc2V9O1xuXG4gICAgaWYgKHVtLmluZGV4T2YoJ3ZpZGVvJykgPj0gMCkge1xuICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IHsgbWFuZGF0b3J5OiB7fSwgb3B0aW9uYWw6IFtdIH07Ly8gc2FtZSBiZWhhdmlvdXIgYXMgdHJ1ZVxuICAgIH1cbiAgICBpZiAodW0uaW5kZXhPZignYXVkaW8nKSA+PSAwKSB7XG4gICAgICAgIGNvbnN0cmFpbnRzLmF1ZGlvID0geyBtYW5kYXRvcnk6IHt9LCBvcHRpb25hbDogW119Oy8vIHNhbWUgYmVoYXZpb3VyIGFzIHRydWVcbiAgICB9XG4gICAgaWYgKHVtLmluZGV4T2YoJ3NjcmVlbicpID49IDApIHtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8gPSB7XG4gICAgICAgICAgICBtYW5kYXRvcnk6IHtcbiAgICAgICAgICAgICAgICBjaHJvbWVNZWRpYVNvdXJjZTogXCJzY3JlZW5cIixcbiAgICAgICAgICAgICAgICBnb29nTGVha3lCdWNrZXQ6IHRydWUsXG4gICAgICAgICAgICAgICAgbWF4V2lkdGg6IHdpbmRvdy5zY3JlZW4ud2lkdGgsXG4gICAgICAgICAgICAgICAgbWF4SGVpZ2h0OiB3aW5kb3cuc2NyZWVuLmhlaWdodCxcbiAgICAgICAgICAgICAgICBtYXhGcmFtZVJhdGU6IDNcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvcHRpb25hbDogW11cbiAgICAgICAgfTtcbiAgICB9XG4gICAgaWYgKHVtLmluZGV4T2YoJ2Rlc2t0b3AnKSA+PSAwKSB7XG4gICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0ge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgICAgY2hyb21lTWVkaWFTb3VyY2U6IFwiZGVza3RvcFwiLFxuICAgICAgICAgICAgICAgIGNocm9tZU1lZGlhU291cmNlSWQ6IGRlc2t0b3BTdHJlYW0sXG4gICAgICAgICAgICAgICAgZ29vZ0xlYWt5QnVja2V0OiB0cnVlLFxuICAgICAgICAgICAgICAgIG1heFdpZHRoOiB3aW5kb3cuc2NyZWVuLndpZHRoLFxuICAgICAgICAgICAgICAgIG1heEhlaWdodDogd2luZG93LnNjcmVlbi5oZWlnaHQsXG4gICAgICAgICAgICAgICAgbWF4RnJhbWVSYXRlOiAzXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb3B0aW9uYWw6IFtdXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKGNvbnN0cmFpbnRzLmF1ZGlvKSB7XG4gICAgICAgIC8vIGlmIGl0IGlzIGdvb2QgZW5vdWdoIGZvciBoYW5nb3V0cy4uLlxuICAgICAgICBjb25zdHJhaW50cy5hdWRpby5vcHRpb25hbC5wdXNoKFxuICAgICAgICAgICAge2dvb2dFY2hvQ2FuY2VsbGF0aW9uOiB0cnVlfSxcbiAgICAgICAgICAgIHtnb29nQXV0b0dhaW5Db250cm9sOiB0cnVlfSxcbiAgICAgICAgICAgIHtnb29nTm9pc2VTdXByZXNzaW9uOiB0cnVlfSxcbiAgICAgICAgICAgIHtnb29nSGlnaHBhc3NGaWx0ZXI6IHRydWV9LFxuICAgICAgICAgICAge2dvb2dOb2lzZXN1cHByZXNzaW9uMjogdHJ1ZX0sXG4gICAgICAgICAgICB7Z29vZ0VjaG9DYW5jZWxsYXRpb24yOiB0cnVlfSxcbiAgICAgICAgICAgIHtnb29nQXV0b0dhaW5Db250cm9sMjogdHJ1ZX1cbiAgICAgICAgKTtcbiAgICB9XG4gICAgaWYgKGNvbnN0cmFpbnRzLnZpZGVvKSB7XG4gICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm9wdGlvbmFsLnB1c2goXG4gICAgICAgICAgICB7Z29vZ05vaXNlUmVkdWN0aW9uOiBmYWxzZX0gLy8gY2hyb21lIDM3IHdvcmthcm91bmQgZm9yIGlzc3VlIDM4MDcsIHJlZW5hYmxlIGluIE0zOFxuICAgICAgICApO1xuICAgICAgICBpZiAodW0uaW5kZXhPZigndmlkZW8nKSA+PSAwKSB7XG4gICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5vcHRpb25hbC5wdXNoKFxuICAgICAgICAgICAgICAgIHtnb29nTGVha3lCdWNrZXQ6IHRydWV9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHVtLmluZGV4T2YoJ3ZpZGVvJykgPj0gMCkge1xuICAgICAgICBzZXRSZXNvbHV0aW9uQ29uc3RyYWludHMoY29uc3RyYWludHMsIHJlc29sdXRpb24sIGlzQW5kcm9pZCk7XG4gICAgfVxuXG4gICAgaWYgKGJhbmR3aWR0aCkgeyAvLyBkb2Vzbid0IHdvcmsgY3VycmVudGx5LCBzZWUgd2VicnRjIGlzc3VlIDE4NDZcbiAgICAgICAgaWYgKCFjb25zdHJhaW50cy52aWRlbykgY29uc3RyYWludHMudmlkZW8gPSB7bWFuZGF0b3J5OiB7fSwgb3B0aW9uYWw6IFtdfTsvL3NhbWUgYmVoYXZpb3VyIGFzIHRydWVcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ub3B0aW9uYWwucHVzaCh7YmFuZHdpZHRoOiBiYW5kd2lkdGh9KTtcbiAgICB9XG4gICAgaWYgKGZwcykgeyAvLyBmb3Igc29tZSBjYW1lcmFzIGl0IG1pZ2h0IGJlIG5lY2Vzc2FyeSB0byByZXF1ZXN0IDMwZnBzXG4gICAgICAgIC8vIHNvIHRoZXkgY2hvb3NlIDMwZnBzIG1qcGcgb3ZlciAxMGZwcyB5dXkyXG4gICAgICAgIGlmICghY29uc3RyYWludHMudmlkZW8pIGNvbnN0cmFpbnRzLnZpZGVvID0ge21hbmRhdG9yeToge30sIG9wdGlvbmFsOiBbXX07Ly8gc2FtZSBiZWhhdmlvdXIgYXMgdHJ1ZTtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWFuZGF0b3J5Lm1pbkZyYW1lUmF0ZSA9IGZwcztcbiAgICB9XG5cbiAgICByZXR1cm4gY29uc3RyYWludHM7XG59XG5cblxuZnVuY3Rpb24gUlRDVXRpbHMoUlRDU2VydmljZSlcbntcbiAgICB0aGlzLnNlcnZpY2UgPSBSVENTZXJ2aWNlO1xuICAgIGlmIChuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdUaGlzIGFwcGVhcnMgdG8gYmUgRmlyZWZveCcpO1xuICAgICAgICB2YXIgdmVyc2lvbiA9IHBhcnNlSW50KG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0ZpcmVmb3hcXC8oWzAtOV0rKVxcLi8pWzFdLCAxMCk7XG4gICAgICAgIGlmICh2ZXJzaW9uID49IDIyKSB7XG4gICAgICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uID0gbW96UlRDUGVlckNvbm5lY3Rpb247XG4gICAgICAgICAgICB0aGlzLmJyb3dzZXIgPSBSVENCcm93c2VyVHlwZS5SVENfQlJPV1NFUl9GSVJFRk9YO1xuICAgICAgICAgICAgdGhpcy5nZXRVc2VyTWVkaWEgPSBuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhLmJpbmQobmF2aWdhdG9yKTtcbiAgICAgICAgICAgIHRoaXMucGNfY29uc3RyYWludHMgPSB7fTtcbiAgICAgICAgICAgIHRoaXMuYXR0YWNoTWVkaWFTdHJlYW0gPSAgZnVuY3Rpb24gKGVsZW1lbnQsIHN0cmVhbSkge1xuICAgICAgICAgICAgICAgIGVsZW1lbnRbMF0ubW96U3JjT2JqZWN0ID0gc3RyZWFtO1xuICAgICAgICAgICAgICAgIGVsZW1lbnRbMF0ucGxheSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZ2V0U3RyZWFtSUQgPSAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgIHZhciB0cmFja3MgPSBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKTtcbiAgICAgICAgICAgICAgICBpZighdHJhY2tzIHx8IHRyYWNrcy5sZW5ndGggPT0gMClcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHRyYWNrcyA9IHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gdHJhY2tzWzBdLmlkLnJlcGxhY2UoL1tcXHssXFx9XS9nLFwiXCIpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZ2V0VmlkZW9TcmMgPSBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBlbGVtZW50Lm1velNyY09iamVjdDtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLnNldFZpZGVvU3JjID0gZnVuY3Rpb24gKGVsZW1lbnQsIHNyYykge1xuICAgICAgICAgICAgICAgIGVsZW1lbnQubW96U3JjT2JqZWN0ID0gc3JjO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIFJUQ1Nlc3Npb25EZXNjcmlwdGlvbiA9IG1velJUQ1Nlc3Npb25EZXNjcmlwdGlvbjtcbiAgICAgICAgICAgIFJUQ0ljZUNhbmRpZGF0ZSA9IG1velJUQ0ljZUNhbmRpZGF0ZTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSBpZiAobmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYSkge1xuICAgICAgICBjb25zb2xlLmxvZygnVGhpcyBhcHBlYXJzIHRvIGJlIENocm9tZScpO1xuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uID0gd2Via2l0UlRDUGVlckNvbm5lY3Rpb247XG4gICAgICAgIHRoaXMuYnJvd3NlciA9IFJUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0NIUk9NRTtcbiAgICAgICAgdGhpcy5nZXRVc2VyTWVkaWEgPSBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhLmJpbmQobmF2aWdhdG9yKTtcbiAgICAgICAgdGhpcy5hdHRhY2hNZWRpYVN0cmVhbSA9IGZ1bmN0aW9uIChlbGVtZW50LCBzdHJlYW0pIHtcbiAgICAgICAgICAgIGVsZW1lbnQuYXR0cignc3JjJywgd2Via2l0VVJMLmNyZWF0ZU9iamVjdFVSTChzdHJlYW0pKTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXRTdHJlYW1JRCA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgIC8vIHN0cmVhbXMgZnJvbSBGRiBlbmRwb2ludHMgaGF2ZSB0aGUgY2hhcmFjdGVycyAneycgYW5kICd9J1xuICAgICAgICAgICAgLy8gdGhhdCBtYWtlIGpRdWVyeSBjaG9rZS5cbiAgICAgICAgICAgIHJldHVybiBzdHJlYW0uaWQucmVwbGFjZSgvW1xceyxcXH1dL2csXCJcIik7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuZ2V0VmlkZW9TcmMgPSBmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgICAgICAgICAgcmV0dXJuIGVsZW1lbnQuZ2V0QXR0cmlidXRlKFwic3JjXCIpO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLnNldFZpZGVvU3JjID0gZnVuY3Rpb24gKGVsZW1lbnQsIHNyYykge1xuICAgICAgICAgICAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJzcmNcIiwgc3JjKTtcbiAgICAgICAgfTtcbiAgICAgICAgLy8gRFRMUyBzaG91bGQgbm93IGJlIGVuYWJsZWQgYnkgZGVmYXVsdCBidXQuLlxuICAgICAgICB0aGlzLnBjX2NvbnN0cmFpbnRzID0geydvcHRpb25hbCc6IFt7J0R0bHNTcnRwS2V5QWdyZWVtZW50JzogJ3RydWUnfV19O1xuICAgICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKCdBbmRyb2lkJykgIT0gLTEpIHtcbiAgICAgICAgICAgIHRoaXMucGNfY29uc3RyYWludHMgPSB7fTsgLy8gZGlzYWJsZSBEVExTIG9uIEFuZHJvaWRcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXdlYmtpdE1lZGlhU3RyZWFtLnByb3RvdHlwZS5nZXRWaWRlb1RyYWNrcykge1xuICAgICAgICAgICAgd2Via2l0TWVkaWFTdHJlYW0ucHJvdG90eXBlLmdldFZpZGVvVHJhY2tzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnZpZGVvVHJhY2tzO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXdlYmtpdE1lZGlhU3RyZWFtLnByb3RvdHlwZS5nZXRBdWRpb1RyYWNrcykge1xuICAgICAgICAgICAgd2Via2l0TWVkaWFTdHJlYW0ucHJvdG90eXBlLmdldEF1ZGlvVHJhY2tzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmF1ZGlvVHJhY2tzO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBlbHNlXG4gICAge1xuICAgICAgICB0cnkgeyBjb25zb2xlLmxvZygnQnJvd3NlciBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgV2ViUlRDLWNhcGFibGUnKTsgfSBjYXRjaCAoZSkgeyB9XG5cbiAgICAgICAgd2luZG93LmxvY2F0aW9uLmhyZWYgPSAnd2VicnRjcmVxdWlyZWQuaHRtbCc7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5icm93c2VyICE9PSBSVENCcm93c2VyVHlwZS5SVENfQlJPV1NFUl9DSFJPTUUgJiZcbiAgICAgICAgY29uZmlnLmVuYWJsZUZpcmVmb3hTdXBwb3J0ICE9PSB0cnVlKSB7XG4gICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gJ2Nocm9tZW9ubHkuaHRtbCc7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbn1cblxuXG5SVENVdGlscy5wcm90b3R5cGUuZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzID0gZnVuY3Rpb24oXG4gICAgdW0sIHN1Y2Nlc3NfY2FsbGJhY2ssIGZhaWx1cmVfY2FsbGJhY2ssIHJlc29sdXRpb24sYmFuZHdpZHRoLCBmcHMsXG4gICAgZGVza3RvcFN0cmVhbSlcbntcbiAgICAvLyBDaGVjayBpZiB3ZSBhcmUgcnVubmluZyBvbiBBbmRyb2lkIGRldmljZVxuICAgIHZhciBpc0FuZHJvaWQgPSBuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ0FuZHJvaWQnKSAhPSAtMTtcblxuICAgIHZhciBjb25zdHJhaW50cyA9IGdldENvbnN0cmFpbnRzKFxuICAgICAgICB1bSwgcmVzb2x1dGlvbiwgYmFuZHdpZHRoLCBmcHMsIGRlc2t0b3BTdHJlYW0sIGlzQW5kcm9pZCk7XG5cbiAgICB2YXIgaXNGRiA9IG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKS5pbmRleE9mKCdmaXJlZm94JykgPiAtMTtcblxuICAgIHRyeSB7XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlU2ltdWxjYXN0XG4gICAgICAgICAgICAmJiBjb25zdHJhaW50cy52aWRlb1xuICAgICAgICAgICAgJiYgY29uc3RyYWludHMudmlkZW8uY2hyb21lTWVkaWFTb3VyY2UgIT09ICdzY3JlZW4nXG4gICAgICAgICAgICAmJiBjb25zdHJhaW50cy52aWRlby5jaHJvbWVNZWRpYVNvdXJjZSAhPT0gJ2Rlc2t0b3AnXG4gICAgICAgICAgICAmJiAhaXNBbmRyb2lkXG5cbiAgICAgICAgICAgIC8vIFdlIGN1cnJlbnRseSBkbyBub3Qgc3VwcG9ydCBGRiwgYXMgaXQgZG9lc24ndCBoYXZlIG11bHRpc3RyZWFtIHN1cHBvcnQuXG4gICAgICAgICAgICAmJiAhaXNGRikge1xuICAgICAgICAgICAgQVBQLnNpbXVsY2FzdC5nZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ29uVXNlck1lZGlhU3VjY2VzcycpO1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKHN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdGYWlsZWQgdG8gZ2V0IGFjY2VzcyB0byBsb2NhbCBtZWRpYS4gRXJyb3IgJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZmFpbHVyZV9jYWxsYmFjaykge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmFpbHVyZV9jYWxsYmFjayhlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgdGhpcy5nZXRVc2VyTWVkaWEoY29uc3RyYWludHMsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnb25Vc2VyTWVkaWFTdWNjZXNzJyk7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3NfY2FsbGJhY2soc3RyZWFtKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ0ZhaWxlZCB0byBnZXQgYWNjZXNzIHRvIGxvY2FsIG1lZGlhLiBFcnJvciAnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IsIGNvbnN0cmFpbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGZhaWx1cmVfY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhaWx1cmVfY2FsbGJhY2soZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignR1VNIGZhaWxlZDogJywgZSk7XG4gICAgICAgIGlmKGZhaWx1cmVfY2FsbGJhY2spIHtcbiAgICAgICAgICAgIGZhaWx1cmVfY2FsbGJhY2soZSk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG4vKipcbiAqIFdlIGFzayBmb3IgYXVkaW8gYW5kIHZpZGVvIGNvbWJpbmVkIHN0cmVhbSBpbiBvcmRlciB0byBnZXQgcGVybWlzc2lvbnMgYW5kXG4gKiBub3QgdG8gYXNrIHR3aWNlLlxuICovXG5SVENVdGlscy5wcm90b3R5cGUub2J0YWluQXVkaW9BbmRWaWRlb1Blcm1pc3Npb25zID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIEdldCBBVlxuICAgIHZhciBjYiA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgY29uc29sZS5sb2coJ2dvdCcsIHN0cmVhbSwgc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkubGVuZ3RoLCBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGgpO1xuICAgICAgICBzZWxmLmhhbmRsZUxvY2FsU3RyZWFtKHN0cmVhbSk7XG4gICAgfTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMoXG4gICAgICAgIFsnYXVkaW8nLCAndmlkZW8nXSxcbiAgICAgICAgY2IsXG4gICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignZmFpbGVkIHRvIG9idGFpbiBhdWRpby92aWRlbyBzdHJlYW0gLSB0cnlpbmcgYXVkaW8gb25seScsIGVycm9yKTtcbiAgICAgICAgICAgIHNlbGYuZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzKFxuICAgICAgICAgICAgICAgIFsnYXVkaW8nXSxcbiAgICAgICAgICAgICAgICBjYixcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignZmFpbGVkIHRvIG9idGFpbiBhdWRpby92aWRlbyBzdHJlYW0gLSBzdG9wJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKFwiRXJyb3JcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIkZhaWxlZCB0byBvYnRhaW4gcGVybWlzc2lvbnMgdG8gdXNlIHRoZSBsb2NhbCBtaWNyb3Bob25lXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiYW5kL29yIGNhbWVyYS5cIik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSxcbiAgICAgICAgICAgIGNvbmZpZy5yZXNvbHV0aW9uIHx8ICczNjAnKTtcbn1cblxuUlRDVXRpbHMucHJvdG90eXBlLmhhbmRsZUxvY2FsU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKVxue1xuICAgIGlmKHdpbmRvdy53ZWJraXRNZWRpYVN0cmVhbSlcbiAgICB7XG4gICAgICAgIHZhciBhdWRpb1N0cmVhbSA9IG5ldyB3ZWJraXRNZWRpYVN0cmVhbSgpO1xuICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBuZXcgd2Via2l0TWVkaWFTdHJlYW0oKTtcbiAgICAgICAgdmFyIGF1ZGlvVHJhY2tzID0gc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCk7XG4gICAgICAgIHZhciB2aWRlb1RyYWNrcyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGF1ZGlvVHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBhdWRpb1N0cmVhbS5hZGRUcmFjayhhdWRpb1RyYWNrc1tpXSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNlcnZpY2UuY3JlYXRlTG9jYWxTdHJlYW0oYXVkaW9TdHJlYW0sIFwiYXVkaW9cIik7XG5cbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHZpZGVvVHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2aWRlb1N0cmVhbS5hZGRUcmFjayh2aWRlb1RyYWNrc1tpXSk7XG4gICAgICAgIH1cblxuXG4gICAgICAgIHRoaXMuc2VydmljZS5jcmVhdGVMb2NhbFN0cmVhbSh2aWRlb1N0cmVhbSwgXCJ2aWRlb1wiKTtcbiAgICB9XG4gICAgZWxzZVxuICAgIHsvL2ZpcmVmb3hcbiAgICAgICAgdGhpcy5zZXJ2aWNlLmNyZWF0ZUxvY2FsU3RyZWFtKHN0cmVhbSwgXCJzdHJlYW1cIik7XG4gICAgfVxuXG59O1xuXG5cblxubW9kdWxlLmV4cG9ydHMgPSBSVENVdGlsczsiLCJ2YXIgVUkgPSB7fTtcblxudmFyIFZpZGVvTGF5b3V0ID0gcmVxdWlyZShcIi4vdmlkZW9sYXlvdXQvVmlkZW9MYXlvdXQuanNcIik7XG52YXIgQXVkaW9MZXZlbHMgPSByZXF1aXJlKFwiLi9hdWRpb19sZXZlbHMvQXVkaW9MZXZlbHMuanNcIik7XG52YXIgUHJlemkgPSByZXF1aXJlKFwiLi9wcmV6aS9QcmV6aS5qc1wiKTtcbnZhciBFdGhlcnBhZCA9IHJlcXVpcmUoXCIuL2V0aGVycGFkL0V0aGVycGFkLmpzXCIpO1xudmFyIENoYXQgPSByZXF1aXJlKFwiLi9zaWRlX3Bhbm5lbHMvY2hhdC9DaGF0LmpzXCIpO1xudmFyIFRvb2xiYXIgPSByZXF1aXJlKFwiLi90b29sYmFycy9Ub29sYmFyXCIpO1xudmFyIFRvb2xiYXJUb2dnbGVyID0gcmVxdWlyZShcIi4vdG9vbGJhcnMvVG9vbGJhclRvZ2dsZXJcIik7XG52YXIgQm90dG9tVG9vbGJhciA9IHJlcXVpcmUoXCIuL3Rvb2xiYXJzL0JvdHRvbVRvb2xiYXJcIik7XG52YXIgQ29udGFjdExpc3QgPSByZXF1aXJlKFwiLi9zaWRlX3Bhbm5lbHMvY29udGFjdGxpc3QvQ29udGFjdExpc3RcIik7XG52YXIgQXZhdGFyID0gcmVxdWlyZShcIi4vYXZhdGFyL0F2YXRhclwiKTtcbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFNldHRpbmdzTWVudSA9IHJlcXVpcmUoXCIuL3NpZGVfcGFubmVscy9zZXR0aW5ncy9TZXR0aW5nc01lbnVcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi9zaWRlX3Bhbm5lbHMvc2V0dGluZ3MvU2V0dGluZ3NcIik7XG52YXIgUGFuZWxUb2dnbGVyID0gcmVxdWlyZShcIi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG52YXIgUm9vbU5hbWVHZW5lcmF0b3IgPSByZXF1aXJlKFwiLi93ZWxjb21lX3BhZ2UvUm9vbW5hbWVHZW5lcmF0b3JcIik7XG5VSS5tZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgbWVzc2FnZUhhbmRsZXIgPSBVSS5tZXNzYWdlSGFuZGxlcjtcbnZhciBBdXRoZW50aWNhdGlvbiAgPSByZXF1aXJlKFwiLi9hdXRoZW50aWNhdGlvbi9BdXRoZW50aWNhdGlvblwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi91dGlsL1VJVXRpbFwiKTtcbnZhciBOaWNrbmFtZUhhbmRsZXIgPSByZXF1aXJlKFwiLi91dGlsL05pY2tuYW1lSGFuZGxlclwiKTtcbnZhciBDUUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL2Nvbm5lY3Rpb25xdWFsaXR5L0NRRXZlbnRzXCIpO1xudmFyIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlc1xuICAgID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvZGVza3RvcHNoYXJpbmcvRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXCIpO1xudmFyIFJUQ0V2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENFdmVudHNcIik7XG52YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9TdHJlYW1FdmVudFR5cGVzXCIpO1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgcm9vbU5hbWUgPSBudWxsO1xuXG5cbmZ1bmN0aW9uIHNldHVwUHJlemkoKVxue1xuICAgICQoXCIjcmVsb2FkUHJlc2VudGF0aW9uTGlua1wiKS5jbGljayhmdW5jdGlvbigpXG4gICAge1xuICAgICAgICBQcmV6aS5yZWxvYWRQcmVzZW50YXRpb24oKTtcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gc2V0dXBDaGF0KClcbntcbiAgICBDaGF0LmluaXQoKTtcbiAgICAkKFwiI3RvZ2dsZV9zbWlsZXlzXCIpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgICBDaGF0LnRvZ2dsZVNtaWxleXMoKTtcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gc2V0dXBUb29sYmFycygpIHtcbiAgICBUb29sYmFyLmluaXQoVUkpO1xuICAgIFRvb2xiYXIuc2V0dXBCdXR0b25zRnJvbUNvbmZpZygpO1xuICAgIEJvdHRvbVRvb2xiYXIuaW5pdCgpO1xufVxuXG5mdW5jdGlvbiBzdHJlYW1IYW5kbGVyKHN0cmVhbSkge1xuICAgIHN3aXRjaCAoc3RyZWFtLnR5cGUpXG4gICAge1xuICAgICAgICBjYXNlIFwiYXVkaW9cIjpcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNoYW5nZUxvY2FsQXVkaW8oc3RyZWFtKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwidmlkZW9cIjpcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNoYW5nZUxvY2FsVmlkZW8oc3RyZWFtKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwic3RyZWFtXCI6XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFN0cmVhbShzdHJlYW0pO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBvbkRpc3Bvc2VDb25mZXJlbmNlKHVubG9hZCkge1xuICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbihmYWxzZSk7XG59O1xuXG5mdW5jdGlvbiBvbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgQ29udGFjdExpc3Qub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBTZXR0aW5nc01lbnUub25EaXNwbGF5TmFtZUNoYW5nZShqaWQsIGRpc3BsYXlOYW1lKTtcbiAgICBWaWRlb0xheW91dC5vbkRpc3BsYXlOYW1lQ2hhbmdlZChqaWQsIGRpc3BsYXlOYW1lKTtcbn1cblxuZnVuY3Rpb24gcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihzdHJlYW1IYW5kbGVyLCBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ1JFQVRFRCk7XG5cbiAgICBBUFAuUlRDLmFkZFN0cmVhbUxpc3RlbmVyKHN0cmVhbUhhbmRsZXIsIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DSEFOR0VEKTtcbiAgICBBUFAuUlRDLmFkZFN0cmVhbUxpc3RlbmVyKGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgVmlkZW9MYXlvdXQub25SZW1vdGVTdHJlYW1BZGRlZChzdHJlYW0pO1xuICAgIH0sIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9SRU1PVEVfQ1JFQVRFRCk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuTEFTVE5fQ0hBTkdFRCwgb25MYXN0TkNoYW5nZWQpO1xuICAgIEFQUC5SVEMuYWRkTGlzdGVuZXIoUlRDRXZlbnRzLkRPTUlOQU5UU1BFQUtFUl9DSEFOR0VELCBmdW5jdGlvbiAocmVzb3VyY2VKaWQpIHtcbiAgICAgICAgVmlkZW9MYXlvdXQub25Eb21pbmFudFNwZWFrZXJDaGFuZ2VkKHJlc291cmNlSmlkKTtcbiAgICB9KTtcbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5MQVNUTl9FTkRQT0lOVF9DSEFOR0VELFxuICAgICAgICBmdW5jdGlvbiAobGFzdE5FbmRwb2ludHMsIGVuZHBvaW50c0VudGVyaW5nTGFzdE4sIHN0cmVhbSkge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQub25MYXN0TkVuZHBvaW50c0NoYW5nZWQobGFzdE5FbmRwb2ludHMsXG4gICAgICAgICAgICAgICAgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Tiwgc3RyZWFtKTtcbiAgICAgICAgfSk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuU0lNVUxDQVNUX0xBWUVSX0NIQU5HRUQsXG4gICAgICAgIGZ1bmN0aW9uIChlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICAgICBWaWRlb0xheW91dC5vblNpbXVsY2FzdExheWVyc0NoYW5nZWQoZW5kcG9pbnRTaW11bGNhc3RMYXllcnMpO1xuICAgICAgICB9KTtcbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5TSU1VTENBU1RfTEFZRVJfQ0hBTkdJTkcsXG4gICAgICAgIGZ1bmN0aW9uIChlbmRwb2ludFNpbXVsY2FzdExheWVycykge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQub25TaW11bGNhc3RMYXllcnNDaGFuZ2luZyhlbmRwb2ludFNpbXVsY2FzdExheWVycyk7XG4gICAgICAgIH0pO1xuICAgIFZpZGVvTGF5b3V0LmluaXQoZXZlbnRFbWl0dGVyKTtcblxuICAgIEFQUC5zdGF0aXN0aWNzLmFkZEF1ZGlvTGV2ZWxMaXN0ZW5lcihmdW5jdGlvbihqaWQsIGF1ZGlvTGV2ZWwpXG4gICAge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQ7XG4gICAgICAgIGlmKGppZCA9PT0gQVBQLnN0YXRpc3RpY3MuTE9DQUxfSklEKVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IEF1ZGlvTGV2ZWxzLkxPQ0FMX0xFVkVMO1xuICAgICAgICAgICAgaWYoQVBQLlJUQy5sb2NhbEF1ZGlvLmlzTXV0ZWQoKSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsID0gMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIEF1ZGlvTGV2ZWxzLnVwZGF0ZUF1ZGlvTGV2ZWwocmVzb3VyY2VKaWQsIGF1ZGlvTGV2ZWwsXG4gICAgICAgICAgICBVSS5nZXRMYXJnZVZpZGVvU3RhdGUoKS51c2VyUmVzb3VyY2VKaWQpO1xuICAgIH0pO1xuICAgIEFQUC5kZXNrdG9wc2hhcmluZy5hZGRMaXN0ZW5lcihmdW5jdGlvbiAoKSB7XG4gICAgICAgIFRvb2xiYXJUb2dnbGVyLnNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbigpO1xuICAgIH0sIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5JTklUKTtcbiAgICBBUFAuZGVza3RvcHNoYXJpbmcuYWRkTGlzdGVuZXIoXG4gICAgICAgIFRvb2xiYXIuY2hhbmdlRGVza3RvcFNoYXJpbmdCdXR0b25TdGF0ZSxcbiAgICAgICAgRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzLlNXSVRDSElOR19ET05FKTtcbiAgICBBUFAuY29ubmVjdGlvbnF1YWxpdHkuYWRkTGlzdGVuZXIoQ1FFdmVudHMuTE9DQUxTVEFUU19VUERBVEVELFxuICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMb2NhbENvbm5lY3Rpb25TdGF0cyk7XG4gICAgQVBQLmNvbm5lY3Rpb25xdWFsaXR5LmFkZExpc3RlbmVyKENRRXZlbnRzLlJFTU9URVNUQVRTX1VQREFURUQsXG4gICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUNvbm5lY3Rpb25TdGF0cyk7XG4gICAgQVBQLmNvbm5lY3Rpb25xdWFsaXR5LmFkZExpc3RlbmVyKENRRXZlbnRzLlNUT1AsXG4gICAgICAgIFZpZGVvTGF5b3V0Lm9uU3RhdHNTdG9wKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkRJU1BPU0VfQ09ORkVSRU5DRSwgb25EaXNwb3NlQ29uZmVyZW5jZSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5LSUNLRUQsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2coXCJTZXNzaW9uIFRlcm1pbmF0ZWRcIixcbiAgICAgICAgICAgIFwiT3VjaCEgWW91IGhhdmUgYmVlbiBraWNrZWQgb3V0IG9mIHRoZSBtZWV0IVwiKTtcbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkJSSURHRV9ET1dOLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcihcIkVycm9yXCIsXG4gICAgICAgICAgICBcIkppdHNpIFZpZGVvYnJpZGdlIGlzIGN1cnJlbnRseSB1bmF2YWlsYWJsZS4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlciFcIik7XG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5VU0VSX0lEX0NIQU5HRUQsIGZ1bmN0aW9uIChmcm9tLCBpZCkge1xuICAgICAgICBBdmF0YXIuc2V0VXNlckF2YXRhcihmcm9tLCBpZCk7XG4gICAgfSk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5DSEFOR0VEX1NUUkVBTVMsIGZ1bmN0aW9uIChqaWQsIGNoYW5nZWRTdHJlYW1zKSB7XG4gICAgICAgIGZvcihzdHJlYW0gaW4gY2hhbmdlZFN0cmVhbXMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIC8vIG1pZ2h0IG5lZWQgdG8gdXBkYXRlIHRoZSBkaXJlY3Rpb24gaWYgcGFydGljaXBhbnQganVzdCB3ZW50IGZyb20gc2VuZHJlY3YgdG8gcmVjdm9ubHlcbiAgICAgICAgICAgIGlmIChzdHJlYW0udHlwZSA9PT0gJ3ZpZGVvJyB8fCBzdHJlYW0udHlwZSA9PT0gJ3NjcmVlbicpIHtcbiAgICAgICAgICAgICAgICB2YXIgZWwgPSAkKCcjcGFydGljaXBhbnRfJyAgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpICsgJz52aWRlbycpO1xuICAgICAgICAgICAgICAgIHN3aXRjaCAoc3RyZWFtLmRpcmVjdGlvbikge1xuICAgICAgICAgICAgICAgICAgICBjYXNlICdzZW5kcmVjdic6XG4gICAgICAgICAgICAgICAgICAgICAgICBlbC5zaG93KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAncmVjdm9ubHknOlxuICAgICAgICAgICAgICAgICAgICAgICAgZWwuaGlkZSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IENoZWNrIGlmIHdlIGhhdmUgdG8gY2hhbmdlIGxhcmdlIHZpZGVvXG4gICAgICAgICAgICAgICAgICAgICAgICAvL1ZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oZWwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICB9KTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLkRJU1BMQVlfTkFNRV9DSEFOR0VELCBvbkRpc3BsYXlOYW1lQ2hhbmdlZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5NVUNfSk9JTkVELCBvbk11Y0pvaW5lZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5MT0NBTFJPTEVfQ0hBTkdFRCwgb25Mb2NhbFJvbGVDaGFuZ2UpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX0VOVEVSLCBvbk11Y0VudGVyZWQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuTVVDX1JPTEVfQ0hBTkdFRCwgb25NdWNSb2xlQ2hhbmdlZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5QUkVTRU5DRV9TVEFUVVMsIG9uTXVjUHJlc2VuY2VTdGF0dXMpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuU1VCSkVDVF9DSEFOR0VELCBjaGF0U2V0U3ViamVjdCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5NRVNTQUdFX1JFQ0VJVkVELCB1cGRhdGVDaGF0Q29udmVyc2F0aW9uKTtcbiAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLk1VQ19MRUZULCBvbk11Y0xlZnQpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuUEFTU1dPUkRfUkVRVUlSRUQsIG9uUGFzc3dvcmRSZXFpdXJlZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5DSEFUX0VSUk9SX1JFQ0VJVkVELCBjaGF0QWRkRXJyb3IpO1xuICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuRVRIRVJQQUQsIGluaXRFdGhlcnBhZCk7XG4gICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5BVVRIRU5USUNBVElPTl9SRVFVSVJFRCwgb25BdXRoZW50aWNhdGlvblJlcXVpcmVkKTtcblxuXG59XG5cblxuLyoqXG4gKiBNdXRlcy91bm11dGVzIHRoZSBsb2NhbCB2aWRlby5cbiAqXG4gKiBAcGFyYW0gbXV0ZSA8dHQ+dHJ1ZTwvdHQ+IHRvIG11dGUgdGhlIGxvY2FsIHZpZGVvOyBvdGhlcndpc2UsIDx0dD5mYWxzZTwvdHQ+XG4gKiBAcGFyYW0gb3B0aW9ucyBhbiBvYmplY3Qgd2hpY2ggc3BlY2lmaWVzIG9wdGlvbmFsIGFyZ3VtZW50cyBzdWNoIGFzIHRoZVxuICogPHR0PmJvb2xlYW48L3R0PiBrZXkgPHR0PmJ5VXNlcjwvdHQ+IHdpdGggZGVmYXVsdCB2YWx1ZSA8dHQ+dHJ1ZTwvdHQ+IHdoaWNoXG4gKiBzcGVjaWZpZXMgd2hldGhlciB0aGUgbWV0aG9kIHdhcyBpbml0aWF0ZWQgaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgKGluXG4gKiBjb250cmFzdCB0byBhbiBhdXRvbWF0aWMgZGVjaXNpb24gdGFrZW4gYnkgdGhlIGFwcGxpY2F0aW9uIGxvZ2ljKVxuICovXG5mdW5jdGlvbiBzZXRWaWRlb011dGUobXV0ZSwgb3B0aW9ucykge1xuICAgIEFQUC54bXBwLnNldFZpZGVvTXV0ZShcbiAgICAgICAgbXV0ZSxcbiAgICAgICAgZnVuY3Rpb24gKG11dGUpIHtcbiAgICAgICAgICAgIHZhciB2aWRlbyA9ICQoJyN2aWRlbycpO1xuICAgICAgICAgICAgdmFyIGNvbW11bmljYXRpdmVDbGFzcyA9IFwiaWNvbi1jYW1lcmFcIjtcbiAgICAgICAgICAgIHZhciBtdXRlQ2xhc3MgPSBcImljb24tY2FtZXJhIGljb24tY2FtZXJhLWRpc2FibGVkXCI7XG5cbiAgICAgICAgICAgIGlmIChtdXRlKSB7XG4gICAgICAgICAgICAgICAgdmlkZW8ucmVtb3ZlQ2xhc3MoY29tbXVuaWNhdGl2ZUNsYXNzKTtcbiAgICAgICAgICAgICAgICB2aWRlby5hZGRDbGFzcyhtdXRlQ2xhc3MpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB2aWRlby5yZW1vdmVDbGFzcyhtdXRlQ2xhc3MpO1xuICAgICAgICAgICAgICAgIHZpZGVvLmFkZENsYXNzKGNvbW11bmljYXRpdmVDbGFzcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIG9wdGlvbnMpO1xufVxuXG5cbmZ1bmN0aW9uIGJpbmRFdmVudHMoKVxue1xuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgYW5kIHJlcG9zaXRpb25zIHZpZGVvcyBpbiBmdWxsIHNjcmVlbiBtb2RlLlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLm9uKCd3ZWJraXRmdWxsc2NyZWVuY2hhbmdlIG1vemZ1bGxzY3JlZW5jaGFuZ2UgZnVsbHNjcmVlbmNoYW5nZScsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnJlc2l6ZUxhcmdlVmlkZW9Db250YWluZXIoKTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoKTtcbiAgICAgICAgfVxuICAgICk7XG5cbiAgICAkKHdpbmRvdykucmVzaXplKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplTGFyZ2VWaWRlb0NvbnRhaW5lcigpO1xuICAgICAgICBWaWRlb0xheW91dC5wb3NpdGlvbkxhcmdlKCk7XG4gICAgfSk7XG59XG5cblVJLnN0YXJ0ID0gZnVuY3Rpb24gKGluaXQpIHtcbiAgICBkb2N1bWVudC50aXRsZSA9IGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRTtcbiAgICBpZihjb25maWcuZW5hYmxlV2VsY29tZVBhZ2UgJiYgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lID09IFwiL1wiICYmXG4gICAgICAgICghd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkIHx8IHdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZCA9PSBcImZhbHNlXCIpKVxuICAgIHtcbiAgICAgICAgJChcIiN2aWRlb2NvbmZlcmVuY2VfcGFnZVwiKS5oaWRlKCk7XG4gICAgICAgIHZhciBzZXR1cFdlbGNvbWVQYWdlID0gcmVxdWlyZShcIi4vd2VsY29tZV9wYWdlL1dlbGNvbWVQYWdlXCIpO1xuICAgICAgICBzZXR1cFdlbGNvbWVQYWdlKCk7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChpbnRlcmZhY2VDb25maWcuU0hPV19KSVRTSV9XQVRFUk1BUkspIHtcbiAgICAgICAgdmFyIGxlZnRXYXRlcm1hcmtEaXZcbiAgICAgICAgICAgID0gJChcIiNsYXJnZVZpZGVvQ29udGFpbmVyIGRpdltjbGFzcz0nd2F0ZXJtYXJrIGxlZnR3YXRlcm1hcmsnXVwiKTtcblxuICAgICAgICBsZWZ0V2F0ZXJtYXJrRGl2LmNzcyh7ZGlzcGxheTogJ2Jsb2NrJ30pO1xuICAgICAgICBsZWZ0V2F0ZXJtYXJrRGl2LnBhcmVudCgpLmdldCgwKS5ocmVmXG4gICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5KSVRTSV9XQVRFUk1BUktfTElOSztcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLlNIT1dfQlJBTkRfV0FURVJNQVJLKSB7XG4gICAgICAgIHZhciByaWdodFdhdGVybWFya0RpdlxuICAgICAgICAgICAgPSAkKFwiI2xhcmdlVmlkZW9Db250YWluZXIgZGl2W2NsYXNzPSd3YXRlcm1hcmsgcmlnaHR3YXRlcm1hcmsnXVwiKTtcblxuICAgICAgICByaWdodFdhdGVybWFya0Rpdi5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICAgICAgcmlnaHRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgID0gaW50ZXJmYWNlQ29uZmlnLkJSQU5EX1dBVEVSTUFSS19MSU5LO1xuICAgICAgICByaWdodFdhdGVybWFya0Rpdi5nZXQoMCkuc3R5bGUuYmFja2dyb3VuZEltYWdlXG4gICAgICAgICAgICA9IFwidXJsKGltYWdlcy9yaWdodHdhdGVybWFyay5wbmcpXCI7XG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX1BPV0VSRURfQlkpIHtcbiAgICAgICAgJChcIiNsYXJnZVZpZGVvQ29udGFpbmVyPmFbY2xhc3M9J3Bvd2VyZWRieSddXCIpLmNzcyh7ZGlzcGxheTogJ2Jsb2NrJ30pO1xuICAgIH1cblxuICAgICQoXCIjd2VsY29tZV9wYWdlXCIpLmhpZGUoKTtcblxuICAgIFZpZGVvTGF5b3V0LnJlc2l6ZUxhcmdlVmlkZW9Db250YWluZXIoKTtcbiAgICAkKFwiI3ZpZGVvc3BhY2VcIikubW91c2Vtb3ZlKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXJUb2dnbGVyLnNob3dUb29sYmFyKCk7XG4gICAgfSk7XG4gICAgLy8gU2V0IHRoZSBkZWZhdWx0cyBmb3IgcHJvbXB0IGRpYWxvZ3MuXG4gICAgalF1ZXJ5LnByb21wdC5zZXREZWZhdWx0cyh7cGVyc2lzdGVudDogZmFsc2V9KTtcblxuXG4gICAgTmlja25hbWVIYW5kbGVyLmluaXQoZXZlbnRFbWl0dGVyKTtcbiAgICByZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIGJpbmRFdmVudHMoKTtcbiAgICBzZXR1cFByZXppKCk7XG4gICAgc2V0dXBUb29sYmFycygpO1xuICAgIHNldHVwQ2hhdCgpO1xuXG5cbiAgICBkb2N1bWVudC50aXRsZSA9IGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRTtcblxuICAgICQoXCIjZG93bmxvYWRsb2dcIikuY2xpY2soZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGR1bXAoZXZlbnQudGFyZ2V0KTtcbiAgICB9KTtcblxuICAgIGlmKGNvbmZpZy5lbmFibGVXZWxjb21lUGFnZSAmJiB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWUgPT0gXCIvXCIgJiZcbiAgICAgICAgKCF3aW5kb3cubG9jYWxTdG9yYWdlLndlbGNvbWVQYWdlRGlzYWJsZWQgfHwgd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkID09IFwiZmFsc2VcIikpXG4gICAge1xuICAgICAgICAkKFwiI3ZpZGVvY29uZmVyZW5jZV9wYWdlXCIpLmhpZGUoKTtcbiAgICAgICAgdmFyIHNldHVwV2VsY29tZVBhZ2UgPSByZXF1aXJlKFwiLi93ZWxjb21lX3BhZ2UvV2VsY29tZVBhZ2VcIik7XG4gICAgICAgIHNldHVwV2VsY29tZVBhZ2UoKTtcblxuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgJChcIiN3ZWxjb21lX3BhZ2VcIikuaGlkZSgpO1xuXG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKS52b2x1bWUgPSAwO1xuXG4gICAgaWYgKCEkKCcjc2V0dGluZ3MnKS5pcygnOnZpc2libGUnKSkge1xuICAgICAgICBjb25zb2xlLmxvZygnaW5pdCcpO1xuICAgICAgICBpbml0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbG9naW5JbmZvLm9uc3VibWl0ID0gZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmIChlLnByZXZlbnREZWZhdWx0KSBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAkKCcjc2V0dGluZ3MnKS5oaWRlKCk7XG4gICAgICAgICAgICBpbml0KCk7XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgdG9hc3RyLm9wdGlvbnMgPSB7XG4gICAgICAgIFwiY2xvc2VCdXR0b25cIjogdHJ1ZSxcbiAgICAgICAgXCJkZWJ1Z1wiOiBmYWxzZSxcbiAgICAgICAgXCJwb3NpdGlvbkNsYXNzXCI6IFwibm90aWZpY2F0aW9uLWJvdHRvbS1yaWdodFwiLFxuICAgICAgICBcIm9uY2xpY2tcIjogbnVsbCxcbiAgICAgICAgXCJzaG93RHVyYXRpb25cIjogXCIzMDBcIixcbiAgICAgICAgXCJoaWRlRHVyYXRpb25cIjogXCIxMDAwXCIsXG4gICAgICAgIFwidGltZU91dFwiOiBcIjIwMDBcIixcbiAgICAgICAgXCJleHRlbmRlZFRpbWVPdXRcIjogXCIxMDAwXCIsXG4gICAgICAgIFwic2hvd0Vhc2luZ1wiOiBcInN3aW5nXCIsXG4gICAgICAgIFwiaGlkZUVhc2luZ1wiOiBcImxpbmVhclwiLFxuICAgICAgICBcInNob3dNZXRob2RcIjogXCJmYWRlSW5cIixcbiAgICAgICAgXCJoaWRlTWV0aG9kXCI6IFwiZmFkZU91dFwiLFxuICAgICAgICBcInJlcG9zaXRpb25cIjogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZihQYW5lbFRvZ2dsZXIuaXNWaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5hZGRDbGFzcyhcIm5vdGlmaWNhdGlvbi1ib3R0b20tcmlnaHQtY2VudGVyXCIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5yZW1vdmVDbGFzcyhcIm5vdGlmaWNhdGlvbi1ib3R0b20tcmlnaHQtY2VudGVyXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBcIm5ld2VzdE9uVG9wXCI6IGZhbHNlXG4gICAgfTtcblxuICAgIFNldHRpbmdzTWVudS5pbml0KCk7XG5cbn07XG5cbmZ1bmN0aW9uIGNoYXRBZGRFcnJvcihlcnJvck1lc3NhZ2UsIG9yaWdpbmFsVGV4dClcbntcbiAgICByZXR1cm4gQ2hhdC5jaGF0QWRkRXJyb3IoZXJyb3JNZXNzYWdlLCBvcmlnaW5hbFRleHQpO1xufTtcblxuZnVuY3Rpb24gY2hhdFNldFN1YmplY3QodGV4dClcbntcbiAgICByZXR1cm4gQ2hhdC5jaGF0U2V0U3ViamVjdCh0ZXh0KTtcbn07XG5cbmZ1bmN0aW9uIHVwZGF0ZUNoYXRDb252ZXJzYXRpb24oZnJvbSwgZGlzcGxheU5hbWUsIG1lc3NhZ2UpIHtcbiAgICByZXR1cm4gQ2hhdC51cGRhdGVDaGF0Q29udmVyc2F0aW9uKGZyb20sIGRpc3BsYXlOYW1lLCBtZXNzYWdlKTtcbn07XG5cbmZ1bmN0aW9uIG9uTXVjSm9pbmVkKGppZCwgaW5mbykge1xuICAgIFRvb2xiYXIudXBkYXRlUm9vbVVybCh3aW5kb3cubG9jYXRpb24uaHJlZik7XG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2FsTmljaycpLmFwcGVuZENoaWxkKFxuICAgICAgICBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpICsgJyAobWUpJylcbiAgICApO1xuXG4gICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICAvLyBBZGQgbXlzZWxmIHRvIHRoZSBjb250YWN0IGxpc3QuXG4gICAgQ29udGFjdExpc3QuYWRkQ29udGFjdChqaWQsIHNldHRpbmdzLmVtYWlsIHx8IHNldHRpbmdzLnVpZCk7XG5cbiAgICAvLyBPbmNlIHdlJ3ZlIGpvaW5lZCB0aGUgbXVjIHNob3cgdGhlIHRvb2xiYXJcbiAgICBUb29sYmFyVG9nZ2xlci5zaG93VG9vbGJhcigpO1xuXG4gICAgLy8gU2hvdyBhdXRoZW50aWNhdGUgYnV0dG9uIGlmIG5lZWRlZFxuICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbihcbiAgICAgICAgICAgIEFQUC54bXBwLmlzRXh0ZXJuYWxBdXRoRW5hYmxlZCgpICYmICFBUFAueG1wcC5pc01vZGVyYXRvcigpKTtcblxuICAgIHZhciBkaXNwbGF5TmFtZSA9ICFjb25maWcuZGlzcGxheUppZHNcbiAgICAgICAgPyBpbmZvLmRpc3BsYXlOYW1lIDogU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcblxuICAgIGlmIChkaXNwbGF5TmFtZSlcbiAgICAgICAgb25EaXNwbGF5TmFtZUNoYW5nZWQoJ2xvY2FsVmlkZW9Db250YWluZXInLCBkaXNwbGF5TmFtZSArICcgKG1lKScpO1xufVxuXG5mdW5jdGlvbiBpbml0RXRoZXJwYWQobmFtZSkge1xuICAgIEV0aGVycGFkLmluaXQobmFtZSk7XG59O1xuXG5mdW5jdGlvbiBvbk11Y0xlZnQoamlkKSB7XG4gICAgY29uc29sZS5sb2coJ2xlZnQubXVjJywgamlkKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSAkKCcjcGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCkgK1xuICAgICAgICAnPi5kaXNwbGF5bmFtZScpLmh0bWwoKTtcbiAgICBtZXNzYWdlSGFuZGxlci5ub3RpZnkoZGlzcGxheU5hbWUgfHwgJ1NvbWVib2R5JyxcbiAgICAgICAgJ2Rpc2Nvbm5lY3RlZCcsXG4gICAgICAgICdkaXNjb25uZWN0ZWQnKTtcbiAgICAvLyBOZWVkIHRvIGNhbGwgdGhpcyB3aXRoIGEgc2xpZ2h0IGRlbGF5LCBvdGhlcndpc2UgdGhlIGVsZW1lbnQgY291bGRuJ3QgYmVcbiAgICAvLyBmb3VuZCBmb3Igc29tZSByZWFzb24uXG4gICAgLy8gWFhYKGdwKSBpdCB3b3JrcyBmaW5lIHdpdGhvdXQgdGhlIHRpbWVvdXQgZm9yIG1lICh3aXRoIENocm9tZSAzOCkuXG4gICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXG4gICAgICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgQ29udGFjdExpc3QucmVtb3ZlQ29udGFjdChqaWQpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVtb3ZlQ29ubmVjdGlvbkluZGljYXRvcihqaWQpO1xuICAgICAgICAgICAgLy8gaGlkZSBoZXJlLCB3YWl0IGZvciB2aWRlbyB0byBjbG9zZSBiZWZvcmUgcmVtb3ZpbmdcbiAgICAgICAgICAgICQoY29udGFpbmVyKS5oaWRlKCk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cbiAgICB9LCAxMCk7XG5cbiAgICBWaWRlb0xheW91dC5wYXJ0aWNpcGFudExlZnQoamlkKTtcblxufTtcblxuXG5mdW5jdGlvbiBvbkxvY2FsUm9sZUNoYW5nZShqaWQsIGluZm8sIHByZXMsIGlzTW9kZXJhdG9yLCBpc0V4dGVybmFsQXV0aEVuYWJsZWQpXG57XG5cbiAgICBjb25zb2xlLmluZm8oXCJNeSByb2xlIGNoYW5nZWQsIG5ldyByb2xlOiBcIiArIGluZm8ucm9sZSk7XG4gICAgb25Nb2RlcmF0b3JTdGF0dXNDaGFuZ2VkKGlzTW9kZXJhdG9yKTtcbiAgICBWaWRlb0xheW91dC5zaG93TW9kZXJhdG9ySW5kaWNhdG9yKCk7XG4gICAgVG9vbGJhci5zaG93QXV0aGVudGljYXRlQnV0dG9uKFxuICAgICAgICAgICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkICYmICFpc01vZGVyYXRvcik7XG5cbiAgICBpZiAoaXNNb2RlcmF0b3IpIHtcbiAgICAgICAgQXV0aGVudGljYXRpb24uY2xvc2VBdXRoZW50aWNhdGlvbldpbmRvdygpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5ub3RpZnkoXG4gICAgICAgICAgICAnTWUnLCAnY29ubmVjdGVkJywgJ01vZGVyYXRvciByaWdodHMgZ3JhbnRlZCAhJyk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBvbk1vZGVyYXRvclN0YXR1c0NoYW5nZWQoaXNNb2RlcmF0b3IpIHtcblxuICAgIFRvb2xiYXIuc2hvd1NpcENhbGxCdXR0b24oaXNNb2RlcmF0b3IpO1xuICAgIFRvb2xiYXIuc2hvd1JlY29yZGluZ0J1dHRvbihcbiAgICAgICAgaXNNb2RlcmF0b3IpOyAvLyYmXG4gICAgLy8gRklYTUU6XG4gICAgLy8gUmVjb3JkaW5nIHZpc2libGUgaWZcbiAgICAvLyB0aGVyZSBhcmUgYXQgbGVhc3QgMigrIDEgZm9jdXMpIHBhcnRpY2lwYW50c1xuICAgIC8vT2JqZWN0LmtleXMoY29ubmVjdGlvbi5lbXVjLm1lbWJlcnMpLmxlbmd0aCA+PSAzKTtcblxuICAgIGlmIChpc01vZGVyYXRvciAmJiBjb25maWcuZXRoZXJwYWRfYmFzZSkge1xuICAgICAgICBFdGhlcnBhZC5pbml0KCk7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gb25QYXNzd29yZFJlcWl1cmVkKGNhbGxiYWNrKSB7XG4gICAgLy8gcGFzc3dvcmQgaXMgcmVxdWlyZWRcbiAgICBUb29sYmFyLmxvY2tMb2NrQnV0dG9uKCk7XG5cbiAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsXG4gICAgICAgICAgICAnPGgyPlBhc3N3b3JkIHJlcXVpcmVkPC9oMj4nICtcbiAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJsb2NrS2V5XCIgdHlwZT1cInRleHRcIiBwbGFjZWhvbGRlcj1cInBhc3N3b3JkXCIgYXV0b2ZvY3VzPicsXG4gICAgICAgIHRydWUsXG4gICAgICAgIFwiT2tcIixcbiAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHt9LFxuICAgICAgICBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NrS2V5JykuZm9jdXMoKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgdmFyIGxvY2tLZXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9ja0tleScpO1xuICAgICAgICAgICAgICAgIGlmIChsb2NrS2V5LnZhbHVlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIFRvb2xiYXIuc2V0U2hhcmVkS2V5KGxvY2tLZXkudmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhsb2NrS2V5LnZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICApO1xufVxuZnVuY3Rpb24gb25NdWNFbnRlcmVkKGppZCwgaWQsIGRpc3BsYXlOYW1lKSB7XG4gICAgbWVzc2FnZUhhbmRsZXIubm90aWZ5KGRpc3BsYXlOYW1lIHx8ICdTb21lYm9keScsXG4gICAgICAgICdjb25uZWN0ZWQnLFxuICAgICAgICAnY29ubmVjdGVkJyk7XG5cbiAgICAvLyBBZGQgUGVlcidzIGNvbnRhaW5lclxuICAgIFZpZGVvTGF5b3V0LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMoamlkLGlkKTtcbn1cblxuZnVuY3Rpb24gb25NdWNQcmVzZW5jZVN0YXR1cyggamlkLCBpbmZvKSB7XG4gICAgVmlkZW9MYXlvdXQuc2V0UHJlc2VuY2VTdGF0dXMoXG4gICAgICAgICAgICAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCksIGluZm8uc3RhdHVzKTtcbn1cblxuZnVuY3Rpb24gb25NdWNSb2xlQ2hhbmdlZChyb2xlLCBkaXNwbGF5TmFtZSkge1xuICAgIFZpZGVvTGF5b3V0LnNob3dNb2RlcmF0b3JJbmRpY2F0b3IoKTtcblxuICAgIGlmIChyb2xlID09PSAnbW9kZXJhdG9yJykge1xuICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSBkaXNwbGF5TmFtZTtcbiAgICAgICAgaWYgKCFkaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgZGlzcGxheU5hbWUgPSAnU29tZWJvZHknO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm5vdGlmeShcbiAgICAgICAgICAgIGRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgJ2Nvbm5lY3RlZCcsXG4gICAgICAgICAgICAgICAgJ01vZGVyYXRvciByaWdodHMgZ3JhbnRlZCB0byAnICsgZGlzcGxheU5hbWUgKyAnIScpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gb25BdXRoZW50aWNhdGlvblJlcXVpcmVkKGludGVydmFsQ2FsbGJhY2spIHtcbiAgICBBdXRoZW50aWNhdGlvbi5vcGVuQXV0aGVudGljYXRpb25EaWFsb2coXG4gICAgICAgIHJvb21OYW1lLCBpbnRlcnZhbENhbGxiYWNrLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBUb29sYmFyLmF1dGhlbnRpY2F0ZUNsaWNrZWQoKTtcbiAgICAgICAgfSk7XG59O1xuXG5cbmZ1bmN0aW9uIG9uTGFzdE5DaGFuZ2VkKG9sZFZhbHVlLCBuZXdWYWx1ZSkge1xuICAgIGlmIChjb25maWcubXV0ZUxvY2FsVmlkZW9JZk5vdEluTGFzdE4pIHtcbiAgICAgICAgc2V0VmlkZW9NdXRlKCFuZXdWYWx1ZSwgeyAnYnlVc2VyJzogZmFsc2UgfSk7XG4gICAgfVxufVxuXG5cblVJLnRvZ2dsZVNtaWxleXMgPSBmdW5jdGlvbiAoKSB7XG4gICAgQ2hhdC50b2dnbGVTbWlsZXlzKCk7XG59O1xuXG5VSS5nZXRTZXR0aW5ncyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbn07XG5cblVJLnRvZ2dsZUZpbG1TdHJpcCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gQm90dG9tVG9vbGJhci50b2dnbGVGaWxtU3RyaXAoKTtcbn07XG5cblVJLnRvZ2dsZUNoYXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ2hhdCgpO1xufTtcblxuVUkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ29udGFjdExpc3QoKTtcbn07XG5cblVJLmlucHV0RGlzcGxheU5hbWVIYW5kbGVyID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgVmlkZW9MYXlvdXQuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIodmFsdWUpO1xufTtcblxuXG5VSS5nZXRMYXJnZVZpZGVvU3RhdGUgPSBmdW5jdGlvbigpXG57XG4gICAgcmV0dXJuIFZpZGVvTGF5b3V0LmdldExhcmdlVmlkZW9TdGF0ZSgpO1xufTtcblxuVUkuZ2VuZXJhdGVSb29tTmFtZSA9IGZ1bmN0aW9uKCkge1xuICAgIGlmKHJvb21OYW1lKVxuICAgICAgICByZXR1cm4gcm9vbU5hbWU7XG4gICAgdmFyIHJvb21ub2RlID0gbnVsbDtcbiAgICB2YXIgcGF0aCA9IHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZTtcblxuICAgIC8vIGRldGVybWluZGUgdGhlIHJvb20gbm9kZSBmcm9tIHRoZSB1cmxcbiAgICAvLyBUT0RPOiBqdXN0IHRoZSByb29tbm9kZSBvciB0aGUgd2hvbGUgYmFyZSBqaWQ/XG4gICAgaWYgKGNvbmZpZy5nZXRyb29tbm9kZSAmJiB0eXBlb2YgY29uZmlnLmdldHJvb21ub2RlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIGN1c3RvbSBmdW5jdGlvbiBtaWdodCBiZSByZXNwb25zaWJsZSBmb3IgZG9pbmcgdGhlIHB1c2hzdGF0ZVxuICAgICAgICByb29tbm9kZSA9IGNvbmZpZy5nZXRyb29tbm9kZShwYXRoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvKiBmYWxsIGJhY2sgdG8gZGVmYXVsdCBzdHJhdGVneVxuICAgICAgICAgKiB0aGlzIGlzIG1ha2luZyBhc3N1bXB0aW9ucyBhYm91dCBob3cgdGhlIFVSTC0+cm9vbSBtYXBwaW5nIGhhcHBlbnMuXG4gICAgICAgICAqIEl0IGN1cnJlbnRseSBhc3N1bWVzIGRlcGxveW1lbnQgYXQgcm9vdCwgd2l0aCBhIHJld3JpdGUgbGlrZSB0aGVcbiAgICAgICAgICogZm9sbG93aW5nIG9uZSAoZm9yIG5naW54KTpcbiAgICAgICAgIGxvY2F0aW9uIH4gXi8oW2EtekEtWjAtOV0rKSQge1xuICAgICAgICAgcmV3cml0ZSBeLyguKikkIC8gYnJlYWs7XG4gICAgICAgICB9XG4gICAgICAgICAqL1xuICAgICAgICBpZiAocGF0aC5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICByb29tbm9kZSA9IHBhdGguc3Vic3RyKDEpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgd29yZCA9IFJvb21OYW1lR2VuZXJhdG9yLmdlbmVyYXRlUm9vbVdpdGhvdXRTZXBhcmF0b3IoKTtcbiAgICAgICAgICAgIHJvb21ub2RlID0gd29yZC50b0xvd2VyQ2FzZSgpO1xuXG4gICAgICAgICAgICB3aW5kb3cuaGlzdG9yeS5wdXNoU3RhdGUoJ1ZpZGVvQ2hhdCcsXG4gICAgICAgICAgICAgICAgICAgICdSb29tOiAnICsgd29yZCwgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lICsgd29yZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByb29tTmFtZSA9IHJvb21ub2RlICsgJ0AnICsgY29uZmlnLmhvc3RzLm11YztcbiAgICByZXR1cm4gcm9vbU5hbWU7XG59O1xuXG5cblVJLmNvbm5lY3Rpb25JbmRpY2F0b3JTaG93TW9yZSA9IGZ1bmN0aW9uKGlkKVxue1xuICAgIHJldHVybiBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0uc2hvd01vcmUoKTtcbn07XG5cblVJLmdldENyZWRlbnRpYWxzID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBzZXR0aW5ncyA9IHRoaXMuZ2V0U2V0dGluZ3MoKTtcbiAgICByZXR1cm4ge1xuICAgICAgICBib3NoOiBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYm9zaFVSTCcpLnZhbHVlLFxuICAgICAgICBwYXNzd29yZDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bhc3N3b3JkJykudmFsdWUsXG4gICAgICAgIGppZDogZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ppZCcpLnZhbHVlLFxuICAgICAgICBlbWFpbDogc2V0dGluZ3MuZW1haWwsXG4gICAgICAgIGRpc3BsYXlOYW1lOiBzZXR0aW5ncy5kaXNwbGF5TmFtZSxcbiAgICAgICAgdWlkOiBzZXR0aW5ncy51aWRcbiAgICB9O1xufTtcblxuVUkuZGlzYWJsZUNvbm5lY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Nvbm5lY3QnKS5kaXNhYmxlZCA9IHRydWU7XG59O1xuXG5VSS5zaG93TG9naW5Qb3B1cCA9IGZ1bmN0aW9uKGNhbGxiYWNrKVxue1xuICAgIGNvbnNvbGUubG9nKCdwYXNzd29yZCBpcyByZXF1aXJlZCcpO1xuXG4gICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhudWxsLFxuICAgICAgICAgICAgJzxoMj5QYXNzd29yZCByZXF1aXJlZDwvaDI+JyArXG4gICAgICAgICAgICAnPGlucHV0IGlkPVwicGFzc3dvcmRyZXF1aXJlZC51c2VybmFtZVwiIHR5cGU9XCJ0ZXh0XCIgcGxhY2Vob2xkZXI9XCJ1c2VyQGRvbWFpbi5uZXRcIiBhdXRvZm9jdXM+JyArXG4gICAgICAgICAgICAnPGlucHV0IGlkPVwicGFzc3dvcmRyZXF1aXJlZC5wYXNzd29yZFwiIHR5cGU9XCJwYXNzd29yZFwiIHBsYWNlaG9sZGVyPVwidXNlciBwYXNzd29yZFwiPicsXG4gICAgICAgIHRydWUsXG4gICAgICAgIFwiT2tcIixcbiAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgdmFyIHVzZXJuYW1lID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bhc3N3b3JkcmVxdWlyZWQudXNlcm5hbWUnKTtcbiAgICAgICAgICAgICAgICB2YXIgcGFzc3dvcmQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncGFzc3dvcmRyZXF1aXJlZC5wYXNzd29yZCcpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHVzZXJuYW1lLnZhbHVlICE9PSBudWxsICYmIHBhc3N3b3JkLnZhbHVlICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sodXNlcm5hbWUudmFsdWUsIHBhc3N3b3JkLnZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Bhc3N3b3JkcmVxdWlyZWQudXNlcm5hbWUnKS5mb2N1cygpO1xuICAgICAgICB9XG4gICAgKTtcbn1cblxuVUkuY2hlY2tGb3JOaWNrbmFtZUFuZEpvaW4gPSBmdW5jdGlvbiAoKSB7XG5cbiAgICBBdXRoZW50aWNhdGlvbi5jbG9zZUF1dGhlbnRpY2F0aW9uRGlhbG9nKCk7XG4gICAgQXV0aGVudGljYXRpb24uc3RvcEludGVydmFsKCk7XG5cbiAgICB2YXIgbmljayA9IG51bGw7XG4gICAgaWYgKGNvbmZpZy51c2VOaWNrcykge1xuICAgICAgICBuaWNrID0gd2luZG93LnByb21wdCgnWW91ciBuaWNrbmFtZSAob3B0aW9uYWwpJyk7XG4gICAgfVxuICAgIEFQUC54bXBwLmpvaW5Sb29tKHJvb21OYW1lLCBjb25maWcudXNlTmlja3MsIG5pY2spO1xufTtcblxuXG5mdW5jdGlvbiBkdW1wKGVsZW0sIGZpbGVuYW1lKSB7XG4gICAgZWxlbSA9IGVsZW0ucGFyZW50Tm9kZTtcbiAgICBlbGVtLmRvd25sb2FkID0gZmlsZW5hbWUgfHwgJ21lZXRsb2cuanNvbic7XG4gICAgZWxlbS5ocmVmID0gJ2RhdGE6YXBwbGljYXRpb24vanNvbjtjaGFyc2V0PXV0Zi04LFxcbic7XG4gICAgdmFyIGRhdGEgPSBBUFAueG1wcC5wb3B1bGF0ZURhdGEoKTtcbiAgICB2YXIgbWV0YWRhdGEgPSB7fTtcbiAgICBtZXRhZGF0YS50aW1lID0gbmV3IERhdGUoKTtcbiAgICBtZXRhZGF0YS51cmwgPSB3aW5kb3cubG9jYXRpb24uaHJlZjtcbiAgICBtZXRhZGF0YS51YSA9IG5hdmlnYXRvci51c2VyQWdlbnQ7XG4gICAgdmFyIGxvZyA9IEFQUC54bXBwLmdldExvZ2dlcigpO1xuICAgIGlmIChsb2cpIHtcbiAgICAgICAgbWV0YWRhdGEueG1wcCA9IGxvZztcbiAgICB9XG4gICAgZGF0YS5tZXRhZGF0YSA9IG1ldGFkYXRhO1xuICAgIGVsZW0uaHJlZiArPSBlbmNvZGVVUklDb21wb25lbnQoSlNPTi5zdHJpbmdpZnkoZGF0YSwgbnVsbCwgJyAgJykpO1xuICAgIHJldHVybiBmYWxzZTtcbn1cblxuVUkuZ2V0Um9vbU5hbWUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHJvb21OYW1lO1xufTtcblxuLyoqXG4gKiBNdXRlcy91bm11dGVzIHRoZSBsb2NhbCB2aWRlby5cbiAqL1xuVUkudG9nZ2xlVmlkZW8gPSBmdW5jdGlvbiAoKSB7XG4gICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI3ZpZGVvXCIsIFwiaWNvbi1jYW1lcmEgaWNvbi1jYW1lcmEtZGlzYWJsZWRcIik7XG5cbiAgICBzZXRWaWRlb011dGUoIUFQUC5SVEMubG9jYWxWaWRlby5pc011dGVkKCkpO1xufTtcblxuLyoqXG4gKiBNdXRlcyAvIHVubXV0ZXMgYXVkaW8gZm9yIHRoZSBsb2NhbCBwYXJ0aWNpcGFudC5cbiAqL1xuVUkudG9nZ2xlQXVkaW8gPSBmdW5jdGlvbigpIHtcbiAgICBVSS5zZXRBdWRpb011dGVkKCFBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKTtcbn07XG5cbi8qKlxuICogU2V0cyBtdXRlZCBhdWRpbyBzdGF0ZSBmb3IgdGhlIGxvY2FsIHBhcnRpY2lwYW50LlxuICovXG5VSS5zZXRBdWRpb011dGVkID0gZnVuY3Rpb24gKG11dGUpIHtcblxuICAgIGlmKCFBUFAueG1wcC5zZXRBdWRpb011dGUobXV0ZSwgZnVuY3Rpb24gKCkge1xuICAgICAgICBWaWRlb0xheW91dC5zaG93TG9jYWxBdWRpb0luZGljYXRvcihtdXRlKTtcblxuICAgICAgICBVSVV0aWwuYnV0dG9uQ2xpY2soXCIjbXV0ZVwiLCBcImljb24tbWljcm9waG9uZSBpY29uLW1pYy1kaXNhYmxlZFwiKTtcbiAgICB9KSlcbiAgICB7XG4gICAgICAgIC8vIFdlIHN0aWxsIGNsaWNrIHRoZSBidXR0b24uXG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNtdXRlXCIsIFwiaWNvbi1taWNyb3Bob25lIGljb24tbWljLWRpc2FibGVkXCIpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG59XG5cblVJLmFkZExpc3RlbmVyID0gZnVuY3Rpb24gKHR5cGUsIGxpc3RlbmVyKSB7XG4gICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbn1cblxuVUkuY2xpY2tPblZpZGVvID0gZnVuY3Rpb24gKHZpZGVvTnVtYmVyKSB7XG4gICAgdmFyIHJlbW90ZVZpZGVvcyA9ICQoXCIudmlkZW9jb250YWluZXI6bm90KCNtaXhlZHN0cmVhbSlcIik7XG4gICAgaWYgKHJlbW90ZVZpZGVvcy5sZW5ndGggPiB2aWRlb051bWJlcikge1xuICAgICAgICByZW1vdGVWaWRlb3NbdmlkZW9OdW1iZXJdLmNsaWNrKCk7XG4gICAgfVxufVxuXG4vL1VzZWQgYnkgdG9ydHVyZVxuVUkuc2hvd1Rvb2xiYXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIFRvb2xiYXJUb2dnbGVyLnNob3dUb29sYmFyKCk7XG59XG5cbi8vVXNlZCBieSB0b3J0dXJlXG5VSS5kb2NrVG9vbGJhciA9IGZ1bmN0aW9uIChpc0RvY2spIHtcbiAgICByZXR1cm4gVG9vbGJhclRvZ2dsZXIuZG9ja1Rvb2xiYXIoaXNEb2NrKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBVSTtcblxuIiwidmFyIENhbnZhc1V0aWwgPSByZXF1aXJlKFwiLi9DYW52YXNVdGlsc1wiKTtcblxuLyoqXG4gKiBUaGUgYXVkaW8gTGV2ZWxzIHBsdWdpbi5cbiAqL1xudmFyIEF1ZGlvTGV2ZWxzID0gKGZ1bmN0aW9uKG15KSB7XG4gICAgdmFyIGF1ZGlvTGV2ZWxDYW52YXNDYWNoZSA9IHt9O1xuXG4gICAgbXkuTE9DQUxfTEVWRUwgPSAnbG9jYWwnO1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgYXVkaW8gbGV2ZWwgY2FudmFzIGZvciB0aGUgZ2l2ZW4gcGVlckppZC4gSWYgdGhlIGNhbnZhc1xuICAgICAqIGRpZG4ndCBleGlzdCB3ZSBjcmVhdGUgaXQuXG4gICAgICovXG4gICAgbXkudXBkYXRlQXVkaW9MZXZlbENhbnZhcyA9IGZ1bmN0aW9uIChwZWVySmlkLCBWaWRlb0xheW91dCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBudWxsO1xuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSBudWxsO1xuICAgICAgICBpZiAoIXBlZXJKaWQpXG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdsb2NhbFZpZGVvQ29udGFpbmVyJztcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHBlZXJKaWQpO1xuXG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgdmlkZW9TcGFuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodmlkZW9TcGFuSWQpO1xuXG4gICAgICAgIGlmICghdmlkZW9TcGFuKSB7XG4gICAgICAgICAgICBpZiAocmVzb3VyY2VKaWQpXG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIk5vIHZpZGVvIGVsZW1lbnQgZm9yIGppZFwiLCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIk5vIHZpZGVvIGVsZW1lbnQgZm9yIGxvY2FsIHZpZGVvLlwiKTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGF1ZGlvTGV2ZWxDYW52YXMgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5jYW52YXMnKTtcblxuICAgICAgICB2YXIgdmlkZW9TcGFjZVdpZHRoID0gJCgnI3JlbW90ZVZpZGVvcycpLndpZHRoKCk7XG4gICAgICAgIHZhciB0aHVtYm5haWxTaXplID0gVmlkZW9MYXlvdXQuY2FsY3VsYXRlVGh1bWJuYWlsU2l6ZSh2aWRlb1NwYWNlV2lkdGgpO1xuICAgICAgICB2YXIgdGh1bWJuYWlsV2lkdGggPSB0aHVtYm5haWxTaXplWzBdO1xuICAgICAgICB2YXIgdGh1bWJuYWlsSGVpZ2h0ID0gdGh1bWJuYWlsU2l6ZVsxXTtcblxuICAgICAgICBpZiAoIWF1ZGlvTGV2ZWxDYW52YXMgfHwgYXVkaW9MZXZlbENhbnZhcy5sZW5ndGggPT09IDApIHtcblxuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcy5jbGFzc05hbWUgPSBcImF1ZGlvbGV2ZWxcIjtcbiAgICAgICAgICAgIGF1ZGlvTGV2ZWxDYW52YXMuc3R5bGUuYm90dG9tID0gXCItXCIgKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIgKyBcInB4XCI7XG4gICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzLnN0eWxlLmxlZnQgPSBcIi1cIiArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiArIFwicHhcIjtcbiAgICAgICAgICAgIHJlc2l6ZUF1ZGlvTGV2ZWxDYW52YXMoIGF1ZGlvTGV2ZWxDYW52YXMsXG4gICAgICAgICAgICAgICAgICAgIHRodW1ibmFpbFdpZHRoLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxIZWlnaHQpO1xuXG4gICAgICAgICAgICB2aWRlb1NwYW4uYXBwZW5kQ2hpbGQoYXVkaW9MZXZlbENhbnZhcyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzID0gYXVkaW9MZXZlbENhbnZhcy5nZXQoMCk7XG5cbiAgICAgICAgICAgIHJlc2l6ZUF1ZGlvTGV2ZWxDYW52YXMoIGF1ZGlvTGV2ZWxDYW52YXMsXG4gICAgICAgICAgICAgICAgICAgIHRodW1ibmFpbFdpZHRoLFxuICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxIZWlnaHQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGF1ZGlvIGxldmVsIFVJIGZvciB0aGUgZ2l2ZW4gcmVzb3VyY2VKaWQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcmVzb3VyY2VKaWQgdGhlIHJlc291cmNlIGppZCBpbmRpY2F0aW5nIHRoZSB2aWRlbyBlbGVtZW50IGZvclxuICAgICAqIHdoaWNoIHdlIGRyYXcgdGhlIGF1ZGlvIGxldmVsXG4gICAgICogQHBhcmFtIGF1ZGlvTGV2ZWwgdGhlIG5ld0F1ZGlvIGxldmVsIHRvIHJlbmRlclxuICAgICAqL1xuICAgIG15LnVwZGF0ZUF1ZGlvTGV2ZWwgPSBmdW5jdGlvbiAocmVzb3VyY2VKaWQsIGF1ZGlvTGV2ZWwsIGxhcmdlVmlkZW9SZXNvdXJjZUppZCkge1xuICAgICAgICBkcmF3QXVkaW9MZXZlbENhbnZhcyhyZXNvdXJjZUppZCwgYXVkaW9MZXZlbCk7XG5cbiAgICAgICAgdmFyIHZpZGVvU3BhbklkID0gZ2V0VmlkZW9TcGFuSWQocmVzb3VyY2VKaWQpO1xuXG4gICAgICAgIHZhciBhdWRpb0xldmVsQ2FudmFzID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+Y2FudmFzJykuZ2V0KDApO1xuXG4gICAgICAgIGlmICghYXVkaW9MZXZlbENhbnZhcylcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICB2YXIgZHJhd0NvbnRleHQgPSBhdWRpb0xldmVsQ2FudmFzLmdldENvbnRleHQoJzJkJyk7XG5cbiAgICAgICAgdmFyIGNhbnZhc0NhY2hlID0gYXVkaW9MZXZlbENhbnZhc0NhY2hlW3Jlc291cmNlSmlkXTtcblxuICAgICAgICBkcmF3Q29udGV4dC5jbGVhclJlY3QgKDAsIDAsXG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhcy53aWR0aCwgYXVkaW9MZXZlbENhbnZhcy5oZWlnaHQpO1xuICAgICAgICBkcmF3Q29udGV4dC5kcmF3SW1hZ2UoY2FudmFzQ2FjaGUsIDAsIDApO1xuXG4gICAgICAgIGlmKHJlc291cmNlSmlkID09PSBBdWRpb0xldmVscy5MT0NBTF9MRVZFTCkge1xuICAgICAgICAgICAgaWYoIUFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXNvdXJjZUppZCA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKHJlc291cmNlSmlkICA9PT0gbGFyZ2VWaWRlb1Jlc291cmNlSmlkKSB7XG4gICAgICAgICAgICBBdWRpb0xldmVscy51cGRhdGVBY3RpdmVTcGVha2VyQXVkaW9MZXZlbChhdWRpb0xldmVsKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBteS51cGRhdGVBY3RpdmVTcGVha2VyQXVkaW9MZXZlbCA9IGZ1bmN0aW9uKGF1ZGlvTGV2ZWwpIHtcbiAgICAgICAgdmFyIGRyYXdDb250ZXh0ID0gJCgnI2FjdGl2ZVNwZWFrZXJBdWRpb0xldmVsJylbMF0uZ2V0Q29udGV4dCgnMmQnKTtcbiAgICAgICAgdmFyIHIgPSBpbnRlcmZhY2VDb25maWcuQUNUSVZFX1NQRUFLRVJfQVZBVEFSX1NJWkUgLyAyO1xuICAgICAgICB2YXIgY2VudGVyID0gKGludGVyZmFjZUNvbmZpZy5BQ1RJVkVfU1BFQUtFUl9BVkFUQVJfU0laRSArIHIpIC8gMjtcblxuICAgICAgICAvLyBTYXZlIHRoZSBwcmV2aW91cyBzdGF0ZSBvZiB0aGUgY29udGV4dC5cbiAgICAgICAgZHJhd0NvbnRleHQuc2F2ZSgpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LmNsZWFyUmVjdCgwLCAwLCAzMDAsIDMwMCk7XG5cbiAgICAgICAgLy8gRHJhdyBhIGNpcmNsZS5cbiAgICAgICAgZHJhd0NvbnRleHQuYXJjKGNlbnRlciwgY2VudGVyLCByLCAwLCAyICogTWF0aC5QSSk7XG5cbiAgICAgICAgLy8gQWRkIGEgc2hhZG93IGFyb3VuZCB0aGUgY2lyY2xlXG4gICAgICAgIGRyYXdDb250ZXh0LnNoYWRvd0NvbG9yID0gaW50ZXJmYWNlQ29uZmlnLlNIQURPV19DT0xPUjtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93Qmx1ciA9IGdldFNoYWRvd0xldmVsKGF1ZGlvTGV2ZWwpO1xuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dPZmZzZXRYID0gMDtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93T2Zmc2V0WSA9IDA7XG5cbiAgICAgICAgLy8gRmlsbCB0aGUgc2hhcGUuXG4gICAgICAgIGRyYXdDb250ZXh0LmZpbGwoKTtcblxuICAgICAgICBkcmF3Q29udGV4dC5zYXZlKCk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQucmVzdG9yZSgpO1xuXG5cbiAgICAgICAgZHJhd0NvbnRleHQuYXJjKGNlbnRlciwgY2VudGVyLCByLCAwLCAyICogTWF0aC5QSSk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xpcCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5jbGVhclJlY3QoMCwgMCwgMjc3LCAyMDApO1xuXG4gICAgICAgIC8vIFJlc3RvcmUgdGhlIHByZXZpb3VzIGNvbnRleHQgc3RhdGUuXG4gICAgICAgIGRyYXdDb250ZXh0LnJlc3RvcmUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVzaXplcyB0aGUgZ2l2ZW4gYXVkaW8gbGV2ZWwgY2FudmFzIHRvIG1hdGNoIHRoZSBnaXZlbiB0aHVtYm5haWwgc2l6ZS5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiByZXNpemVBdWRpb0xldmVsQ2FudmFzKGF1ZGlvTGV2ZWxDYW52YXMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRodW1ibmFpbEhlaWdodCkge1xuICAgICAgICBhdWRpb0xldmVsQ2FudmFzLndpZHRoID0gdGh1bWJuYWlsV2lkdGggKyBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBO1xuICAgICAgICBhdWRpb0xldmVsQ2FudmFzLmhlaWdodCA9IHRodW1ibmFpbEhlaWdodCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGF1ZGlvIGxldmVsIGNhbnZhcyBpbnRvIHRoZSBjYWNoZWQgY2FudmFzIG9iamVjdC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSByZXNvdXJjZUppZCB0aGUgcmVzb3VyY2UgamlkIGluZGljYXRpbmcgdGhlIHZpZGVvIGVsZW1lbnQgZm9yXG4gICAgICogd2hpY2ggd2UgZHJhdyB0aGUgYXVkaW8gbGV2ZWxcbiAgICAgKiBAcGFyYW0gYXVkaW9MZXZlbCB0aGUgbmV3QXVkaW8gbGV2ZWwgdG8gcmVuZGVyXG4gICAgICovXG4gICAgZnVuY3Rpb24gZHJhd0F1ZGlvTGV2ZWxDYW52YXMocmVzb3VyY2VKaWQsIGF1ZGlvTGV2ZWwpIHtcbiAgICAgICAgaWYgKCFhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdKSB7XG5cbiAgICAgICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IGdldFZpZGVvU3BhbklkKHJlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgdmFyIGF1ZGlvTGV2ZWxDYW52YXNPcmlnID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+Y2FudmFzJykuZ2V0KDApO1xuXG4gICAgICAgICAgICAvKlxuICAgICAgICAgICAgICogRklYTUUgVGVzdGluZyBoYXMgc2hvd24gdGhhdCBhdWRpb0xldmVsQ2FudmFzT3JpZyBtYXkgbm90IGV4aXN0LlxuICAgICAgICAgICAgICogSW4gc3VjaCBhIGNhc2UsIHRoZSBtZXRob2QgQ2FudmFzVXRpbC5jbG9uZUNhbnZhcyBtYXkgdGhyb3cgYW5cbiAgICAgICAgICAgICAqIGVycm9yLiBTaW5jZSBhdWRpbyBsZXZlbHMgYXJlIGZyZXF1ZW50bHkgdXBkYXRlZCwgdGhlIGVycm9ycyBoYXZlXG4gICAgICAgICAgICAgKiBiZWVuIG9ic2VydmVkIHRvIHBpbGUgaW50byB0aGUgY29uc29sZSwgc3RyYWluIHRoZSBDUFUuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIGlmIChhdWRpb0xldmVsQ2FudmFzT3JpZylcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdXG4gICAgICAgICAgICAgICAgICAgID0gQ2FudmFzVXRpbC5jbG9uZUNhbnZhcyhhdWRpb0xldmVsQ2FudmFzT3JpZyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgY2FudmFzID0gYXVkaW9MZXZlbENhbnZhc0NhY2hlW3Jlc291cmNlSmlkXTtcblxuICAgICAgICBpZiAoIWNhbnZhcylcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICB2YXIgZHJhd0NvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcblxuICAgICAgICBkcmF3Q29udGV4dC5jbGVhclJlY3QoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KTtcblxuICAgICAgICB2YXIgc2hhZG93TGV2ZWwgPSBnZXRTaGFkb3dMZXZlbChhdWRpb0xldmVsKTtcblxuICAgICAgICBpZiAoc2hhZG93TGV2ZWwgPiAwKVxuICAgICAgICAgICAgLy8gZHJhd0NvbnRleHQsIHgsIHksIHcsIGgsIHIsIHNoYWRvd0NvbG9yLCBzaGFkb3dMZXZlbFxuICAgICAgICAgICAgQ2FudmFzVXRpbC5kcmF3Um91bmRSZWN0R2xvdyggICBkcmF3Q29udGV4dCxcbiAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIsIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMixcbiAgICAgICAgICAgICAgICBjYW52YXMud2lkdGggLSBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLFxuICAgICAgICAgICAgICAgIGNhbnZhcy5oZWlnaHQgLSBpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLFxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfUkFESVVTLFxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5TSEFET1dfQ09MT1IsXG4gICAgICAgICAgICAgICAgc2hhZG93TGV2ZWwpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHNoYWRvdy9nbG93IGxldmVsIGZvciB0aGUgZ2l2ZW4gYXVkaW8gbGV2ZWwuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gYXVkaW9MZXZlbCB0aGUgYXVkaW8gbGV2ZWwgZnJvbSB3aGljaCB3ZSBkZXRlcm1pbmUgdGhlIHNoYWRvd1xuICAgICAqIGxldmVsXG4gICAgICovXG4gICAgZnVuY3Rpb24gZ2V0U2hhZG93TGV2ZWwgKGF1ZGlvTGV2ZWwpIHtcbiAgICAgICAgdmFyIHNoYWRvd0xldmVsID0gMDtcblxuICAgICAgICBpZiAoYXVkaW9MZXZlbCA8PSAwLjMpIHtcbiAgICAgICAgICAgIHNoYWRvd0xldmVsID0gTWF0aC5yb3VuZChpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIqKGF1ZGlvTGV2ZWwvMC4zKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoYXVkaW9MZXZlbCA8PSAwLjYpIHtcbiAgICAgICAgICAgIHNoYWRvd0xldmVsID0gTWF0aC5yb3VuZChpbnRlcmZhY2VDb25maWcuQ0FOVkFTX0VYVFJBLzIqKChhdWRpb0xldmVsIC0gMC4zKSAvIDAuMykpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgc2hhZG93TGV2ZWwgPSBNYXRoLnJvdW5kKGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEvMiooKGF1ZGlvTGV2ZWwgLSAwLjYpIC8gMC40KSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHNoYWRvd0xldmVsO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHZpZGVvIHNwYW4gaWQgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gcmVzb3VyY2VKaWQgb3IgbG9jYWxcbiAgICAgKiB1c2VyLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGdldFZpZGVvU3BhbklkKHJlc291cmNlSmlkKSB7XG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChyZXNvdXJjZUppZCA9PT0gQXVkaW9MZXZlbHMuTE9DQUxfTEVWRUxcbiAgICAgICAgICAgICAgICB8fCAoQVBQLnhtcHAubXlSZXNvdXJjZSgpICYmIHJlc291cmNlSmlkXG4gICAgICAgICAgICAgICAgICAgID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpKVxuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZDtcblxuICAgICAgICByZXR1cm4gdmlkZW9TcGFuSWQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSW5kaWNhdGVzIHRoYXQgdGhlIHJlbW90ZSB2aWRlbyBoYXMgYmVlbiByZXNpemVkLlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ3JlbW90ZXZpZGVvLnJlc2l6ZWQnLCBmdW5jdGlvbiAoZXZlbnQsIHdpZHRoLCBoZWlnaHQpIHtcbiAgICAgICAgdmFyIHJlc2l6ZWQgPSBmYWxzZTtcbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcz5zcGFuPmNhbnZhcycpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICB2YXIgY2FudmFzID0gJCh0aGlzKS5nZXQoMCk7XG4gICAgICAgICAgICBpZiAoY2FudmFzLndpZHRoICE9PSB3aWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEpIHtcbiAgICAgICAgICAgICAgICBjYW52YXMud2lkdGggPSB3aWR0aCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgICAgICAgICAgcmVzaXplZCA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChjYW52YXMuaGVpZ2ggIT09IGhlaWdodCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkEpIHtcbiAgICAgICAgICAgICAgICBjYW52YXMuaGVpZ2h0ID0gaGVpZ2h0ICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgICAgICByZXNpemVkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHJlc2l6ZWQpXG4gICAgICAgICAgICBPYmplY3Qua2V5cyhhdWRpb0xldmVsQ2FudmFzQ2FjaGUpLmZvckVhY2goZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9MZXZlbENhbnZhc0NhY2hlW3Jlc291cmNlSmlkXS53aWR0aFxuICAgICAgICAgICAgICAgICAgICA9IHdpZHRoICsgaW50ZXJmYWNlQ29uZmlnLkNBTlZBU19FWFRSQTtcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsQ2FudmFzQ2FjaGVbcmVzb3VyY2VKaWRdLmhlaWdodFxuICAgICAgICAgICAgICAgICAgICA9IGhlaWdodCArIGludGVyZmFjZUNvbmZpZy5DQU5WQVNfRVhUUkE7XG4gICAgICAgICAgICB9KTtcbiAgICB9KTtcblxuICAgIHJldHVybiBteTtcblxufSkoQXVkaW9MZXZlbHMgfHwge30pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEF1ZGlvTGV2ZWxzOyIsIi8qKlxuICogVXRpbGl0eSBjbGFzcyBmb3IgZHJhd2luZyBjYW52YXMgc2hhcGVzLlxuICovXG52YXIgQ2FudmFzVXRpbCA9IChmdW5jdGlvbihteSkge1xuXG4gICAgLyoqXG4gICAgICogRHJhd3MgYSByb3VuZCByZWN0YW5nbGUgd2l0aCBhIGdsb3cuIFRoZSBnbG93V2lkdGggaW5kaWNhdGVzIHRoZSBkZXB0aFxuICAgICAqIG9mIHRoZSBnbG93LlxuICAgICAqXG4gICAgICogQHBhcmFtIGRyYXdDb250ZXh0IHRoZSBjb250ZXh0IG9mIHRoZSBjYW52YXMgdG8gZHJhdyB0b1xuICAgICAqIEBwYXJhbSB4IHRoZSB4IGNvb3JkaW5hdGUgb2YgdGhlIHJvdW5kIHJlY3RhbmdsZVxuICAgICAqIEBwYXJhbSB5IHRoZSB5IGNvb3JkaW5hdGUgb2YgdGhlIHJvdW5kIHJlY3RhbmdsZVxuICAgICAqIEBwYXJhbSB3IHRoZSB3aWR0aCBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIGggdGhlIGhlaWdodCBvZiB0aGUgcm91bmQgcmVjdGFuZ2xlXG4gICAgICogQHBhcmFtIGdsb3dDb2xvciB0aGUgY29sb3Igb2YgdGhlIGdsb3dcbiAgICAgKiBAcGFyYW0gZ2xvd1dpZHRoIHRoZSB3aWR0aCBvZiB0aGUgZ2xvd1xuICAgICAqL1xuICAgIG15LmRyYXdSb3VuZFJlY3RHbG93XG4gICAgICAgID0gZnVuY3Rpb24oZHJhd0NvbnRleHQsIHgsIHksIHcsIGgsIHIsIGdsb3dDb2xvciwgZ2xvd1dpZHRoKSB7XG5cbiAgICAgICAgLy8gU2F2ZSB0aGUgcHJldmlvdXMgc3RhdGUgb2YgdGhlIGNvbnRleHQuXG4gICAgICAgIGRyYXdDb250ZXh0LnNhdmUoKTtcblxuICAgICAgICBpZiAodyA8IDIgKiByKSByID0gdyAvIDI7XG4gICAgICAgIGlmIChoIDwgMiAqIHIpIHIgPSBoIC8gMjtcblxuICAgICAgICAvLyBEcmF3IGEgcm91bmQgcmVjdGFuZ2xlLlxuICAgICAgICBkcmF3Q29udGV4dC5iZWdpblBhdGgoKTtcbiAgICAgICAgZHJhd0NvbnRleHQubW92ZVRvKHgrciwgeSk7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgrdywgeSwgICB4K3csIHkraCwgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgrdywgeStoLCB4LCAgIHkraCwgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgsICAgeStoLCB4LCAgIHksICAgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmFyY1RvKHgsICAgeSwgICB4K3csIHksICAgcik7XG4gICAgICAgIGRyYXdDb250ZXh0LmNsb3NlUGF0aCgpO1xuXG4gICAgICAgIC8vIEFkZCBhIHNoYWRvdyBhcm91bmQgdGhlIHJlY3RhbmdsZVxuICAgICAgICBkcmF3Q29udGV4dC5zaGFkb3dDb2xvciA9IGdsb3dDb2xvcjtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93Qmx1ciA9IGdsb3dXaWR0aDtcbiAgICAgICAgZHJhd0NvbnRleHQuc2hhZG93T2Zmc2V0WCA9IDA7XG4gICAgICAgIGRyYXdDb250ZXh0LnNoYWRvd09mZnNldFkgPSAwO1xuXG4gICAgICAgIC8vIEZpbGwgdGhlIHNoYXBlLlxuICAgICAgICBkcmF3Q29udGV4dC5maWxsKCk7XG5cbiAgICAgICAgZHJhd0NvbnRleHQuc2F2ZSgpO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LnJlc3RvcmUoKTtcblxuLy8gICAgICAxKSBVbmNvbW1lbnQgdGhpcyBsaW5lIHRvIHVzZSBDb21wb3NpdGUgT3BlcmF0aW9uLCB3aGljaCBpcyBkb2luZyB0aGVcbi8vICAgICAgc2FtZSBhcyB0aGUgY2xpcCBmdW5jdGlvbiBiZWxvdyBhbmQgaXMgYWxzbyBhbnRpYWxpYXNpbmcgdGhlIHJvdW5kXG4vLyAgICAgIGJvcmRlciwgYnV0IGlzIHNhaWQgdG8gYmUgbGVzcyBmYXN0IHBlcmZvcm1hbmNlIHdpc2UuXG5cbi8vICAgICAgZHJhd0NvbnRleHQuZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSdkZXN0aW5hdGlvbi1vdXQnO1xuXG4gICAgICAgIGRyYXdDb250ZXh0LmJlZ2luUGF0aCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5tb3ZlVG8oeCtyLCB5KTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5LCAgIHgrdywgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCt3LCB5K2gsIHgsICAgeStoLCByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5K2gsIHgsICAgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuYXJjVG8oeCwgICB5LCAgIHgrdywgeSwgICByKTtcbiAgICAgICAgZHJhd0NvbnRleHQuY2xvc2VQYXRoKCk7XG5cbi8vICAgICAgMikgVW5jb21tZW50IHRoaXMgbGluZSB0byB1c2UgQ29tcG9zaXRlIE9wZXJhdGlvbiwgd2hpY2ggaXMgZG9pbmcgdGhlXG4vLyAgICAgIHNhbWUgYXMgdGhlIGNsaXAgZnVuY3Rpb24gYmVsb3cgYW5kIGlzIGFsc28gYW50aWFsaWFzaW5nIHRoZSByb3VuZFxuLy8gICAgICBib3JkZXIsIGJ1dCBpcyBzYWlkIHRvIGJlIGxlc3MgZmFzdCBwZXJmb3JtYW5jZSB3aXNlLlxuXG4vLyAgICAgIGRyYXdDb250ZXh0LmZpbGwoKTtcblxuICAgICAgICAvLyBDb21tZW50IHRoZXNlIHR3byBsaW5lcyBpZiBjaG9vc2luZyB0byBkbyB0aGUgc2FtZSB3aXRoIGNvbXBvc2l0ZVxuICAgICAgICAvLyBvcGVyYXRpb24gYWJvdmUgMSBhbmQgMi5cbiAgICAgICAgZHJhd0NvbnRleHQuY2xpcCgpO1xuICAgICAgICBkcmF3Q29udGV4dC5jbGVhclJlY3QoMCwgMCwgMjc3LCAyMDApO1xuXG4gICAgICAgIC8vIFJlc3RvcmUgdGhlIHByZXZpb3VzIGNvbnRleHQgc3RhdGUuXG4gICAgICAgIGRyYXdDb250ZXh0LnJlc3RvcmUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2xvbmVzIHRoZSBnaXZlbiBjYW52YXMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHRoZSBuZXcgY2xvbmVkIGNhbnZhcy5cbiAgICAgKi9cbiAgICBteS5jbG9uZUNhbnZhcyA9IGZ1bmN0aW9uIChvbGRDYW52YXMpIHtcbiAgICAgICAgLypcbiAgICAgICAgICogRklYTUUgVGVzdGluZyBoYXMgc2hvd24gdGhhdCBvbGRDYW52YXMgbWF5IG5vdCBleGlzdC4gSW4gc3VjaCBhIGNhc2UsXG4gICAgICAgICAqIHRoZSBtZXRob2QgQ2FudmFzVXRpbC5jbG9uZUNhbnZhcyBtYXkgdGhyb3cgYW4gZXJyb3IuIFNpbmNlIGF1ZGlvXG4gICAgICAgICAqIGxldmVscyBhcmUgZnJlcXVlbnRseSB1cGRhdGVkLCB0aGUgZXJyb3JzIGhhdmUgYmVlbiBvYnNlcnZlZCB0byBwaWxlXG4gICAgICAgICAqIGludG8gdGhlIGNvbnNvbGUsIHN0cmFpbiB0aGUgQ1BVLlxuICAgICAgICAgKi9cbiAgICAgICAgaWYgKCFvbGRDYW52YXMpXG4gICAgICAgICAgICByZXR1cm4gb2xkQ2FudmFzO1xuXG4gICAgICAgIC8vY3JlYXRlIGEgbmV3IGNhbnZhc1xuICAgICAgICB2YXIgbmV3Q2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICAgIHZhciBjb250ZXh0ID0gbmV3Q2FudmFzLmdldENvbnRleHQoJzJkJyk7XG5cbiAgICAgICAgLy9zZXQgZGltZW5zaW9uc1xuICAgICAgICBuZXdDYW52YXMud2lkdGggPSBvbGRDYW52YXMud2lkdGg7XG4gICAgICAgIG5ld0NhbnZhcy5oZWlnaHQgPSBvbGRDYW52YXMuaGVpZ2h0O1xuXG4gICAgICAgIC8vYXBwbHkgdGhlIG9sZCBjYW52YXMgdG8gdGhlIG5ldyBvbmVcbiAgICAgICAgY29udGV4dC5kcmF3SW1hZ2Uob2xkQ2FudmFzLCAwLCAwKTtcblxuICAgICAgICAvL3JldHVybiB0aGUgbmV3IGNhbnZhc1xuICAgICAgICByZXR1cm4gbmV3Q2FudmFzO1xuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG59KShDYW52YXNVdGlsIHx8IHt9KTtcblxubW9kdWxlLmV4cG9ydHMgPSBDYW52YXNVdGlsOyIsIi8qIEluaXRpYWwgXCJhdXRoZW50aWNhdGlvbiByZXF1aXJlZFwiIGRpYWxvZyAqL1xudmFyIGF1dGhEaWFsb2cgPSBudWxsO1xuLyogTG9vcCByZXRyeSBJRCB0aGF0IHdpdHMgZm9yIG90aGVyIHVzZXIgdG8gY3JlYXRlIHRoZSByb29tICovXG52YXIgYXV0aFJldHJ5SWQgPSBudWxsO1xudmFyIGF1dGhlbnRpY2F0aW9uV2luZG93ID0gbnVsbDtcblxudmFyIEF1dGhlbnRpY2F0aW9uID0ge1xuICAgIG9wZW5BdXRoZW50aWNhdGlvbkRpYWxvZzogZnVuY3Rpb24gKHJvb21OYW1lLCBpbnRlcnZhbENhbGxiYWNrLCBjYWxsYmFjaykge1xuICAgICAgICAvLyBUaGlzIGlzIHRoZSBsb29wIHRoYXQgd2lsbCB3YWl0IGZvciB0aGUgcm9vbSB0byBiZSBjcmVhdGVkIGJ5XG4gICAgICAgIC8vIHNvbWVvbmUgZWxzZS4gJ2F1dGhfcmVxdWlyZWQubW9kZXJhdG9yJyB3aWxsIGJyaW5nIHVzIGJhY2sgaGVyZS5cbiAgICAgICAgYXV0aFJldHJ5SWQgPSB3aW5kb3cuc2V0VGltZW91dChpbnRlcnZhbENhbGxiYWNrICwgNTAwMCk7XG4gICAgICAgIC8vIFNob3cgcHJvbXB0IG9ubHkgaWYgaXQncyBub3Qgb3BlblxuICAgICAgICBpZiAoYXV0aERpYWxvZyAhPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIGV4dHJhY3Qgcm9vbSBuYW1lIGZyb20gJ3Jvb21AbXVjLnNlcnZlci5uZXQnXG4gICAgICAgIHZhciByb29tID0gcm9vbU5hbWUuc3Vic3RyKDAsIHJvb21OYW1lLmluZGV4T2YoJ0AnKSk7XG5cbiAgICAgICAgYXV0aERpYWxvZyA9IG1lc3NhZ2VIYW5kbGVyLm9wZW5EaWFsb2coXG4gICAgICAgICAgICAnU3RvcCcsXG4gICAgICAgICAgICAgICAgJ0F1dGhlbnRpY2F0aW9uIGlzIHJlcXVpcmVkIHRvIGNyZWF0ZSByb29tOjxici8+PGI+JyArIHJvb20gK1xuICAgICAgICAgICAgICAgICc8L2I+PC9icj4gWW91IGNhbiBlaXRoZXIgYXV0aGVudGljYXRlIHRvIGNyZWF0ZSB0aGUgcm9vbSBvciAnICtcbiAgICAgICAgICAgICAgICAnanVzdCB3YWl0IGZvciBzb21lb25lIGVsc2UgdG8gZG8gc28uJyxcbiAgICAgICAgICAgIHRydWUsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgQXV0aGVudGljYXRlOiAnYXV0aE5vdydcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAob25TdWJtaXRFdmVudCwgc3VibWl0VmFsdWUpIHtcblxuICAgICAgICAgICAgICAgIC8vIERvIG5vdCBjbG9zZSB0aGUgZGlhbG9nIHlldFxuICAgICAgICAgICAgICAgIG9uU3VibWl0RXZlbnQucHJldmVudERlZmF1bHQoKTtcblxuICAgICAgICAgICAgICAgIC8vIE9wZW4gbG9naW4gcG9wdXBcbiAgICAgICAgICAgICAgICBpZiAoc3VibWl0VmFsdWUgPT09ICdhdXRoTm93Jykge1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuICAgIGNsb3NlQXV0aGVudGljYXRpb25XaW5kb3c6ZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoYXV0aGVudGljYXRpb25XaW5kb3cpIHtcbiAgICAgICAgICAgIGF1dGhlbnRpY2F0aW9uV2luZG93LmNsb3NlKCk7XG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbldpbmRvdyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGZvY3VzQXV0aGVudGljYXRpb25XaW5kb3c6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gSWYgYXV0aCB3aW5kb3cgZXhpc3RzIGp1c3QgYnJpbmcgaXQgdG8gdGhlIGZyb250XG4gICAgICAgIGlmIChhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgYXV0aGVudGljYXRpb25XaW5kb3cuZm9jdXMoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgY2xvc2VBdXRoZW50aWNhdGlvbkRpYWxvZzogZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBDbG9zZSBhdXRoZW50aWNhdGlvbiBkaWFsb2cgaWYgb3BlbmVkXG4gICAgICAgIGlmIChhdXRoRGlhbG9nKSB7XG4gICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuY2xvc2VEaWFsb2coKTtcbiAgICAgICAgICAgIGF1dGhEaWFsb2cgPSBudWxsO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBjcmVhdGVBdXRoZW50aWNhdGlvbldpbmRvdzogZnVuY3Rpb24gKGNhbGxiYWNrLCB1cmwpIHtcbiAgICAgICAgYXV0aGVudGljYXRpb25XaW5kb3cgPSBtZXNzYWdlSGFuZGxlci5vcGVuQ2VudGVyZWRQb3B1cChcbiAgICAgICAgICAgIHVybCwgOTEwLCA2NjAsXG4gICAgICAgICAgICAvLyBPbiBjbG9zZWRcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAvLyBDbG9zZSBhdXRoZW50aWNhdGlvbiBkaWFsb2cgaWYgb3BlbmVkXG4gICAgICAgICAgICAgICAgaWYgKGF1dGhEaWFsb2cpIHtcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIuY2xvc2VEaWFsb2coKTtcbiAgICAgICAgICAgICAgICAgICAgYXV0aERpYWxvZyA9IG51bGw7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICAgICAgYXV0aGVudGljYXRpb25XaW5kb3cgPSBudWxsO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBhdXRoZW50aWNhdGlvbldpbmRvdztcbiAgICB9LFxuICAgIHN0b3BJbnRlcnZhbDogZnVuY3Rpb24gKCkge1xuICAgICAgICAvLyBDbGVhciByZXRyeSBpbnRlcnZhbCwgc28gdGhhdCB3ZSBkb24ndCBjYWxsICdkb0pvaW5BZnRlckZvY3VzJyB0d2ljZVxuICAgICAgICBpZiAoYXV0aFJldHJ5SWQpIHtcbiAgICAgICAgICAgIHdpbmRvdy5jbGVhclRpbWVvdXQoYXV0aFJldHJ5SWQpO1xuICAgICAgICAgICAgYXV0aFJldHJ5SWQgPSBudWxsO1xuICAgICAgICB9XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBBdXRoZW50aWNhdGlvbjsiLCJ2YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL3NldHRpbmdzL1NldHRpbmdzXCIpO1xudmFyIE1lZGlhU3RyZWFtVHlwZSA9IHJlcXVpcmUoXCIuLi8uLi8uLi9zZXJ2aWNlL1JUQy9NZWRpYVN0cmVhbVR5cGVzXCIpO1xuXG52YXIgdXNlcnMgPSB7fTtcbnZhciBhY3RpdmVTcGVha2VySmlkO1xuXG5mdW5jdGlvbiBzZXRWaXNpYmlsaXR5KHNlbGVjdG9yLCBzaG93KSB7XG4gICAgaWYgKHNlbGVjdG9yICYmIHNlbGVjdG9yLmxlbmd0aCA+IDApIHtcbiAgICAgICAgc2VsZWN0b3IuY3NzKFwidmlzaWJpbGl0eVwiLCBzaG93ID8gXCJ2aXNpYmxlXCIgOiBcImhpZGRlblwiKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGlzVXNlck11dGVkKGppZCkge1xuICAgIC8vIFhYWChncCkgd2UgbWF5IHdhbnQgdG8gcmVuYW1lIHRoaXMgbWV0aG9kIHRvIHNvbWV0aGluZyBsaWtlXG4gICAgLy8gaXNVc2VyU3RyZWFtaW5nLCBmb3IgZXhhbXBsZS5cbiAgICBpZiAoamlkICYmIGppZCAhPSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIGlmICghcmVxdWlyZShcIi4uL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0XCIpLmlzSW5MYXN0TihyZXNvdXJjZSkpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXSB8fCAhQVBQLlJUQy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gQVBQLlJUQy5yZW1vdGVTdHJlYW1zW2ppZF1bTWVkaWFTdHJlYW1UeXBlLlZJREVPX1RZUEVdLm11dGVkO1xufVxuXG5mdW5jdGlvbiBnZXRHcmF2YXRhclVybChpZCwgc2l6ZSkge1xuICAgIGlmKGlkID09PSBBUFAueG1wcC5teUppZCgpIHx8ICFpZCkge1xuICAgICAgICBpZCA9IFNldHRpbmdzLmdldFNldHRpbmdzKCkudWlkO1xuICAgIH1cbiAgICByZXR1cm4gJ2h0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvJyArXG4gICAgICAgIE1ENS5oZXhkaWdlc3QoaWQudHJpbSgpLnRvTG93ZXJDYXNlKCkpICtcbiAgICAgICAgXCI/ZD13YXZhdGFyJnNpemU9XCIgKyAoc2l6ZSB8fCBcIjMwXCIpO1xufVxuXG52YXIgQXZhdGFyID0ge1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgdXNlcidzIGF2YXRhciBpbiB0aGUgc2V0dGluZ3MgbWVudShpZiBsb2NhbCB1c2VyKSwgY29udGFjdCBsaXN0XG4gICAgICogYW5kIHRodW1ibmFpbFxuICAgICAqIEBwYXJhbSBqaWQgamlkIG9mIHRoZSB1c2VyXG4gICAgICogQHBhcmFtIGlkIGVtYWlsIG9yIHVzZXJJRCB0byBiZSB1c2VkIGFzIGEgaGFzaFxuICAgICAqL1xuICAgIHNldFVzZXJBdmF0YXI6IGZ1bmN0aW9uIChqaWQsIGlkKSB7XG4gICAgICAgIGlmIChpZCkge1xuICAgICAgICAgICAgaWYgKHVzZXJzW2ppZF0gPT09IGlkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdXNlcnNbamlkXSA9IGlkO1xuICAgICAgICB9XG4gICAgICAgIHZhciB0aHVtYlVybCA9IGdldEdyYXZhdGFyVXJsKHVzZXJzW2ppZF0gfHwgamlkLCAxMDApO1xuICAgICAgICB2YXIgY29udGFjdExpc3RVcmwgPSBnZXRHcmF2YXRhclVybCh1c2Vyc1tqaWRdIHx8IGppZCk7XG4gICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIHZhciB0aHVtYm5haWwgPSAkKCcjcGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcbiAgICAgICAgdmFyIGF2YXRhciA9ICQoJyNhdmF0YXJfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAvLyBzZXQgdGhlIGF2YXRhciBpbiB0aGUgc2V0dGluZ3MgbWVudSBpZiBpdCBpcyBsb2NhbCB1c2VyIGFuZCBnZXQgdGhlXG4gICAgICAgIC8vIGxvY2FsIHZpZGVvIGNvbnRhaW5lclxuICAgICAgICBpZiAoamlkID09PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAkKCcjYXZhdGFyJykuZ2V0KDApLnNyYyA9IHRodW1iVXJsO1xuICAgICAgICAgICAgdGh1bWJuYWlsID0gJCgnI2xvY2FsVmlkZW9Db250YWluZXInKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNldCB0aGUgYXZhdGFyIGluIHRoZSBjb250YWN0IGxpc3RcbiAgICAgICAgdmFyIGNvbnRhY3QgPSAkKCcjJyArIHJlc291cmNlSmlkICsgJz5pbWcnKTtcbiAgICAgICAgaWYgKGNvbnRhY3QgJiYgY29udGFjdC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb250YWN0LmdldCgwKS5zcmMgPSBjb250YWN0TGlzdFVybDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNldCB0aGUgYXZhdGFyIGluIHRoZSB0aHVtYm5haWxcbiAgICAgICAgaWYgKGF2YXRhciAmJiBhdmF0YXIubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgYXZhdGFyWzBdLnNyYyA9IHRodW1iVXJsO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHRodW1ibmFpbCAmJiB0aHVtYm5haWwubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGF2YXRhciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xuICAgICAgICAgICAgICAgIGF2YXRhci5pZCA9ICdhdmF0YXJfJyArIHJlc291cmNlSmlkO1xuICAgICAgICAgICAgICAgIGF2YXRhci5jbGFzc05hbWUgPSAndXNlckF2YXRhcic7XG4gICAgICAgICAgICAgICAgYXZhdGFyLnNyYyA9IHRodW1iVXJsO1xuICAgICAgICAgICAgICAgIHRodW1ibmFpbC5hcHBlbmQoYXZhdGFyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vaWYgdGhlIHVzZXIgaXMgdGhlIGN1cnJlbnQgYWN0aXZlIHNwZWFrZXIgLSB1cGRhdGUgdGhlIGFjdGl2ZSBzcGVha2VyXG4gICAgICAgIC8vIGF2YXRhclxuICAgICAgICBpZiAoamlkID09PSBhY3RpdmVTcGVha2VySmlkKSB7XG4gICAgICAgICAgICB0aGlzLnVwZGF0ZUFjdGl2ZVNwZWFrZXJBdmF0YXJTcmMoamlkKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyBvciBzaG93cyB0aGUgdXNlcidzIGF2YXRhclxuICAgICAqIEBwYXJhbSBqaWQgamlkIG9mIHRoZSB1c2VyXG4gICAgICogQHBhcmFtIHNob3cgd2hldGhlciB3ZSBzaG91bGQgc2hvdyB0aGUgYXZhdGFyIG9yIG5vdFxuICAgICAqIHZpZGVvIGJlY2F1c2UgdGhlcmUgaXMgbm8gZG9taW5hbnQgc3BlYWtlciBhbmQgbm8gZm9jdXNlZCBzcGVha2VyXG4gICAgICovXG4gICAgc2hvd1VzZXJBdmF0YXI6IGZ1bmN0aW9uIChqaWQsIHNob3cpIHtcbiAgICAgICAgaWYgKHVzZXJzW2ppZF0pIHtcbiAgICAgICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgICAgICB2YXIgdmlkZW8gPSAkKCcjcGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkICsgJz52aWRlbycpO1xuICAgICAgICAgICAgdmFyIGF2YXRhciA9ICQoJyNhdmF0YXJfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgaWYgKGppZCA9PT0gQVBQLnhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgICAgIHZpZGVvID0gJCgnI2xvY2FsVmlkZW9XcmFwcGVyPnZpZGVvJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoc2hvdyA9PT0gdW5kZWZpbmVkIHx8IHNob3cgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBzaG93ID0gaXNVc2VyTXV0ZWQoamlkKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy9pZiB0aGUgdXNlciBpcyB0aGUgY3VycmVudGx5IGZvY3VzZWQsIHRoZSBkb21pbmFudCBzcGVha2VyIG9yIGlmXG4gICAgICAgICAgICAvL3RoZXJlIGlzIG5vIGZvY3VzZWQgYW5kIG5vIGRvbWluYW50IHNwZWFrZXIgYW5kIHRoZSBsYXJnZSB2aWRlbyBpc1xuICAgICAgICAgICAgLy9jdXJyZW50bHkgc2hvd25cbiAgICAgICAgICAgIGlmIChhY3RpdmVTcGVha2VySmlkID09PSBqaWQgJiYgcmVxdWlyZShcIi4uL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0XCIpLmlzTGFyZ2VWaWRlb09uVG9wKCkpIHtcbiAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KCQoXCIjbGFyZ2VWaWRlb1wiKSwgIXNob3cpO1xuICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkoJCgnI2FjdGl2ZVNwZWFrZXInKSwgc2hvdyk7XG4gICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eShhdmF0YXIsIGZhbHNlKTtcbiAgICAgICAgICAgICAgICBzZXRWaXNpYmlsaXR5KHZpZGVvLCBmYWxzZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmICh2aWRlbyAmJiB2aWRlby5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNldFZpc2liaWxpdHkodmlkZW8sICFzaG93KTtcbiAgICAgICAgICAgICAgICAgICAgc2V0VmlzaWJpbGl0eShhdmF0YXIsIHNob3cpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHRoZSBzcmMgb2YgdGhlIGFjdGl2ZSBzcGVha2VyIGF2YXRhclxuICAgICAqIEBwYXJhbSBqaWQgb2YgdGhlIGN1cnJlbnQgYWN0aXZlIHNwZWFrZXJcbiAgICAgKi9cbiAgICB1cGRhdGVBY3RpdmVTcGVha2VyQXZhdGFyU3JjOiBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgIGlmICghamlkKSB7XG4gICAgICAgICAgICBqaWQgPSBBUFAueG1wcC5maW5kSmlkRnJvbVJlc291cmNlKFxuICAgICAgICAgICAgICAgIHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKS5nZXRMYXJnZVZpZGVvU3RhdGUoKS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBhdmF0YXIgPSAkKFwiI2FjdGl2ZVNwZWFrZXJBdmF0YXJcIilbMF07XG4gICAgICAgIHZhciB1cmwgPSBnZXRHcmF2YXRhclVybCh1c2Vyc1tqaWRdLFxuICAgICAgICAgICAgaW50ZXJmYWNlQ29uZmlnLkFDVElWRV9TUEVBS0VSX0FWQVRBUl9TSVpFKTtcbiAgICAgICAgaWYgKGppZCA9PT0gYWN0aXZlU3BlYWtlckppZCAmJiBhdmF0YXIuc3JjID09PSB1cmwpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhY3RpdmVTcGVha2VySmlkID0gamlkO1xuICAgICAgICB2YXIgaXNNdXRlZCA9IGlzVXNlck11dGVkKGppZCk7XG4gICAgICAgIGlmIChqaWQgJiYgaXNNdXRlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgYXZhdGFyLnNyYyA9IHVybDtcbiAgICAgICAgICAgIHNldFZpc2liaWxpdHkoJChcIiNsYXJnZVZpZGVvXCIpLCAhaXNNdXRlZCk7XG4gICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoamlkLCBpc011dGVkKTtcbiAgICAgICAgfVxuICAgIH1cblxufTtcblxuXG5tb2R1bGUuZXhwb3J0cyA9IEF2YXRhcjsiLCIvKiBnbG9iYWwgJCwgY29uZmlnLFxuICAgc2V0TGFyZ2VWaWRlb1Zpc2libGUsIFV0aWwgKi9cblxudmFyIFZpZGVvTGF5b3V0ID0gcmVxdWlyZShcIi4uL3ZpZGVvbGF5b3V0L1ZpZGVvTGF5b3V0XCIpO1xudmFyIFByZXppID0gcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpO1xudmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi91dGlsL1VJVXRpbFwiKTtcblxudmFyIGV0aGVycGFkTmFtZSA9IG51bGw7XG52YXIgZXRoZXJwYWRJRnJhbWUgPSBudWxsO1xudmFyIGRvbWFpbiA9IG51bGw7XG52YXIgb3B0aW9ucyA9IFwiP3Nob3dDb250cm9scz10cnVlJnNob3dDaGF0PWZhbHNlJnNob3dMaW5lTnVtYmVycz10cnVlJnVzZU1vbm9zcGFjZUZvbnQ9ZmFsc2VcIjtcblxuXG4vKipcbiAqIFJlc2l6ZXMgdGhlIGV0aGVycGFkLlxuICovXG5mdW5jdGlvbiByZXNpemUoKSB7XG4gICAgaWYgKCQoJyNldGhlcnBhZD5pZnJhbWUnKS5sZW5ndGgpIHtcbiAgICAgICAgdmFyIHJlbW90ZVZpZGVvcyA9ICQoJyNyZW1vdGVWaWRlb3MnKTtcbiAgICAgICAgdmFyIGF2YWlsYWJsZUhlaWdodFxuICAgICAgICAgICAgPSB3aW5kb3cuaW5uZXJIZWlnaHQgLSByZW1vdGVWaWRlb3Mub3V0ZXJIZWlnaHQoKTtcbiAgICAgICAgdmFyIGF2YWlsYWJsZVdpZHRoID0gVUlVdGlsLmdldEF2YWlsYWJsZVZpZGVvV2lkdGgoKTtcblxuICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykud2lkdGgoYXZhaWxhYmxlV2lkdGgpO1xuICAgICAgICAkKCcjZXRoZXJwYWQ+aWZyYW1lJykuaGVpZ2h0KGF2YWlsYWJsZUhlaWdodCk7XG4gICAgfVxufVxuXG4vKipcbiAqIFNoYXJlcyB0aGUgRXRoZXJwYWQgbmFtZSB3aXRoIG90aGVyIHBhcnRpY2lwYW50cy5cbiAqL1xuZnVuY3Rpb24gc2hhcmVFdGhlcnBhZCgpIHtcbiAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwiZXRoZXJwYWRcIiwgZXRoZXJwYWROYW1lKTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBFdGhlcnBhZCBidXR0b24gYW5kIGFkZHMgaXQgdG8gdGhlIHRvb2xiYXIuXG4gKi9cbmZ1bmN0aW9uIGVuYWJsZUV0aGVycGFkQnV0dG9uKCkge1xuICAgIGlmICghJCgnI2V0aGVycGFkQnV0dG9uJykuaXMoXCI6dmlzaWJsZVwiKSlcbiAgICAgICAgJCgnI2V0aGVycGFkQnV0dG9uJykuY3NzKHtkaXNwbGF5OiAnaW5saW5lLWJsb2NrJ30pO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIElGcmFtZSBmb3IgdGhlIGV0aGVycGFkLlxuICovXG5mdW5jdGlvbiBjcmVhdGVJRnJhbWUoKSB7XG4gICAgZXRoZXJwYWRJRnJhbWUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpZnJhbWUnKTtcbiAgICBldGhlcnBhZElGcmFtZS5zcmMgPSBkb21haW4gKyBldGhlcnBhZE5hbWUgKyBvcHRpb25zO1xuICAgIGV0aGVycGFkSUZyYW1lLmZyYW1lQm9yZGVyID0gMDtcbiAgICBldGhlcnBhZElGcmFtZS5zY3JvbGxpbmcgPSBcIm5vXCI7XG4gICAgZXRoZXJwYWRJRnJhbWUud2lkdGggPSAkKCcjbGFyZ2VWaWRlb0NvbnRhaW5lcicpLndpZHRoKCkgfHwgNjQwO1xuICAgIGV0aGVycGFkSUZyYW1lLmhlaWdodCA9ICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykuaGVpZ2h0KCkgfHwgNDgwO1xuICAgIGV0aGVycGFkSUZyYW1lLnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAndmlzaWJpbGl0eTogaGlkZGVuOycpO1xuXG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2V0aGVycGFkJykuYXBwZW5kQ2hpbGQoZXRoZXJwYWRJRnJhbWUpO1xuXG4gICAgZXRoZXJwYWRJRnJhbWUub25sb2FkID0gZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgZG9jdW1lbnQuZG9tYWluID0gZG9jdW1lbnQuZG9tYWluO1xuICAgICAgICBidWJibGVJZnJhbWVNb3VzZU1vdmUoZXRoZXJwYWRJRnJhbWUpO1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgLy8gdGhlIGlmcmFtZXMgaW5zaWRlIG9mIHRoZSBldGhlcnBhZCBhcmVcbiAgICAgICAgICAgIC8vIG5vdCB5ZXQgbG9hZGVkIHdoZW4gdGhlIGV0aGVycGFkIGlmcmFtZSBpcyBsb2FkZWRcbiAgICAgICAgICAgIHZhciBvdXRlciA9IGV0aGVycGFkSUZyYW1lLlxuICAgICAgICAgICAgICAgIGNvbnRlbnREb2N1bWVudC5nZXRFbGVtZW50c0J5TmFtZShcImFjZV9vdXRlclwiKVswXTtcbiAgICAgICAgICAgIGJ1YmJsZUlmcmFtZU1vdXNlTW92ZShvdXRlcik7XG4gICAgICAgICAgICB2YXIgaW5uZXIgPSBvdXRlci5cbiAgICAgICAgICAgICAgICBjb250ZW50RG9jdW1lbnQuZ2V0RWxlbWVudHNCeU5hbWUoXCJhY2VfaW5uZXJcIilbMF07XG4gICAgICAgICAgICBidWJibGVJZnJhbWVNb3VzZU1vdmUoaW5uZXIpO1xuICAgICAgICB9LCAyMDAwKTtcbiAgICB9O1xufVxuXG5mdW5jdGlvbiBidWJibGVJZnJhbWVNb3VzZU1vdmUoaWZyYW1lKXtcbiAgICB2YXIgZXhpc3RpbmdPbk1vdXNlTW92ZSA9IGlmcmFtZS5jb250ZW50V2luZG93Lm9ubW91c2Vtb3ZlO1xuICAgIGlmcmFtZS5jb250ZW50V2luZG93Lm9ubW91c2Vtb3ZlID0gZnVuY3Rpb24oZSl7XG4gICAgICAgIGlmKGV4aXN0aW5nT25Nb3VzZU1vdmUpIGV4aXN0aW5nT25Nb3VzZU1vdmUoZSk7XG4gICAgICAgIHZhciBldnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudChcIk1vdXNlRXZlbnRzXCIpO1xuICAgICAgICB2YXIgYm91bmRpbmdDbGllbnRSZWN0ID0gaWZyYW1lLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBldnQuaW5pdE1vdXNlRXZlbnQoXG4gICAgICAgICAgICBcIm1vdXNlbW92ZVwiLFxuICAgICAgICAgICAgdHJ1ZSwgLy8gYnViYmxlc1xuICAgICAgICAgICAgZmFsc2UsIC8vIG5vdCBjYW5jZWxhYmxlXG4gICAgICAgICAgICB3aW5kb3csXG4gICAgICAgICAgICBlLmRldGFpbCxcbiAgICAgICAgICAgIGUuc2NyZWVuWCxcbiAgICAgICAgICAgIGUuc2NyZWVuWSxcbiAgICAgICAgICAgICAgICBlLmNsaWVudFggKyBib3VuZGluZ0NsaWVudFJlY3QubGVmdCxcbiAgICAgICAgICAgICAgICBlLmNsaWVudFkgKyBib3VuZGluZ0NsaWVudFJlY3QudG9wLFxuICAgICAgICAgICAgZS5jdHJsS2V5LFxuICAgICAgICAgICAgZS5hbHRLZXksXG4gICAgICAgICAgICBlLnNoaWZ0S2V5LFxuICAgICAgICAgICAgZS5tZXRhS2V5LFxuICAgICAgICAgICAgZS5idXR0b24sXG4gICAgICAgICAgICBudWxsIC8vIG5vIHJlbGF0ZWQgZWxlbWVudFxuICAgICAgICApO1xuICAgICAgICBpZnJhbWUuZGlzcGF0Y2hFdmVudChldnQpO1xuICAgIH07XG59XG5cblxuLyoqXG4gKiBPbiB2aWRlbyBzZWxlY3RlZCBldmVudC5cbiAqL1xuJChkb2N1bWVudCkuYmluZCgndmlkZW8uc2VsZWN0ZWQnLCBmdW5jdGlvbiAoZXZlbnQsIGlzUHJlc2VudGF0aW9uKSB7XG4gICAgaWYgKGNvbmZpZy5ldGhlcnBhZF9iYXNlICYmIGV0aGVycGFkSUZyYW1lICYmIGV0aGVycGFkSUZyYW1lLnN0eWxlLnZpc2liaWxpdHkgIT09ICdoaWRkZW4nKVxuICAgICAgICBFdGhlcnBhZC50b2dnbGVFdGhlcnBhZChpc1ByZXNlbnRhdGlvbik7XG59KTtcblxuXG52YXIgRXRoZXJwYWQgPSB7XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZXMgdGhlIGV0aGVycGFkLlxuICAgICAqL1xuICAgIGluaXQ6IGZ1bmN0aW9uIChuYW1lKSB7XG5cbiAgICAgICAgaWYgKGNvbmZpZy5ldGhlcnBhZF9iYXNlICYmICFldGhlcnBhZE5hbWUpIHtcblxuICAgICAgICAgICAgZG9tYWluID0gY29uZmlnLmV0aGVycGFkX2Jhc2U7XG5cbiAgICAgICAgICAgIGlmICghbmFtZSkge1xuICAgICAgICAgICAgICAgIC8vIEluIGNhc2Ugd2UncmUgdGhlIGZvY3VzIHdlIGdlbmVyYXRlIHRoZSBuYW1lLlxuICAgICAgICAgICAgICAgIGV0aGVycGFkTmFtZSA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZyg3KSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdfJyArIChuZXcgRGF0ZSgpLmdldFRpbWUoKSkudG9TdHJpbmcoKTtcbiAgICAgICAgICAgICAgICBzaGFyZUV0aGVycGFkKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgZXRoZXJwYWROYW1lID0gbmFtZTtcblxuICAgICAgICAgICAgZW5hYmxlRXRoZXJwYWRCdXR0b24oKTtcblxuICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiBSZXNpemVzIHRoZSBldGhlcnBhZCwgd2hlbiB0aGUgd2luZG93IGlzIHJlc2l6ZWQuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICQod2luZG93KS5yZXNpemUoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJlc2l6ZSgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogT3BlbnMvaGlkZXMgdGhlIEV0aGVycGFkLlxuICAgICAqL1xuICAgIHRvZ2dsZUV0aGVycGFkOiBmdW5jdGlvbiAoaXNQcmVzZW50YXRpb24pIHtcbiAgICAgICAgaWYgKCFldGhlcnBhZElGcmFtZSlcbiAgICAgICAgICAgIGNyZWF0ZUlGcmFtZSgpO1xuXG4gICAgICAgIHZhciBsYXJnZVZpZGVvID0gbnVsbDtcbiAgICAgICAgaWYgKFByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKVxuICAgICAgICAgICAgbGFyZ2VWaWRlbyA9ICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJyk7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGxhcmdlVmlkZW8gPSAkKCcjbGFyZ2VWaWRlbycpO1xuXG4gICAgICAgIGlmICgkKCcjZXRoZXJwYWQ+aWZyYW1lJykuY3NzKCd2aXNpYmlsaXR5JykgPT09ICdoaWRkZW4nKSB7XG4gICAgICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcbiAgICAgICAgICAgIGxhcmdlVmlkZW8uZmFkZU91dCgzMDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAoUHJlemkuaXNQcmVzZW50YXRpb25WaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlby5jc3Moe29wYWNpdHk6ICcwJ30pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldExhcmdlVmlkZW9WaXNpYmxlKGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmZhZGVJbigzMDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLmJhY2tncm91bmQgPSAnI2VlZWVlZSc7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkJykuY3NzKHt6SW5kZXg6IDJ9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKCQoJyNldGhlcnBhZD5pZnJhbWUnKSkge1xuICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmZhZGVPdXQoMzAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgJCgnI2V0aGVycGFkPmlmcmFtZScpLmNzcyh7dmlzaWJpbGl0eTogJ2hpZGRlbid9KTtcbiAgICAgICAgICAgICAgICAkKCcjZXRoZXJwYWQnKS5jc3Moe3pJbmRleDogMH0pO1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuYmFja2dyb3VuZCA9ICdibGFjayc7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgaWYgKCFpc1ByZXNlbnRhdGlvbikge1xuICAgICAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZUluKDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zZXRMYXJnZVZpZGVvVmlzaWJsZSh0cnVlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXNpemUoKTtcbiAgICB9LFxuXG4gICAgaXNWaXNpYmxlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGV0aGVycGFkSWZyYW1lID0gJCgnI2V0aGVycGFkPmlmcmFtZScpO1xuICAgICAgICByZXR1cm4gZXRoZXJwYWRJZnJhbWUgJiYgZXRoZXJwYWRJZnJhbWUuaXMoJzp2aXNpYmxlJyk7XG4gICAgfVxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEV0aGVycGFkO1xuIiwidmFyIFRvb2xiYXJUb2dnbGVyID0gcmVxdWlyZShcIi4uL3Rvb2xiYXJzL1Rvb2xiYXJUb2dnbGVyXCIpO1xudmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi91dGlsL1VJVXRpbFwiKTtcbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKTtcbnZhciBtZXNzYWdlSGFuZGxlciA9IHJlcXVpcmUoXCIuLi91dGlsL01lc3NhZ2VIYW5kbGVyXCIpO1xudmFyIFByZXppUGxheWVyID0gcmVxdWlyZShcIi4vUHJlemlQbGF5ZXJcIik7XG5cbnZhciBwcmV6aVBsYXllciA9IG51bGw7XG5cbnZhciBQcmV6aSA9IHtcblxuXG4gICAgLyoqXG4gICAgICogUmVsb2FkcyB0aGUgY3VycmVudCBwcmVzZW50YXRpb24uXG4gICAgICovXG4gICAgcmVsb2FkUHJlc2VudGF0aW9uOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIGlmcmFtZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHByZXppUGxheWVyLm9wdGlvbnMucHJlemlJZCk7XG4gICAgICAgIGlmcmFtZS5zcmMgPSBpZnJhbWUuc3JjO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIDx0dD50cnVlPC90dD4gaWYgdGhlIHByZXNlbnRhdGlvbiBpcyB2aXNpYmxlLCA8dHQ+ZmFsc2U8L3R0PiAtXG4gICAgICogb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIGlzUHJlc2VudGF0aW9uVmlzaWJsZTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gKCQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykgIT0gbnVsbFxuICAgICAgICAgICAgICAgICYmICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykuY3NzKCdvcGFjaXR5JykgPT0gMSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIHRoZSBQcmV6aSBkaWFsb2csIGZyb20gd2hpY2ggdGhlIHVzZXIgY291bGQgY2hvb3NlIGEgcHJlc2VudGF0aW9uXG4gICAgICogdG8gbG9hZC5cbiAgICAgKi9cbiAgICBvcGVuUHJlemlEaWFsb2c6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgbXlwcmV6aSA9IEFQUC54bXBwLmdldFByZXppKCk7XG4gICAgICAgIGlmIChteXByZXppKSB7XG4gICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKFwiUmVtb3ZlIFByZXppXCIsXG4gICAgICAgICAgICAgICAgXCJBcmUgeW91IHN1cmUgeW91IHdvdWxkIGxpa2UgdG8gcmVtb3ZlIHlvdXIgUHJlemk/XCIsXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgXCJSZW1vdmVcIixcbiAgICAgICAgICAgICAgICBmdW5jdGlvbihlLHYsbSxmKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC54bXBwLnJlbW92ZVByZXppRnJvbVByZXNlbmNlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKHByZXppUGxheWVyICE9IG51bGwpIHtcbiAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXCJTaGFyZSBhIFByZXppXCIsXG4gICAgICAgICAgICAgICAgXCJBbm90aGVyIHBhcnRpY2lwYW50IGlzIGFscmVhZHkgc2hhcmluZyBhIFByZXppLlwiICtcbiAgICAgICAgICAgICAgICAgICAgXCJUaGlzIGNvbmZlcmVuY2UgYWxsb3dzIG9ubHkgb25lIFByZXppIGF0IGEgdGltZS5cIixcbiAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICBcIk9rXCIsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24oZSx2LG0sZikge1xuICAgICAgICAgICAgICAgICAgICAkLnByb21wdC5jbG9zZSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB2YXIgb3BlblByZXppU3RhdGUgPSB7XG4gICAgICAgICAgICAgICAgc3RhdGUwOiB7XG4gICAgICAgICAgICAgICAgICAgIGh0bWw6ICAgJzxoMj5TaGFyZSBhIFByZXppPC9oMj4nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwicHJlemlVcmxcIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwbGFjZWhvbGRlcj1cImUuZy4gJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2h0dHA6Ly9wcmV6aS5jb20vd3o3dmhqeWNsN2U2L215LXByZXppXCIgYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICAgICAgICAgIHBlcnNpc3RlbnQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBidXR0b25zOiB7IFwiU2hhcmVcIjogdHJ1ZSAsIFwiQ2FuY2VsXCI6IGZhbHNlfSxcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMSxcbiAgICAgICAgICAgICAgICAgICAgc3VibWl0OiBmdW5jdGlvbihlLHYsbSxmKXtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHYpXG4gICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHByZXppVXJsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ByZXppVXJsJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocHJlemlVcmwudmFsdWUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgdXJsVmFsdWVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gZW5jb2RlVVJJKFVJVXRpbC5lc2NhcGVIdG1sKHByZXppVXJsLnZhbHVlKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHVybFZhbHVlLmluZGV4T2YoJ2h0dHA6Ly9wcmV6aS5jb20vJykgIT0gMFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJiYgdXJsVmFsdWUuaW5kZXhPZignaHR0cHM6Ly9wcmV6aS5jb20vJykgIT0gMClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuZ29Ub1N0YXRlKCdzdGF0ZTEnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBwcmVzSWRUbXAgPSB1cmxWYWx1ZS5zdWJzdHJpbmcoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybFZhbHVlLmluZGV4T2YoXCJwcmV6aS5jb20vXCIpICsgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpc0FscGhhbnVtZXJpYyhwcmVzSWRUbXApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8IHByZXNJZFRtcC5pbmRleE9mKCcvJykgPCAyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuZ29Ub1N0YXRlKCdzdGF0ZTEnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwicHJlemlcIiwgdXJsVmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQucHJvbXB0LmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgc3RhdGUxOiB7XG4gICAgICAgICAgICAgICAgICAgIGh0bWw6ICAgJzxoMj5TaGFyZSBhIFByZXppPC9oMj4nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGxlYXNlIHByb3ZpZGUgYSBjb3JyZWN0IHByZXppIGxpbmsuJyxcbiAgICAgICAgICAgICAgICAgICAgcGVyc2lzdGVudDogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGJ1dHRvbnM6IHsgXCJCYWNrXCI6IHRydWUsIFwiQ2FuY2VsXCI6IGZhbHNlIH0sXG4gICAgICAgICAgICAgICAgICAgIGRlZmF1bHRCdXR0b246IDEsXG4gICAgICAgICAgICAgICAgICAgIHN1Ym1pdDpmdW5jdGlvbihlLHYsbSxmKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZih2PT0wKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICQucHJvbXB0LmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5wcm9tcHQuZ29Ub1N0YXRlKCdzdGF0ZTAnKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB2YXIgZm9jdXNQcmV6aVVybCA9ICBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwcmV6aVVybCcpLmZvY3VzKCk7XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5EaWFsb2dXaXRoU3RhdGVzKG9wZW5QcmV6aVN0YXRlLCBmb2N1c1ByZXppVXJsLCBmb2N1c1ByZXppVXJsKTtcbiAgICAgICAgfVxuICAgIH1cblxufTtcblxuLyoqXG4gKiBBIG5ldyBwcmVzZW50YXRpb24gaGFzIGJlZW4gYWRkZWQuXG4gKlxuICogQHBhcmFtIGV2ZW50IHRoZSBldmVudCBpbmRpY2F0aW5nIHRoZSBhZGQgb2YgYSBwcmVzZW50YXRpb25cbiAqIEBwYXJhbSBqaWQgdGhlIGppZCBmcm9tIHdoaWNoIHRoZSBwcmVzZW50YXRpb24gd2FzIGFkZGVkXG4gKiBAcGFyYW0gcHJlc1VybCB1cmwgb2YgdGhlIHByZXNlbnRhdGlvblxuICogQHBhcmFtIGN1cnJlbnRTbGlkZSB0aGUgY3VycmVudCBzbGlkZSB0byB3aGljaCB3ZSBzaG91bGQgbW92ZVxuICovXG5mdW5jdGlvbiBwcmVzZW50YXRpb25BZGRlZChldmVudCwgamlkLCBwcmVzVXJsLCBjdXJyZW50U2xpZGUpIHtcbiAgICBjb25zb2xlLmxvZyhcInByZXNlbnRhdGlvbiBhZGRlZFwiLCBwcmVzVXJsKTtcblxuICAgIHZhciBwcmVzSWQgPSBnZXRQcmVzZW50YXRpb25JZChwcmVzVXJsKTtcblxuICAgIHZhciBlbGVtZW50SWQgPSAncGFydGljaXBhbnRfJ1xuICAgICAgICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZClcbiAgICAgICAgKyAnXycgKyBwcmVzSWQ7XG5cbiAgICAvLyBXZSBleHBsaWNpdGx5IGRvbid0IHNwZWNpZnkgdGhlIHBlZXIgamlkIGhlcmUsIGJlY2F1c2Ugd2UgZG9uJ3Qgd2FudFxuICAgIC8vIHRoaXMgdmlkZW8gdG8gYmUgZGVhbHQgd2l0aCBhcyBhIHBlZXIgcmVsYXRlZCBvbmUgKGZvciBleGFtcGxlIHdlXG4gICAgLy8gZG9uJ3Qgd2FudCB0byBzaG93IGEgbXV0ZS9raWNrIG1lbnUgZm9yIHRoaXMgb25lLCBldGMuKS5cbiAgICBWaWRlb0xheW91dC5hZGRSZW1vdGVWaWRlb0NvbnRhaW5lcihudWxsLCBlbGVtZW50SWQpO1xuICAgIFZpZGVvTGF5b3V0LnJlc2l6ZVRodW1ibmFpbHMoKTtcblxuICAgIHZhciBjb250cm9sc0VuYWJsZWQgPSBmYWxzZTtcbiAgICBpZiAoamlkID09PSBBUFAueG1wcC5teUppZCgpKVxuICAgICAgICBjb250cm9sc0VuYWJsZWQgPSB0cnVlO1xuXG4gICAgc2V0UHJlc2VudGF0aW9uVmlzaWJsZSh0cnVlKTtcbiAgICAkKCcjbGFyZ2VWaWRlb0NvbnRhaW5lcicpLmhvdmVyKFxuICAgICAgICBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChQcmV6aS5pc1ByZXNlbnRhdGlvblZpc2libGUoKSkge1xuICAgICAgICAgICAgICAgIHZhciByZWxvYWRCdXR0b25SaWdodCA9IHdpbmRvdy5pbm5lcldpZHRoXG4gICAgICAgICAgICAgICAgICAgIC0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5vZmZzZXQoKS5sZWZ0XG4gICAgICAgICAgICAgICAgICAgIC0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS53aWR0aCgpO1xuXG4gICAgICAgICAgICAgICAgJCgnI3JlbG9hZFByZXNlbnRhdGlvbicpLmNzcyh7ICByaWdodDogcmVsb2FkQnV0dG9uUmlnaHQsXG4gICAgICAgICAgICAgICAgICAgIGRpc3BsYXk6J2lubGluZS1ibG9jayd9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoIVByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKVxuICAgICAgICAgICAgICAgICQoJyNyZWxvYWRQcmVzZW50YXRpb24nKS5jc3Moe2Rpc3BsYXk6J25vbmUnfSk7XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB2YXIgZSA9IGV2ZW50LnRvRWxlbWVudCB8fCBldmVudC5yZWxhdGVkVGFyZ2V0O1xuXG4gICAgICAgICAgICAgICAgaWYgKGUgJiYgZS5pZCAhPSAncmVsb2FkUHJlc2VudGF0aW9uJyAmJiBlLmlkICE9ICdoZWFkZXInKVxuICAgICAgICAgICAgICAgICAgICAkKCcjcmVsb2FkUHJlc2VudGF0aW9uJykuY3NzKHtkaXNwbGF5Oidub25lJ30pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgIHByZXppUGxheWVyID0gbmV3IFByZXppUGxheWVyKFxuICAgICAgICAncHJlc2VudGF0aW9uJyxcbiAgICAgICAge3ByZXppSWQ6IHByZXNJZCxcbiAgICAgICAgICAgIHdpZHRoOiBnZXRQcmVzZW50YXRpb25XaWR0aCgpLFxuICAgICAgICAgICAgaGVpZ2h0OiBnZXRQcmVzZW50YXRpb25IZWloZ3QoKSxcbiAgICAgICAgICAgIGNvbnRyb2xzOiBjb250cm9sc0VuYWJsZWQsXG4gICAgICAgICAgICBkZWJ1ZzogdHJ1ZVxuICAgICAgICB9KTtcblxuICAgICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykuYXR0cignaWQnLCBwcmV6aVBsYXllci5vcHRpb25zLnByZXppSWQpO1xuXG4gICAgcHJlemlQbGF5ZXIub24oUHJlemlQbGF5ZXIuRVZFTlRfU1RBVFVTLCBmdW5jdGlvbihldmVudCkge1xuICAgICAgICBjb25zb2xlLmxvZyhcInByZXppIHN0YXR1c1wiLCBldmVudC52YWx1ZSk7XG4gICAgICAgIGlmIChldmVudC52YWx1ZSA9PSBQcmV6aVBsYXllci5TVEFUVVNfQ09OVEVOVF9SRUFEWSkge1xuICAgICAgICAgICAgaWYgKGppZCAhPSBBUFAueG1wcC5teUppZCgpKVxuICAgICAgICAgICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50U2xpZGUpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICBwcmV6aVBsYXllci5vbihQcmV6aVBsYXllci5FVkVOVF9DVVJSRU5UX1NURVAsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiZXZlbnQgdmFsdWVcIiwgZXZlbnQudmFsdWUpO1xuICAgICAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwicHJlemlTbGlkZVwiLCBldmVudC52YWx1ZSk7XG4gICAgfSk7XG5cbiAgICAkKFwiI1wiICsgZWxlbWVudElkKS5jc3MoICdiYWNrZ3JvdW5kLWltYWdlJyxcbiAgICAgICAgJ3VybCguLi9pbWFnZXMvYXZhdGFycHJlemkucG5nKScpO1xuICAgICQoXCIjXCIgKyBlbGVtZW50SWQpLmNsaWNrKFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZXRQcmVzZW50YXRpb25WaXNpYmxlKHRydWUpO1xuICAgICAgICB9XG4gICAgKTtcbn07XG5cbi8qKlxuICogQSBwcmVzZW50YXRpb24gaGFzIGJlZW4gcmVtb3ZlZC5cbiAqXG4gKiBAcGFyYW0gZXZlbnQgdGhlIGV2ZW50IGluZGljYXRpbmcgdGhlIHJlbW92ZSBvZiBhIHByZXNlbnRhdGlvblxuICogQHBhcmFtIGppZCB0aGUgamlkIGZvciB3aGljaCB0aGUgcHJlc2VudGF0aW9uIHdhcyByZW1vdmVkXG4gKiBAcGFyYW0gdGhlIHVybCBvZiB0aGUgcHJlc2VudGF0aW9uXG4gKi9cbmZ1bmN0aW9uIHByZXNlbnRhdGlvblJlbW92ZWQoZXZlbnQsIGppZCwgcHJlc1VybCkge1xuICAgIGNvbnNvbGUubG9nKCdwcmVzZW50YXRpb24gcmVtb3ZlZCcsIHByZXNVcmwpO1xuICAgIHZhciBwcmVzSWQgPSBnZXRQcmVzZW50YXRpb25JZChwcmVzVXJsKTtcbiAgICBzZXRQcmVzZW50YXRpb25WaXNpYmxlKGZhbHNlKTtcbiAgICAkKCcjcGFydGljaXBhbnRfJ1xuICAgICAgICArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZClcbiAgICAgICAgKyAnXycgKyBwcmVzSWQpLnJlbW92ZSgpO1xuICAgICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykucmVtb3ZlKCk7XG4gICAgaWYgKHByZXppUGxheWVyICE9IG51bGwpIHtcbiAgICAgICAgcHJlemlQbGF5ZXIuZGVzdHJveSgpO1xuICAgICAgICBwcmV6aVBsYXllciA9IG51bGw7XG4gICAgfVxufTtcblxuLyoqXG4gKiBJbmRpY2F0ZXMgaWYgdGhlIGdpdmVuIHN0cmluZyBpcyBhbiBhbHBoYW51bWVyaWMgc3RyaW5nLlxuICogTm90ZSB0aGF0IHNvbWUgc3BlY2lhbCBjaGFyYWN0ZXJzIGFyZSBhbHNvIGFsbG93ZWQgKC0sIF8gLCAvLCAmLCA/LCA9LCA7KSBmb3IgdGhlXG4gKiBwdXJwb3NlIG9mIGNoZWNraW5nIFVSSXMuXG4gKi9cbmZ1bmN0aW9uIGlzQWxwaGFudW1lcmljKHVuc2FmZVRleHQpIHtcbiAgICB2YXIgcmVnZXggPSAvXlthLXowLTktX1xcLyZcXD89O10rJC9pO1xuICAgIHJldHVybiByZWdleC50ZXN0KHVuc2FmZVRleHQpO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHByZXNlbnRhdGlvbiBpZCBmcm9tIHRoZSBnaXZlbiB1cmwuXG4gKi9cbmZ1bmN0aW9uIGdldFByZXNlbnRhdGlvbklkIChwcmVzVXJsKSB7XG4gICAgdmFyIHByZXNJZFRtcCA9IHByZXNVcmwuc3Vic3RyaW5nKHByZXNVcmwuaW5kZXhPZihcInByZXppLmNvbS9cIikgKyAxMCk7XG4gICAgcmV0dXJuIHByZXNJZFRtcC5zdWJzdHJpbmcoMCwgcHJlc0lkVG1wLmluZGV4T2YoJy8nKSk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgcHJlc2VudGF0aW9uIHdpZHRoLlxuICovXG5mdW5jdGlvbiBnZXRQcmVzZW50YXRpb25XaWR0aCgpIHtcbiAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBVSVV0aWwuZ2V0QXZhaWxhYmxlVmlkZW9XaWR0aCgpO1xuICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSBnZXRQcmVzZW50YXRpb25IZWloZ3QoKTtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IDE2LjAgLyA5LjA7XG4gICAgaWYgKGF2YWlsYWJsZUhlaWdodCA8IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW8pIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBNYXRoLmZsb29yKGF2YWlsYWJsZUhlaWdodCAqIGFzcGVjdFJhdGlvKTtcbiAgICB9XG4gICAgcmV0dXJuIGF2YWlsYWJsZVdpZHRoO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHByZXNlbnRhdGlvbiBoZWlnaHQuXG4gKi9cbmZ1bmN0aW9uIGdldFByZXNlbnRhdGlvbkhlaWhndCgpIHtcbiAgICB2YXIgcmVtb3RlVmlkZW9zID0gJCgnI3JlbW90ZVZpZGVvcycpO1xuICAgIHJldHVybiB3aW5kb3cuaW5uZXJIZWlnaHQgLSByZW1vdGVWaWRlb3Mub3V0ZXJIZWlnaHQoKTtcbn1cblxuLyoqXG4gKiBSZXNpemVzIHRoZSBwcmVzZW50YXRpb24gaWZyYW1lLlxuICovXG5mdW5jdGlvbiByZXNpemUoKSB7XG4gICAgaWYgKCQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykpIHtcbiAgICAgICAgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS53aWR0aChnZXRQcmVzZW50YXRpb25XaWR0aCgpKTtcbiAgICAgICAgJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKS5oZWlnaHQoZ2V0UHJlc2VudGF0aW9uSGVpaGd0KCkpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBTaG93cy9oaWRlcyBhIHByZXNlbnRhdGlvbi5cbiAqL1xuZnVuY3Rpb24gc2V0UHJlc2VudGF0aW9uVmlzaWJsZSh2aXNpYmxlKSB7XG4gICAgdmFyIHByZXppID0gJCgnI3ByZXNlbnRhdGlvbj5pZnJhbWUnKTtcbiAgICBpZiAodmlzaWJsZSkge1xuICAgICAgICAvLyBUcmlnZ2VyIHRoZSB2aWRlby5zZWxlY3RlZCBldmVudCB0byBpbmRpY2F0ZSBhIGNoYW5nZSBpbiB0aGVcbiAgICAgICAgLy8gbGFyZ2UgdmlkZW8uXG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoXCJ2aWRlby5zZWxlY3RlZFwiLCBbdHJ1ZV0pO1xuXG4gICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZU91dCgzMDApO1xuICAgICAgICBwcmV6aS5mYWRlSW4oMzAwLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHByZXppLmNzcyh7b3BhY2l0eTonMSd9KTtcbiAgICAgICAgICAgIFRvb2xiYXJUb2dnbGVyLmRvY2tUb29sYmFyKHRydWUpO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2V0TGFyZ2VWaWRlb1Zpc2libGUoZmFsc2UpO1xuICAgICAgICB9KTtcbiAgICAgICAgJCgnI2FjdGl2ZVNwZWFrZXInKS5jc3MoJ3Zpc2liaWxpdHknLCAnaGlkZGVuJyk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBpZiAocHJlemkuY3NzKCdvcGFjaXR5JykgPT0gJzEnKSB7XG4gICAgICAgICAgICBwcmV6aS5mYWRlT3V0KDMwMCwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHByZXppLmNzcyh7b3BhY2l0eTonMCd9KTtcbiAgICAgICAgICAgICAgICAkKCcjcmVsb2FkUHJlc2VudGF0aW9uJykuY3NzKHtkaXNwbGF5Oidub25lJ30pO1xuICAgICAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuZmFkZUluKDMwMCwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNldExhcmdlVmlkZW9WaXNpYmxlKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcihmYWxzZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBQcmVzZW50YXRpb24gaGFzIGJlZW4gcmVtb3ZlZC5cbiAqL1xuJChkb2N1bWVudCkuYmluZCgncHJlc2VudGF0aW9ucmVtb3ZlZC5tdWMnLCBwcmVzZW50YXRpb25SZW1vdmVkKTtcblxuLyoqXG4gKiBQcmVzZW50YXRpb24gaGFzIGJlZW4gYWRkZWQuXG4gKi9cbiQoZG9jdW1lbnQpLmJpbmQoJ3ByZXNlbnRhdGlvbmFkZGVkLm11YycsIHByZXNlbnRhdGlvbkFkZGVkKTtcblxuLypcbiAqIEluZGljYXRlcyBwcmVzZW50YXRpb24gc2xpZGUgY2hhbmdlLlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCdnb3Rvc2xpZGUubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIHByZXNVcmwsIGN1cnJlbnQpIHtcbiAgICBpZiAocHJlemlQbGF5ZXIgJiYgcHJlemlQbGF5ZXIuZ2V0Q3VycmVudFN0ZXAoKSAhPSBjdXJyZW50KSB7XG4gICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50KTtcblxuICAgICAgICB2YXIgYW5pbWF0aW9uU3RlcHNBcnJheSA9IHByZXppUGxheWVyLmdldEFuaW1hdGlvbkNvdW50T25TdGVwcygpO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHBhcnNlSW50KGFuaW1hdGlvblN0ZXBzQXJyYXlbY3VycmVudF0pOyBpKyspIHtcbiAgICAgICAgICAgIHByZXppUGxheWVyLmZseVRvU3RlcChjdXJyZW50LCBpKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG4vKipcbiAqIE9uIHZpZGVvIHNlbGVjdGVkIGV2ZW50LlxuICovXG4kKGRvY3VtZW50KS5iaW5kKCd2aWRlby5zZWxlY3RlZCcsIGZ1bmN0aW9uIChldmVudCwgaXNQcmVzZW50YXRpb24pIHtcbiAgICBpZiAoIWlzUHJlc2VudGF0aW9uICYmICQoJyNwcmVzZW50YXRpb24+aWZyYW1lJykpIHtcbiAgICAgICAgc2V0UHJlc2VudGF0aW9uVmlzaWJsZShmYWxzZSk7XG4gICAgfVxufSk7XG5cbiQod2luZG93KS5yZXNpemUoZnVuY3Rpb24gKCkge1xuICAgIHJlc2l6ZSgpO1xufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gUHJlemk7XG4iLCIoZnVuY3Rpb24oKSB7XG4gICAgXCJ1c2Ugc3RyaWN0XCI7XG4gICAgdmFyIF9fYmluZCA9IGZ1bmN0aW9uKGZuLCBtZSl7IHJldHVybiBmdW5jdGlvbigpeyByZXR1cm4gZm4uYXBwbHkobWUsIGFyZ3VtZW50cyk7IH07IH07XG5cbiAgICB3aW5kb3cuUHJlemlQbGF5ZXIgPSAoZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgUHJlemlQbGF5ZXIuQVBJX1ZFUlNJT04gPSAxO1xuICAgICAgICBQcmV6aVBsYXllci5DVVJSRU5UX1NURVAgPSAnY3VycmVudFN0ZXAnO1xuICAgICAgICBQcmV6aVBsYXllci5DVVJSRU5UX0FOSU1BVElPTl9TVEVQID0gJ2N1cnJlbnRBbmltYXRpb25TdGVwJztcbiAgICAgICAgUHJlemlQbGF5ZXIuQ1VSUkVOVF9PQkpFQ1QgPSAnY3VycmVudE9iamVjdCc7XG4gICAgICAgIFByZXppUGxheWVyLlNUQVRVU19MT0FESU5HID0gJ2xvYWRpbmcnO1xuICAgICAgICBQcmV6aVBsYXllci5TVEFUVVNfUkVBRFkgPSAncmVhZHknO1xuICAgICAgICBQcmV6aVBsYXllci5TVEFUVVNfQ09OVEVOVF9SRUFEWSA9ICdjb250ZW50cmVhZHknO1xuICAgICAgICBQcmV6aVBsYXllci5FVkVOVF9DVVJSRU5UX1NURVAgPSBcImN1cnJlbnRTdGVwQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX0NVUlJFTlRfQU5JTUFUSU9OX1NURVAgPSBcImN1cnJlbnRBbmltYXRpb25TdGVwQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX0NVUlJFTlRfT0JKRUNUID0gXCJjdXJyZW50T2JqZWN0Q2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX1NUQVRVUyA9IFwic3RhdHVzQ2hhbmdlXCI7XG4gICAgICAgIFByZXppUGxheWVyLkVWRU5UX1BMQVlJTkcgPSBcImlzQXV0b1BsYXlpbmdDaGFuZ2VcIjtcbiAgICAgICAgUHJlemlQbGF5ZXIuRVZFTlRfSVNfTU9WSU5HID0gXCJpc01vdmluZ0NoYW5nZVwiO1xuICAgICAgICBQcmV6aVBsYXllci5kb21haW4gPSBcImh0dHBzOi8vcHJlemkuY29tXCI7XG4gICAgICAgIFByZXppUGxheWVyLnBhdGggPSBcIi9wbGF5ZXIvXCI7XG4gICAgICAgIFByZXppUGxheWVyLnBsYXllcnMgPSB7fTtcbiAgICAgICAgUHJlemlQbGF5ZXIuYmluZGVkX21ldGhvZHMgPSBbJ2NoYW5nZXNIYW5kbGVyJ107XG5cbiAgICAgICAgUHJlemlQbGF5ZXIuY3JlYXRlTXVsdGlwbGVQbGF5ZXJzID0gZnVuY3Rpb24ob3B0aW9uQXJyYXkpe1xuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8b3B0aW9uQXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgb3B0aW9uU2V0ID0gb3B0aW9uQXJyYXlbaV07XG4gICAgICAgICAgICAgICAgbmV3IFByZXppUGxheWVyKG9wdGlvblNldC5pZCwgb3B0aW9uU2V0KTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIubWVzc2FnZVJlY2VpdmVkID0gZnVuY3Rpb24oZXZlbnQpe1xuICAgICAgICAgICAgdmFyIG1lc3NhZ2UsIGl0ZW0sIHBsYXllcjtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZSA9IEpTT04ucGFyc2UoZXZlbnQuZGF0YSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7fVxuICAgICAgICAgICAgaWYgKG1lc3NhZ2UuaWQgJiYgKHBsYXllciA9IFByZXppUGxheWVyLnBsYXllcnNbbWVzc2FnZS5pZF0pKXtcbiAgICAgICAgICAgICAgICBpZiAocGxheWVyLm9wdGlvbnMuZGVidWcgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbnNvbGUgJiYgY29uc29sZS5sb2cpIGNvbnNvbGUubG9nKCdyZWNlaXZlZCcsIG1lc3NhZ2UpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAobWVzc2FnZS50eXBlID09PSBcImNoYW5nZXNcIil7XG4gICAgICAgICAgICAgICAgICAgIHBsYXllci5jaGFuZ2VzSGFuZGxlcihtZXNzYWdlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaT0wOyBpPHBsYXllci5jYWxsYmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaXRlbSA9IHBsYXllci5jYWxsYmFja3NbaV07XG4gICAgICAgICAgICAgICAgICAgIGlmIChpdGVtICYmIG1lc3NhZ2UudHlwZSA9PT0gaXRlbS5ldmVudCl7XG4gICAgICAgICAgICAgICAgICAgICAgICBpdGVtLmNhbGxiYWNrKG1lc3NhZ2UpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGZ1bmN0aW9uIFByZXppUGxheWVyKGlkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGFyYW1zLCBwYXJhbVN0cmluZyA9IFwiXCIsIF90aGlzID0gdGhpcztcbiAgICAgICAgICAgIGlmIChQcmV6aVBsYXllci5wbGF5ZXJzW2lkXSl7XG4gICAgICAgICAgICAgICAgUHJlemlQbGF5ZXIucGxheWVyc1tpZF0uZGVzdHJveSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8UHJlemlQbGF5ZXIuYmluZGVkX21ldGhvZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgbWV0aG9kX25hbWUgPSBQcmV6aVBsYXllci5iaW5kZWRfbWV0aG9kc1tpXTtcbiAgICAgICAgICAgICAgICBfdGhpc1ttZXRob2RfbmFtZV0gPSBfX2JpbmQoX3RoaXNbbWV0aG9kX25hbWVdLCBfdGhpcyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXMgPSB7J3N0YXR1cyc6IFByZXppUGxheWVyLlNUQVRVU19MT0FESU5HfTtcbiAgICAgICAgICAgIHRoaXMudmFsdWVzW1ByZXppUGxheWVyLkNVUlJFTlRfU1RFUF0gPSAwO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXNbUHJlemlQbGF5ZXIuQ1VSUkVOVF9BTklNQVRJT05fU1RFUF0gPSAwO1xuICAgICAgICAgICAgdGhpcy52YWx1ZXNbUHJlemlQbGF5ZXIuQ1VSUkVOVF9PQkpFQ1RdID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG4gICAgICAgICAgICB0aGlzLmlkID0gaWQ7XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG4gICAgICAgICAgICBpZiAoIXRoaXMuZW1iZWRUbykge1xuICAgICAgICAgICAgICAgIHRocm93IFwiVGhlIGVsZW1lbnQgaWQgaXMgbm90IGF2YWlsYWJsZS5cIjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuaWZyYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaWZyYW1lJyk7XG4gICAgICAgICAgICBwYXJhbXMgPSBbXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnb2lkJywgdmFsdWU6IG9wdGlvbnMucHJlemlJZCB9LFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2V4cGxvcmFibGUnLCB2YWx1ZTogb3B0aW9ucy5leHBsb3JhYmxlID8gMSA6IDAgfSxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjb250cm9scycsIHZhbHVlOiBvcHRpb25zLmNvbnRyb2xzID8gMSA6IDAgfVxuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIGZvcih2YXIgaT0wOyBpPHBhcmFtcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBwYXJhbSA9IHBhcmFtc1tpXTtcbiAgICAgICAgICAgICAgICBwYXJhbVN0cmluZyArPSAoaT09PTAgPyBcIj9cIiA6IFwiJlwiKSArIHBhcmFtLm5hbWUgKyBcIj1cIiArIHBhcmFtLnZhbHVlO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLnNyYyA9IFByZXppUGxheWVyLmRvbWFpbiArIFByZXppUGxheWVyLnBhdGggKyBwYXJhbVN0cmluZztcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLmZyYW1lQm9yZGVyID0gMDtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLnNjcm9sbGluZyA9IFwibm9cIjtcbiAgICAgICAgICAgIHRoaXMuaWZyYW1lLndpZHRoID0gb3B0aW9ucy53aWR0aCB8fCA2NDA7XG4gICAgICAgICAgICB0aGlzLmlmcmFtZS5oZWlnaHQgPSBvcHRpb25zLmhlaWdodCB8fCA0ODA7XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8uaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICAvLyBKSVRTSTogSU4gQ0FTRSBTT01FVEhJTkcgR09FUyBXUk9ORy5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lbWJlZFRvLmFwcGVuZENoaWxkKHRoaXMuaWZyYW1lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIkNBVENIIEVSUk9SXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBKSVRTSTogSW5jcmVhc2UgaW50ZXJ2YWwgZnJvbSAyMDAgdG8gNTAwLCB3aGljaCBmaXhlcyBwcmV6aVxuICAgICAgICAgICAgLy8gY3Jhc2hlcyBmb3IgdXMuXG4gICAgICAgICAgICB0aGlzLmluaXRQb2xsSW50ZXJ2YWwgPSBzZXRJbnRlcnZhbChmdW5jdGlvbigpe1xuICAgICAgICAgICAgICAgIF90aGlzLnNlbmRNZXNzYWdlKHsnYWN0aW9uJzogJ2luaXQnfSk7XG4gICAgICAgICAgICB9LCA1MDApO1xuICAgICAgICAgICAgUHJlemlQbGF5ZXIucGxheWVyc1tpZF0gPSB0aGlzO1xuICAgICAgICB9XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmNoYW5nZXNIYW5kbGVyID0gZnVuY3Rpb24obWVzc2FnZSkge1xuICAgICAgICAgICAgdmFyIGtleSwgdmFsdWUsIGosIGl0ZW07XG4gICAgICAgICAgICBpZiAodGhpcy5pbml0UG9sbEludGVydmFsKSB7XG4gICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmluaXRQb2xsSW50ZXJ2YWwpO1xuICAgICAgICAgICAgICAgIHRoaXMuaW5pdFBvbGxJbnRlcnZhbCA9IGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZm9yIChrZXkgaW4gbWVzc2FnZS5kYXRhKSB7XG4gICAgICAgICAgICAgICAgaWYgKG1lc3NhZ2UuZGF0YS5oYXNPd25Qcm9wZXJ0eShrZXkpKXtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBtZXNzYWdlLmRhdGFba2V5XTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy52YWx1ZXNba2V5XSA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGo9MDsgajx0aGlzLmNhbGxiYWNrcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgaXRlbSA9IHRoaXMuY2FsbGJhY2tzW2pdO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGl0ZW0gJiYgaXRlbS5ldmVudCA9PT0ga2V5ICsgXCJDaGFuZ2VcIil7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbS5jYWxsYmFjayh7dHlwZTogaXRlbS5ldmVudCwgdmFsdWU6IHZhbHVlfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmluaXRQb2xsSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHRoaXMuaW5pdFBvbGxJbnRlcnZhbCk7XG4gICAgICAgICAgICAgICAgdGhpcy5pbml0UG9sbEludGVydmFsID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmVtYmVkVG8uaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnNlbmRNZXNzYWdlID0gZnVuY3Rpb24obWVzc2FnZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5kZWJ1ZyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgIGlmIChjb25zb2xlICYmIGNvbnNvbGUubG9nKSBjb25zb2xlLmxvZygnc2VudCcsIG1lc3NhZ2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbWVzc2FnZS52ZXJzaW9uID0gUHJlemlQbGF5ZXIuQVBJX1ZFUlNJT047XG4gICAgICAgICAgICBtZXNzYWdlLmlkID0gdGhpcy5pZDtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmlmcmFtZS5jb250ZW50V2luZG93LnBvc3RNZXNzYWdlKEpTT04uc3RyaW5naWZ5KG1lc3NhZ2UpLCAnKicpO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5uZXh0U3RlcCA9IC8qIG5leHRTdGVwIGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvTmV4dFN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9OZXh0U3RlcCddXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUucHJldmlvdXNTdGVwID0gLyogcHJldmlvdXNTdGVwIGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvUHJldmlvdXNTdGVwID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnbW92ZVRvUHJldlN0ZXAnXVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnRvU3RlcCA9IC8qIHRvU3RlcCBpcyBERVBSRUNBVEVEICovXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5mbHlUb1N0ZXAgPSBmdW5jdGlvbihzdGVwLCBhbmltYXRpb25fc3RlcCkge1xuICAgICAgICAgICAgdmFyIG9iaiA9IHRoaXM7XG4gICAgICAgICAgICAvLyBjaGVjayBhbmltYXRpb25fc3RlcFxuICAgICAgICAgICAgaWYgKGFuaW1hdGlvbl9zdGVwID4gMCAmJlxuICAgICAgICAgICAgICAgIG9iai52YWx1ZXMuYW5pbWF0aW9uQ291bnRPblN0ZXBzICYmXG4gICAgICAgICAgICAgICAgb2JqLnZhbHVlcy5hbmltYXRpb25Db3VudE9uU3RlcHNbc3RlcF0gPD0gYW5pbWF0aW9uX3N0ZXApIHtcbiAgICAgICAgICAgICAgICBhbmltYXRpb25fc3RlcCA9IG9iai52YWx1ZXMuYW5pbWF0aW9uQ291bnRPblN0ZXBzW3N0ZXBdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8ganVtcCB0byBhbmltYXRpb24gc3RlcHMgYnkgY2FsbGluZyBmbHlUb05leHRTdGVwKClcbiAgICAgICAgICAgIGZ1bmN0aW9uIGRvQW5pbWF0aW9uU3RlcHMoKSB7XG4gICAgICAgICAgICAgICAgaWYgKG9iai52YWx1ZXMuaXNNb3ZpbmcgPT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGRvQW5pbWF0aW9uU3RlcHMsIDEwMCk7IC8vIHdhaXQgdW50aWwgdGhlIGZsaWdodCBlbmRzXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgd2hpbGUgKGFuaW1hdGlvbl9zdGVwLS0gPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIG9iai5mbHlUb05leHRTdGVwKCk7IC8vIGRvIHRoZSBhbmltYXRpb24gc3RlcHNcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZXRUaW1lb3V0KGRvQW5pbWF0aW9uU3RlcHMsIDIwMCk7IC8vIDIwMG1zIGlzIHRoZSBpbnRlcm5hbCBcInJlcG9ydGluZ1wiIHRpbWVcbiAgICAgICAgICAgIC8vIGp1bXAgdG8gdGhlIHN0ZXBcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9TdGVwJywgc3RlcF1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS50b09iamVjdCA9IC8qIHRvT2JqZWN0IGlzIERFUFJFQ0FURUQgKi9cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmZseVRvT2JqZWN0ID0gZnVuY3Rpb24ob2JqZWN0SWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNlbmRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAnYWN0aW9uJzogJ3ByZXNlbnQnLFxuICAgICAgICAgICAgICAgICdkYXRhJzogWydtb3ZlVG9PYmplY3QnLCBvYmplY3RJZF1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5wbGF5ID0gZnVuY3Rpb24oZGVmYXVsdERlbGF5KSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnc3RhcnRBdXRvUGxheScsIGRlZmF1bHREZWxheV1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5zdG9wID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgJ2FjdGlvbic6ICdwcmVzZW50JyxcbiAgICAgICAgICAgICAgICAnZGF0YSc6IFsnc3RvcEF1dG9QbGF5J11cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5wYXVzZSA9IGZ1bmN0aW9uKGRlZmF1bHREZWxheSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2VuZE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgICdhY3Rpb24nOiAncHJlc2VudCcsXG4gICAgICAgICAgICAgICAgJ2RhdGEnOiBbJ3BhdXNlQXV0b1BsYXknLCBkZWZhdWx0RGVsYXldXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudFN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5jdXJyZW50U3RlcDtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudEFuaW1hdGlvblN0ZXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5jdXJyZW50QW5pbWF0aW9uU3RlcDtcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0Q3VycmVudE9iamVjdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzLmN1cnJlbnRPYmplY3Q7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmdldFN0YXR1cyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzLnN0YXR1cztcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuaXNQbGF5aW5nID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMuaXNBdXRvUGxheWluZztcbiAgICAgICAgfTtcblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0U3RlcENvdW50ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMuc3RlcENvdW50O1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5nZXRBbmltYXRpb25Db3VudE9uU3RlcHMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5hbmltYXRpb25Db3VudE9uU3RlcHM7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLmdldFRpdGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMudGl0bGU7XG4gICAgICAgIH07XG5cbiAgICAgICAgUHJlemlQbGF5ZXIucHJvdG90eXBlLnNldERpbWVuc2lvbnMgPSBmdW5jdGlvbihkaW1zKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBwYXJhbWV0ZXIgaW4gZGltcykge1xuICAgICAgICAgICAgICAgIHRoaXMuaWZyYW1lW3BhcmFtZXRlcl0gPSBkaW1zW3BhcmFtZXRlcl07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUuZ2V0RGltZW5zaW9ucyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICB3aWR0aDogcGFyc2VJbnQodGhpcy5pZnJhbWUud2lkdGgsIDEwKSxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHBhcnNlSW50KHRoaXMuaWZyYW1lLmhlaWdodCwgMTApXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBQcmV6aVBsYXllci5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldmVudCwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgIHRoaXMuY2FsbGJhY2tzLnB1c2goe1xuICAgICAgICAgICAgICAgIGV2ZW50OiBldmVudCxcbiAgICAgICAgICAgICAgICBjYWxsYmFjazogY2FsbGJhY2tcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIFByZXppUGxheWVyLnByb3RvdHlwZS5vZmYgPSBmdW5jdGlvbihldmVudCwgY2FsbGJhY2spIHtcbiAgICAgICAgICAgIHZhciBqLCBpdGVtO1xuICAgICAgICAgICAgaWYgKGV2ZW50ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNhbGxiYWNrcyA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaiA9IHRoaXMuY2FsbGJhY2tzLmxlbmd0aDtcbiAgICAgICAgICAgIHdoaWxlIChqLS0pIHtcbiAgICAgICAgICAgICAgICBpdGVtID0gdGhpcy5jYWxsYmFja3Nbal07XG4gICAgICAgICAgICAgICAgaWYgKGl0ZW0gJiYgaXRlbS5ldmVudCA9PT0gZXZlbnQgJiYgKGNhbGxiYWNrID09PSB1bmRlZmluZWQgfHwgaXRlbS5jYWxsYmFjayA9PT0gY2FsbGJhY2spKXtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jYWxsYmFja3Muc3BsaWNlKGosIDEpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBpZiAod2luZG93LmFkZEV2ZW50TGlzdGVuZXIpIHtcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgUHJlemlQbGF5ZXIubWVzc2FnZVJlY2VpdmVkLCBmYWxzZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB3aW5kb3cuYXR0YWNoRXZlbnQoJ29ubWVzc2FnZScsIFByZXppUGxheWVyLm1lc3NhZ2VSZWNlaXZlZCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gUHJlemlQbGF5ZXI7XG5cbiAgICB9KSgpO1xuXG59KSgpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFByZXppUGxheWVyO1xuIiwidmFyIENoYXQgPSByZXF1aXJlKFwiLi9jaGF0L0NoYXRcIik7XG52YXIgQ29udGFjdExpc3QgPSByZXF1aXJlKFwiLi9jb250YWN0bGlzdC9Db250YWN0TGlzdFwiKTtcbnZhciBTZXR0aW5ncyA9IHJlcXVpcmUoXCIuL3NldHRpbmdzL1NldHRpbmdzXCIpO1xudmFyIFNldHRpbmdzTWVudSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzL1NldHRpbmdzTWVudVwiKTtcbnZhciBWaWRlb0xheW91dCA9IHJlcXVpcmUoXCIuLi92aWRlb2xheW91dC9WaWRlb0xheW91dFwiKTtcbnZhciBUb29sYmFyVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi90b29sYmFycy9Ub29sYmFyVG9nZ2xlclwiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vdXRpbC9VSVV0aWxcIik7XG5cbi8qKlxuICogVG9nZ2xlciBmb3IgdGhlIGNoYXQsIGNvbnRhY3QgbGlzdCwgc2V0dGluZ3MgbWVudSwgZXRjLi5cbiAqL1xudmFyIFBhbmVsVG9nZ2xlciA9IChmdW5jdGlvbihteSkge1xuXG4gICAgdmFyIGN1cnJlbnRseU9wZW4gPSBudWxsO1xuICAgIHZhciBidXR0b25zID0ge1xuICAgICAgICAnI2NoYXRzcGFjZSc6ICcjY2hhdEJvdHRvbUJ1dHRvbicsXG4gICAgICAgICcjY29udGFjdGxpc3QnOiAnI2NvbnRhY3RMaXN0QnV0dG9uJyxcbiAgICAgICAgJyNzZXR0aW5nc21lbnUnOiAnI3NldHRpbmdzQnV0dG9uJ1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXNpemVzIHRoZSB2aWRlbyBhcmVhXG4gICAgICogQHBhcmFtIGlzQ2xvc2luZyB3aGV0aGVyIHRoZSBzaWRlIHBhbmVsIGlzIGdvaW5nIHRvIGJlIGNsb3NlZCBvciBpcyBnb2luZyB0byBvcGVuIC8gcmVtYWluIG9wZW5lZFxuICAgICAqIEBwYXJhbSBjb21wbGV0ZUZ1bmN0aW9uIGEgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW4gdGhlIHZpZGVvIHNwYWNlIGlzIHJlc2l6ZWRcbiAgICAgKi9cbiAgICB2YXIgcmVzaXplVmlkZW9BcmVhID0gZnVuY3Rpb24oaXNDbG9zaW5nLCBjb21wbGV0ZUZ1bmN0aW9uKSB7XG4gICAgICAgIHZhciB2aWRlb3NwYWNlID0gJCgnI3ZpZGVvc3BhY2UnKTtcblxuICAgICAgICB2YXIgcGFuZWxTaXplID0gaXNDbG9zaW5nID8gWzAsIDBdIDogUGFuZWxUb2dnbGVyLmdldFBhbmVsU2l6ZSgpO1xuICAgICAgICB2YXIgdmlkZW9zcGFjZVdpZHRoID0gd2luZG93LmlubmVyV2lkdGggLSBwYW5lbFNpemVbMF07XG4gICAgICAgIHZhciB2aWRlb3NwYWNlSGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuICAgICAgICB2YXIgdmlkZW9TaXplXG4gICAgICAgICAgICA9IFZpZGVvTGF5b3V0LmdldFZpZGVvU2l6ZShudWxsLCBudWxsLCB2aWRlb3NwYWNlV2lkdGgsIHZpZGVvc3BhY2VIZWlnaHQpO1xuICAgICAgICB2YXIgdmlkZW9XaWR0aCA9IHZpZGVvU2l6ZVswXTtcbiAgICAgICAgdmFyIHZpZGVvSGVpZ2h0ID0gdmlkZW9TaXplWzFdO1xuICAgICAgICB2YXIgdmlkZW9Qb3NpdGlvbiA9IFZpZGVvTGF5b3V0LmdldFZpZGVvUG9zaXRpb24odmlkZW9XaWR0aCxcbiAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgdmlkZW9zcGFjZUhlaWdodCk7XG4gICAgICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblswXTtcbiAgICAgICAgdmFyIHZlcnRpY2FsSW5kZW50ID0gdmlkZW9Qb3NpdGlvblsxXTtcblxuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9zcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHRodW1ibmFpbHNXaWR0aCA9IHRodW1ibmFpbFNpemVbMF07XG4gICAgICAgIHZhciB0aHVtYm5haWxzSGVpZ2h0ID0gdGh1bWJuYWlsU2l6ZVsxXTtcbiAgICAgICAgLy9mb3IgY2hhdFxuXG4gICAgICAgIHZpZGVvc3BhY2UuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgcmlnaHQ6IHBhbmVsU2l6ZVswXSxcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgIGhlaWdodDogdmlkZW9zcGFjZUhlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMCxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZTogY29tcGxldGVGdW5jdGlvblxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcycpLmFuaW1hdGUoe1xuICAgICAgICAgICAgICAgIGhlaWdodDogdGh1bWJuYWlsc0hlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcz5zcGFuJykuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgaGVpZ2h0OiB0aHVtYm5haWxzSGVpZ2h0LFxuICAgICAgICAgICAgICAgIHdpZHRoOiB0aHVtYm5haWxzV2lkdGhcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDAsXG4gICAgICAgICAgICAgICAgY29tcGxldGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwicmVtb3RldmlkZW8ucmVzaXplZFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3RodW1ibmFpbHNXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHVtYm5haWxzSGVpZ2h0XSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9zcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgIGhlaWdodDogdmlkZW9zcGFjZUhlaWdodFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgZHVyYXRpb246IDUwMFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgJCgnI2xhcmdlVmlkZW8nKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICB3aWR0aDogdmlkZW9XaWR0aCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgIHRvcDogdmVydGljYWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgYm90dG9tOiB2ZXJ0aWNhbEluZGVudCxcbiAgICAgICAgICAgICAgICBsZWZ0OiBob3Jpem9udGFsSW5kZW50LFxuICAgICAgICAgICAgICAgIHJpZ2h0OiBob3Jpem9udGFsSW5kZW50XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVG9nZ2xlcyB0aGUgd2luZG93cyBpbiB0aGUgc2lkZSBwYW5lbFxuICAgICAqIEBwYXJhbSBvYmplY3QgdGhlIHdpbmRvdyB0aGF0IHNob3VsZCBiZSBzaG93blxuICAgICAqIEBwYXJhbSBzZWxlY3RvciB0aGUgc2VsZWN0b3IgZm9yIHRoZSBlbGVtZW50IGNvbnRhaW5pbmcgdGhlIHBhbmVsXG4gICAgICogQHBhcmFtIG9uT3BlbkNvbXBsZXRlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBwYW5lbCBpcyBvcGVuZWRcbiAgICAgKiBAcGFyYW0gb25PcGVuIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBpZiB0aGUgd2luZG93IGlzIGdvaW5nIHRvIGJlIG9wZW5lZFxuICAgICAqIEBwYXJhbSBvbkNsb3NlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBpZiB0aGUgd2luZG93IGlzIGdvaW5nIHRvIGJlIGNsb3NlZFxuICAgICAqL1xuICAgIHZhciB0b2dnbGUgPSBmdW5jdGlvbihvYmplY3QsIHNlbGVjdG9yLCBvbk9wZW5Db21wbGV0ZSwgb25PcGVuLCBvbkNsb3NlKSB7XG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhidXR0b25zW3NlbGVjdG9yXSwgXCJhY3RpdmVcIik7XG5cbiAgICAgICAgaWYgKG9iamVjdC5pc1Zpc2libGUoKSkge1xuICAgICAgICAgICAgJChcIiN0b2FzdC1jb250YWluZXJcIikuYW5pbWF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIHJpZ2h0OiAnNXB4J1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBxdWV1ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICQoc2VsZWN0b3IpLmhpZGUoXCJzbGlkZVwiLCB7XG4gICAgICAgICAgICAgICAgZGlyZWN0aW9uOiBcInJpZ2h0XCIsXG4gICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiA1MDBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaWYodHlwZW9mIG9uQ2xvc2UgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgIG9uQ2xvc2UoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY3VycmVudGx5T3BlbiA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBVbmRvY2sgdGhlIHRvb2xiYXIgd2hlbiB0aGUgY2hhdCBpcyBzaG93biBhbmQgaWYgd2UncmUgaW4gYVxuICAgICAgICAgICAgLy8gdmlkZW8gbW9kZS5cbiAgICAgICAgICAgIGlmIChWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9WaXNpYmxlKCkpIHtcbiAgICAgICAgICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcihmYWxzZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKGN1cnJlbnRseU9wZW4pIHtcbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudCA9ICQoY3VycmVudGx5T3Blbik7XG4gICAgICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKGJ1dHRvbnNbY3VycmVudGx5T3Blbl0sIFwiYWN0aXZlXCIpO1xuICAgICAgICAgICAgICAgIGN1cnJlbnQuY3NzKCd6LWluZGV4JywgNCk7XG4gICAgICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY3NzKCdkaXNwbGF5JywgJ25vbmUnKTtcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jc3MoJ3otaW5kZXgnLCA1KTtcbiAgICAgICAgICAgICAgICB9LCA1MDApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAkKFwiI3RvYXN0LWNvbnRhaW5lclwiKS5hbmltYXRlKHtcbiAgICAgICAgICAgICAgICAgICAgcmlnaHQ6IChQYW5lbFRvZ2dsZXIuZ2V0UGFuZWxTaXplKClbMF0gKyA1KSArICdweCdcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgcXVldWU6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAkKHNlbGVjdG9yKS5zaG93KFwic2xpZGVcIiwge1xuICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogXCJyaWdodFwiLFxuICAgICAgICAgICAgICAgIHF1ZXVlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBkdXJhdGlvbjogNTAwLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlOiBvbk9wZW5Db21wbGV0ZVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZih0eXBlb2Ygb25PcGVuID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICAgICAgICBvbk9wZW4oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY3VycmVudGx5T3BlbiA9IHNlbGVjdG9yO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIC8gY2xvc2VzIHRoZSBjaGF0IGFyZWEuXG4gICAgICovXG4gICAgbXkudG9nZ2xlQ2hhdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgY2hhdENvbXBsZXRlRnVuY3Rpb24gPSBDaGF0LmlzVmlzaWJsZSgpID9cbiAgICAgICAgICAgIGZ1bmN0aW9uKCkge30gOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBDaGF0LnNjcm9sbENoYXRUb0JvdHRvbSgpO1xuICAgICAgICAgICAgJCgnI2NoYXRzcGFjZScpLnRyaWdnZXIoJ3Nob3duJyk7XG4gICAgICAgIH07XG5cbiAgICAgICAgcmVzaXplVmlkZW9BcmVhKENoYXQuaXNWaXNpYmxlKCksIGNoYXRDb21wbGV0ZUZ1bmN0aW9uKTtcblxuICAgICAgICB0b2dnbGUoQ2hhdCxcbiAgICAgICAgICAgICcjY2hhdHNwYWNlJyxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAvLyBSZXF1ZXN0IHRoZSBmb2N1cyBpbiB0aGUgbmlja25hbWUgZmllbGQgb3IgdGhlIGNoYXQgaW5wdXQgZmllbGQuXG4gICAgICAgICAgICAgICAgaWYgKCQoJyNuaWNrbmFtZScpLmNzcygndmlzaWJpbGl0eScpID09PSAndmlzaWJsZScpIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI25pY2tpbnB1dCcpLmZvY3VzKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgJCgnI3VzZXJtc2cnKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgQ2hhdC5yZXNpemVDaGF0LFxuICAgICAgICAgICAgbnVsbCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIE9wZW5zIC8gY2xvc2VzIHRoZSBjb250YWN0IGxpc3QgYXJlYS5cbiAgICAgKi9cbiAgICBteS50b2dnbGVDb250YWN0TGlzdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGNvbXBsZXRlRnVuY3Rpb24gPSBDb250YWN0TGlzdC5pc1Zpc2libGUoKSA/XG4gICAgICAgICAgICBmdW5jdGlvbigpIHt9IDogZnVuY3Rpb24gKCkgeyAkKCcjY29udGFjdGxpc3QnKS50cmlnZ2VyKCdzaG93bicpO307XG4gICAgICAgIHJlc2l6ZVZpZGVvQXJlYShDb250YWN0TGlzdC5pc1Zpc2libGUoKSwgY29tcGxldGVGdW5jdGlvbik7XG5cbiAgICAgICAgdG9nZ2xlKENvbnRhY3RMaXN0LFxuICAgICAgICAgICAgJyNjb250YWN0bGlzdCcsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgQ29udGFjdExpc3Quc2V0VmlzdWFsTm90aWZpY2F0aW9uKGZhbHNlKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgLyBjbG9zZXMgdGhlIHNldHRpbmdzIG1lbnVcbiAgICAgKi9cbiAgICBteS50b2dnbGVTZXR0aW5nc01lbnUgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmVzaXplVmlkZW9BcmVhKFNldHRpbmdzTWVudS5pc1Zpc2libGUoKSwgZnVuY3Rpb24gKCl7fSk7XG4gICAgICAgIHRvZ2dsZShTZXR0aW5nc01lbnUsXG4gICAgICAgICAgICAnI3NldHRpbmdzbWVudScsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNldHRpbmdzID0gU2V0dGluZ3MuZ2V0U2V0dGluZ3MoKTtcbiAgICAgICAgICAgICAgICAkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUgPSBzZXR0aW5ncy5kaXNwbGF5TmFtZTtcbiAgICAgICAgICAgICAgICAkKCcjc2V0RW1haWwnKS5nZXQoMCkudmFsdWUgPSBzZXR0aW5ncy5lbWFpbDtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBudWxsKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2l6ZSBvZiB0aGUgc2lkZSBwYW5lbC5cbiAgICAgKi9cbiAgICBteS5nZXRQYW5lbFNpemUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG4gICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuXG4gICAgICAgIHZhciBwYW5lbFdpZHRoID0gMjAwO1xuICAgICAgICBpZiAoYXZhaWxhYmxlV2lkdGggKiAwLjIgPCAyMDApIHtcbiAgICAgICAgICAgIHBhbmVsV2lkdGggPSBhdmFpbGFibGVXaWR0aCAqIDAuMjtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBbcGFuZWxXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbiAgICB9O1xuXG4gICAgbXkuaXNWaXNpYmxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAoQ2hhdC5pc1Zpc2libGUoKSB8fCBDb250YWN0TGlzdC5pc1Zpc2libGUoKSB8fCBTZXR0aW5nc01lbnUuaXNWaXNpYmxlKCkpO1xuICAgIH07XG5cbiAgICByZXR1cm4gbXk7XG5cbn0oUGFuZWxUb2dnbGVyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gUGFuZWxUb2dnbGVyOyIsIi8qIGdsb2JhbCAkLCBVdGlsLCBuaWNrbmFtZTp0cnVlICovXG52YXIgUmVwbGFjZW1lbnQgPSByZXF1aXJlKFwiLi9SZXBsYWNlbWVudFwiKTtcbnZhciBDb21tYW5kc1Byb2Nlc3NvciA9IHJlcXVpcmUoXCIuL0NvbW1hbmRzXCIpO1xudmFyIFRvb2xiYXJUb2dnbGVyID0gcmVxdWlyZShcIi4uLy4uL3Rvb2xiYXJzL1Rvb2xiYXJUb2dnbGVyXCIpO1xudmFyIHNtaWxleXMgPSByZXF1aXJlKFwiLi9zbWlsZXlzLmpzb25cIikuc21pbGV5cztcbnZhciBOaWNrbmFtZUhhbmRsZXIgPSByZXF1aXJlKFwiLi4vLi4vdXRpbC9OaWNrbmFtZUhhbmRsZXJcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uLy4uL3V0aWwvVUlVdGlsXCIpO1xudmFyIFVJRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uLy4uLy4uL3NlcnZpY2UvVUkvVUlFdmVudHNcIik7XG5cbnZhciBub3RpZmljYXRpb25JbnRlcnZhbCA9IGZhbHNlO1xudmFyIHVucmVhZE1lc3NhZ2VzID0gMDtcblxuXG4vKipcbiAqIFNob3dzL2hpZGVzIGEgdmlzdWFsIG5vdGlmaWNhdGlvbiwgaW5kaWNhdGluZyB0aGF0IGEgbWVzc2FnZSBoYXMgYXJyaXZlZC5cbiAqL1xuZnVuY3Rpb24gc2V0VmlzdWFsTm90aWZpY2F0aW9uKHNob3cpIHtcbiAgICB2YXIgdW5yZWFkTXNnRWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1bnJlYWRNZXNzYWdlcycpO1xuICAgIHZhciB1bnJlYWRNc2dCb3R0b21FbGVtZW50XG4gICAgICAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2JvdHRvbVVucmVhZE1lc3NhZ2VzJyk7XG5cbiAgICB2YXIgZ2xvd2VyID0gJCgnI2NoYXRCdXR0b24nKTtcbiAgICB2YXIgYm90dG9tR2xvd2VyID0gJCgnI2NoYXRCb3R0b21CdXR0b24nKTtcblxuICAgIGlmICh1bnJlYWRNZXNzYWdlcykge1xuICAgICAgICB1bnJlYWRNc2dFbGVtZW50LmlubmVySFRNTCA9IHVucmVhZE1lc3NhZ2VzLnRvU3RyaW5nKCk7XG4gICAgICAgIHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQuaW5uZXJIVE1MID0gdW5yZWFkTWVzc2FnZXMudG9TdHJpbmcoKTtcblxuICAgICAgICBUb29sYmFyVG9nZ2xlci5kb2NrVG9vbGJhcih0cnVlKTtcblxuICAgICAgICB2YXIgY2hhdEJ1dHRvbkVsZW1lbnRcbiAgICAgICAgICAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NoYXRCdXR0b24nKS5wYXJlbnROb2RlO1xuICAgICAgICB2YXIgbGVmdEluZGVudCA9IChVSVV0aWwuZ2V0VGV4dFdpZHRoKGNoYXRCdXR0b25FbGVtZW50KSAtXG4gICAgICAgICAgICBVSVV0aWwuZ2V0VGV4dFdpZHRoKHVucmVhZE1zZ0VsZW1lbnQpKSAvIDI7XG4gICAgICAgIHZhciB0b3BJbmRlbnQgPSAoVUlVdGlsLmdldFRleHRIZWlnaHQoY2hhdEJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFVJVXRpbC5nZXRUZXh0SGVpZ2h0KHVucmVhZE1zZ0VsZW1lbnQpKSAvIDIgLSAzO1xuXG4gICAgICAgIHVucmVhZE1zZ0VsZW1lbnQuc2V0QXR0cmlidXRlKFxuICAgICAgICAgICAgJ3N0eWxlJyxcbiAgICAgICAgICAgICAgICAndG9wOicgKyB0b3BJbmRlbnQgK1xuICAgICAgICAgICAgICAgICc7IGxlZnQ6JyArIGxlZnRJbmRlbnQgKyAnOycpO1xuXG4gICAgICAgIHZhciBjaGF0Qm90dG9tQnV0dG9uRWxlbWVudFxuICAgICAgICAgICAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2hhdEJvdHRvbUJ1dHRvbicpLnBhcmVudE5vZGU7XG4gICAgICAgIHZhciBib3R0b21MZWZ0SW5kZW50ID0gKFVJVXRpbC5nZXRUZXh0V2lkdGgoY2hhdEJvdHRvbUJ1dHRvbkVsZW1lbnQpIC1cbiAgICAgICAgICAgIFVJVXRpbC5nZXRUZXh0V2lkdGgodW5yZWFkTXNnQm90dG9tRWxlbWVudCkpIC8gMjtcbiAgICAgICAgdmFyIGJvdHRvbVRvcEluZGVudCA9IChVSVV0aWwuZ2V0VGV4dEhlaWdodChjaGF0Qm90dG9tQnV0dG9uRWxlbWVudCkgLVxuICAgICAgICAgICAgVUlVdGlsLmdldFRleHRIZWlnaHQodW5yZWFkTXNnQm90dG9tRWxlbWVudCkpIC8gMiAtIDI7XG5cbiAgICAgICAgdW5yZWFkTXNnQm90dG9tRWxlbWVudC5zZXRBdHRyaWJ1dGUoXG4gICAgICAgICAgICAnc3R5bGUnLFxuICAgICAgICAgICAgICAgICd0b3A6JyArIGJvdHRvbVRvcEluZGVudCArXG4gICAgICAgICAgICAgICAgJzsgbGVmdDonICsgYm90dG9tTGVmdEluZGVudCArICc7Jyk7XG5cblxuICAgICAgICBpZiAoIWdsb3dlci5oYXNDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpKSB7XG4gICAgICAgICAgICBnbG93ZXIucmVtb3ZlQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgICAgICAgICAgZ2xvd2VyLmFkZENsYXNzKCdpY29uLWNoYXQtc2ltcGxlJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHVucmVhZE1zZ0VsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHVucmVhZE1zZ0JvdHRvbUVsZW1lbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnaWNvbi1jaGF0LXNpbXBsZScpO1xuICAgICAgICBnbG93ZXIuYWRkQ2xhc3MoJ2ljb24tY2hhdCcpO1xuICAgIH1cblxuICAgIGlmIChzaG93ICYmICFub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBnbG93ZXIudG9nZ2xlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgYm90dG9tR2xvd2VyLnRvZ2dsZUNsYXNzKCdhY3RpdmUgZ2xvd2luZycpO1xuICAgICAgICB9LCA4MDApO1xuICAgIH1cbiAgICBlbHNlIGlmICghc2hvdyAmJiBub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChub3RpZmljYXRpb25JbnRlcnZhbCk7XG4gICAgICAgIG5vdGlmaWNhdGlvbkludGVydmFsID0gZmFsc2U7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgIGJvdHRvbUdsb3dlci5yZW1vdmVDbGFzcygnZ2xvd2luZycpO1xuICAgICAgICBib3R0b21HbG93ZXIuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgIH1cbn1cblxuXG4vKipcbiAqIFJldHVybnMgdGhlIGN1cnJlbnQgdGltZSBpbiB0aGUgZm9ybWF0IGl0IGlzIHNob3duIHRvIHRoZSB1c2VyXG4gKiBAcmV0dXJucyB7c3RyaW5nfVxuICovXG5mdW5jdGlvbiBnZXRDdXJyZW50VGltZSgpIHtcbiAgICB2YXIgbm93ICAgICA9IG5ldyBEYXRlKCk7XG4gICAgdmFyIGhvdXIgICAgPSBub3cuZ2V0SG91cnMoKTtcbiAgICB2YXIgbWludXRlICA9IG5vdy5nZXRNaW51dGVzKCk7XG4gICAgdmFyIHNlY29uZCAgPSBub3cuZ2V0U2Vjb25kcygpO1xuICAgIGlmKGhvdXIudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgaG91ciA9ICcwJytob3VyO1xuICAgIH1cbiAgICBpZihtaW51dGUudG9TdHJpbmcoKS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgbWludXRlID0gJzAnK21pbnV0ZTtcbiAgICB9XG4gICAgaWYoc2Vjb25kLnRvU3RyaW5nKCkubGVuZ3RoID09PSAxKSB7XG4gICAgICAgIHNlY29uZCA9ICcwJytzZWNvbmQ7XG4gICAgfVxuICAgIHJldHVybiBob3VyKyc6JyttaW51dGUrJzonK3NlY29uZDtcbn1cblxuZnVuY3Rpb24gdG9nZ2xlU21pbGV5cygpXG57XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c0NvbnRhaW5lcicpO1xuICAgIGlmKCFzbWlsZXlzLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgIHNtaWxleXMuc2hvdyhcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHNtaWxleXMuaGlkZShcInNsaWRlXCIsIHsgZGlyZWN0aW9uOiBcImRvd25cIiwgZHVyYXRpb246IDMwMH0pO1xuICAgIH1cbiAgICAkKCcjdXNlcm1zZycpLmZvY3VzKCk7XG59XG5cbmZ1bmN0aW9uIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBudW1iZXIpIHtcbiAgICBzbWlsZXkub25jbGljayA9IGZ1bmN0aW9uIGFkZFNtaWxleVRvTWVzc2FnZSgpIHtcbiAgICAgICAgdmFyIHVzZXJtc2cgPSAkKCcjdXNlcm1zZycpO1xuICAgICAgICB2YXIgbWVzc2FnZSA9IHVzZXJtc2cudmFsKCk7XG4gICAgICAgIG1lc3NhZ2UgKz0gc21pbGV5c1snc21pbGV5JyArIG51bWJlcl07XG4gICAgICAgIHVzZXJtc2cudmFsKG1lc3NhZ2UpO1xuICAgICAgICB1c2VybXNnLmdldCgwKS5zZXRTZWxlY3Rpb25SYW5nZShtZXNzYWdlLmxlbmd0aCwgbWVzc2FnZS5sZW5ndGgpO1xuICAgICAgICB0b2dnbGVTbWlsZXlzKCk7XG4gICAgICAgIHVzZXJtc2cuZm9jdXMoKTtcbiAgICB9O1xufVxuXG4vKipcbiAqIEFkZHMgdGhlIHNtaWxleXMgY29udGFpbmVyIHRvIHRoZSBjaGF0XG4gKi9cbmZ1bmN0aW9uIGFkZFNtaWxleXMoKSB7XG4gICAgdmFyIHNtaWxleXNDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBzbWlsZXlzQ29udGFpbmVyLmlkID0gJ3NtaWxleXNDb250YWluZXInO1xuICAgIGZvcih2YXIgaSA9IDE7IGkgPD0gMjE7IGkrKykge1xuICAgICAgICB2YXIgc21pbGV5Q29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNtaWxleUNvbnRhaW5lci5pZCA9ICdzbWlsZXknICsgaTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmNsYXNzTmFtZSA9ICdzbWlsZXlDb250YWluZXInO1xuICAgICAgICB2YXIgc21pbGV5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gICAgICAgIHNtaWxleS5zcmMgPSAnaW1hZ2VzL3NtaWxleXMvc21pbGV5JyArIGkgKyAnLnN2Zyc7XG4gICAgICAgIHNtaWxleS5jbGFzc05hbWUgPSAgJ3NtaWxleSc7XG4gICAgICAgIGFkZENsaWNrRnVuY3Rpb24oc21pbGV5LCBpKTtcbiAgICAgICAgc21pbGV5Q29udGFpbmVyLmFwcGVuZENoaWxkKHNtaWxleSk7XG4gICAgICAgIHNtaWxleXNDb250YWluZXIuYXBwZW5kQ2hpbGQoc21pbGV5Q29udGFpbmVyKTtcbiAgICB9XG5cbiAgICAkKFwiI2NoYXRzcGFjZVwiKS5hcHBlbmQoc21pbGV5c0NvbnRhaW5lcik7XG59XG5cbi8qKlxuICogUmVzaXplcyB0aGUgY2hhdCBjb252ZXJzYXRpb24uXG4gKi9cbmZ1bmN0aW9uIHJlc2l6ZUNoYXRDb252ZXJzYXRpb24oKSB7XG4gICAgdmFyIG1zZ2FyZWFIZWlnaHQgPSAkKCcjdXNlcm1zZycpLm91dGVySGVpZ2h0KCk7XG4gICAgdmFyIGNoYXRzcGFjZSA9ICQoJyNjaGF0c3BhY2UnKTtcbiAgICB2YXIgd2lkdGggPSBjaGF0c3BhY2Uud2lkdGgoKTtcbiAgICB2YXIgY2hhdCA9ICQoJyNjaGF0Y29udmVyc2F0aW9uJyk7XG4gICAgdmFyIHNtaWxleXMgPSAkKCcjc21pbGV5c2FyZWEnKTtcblxuICAgIHNtaWxleXMuaGVpZ2h0KG1zZ2FyZWFIZWlnaHQpO1xuICAgICQoXCIjc21pbGV5c1wiKS5jc3MoJ2JvdHRvbScsIChtc2dhcmVhSGVpZ2h0IC0gMjYpIC8gMik7XG4gICAgJCgnI3NtaWxleXNDb250YWluZXInKS5jc3MoJ2JvdHRvbScsIG1zZ2FyZWFIZWlnaHQpO1xuICAgIGNoYXQud2lkdGgod2lkdGggLSAxMCk7XG4gICAgY2hhdC5oZWlnaHQod2luZG93LmlubmVySGVpZ2h0IC0gMTUgLSBtc2dhcmVhSGVpZ2h0KTtcbn1cblxuLyoqXG4gKiBDaGF0IHJlbGF0ZWQgdXNlciBpbnRlcmZhY2UuXG4gKi9cbnZhciBDaGF0ID0gKGZ1bmN0aW9uIChteSkge1xuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemVzIGNoYXQgcmVsYXRlZCBpbnRlcmZhY2UuXG4gICAgICovXG4gICAgbXkuaW5pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYoTmlja25hbWVIYW5kbGVyLmdldE5pY2tuYW1lKCkpXG4gICAgICAgICAgICBDaGF0LnNldENoYXRDb252ZXJzYXRpb25Nb2RlKHRydWUpO1xuICAgICAgICBOaWNrbmFtZUhhbmRsZXIuYWRkTGlzdGVuZXIoVUlFdmVudHMuTklDS05BTUVfQ0hBTkdFRCxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChuaWNrbmFtZSkge1xuICAgICAgICAgICAgICAgIENoYXQuc2V0Q2hhdENvbnZlcnNhdGlvbk1vZGUodHJ1ZSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAkKCcjbmlja2lucHV0Jykua2V5ZG93bihmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5rZXlDb2RlID09PSAxMykge1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgdmFyIHZhbCA9IFVJVXRpbC5lc2NhcGVIdG1sKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUgPSAnJztcbiAgICAgICAgICAgICAgICBpZiAoIU5pY2tuYW1lSGFuZGxlci5nZXROaWNrbmFtZSgpKSB7XG4gICAgICAgICAgICAgICAgICAgIE5pY2tuYW1lSGFuZGxlci5zZXROaWNrbmFtZSh2YWwpO1xuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgICQoJyN1c2VybXNnJykua2V5ZG93bihmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5rZXlDb2RlID09PSAxMykge1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgdmFyIHZhbHVlID0gdGhpcy52YWx1ZTtcbiAgICAgICAgICAgICAgICAkKCcjdXNlcm1zZycpLnZhbCgnJykudHJpZ2dlcignYXV0b3NpemUucmVzaXplJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5mb2N1cygpO1xuICAgICAgICAgICAgICAgIHZhciBjb21tYW5kID0gbmV3IENvbW1hbmRzUHJvY2Vzc29yKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBpZihjb21tYW5kLmlzQ29tbWFuZCgpKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgY29tbWFuZC5wcm9jZXNzQ29tbWFuZCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWVzc2FnZSA9IFVJVXRpbC5lc2NhcGVIdG1sKHZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuc2VuZENoYXRNZXNzYWdlKG1lc3NhZ2UsIE5pY2tuYW1lSGFuZGxlci5nZXROaWNrbmFtZSgpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBvblRleHRBcmVhUmVzaXplID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmVzaXplQ2hhdENvbnZlcnNhdGlvbigpO1xuICAgICAgICAgICAgQ2hhdC5zY3JvbGxDaGF0VG9Cb3R0b20oKTtcbiAgICAgICAgfTtcbiAgICAgICAgJCgnI3VzZXJtc2cnKS5hdXRvc2l6ZSh7Y2FsbGJhY2s6IG9uVGV4dEFyZWFSZXNpemV9KTtcblxuICAgICAgICAkKFwiI2NoYXRzcGFjZVwiKS5iaW5kKFwic2hvd25cIixcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB1bnJlYWRNZXNzYWdlcyA9IDA7XG4gICAgICAgICAgICAgICAgc2V0VmlzdWFsTm90aWZpY2F0aW9uKGZhbHNlKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIGFkZFNtaWxleXMoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwZW5kcyB0aGUgZ2l2ZW4gbWVzc2FnZSB0byB0aGUgY2hhdCBjb252ZXJzYXRpb24uXG4gICAgICovXG4gICAgbXkudXBkYXRlQ2hhdENvbnZlcnNhdGlvbiA9IGZ1bmN0aW9uIChmcm9tLCBkaXNwbGF5TmFtZSwgbWVzc2FnZSkge1xuICAgICAgICB2YXIgZGl2Q2xhc3NOYW1lID0gJyc7XG5cbiAgICAgICAgaWYgKEFQUC54bXBwLm15SmlkKCkgPT09IGZyb20pIHtcbiAgICAgICAgICAgIGRpdkNsYXNzTmFtZSA9IFwibG9jYWx1c2VyXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBkaXZDbGFzc05hbWUgPSBcInJlbW90ZXVzZXJcIjtcblxuICAgICAgICAgICAgaWYgKCFDaGF0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgICAgICAgICAgdW5yZWFkTWVzc2FnZXMrKztcbiAgICAgICAgICAgICAgICBVSVV0aWwucGxheVNvdW5kTm90aWZpY2F0aW9uKCdjaGF0Tm90aWZpY2F0aW9uJyk7XG4gICAgICAgICAgICAgICAgc2V0VmlzdWFsTm90aWZpY2F0aW9uKHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVwbGFjZSBsaW5rcyBhbmQgc21pbGV5c1xuICAgICAgICAvLyBTdHJvcGhlIGFscmVhZHkgZXNjYXBlcyBzcGVjaWFsIHN5bWJvbHMgb24gc2VuZGluZyxcbiAgICAgICAgLy8gc28gd2UgZXNjYXBlIGhlcmUgb25seSB0YWdzIHRvIGF2b2lkIGRvdWJsZSAmYW1wO1xuICAgICAgICB2YXIgZXNjTWVzc2FnZSA9IG1lc3NhZ2UucmVwbGFjZSgvPC9nLCAnJmx0OycpLlxuICAgICAgICAgICAgcmVwbGFjZSgvPi9nLCAnJmd0OycpLnJlcGxhY2UoL1xcbi9nLCAnPGJyLz4nKTtcbiAgICAgICAgdmFyIGVzY0Rpc3BsYXlOYW1lID0gVUlVdGlsLmVzY2FwZUh0bWwoZGlzcGxheU5hbWUpO1xuICAgICAgICBtZXNzYWdlID0gUmVwbGFjZW1lbnQucHJvY2Vzc1JlcGxhY2VtZW50cyhlc2NNZXNzYWdlKTtcblxuICAgICAgICB2YXIgbWVzc2FnZUNvbnRhaW5lciA9XG4gICAgICAgICAgICAnPGRpdiBjbGFzcz1cImNoYXRtZXNzYWdlXCI+JytcbiAgICAgICAgICAgICAgICAnPGltZyBzcmM9XCIuLi9pbWFnZXMvY2hhdEFycm93LnN2Z1wiIGNsYXNzPVwiY2hhdEFycm93XCI+JyArXG4gICAgICAgICAgICAgICAgJzxkaXYgY2xhc3M9XCJ1c2VybmFtZSAnICsgZGl2Q2xhc3NOYW1lICsnXCI+JyArIGVzY0Rpc3BsYXlOYW1lICtcbiAgICAgICAgICAgICAgICAnPC9kaXY+JyArICc8ZGl2IGNsYXNzPVwidGltZXN0YW1wXCI+JyArIGdldEN1cnJlbnRUaW1lKCkgK1xuICAgICAgICAgICAgICAgICc8L2Rpdj4nICsgJzxkaXYgY2xhc3M9XCJ1c2VybWVzc2FnZVwiPicgKyBtZXNzYWdlICsgJzwvZGl2PicgK1xuICAgICAgICAgICAgJzwvZGl2Pic7XG5cbiAgICAgICAgJCgnI2NoYXRjb252ZXJzYXRpb24nKS5hcHBlbmQobWVzc2FnZUNvbnRhaW5lcik7XG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYW5pbWF0ZShcbiAgICAgICAgICAgICAgICB7IHNjcm9sbFRvcDogJCgnI2NoYXRjb252ZXJzYXRpb24nKVswXS5zY3JvbGxIZWlnaHR9LCAxMDAwKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQXBwZW5kcyBlcnJvciBtZXNzYWdlIHRvIHRoZSBjb252ZXJzYXRpb25cbiAgICAgKiBAcGFyYW0gZXJyb3JNZXNzYWdlIHRoZSByZWNlaXZlZCBlcnJvciBtZXNzYWdlLlxuICAgICAqIEBwYXJhbSBvcmlnaW5hbFRleHQgdGhlIG9yaWdpbmFsIG1lc3NhZ2UuXG4gICAgICovXG4gICAgbXkuY2hhdEFkZEVycm9yID0gZnVuY3Rpb24oZXJyb3JNZXNzYWdlLCBvcmlnaW5hbFRleHQpXG4gICAge1xuICAgICAgICBlcnJvck1lc3NhZ2UgPSBVSVV0aWwuZXNjYXBlSHRtbChlcnJvck1lc3NhZ2UpO1xuICAgICAgICBvcmlnaW5hbFRleHQgPSBVSVV0aWwuZXNjYXBlSHRtbChvcmlnaW5hbFRleHQpO1xuXG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYXBwZW5kKFxuICAgICAgICAgICAgJzxkaXYgY2xhc3M9XCJlcnJvck1lc3NhZ2VcIj48Yj5FcnJvcjogPC9iPicgKyAnWW91ciBtZXNzYWdlJyArXG4gICAgICAgICAgICAob3JpZ2luYWxUZXh0PyAoJyBcXFwiJysgb3JpZ2luYWxUZXh0ICsgJ1xcXCInKSA6IFwiXCIpICtcbiAgICAgICAgICAgICcgd2FzIG5vdCBzZW50LicgK1xuICAgICAgICAgICAgKGVycm9yTWVzc2FnZT8gKCcgUmVhc29uOiAnICsgZXJyb3JNZXNzYWdlKSA6ICcnKSArICAnPC9kaXY+Jyk7XG4gICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuYW5pbWF0ZShcbiAgICAgICAgICAgIHsgc2Nyb2xsVG9wOiAkKCcjY2hhdGNvbnZlcnNhdGlvbicpWzBdLnNjcm9sbEhlaWdodH0sIDEwMDApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBzdWJqZWN0IHRvIHRoZSBVSVxuICAgICAqIEBwYXJhbSBzdWJqZWN0IHRoZSBzdWJqZWN0XG4gICAgICovXG4gICAgbXkuY2hhdFNldFN1YmplY3QgPSBmdW5jdGlvbihzdWJqZWN0KVxuICAgIHtcbiAgICAgICAgaWYoc3ViamVjdClcbiAgICAgICAgICAgIHN1YmplY3QgPSBzdWJqZWN0LnRyaW0oKTtcbiAgICAgICAgJCgnI3N1YmplY3QnKS5odG1sKFJlcGxhY2VtZW50LmxpbmtpZnkoVUlVdGlsLmVzY2FwZUh0bWwoc3ViamVjdCkpKTtcbiAgICAgICAgaWYoc3ViamVjdCA9PT0gXCJcIilcbiAgICAgICAge1xuICAgICAgICAgICAgJChcIiNzdWJqZWN0XCIpLmNzcyh7ZGlzcGxheTogXCJub25lXCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgICQoXCIjc3ViamVjdFwiKS5jc3Moe2Rpc3BsYXk6IFwiYmxvY2tcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuXG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBjaGF0IGNvbnZlcnNhdGlvbiBtb2RlLlxuICAgICAqL1xuICAgIG15LnNldENoYXRDb252ZXJzYXRpb25Nb2RlID0gZnVuY3Rpb24gKGlzQ29udmVyc2F0aW9uTW9kZSkge1xuICAgICAgICBpZiAoaXNDb252ZXJzYXRpb25Nb2RlKSB7XG4gICAgICAgICAgICAkKCcjbmlja25hbWUnKS5jc3Moe3Zpc2liaWxpdHk6ICdoaWRkZW4nfSk7XG4gICAgICAgICAgICAkKCcjY2hhdGNvbnZlcnNhdGlvbicpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcjdXNlcm1zZycpLmNzcyh7dmlzaWJpbGl0eTogJ3Zpc2libGUnfSk7XG4gICAgICAgICAgICAkKCcjc21pbGV5c2FyZWEnKS5jc3Moe3Zpc2liaWxpdHk6ICd2aXNpYmxlJ30pO1xuICAgICAgICAgICAgJCgnI3VzZXJtc2cnKS5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIGNoYXQgYXJlYS5cbiAgICAgKi9cbiAgICBteS5yZXNpemVDaGF0ID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgY2hhdFNpemUgPSByZXF1aXJlKFwiLi4vU2lkZVBhbmVsVG9nZ2xlclwiKS5nZXRQYW5lbFNpemUoKTtcblxuICAgICAgICAkKCcjY2hhdHNwYWNlJykud2lkdGgoY2hhdFNpemVbMF0pO1xuICAgICAgICAkKCcjY2hhdHNwYWNlJykuaGVpZ2h0KGNoYXRTaXplWzFdKTtcblxuICAgICAgICByZXNpemVDaGF0Q29udmVyc2F0aW9uKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEluZGljYXRlcyBpZiB0aGUgY2hhdCBpcyBjdXJyZW50bHkgdmlzaWJsZS5cbiAgICAgKi9cbiAgICBteS5pc1Zpc2libGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAkKCcjY2hhdHNwYWNlJykuaXMoXCI6dmlzaWJsZVwiKTtcbiAgICB9O1xuICAgIC8qKlxuICAgICAqIFNob3dzIGFuZCBoaWRlcyB0aGUgd2luZG93IHdpdGggdGhlIHNtaWxleXNcbiAgICAgKi9cbiAgICBteS50b2dnbGVTbWlsZXlzID0gdG9nZ2xlU21pbGV5cztcblxuICAgIC8qKlxuICAgICAqIFNjcm9sbHMgY2hhdCB0byB0aGUgYm90dG9tLlxuICAgICAqL1xuICAgIG15LnNjcm9sbENoYXRUb0JvdHRvbSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJykuc2Nyb2xsVG9wKFxuICAgICAgICAgICAgICAgICQoJyNjaGF0Y29udmVyc2F0aW9uJylbMF0uc2Nyb2xsSGVpZ2h0KTtcbiAgICAgICAgfSwgNSk7XG4gICAgfTtcblxuXG4gICAgcmV0dXJuIG15O1xufShDaGF0IHx8IHt9KSk7XG5tb2R1bGUuZXhwb3J0cyA9IENoYXQ7IiwidmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi8uLi91dGlsL1VJVXRpbFwiKTtcblxuLyoqXG4gKiBMaXN0IHdpdGggc3VwcG9ydGVkIGNvbW1hbmRzLiBUaGUga2V5cyBhcmUgdGhlIG5hbWVzIG9mIHRoZSBjb21tYW5kcyBhbmRcbiAqIHRoZSB2YWx1ZSBpcyB0aGUgZnVuY3Rpb24gdGhhdCBwcm9jZXNzZXMgdGhlIG1lc3NhZ2UuXG4gKiBAdHlwZSB7e1N0cmluZzogZnVuY3Rpb259fVxuICovXG52YXIgY29tbWFuZHMgPSB7XG4gICAgXCJ0b3BpY1wiIDogcHJvY2Vzc1RvcGljXG59O1xuXG4vKipcbiAqIEV4dHJhY3RzIHRoZSBjb21tYW5kIGZyb20gdGhlIG1lc3NhZ2UuXG4gKiBAcGFyYW0gbWVzc2FnZSB0aGUgcmVjZWl2ZWQgbWVzc2FnZVxuICogQHJldHVybnMge3N0cmluZ30gdGhlIGNvbW1hbmRcbiAqL1xuZnVuY3Rpb24gZ2V0Q29tbWFuZChtZXNzYWdlKVxue1xuICAgIGlmKG1lc3NhZ2UpXG4gICAge1xuICAgICAgICBmb3IodmFyIGNvbW1hbmQgaW4gY29tbWFuZHMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlmKG1lc3NhZ2UuaW5kZXhPZihcIi9cIiArIGNvbW1hbmQpID09IDApXG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbW1hbmQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFwiXCI7XG59O1xuXG4vKipcbiAqIFByb2Nlc3NlcyB0aGUgZGF0YSBmb3IgdG9waWMgY29tbWFuZC5cbiAqIEBwYXJhbSBjb21tYW5kQXJndW1lbnRzIHRoZSBhcmd1bWVudHMgb2YgdGhlIHRvcGljIGNvbW1hbmQuXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NUb3BpYyhjb21tYW5kQXJndW1lbnRzKVxue1xuICAgIHZhciB0b3BpYyA9IFVJVXRpbC5lc2NhcGVIdG1sKGNvbW1hbmRBcmd1bWVudHMpO1xuICAgIEFQUC54bXBwLnNldFN1YmplY3QodG9waWMpO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdHMgbmV3IENvbW1hbmRQcm9jY2Vzc29yIGluc3RhbmNlIGZyb20gYSBtZXNzYWdlIHRoYXRcbiAqIGhhbmRsZXMgY29tbWFuZHMgcmVjZWl2ZWQgdmlhIGNoYXQgbWVzc2FnZXMuXG4gKiBAcGFyYW0gbWVzc2FnZSB0aGUgbWVzc2FnZVxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIENvbW1hbmRzUHJvY2Vzc29yKG1lc3NhZ2UpXG57XG5cblxuICAgIHZhciBjb21tYW5kID0gZ2V0Q29tbWFuZChtZXNzYWdlKTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG5hbWUgb2YgdGhlIGNvbW1hbmQuXG4gICAgICogQHJldHVybnMge1N0cmluZ30gdGhlIGNvbW1hbmRcbiAgICAgKi9cbiAgICB0aGlzLmdldENvbW1hbmQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICByZXR1cm4gY29tbWFuZDtcbiAgICB9O1xuXG5cbiAgICB2YXIgbWVzc2FnZUFyZ3VtZW50ID0gbWVzc2FnZS5zdWJzdHIoY29tbWFuZC5sZW5ndGggKyAyKTtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGFyZ3VtZW50cyBvZiB0aGUgY29tbWFuZC5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAqL1xuICAgIHRoaXMuZ2V0QXJndW1lbnQgPSBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICByZXR1cm4gbWVzc2FnZUFyZ3VtZW50O1xuICAgIH07XG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgdGhpcyBpbnN0YW5jZSBpcyB2YWxpZCBjb21tYW5kIG9yIG5vdC5cbiAqIEByZXR1cm5zIHtib29sZWFufVxuICovXG5Db21tYW5kc1Byb2Nlc3Nvci5wcm90b3R5cGUuaXNDb21tYW5kID0gZnVuY3Rpb24oKVxue1xuICAgIGlmKHRoaXMuZ2V0Q29tbWFuZCgpKVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICByZXR1cm4gZmFsc2U7XG59O1xuXG4vKipcbiAqIFByb2Nlc3NlcyB0aGUgY29tbWFuZC5cbiAqL1xuQ29tbWFuZHNQcm9jZXNzb3IucHJvdG90eXBlLnByb2Nlc3NDb21tYW5kID0gZnVuY3Rpb24oKVxue1xuICAgIGlmKCF0aGlzLmlzQ29tbWFuZCgpKVxuICAgICAgICByZXR1cm47XG5cbiAgICBjb21tYW5kc1t0aGlzLmdldENvbW1hbmQoKV0odGhpcy5nZXRBcmd1bWVudCgpKTtcblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb21tYW5kc1Byb2Nlc3NvcjsiLCJ2YXIgU21pbGV5cyA9IHJlcXVpcmUoXCIuL3NtaWxleXMuanNvblwiKTtcbi8qKlxuICogUHJvY2Vzc2VzIGxpbmtzIGFuZCBzbWlsZXlzIGluIFwiYm9keVwiXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NSZXBsYWNlbWVudHMoYm9keSlcbntcbiAgICAvL21ha2UgbGlua3MgY2xpY2thYmxlXG4gICAgYm9keSA9IGxpbmtpZnkoYm9keSk7XG5cbiAgICAvL2FkZCBzbWlsZXlzXG4gICAgYm9keSA9IHNtaWxpZnkoYm9keSk7XG5cbiAgICByZXR1cm4gYm9keTtcbn1cblxuLyoqXG4gKiBGaW5kcyBhbmQgcmVwbGFjZXMgYWxsIGxpbmtzIGluIHRoZSBsaW5rcyBpbiBcImJvZHlcIlxuICogd2l0aCB0aGVpciA8YSBocmVmPVwiXCI+PC9hPlxuICovXG5mdW5jdGlvbiBsaW5raWZ5KGlucHV0VGV4dClcbntcbiAgICB2YXIgcmVwbGFjZWRUZXh0LCByZXBsYWNlUGF0dGVybjEsIHJlcGxhY2VQYXR0ZXJuMiwgcmVwbGFjZVBhdHRlcm4zO1xuXG4gICAgLy9VUkxzIHN0YXJ0aW5nIHdpdGggaHR0cDovLywgaHR0cHM6Ly8sIG9yIGZ0cDovL1xuICAgIHJlcGxhY2VQYXR0ZXJuMSA9IC8oXFxiKGh0dHBzP3xmdHApOlxcL1xcL1stQS1aMC05KyZAI1xcLyU/PX5ffCE6LC47XSpbLUEtWjAtOSsmQCNcXC8lPX5ffF0pL2dpbTtcbiAgICByZXBsYWNlZFRleHQgPSBpbnB1dFRleHQucmVwbGFjZShyZXBsYWNlUGF0dGVybjEsICc8YSBocmVmPVwiJDFcIiB0YXJnZXQ9XCJfYmxhbmtcIj4kMTwvYT4nKTtcblxuICAgIC8vVVJMcyBzdGFydGluZyB3aXRoIFwid3d3LlwiICh3aXRob3V0IC8vIGJlZm9yZSBpdCwgb3IgaXQnZCByZS1saW5rIHRoZSBvbmVzIGRvbmUgYWJvdmUpLlxuICAgIHJlcGxhY2VQYXR0ZXJuMiA9IC8oXnxbXlxcL10pKHd3d1xcLltcXFNdKyhcXGJ8JCkpL2dpbTtcbiAgICByZXBsYWNlZFRleHQgPSByZXBsYWNlZFRleHQucmVwbGFjZShyZXBsYWNlUGF0dGVybjIsICckMTxhIGhyZWY9XCJodHRwOi8vJDJcIiB0YXJnZXQ9XCJfYmxhbmtcIj4kMjwvYT4nKTtcblxuICAgIC8vQ2hhbmdlIGVtYWlsIGFkZHJlc3NlcyB0byBtYWlsdG86OiBsaW5rcy5cbiAgICByZXBsYWNlUGF0dGVybjMgPSAvKChbYS16QS1aMC05XFwtXFxfXFwuXSkrQFthLXpBLVpcXF9dKz8oXFwuW2EtekEtWl17Miw2fSkrKS9naW07XG4gICAgcmVwbGFjZWRUZXh0ID0gcmVwbGFjZWRUZXh0LnJlcGxhY2UocmVwbGFjZVBhdHRlcm4zLCAnPGEgaHJlZj1cIm1haWx0bzokMVwiPiQxPC9hPicpO1xuXG4gICAgcmV0dXJuIHJlcGxhY2VkVGV4dDtcbn1cblxuLyoqXG4gKiBSZXBsYWNlcyBjb21tb24gc21pbGV5IHN0cmluZ3Mgd2l0aCBpbWFnZXNcbiAqL1xuZnVuY3Rpb24gc21pbGlmeShib2R5KVxue1xuICAgIGlmKCFib2R5KSB7XG4gICAgICAgIHJldHVybiBib2R5O1xuICAgIH1cblxuICAgIHZhciByZWdleHMgPSBTbWlsZXlzW1wicmVnZXhzXCJdO1xuICAgIGZvcih2YXIgc21pbGV5IGluIHJlZ2V4cykge1xuICAgICAgICBpZihyZWdleHMuaGFzT3duUHJvcGVydHkoc21pbGV5KSkge1xuICAgICAgICAgICAgYm9keSA9IGJvZHkucmVwbGFjZShyZWdleHNbc21pbGV5XSxcbiAgICAgICAgICAgICAgICAgICAgJzxpbWcgY2xhc3M9XCJzbWlsZXlcIiBzcmM9XCJpbWFnZXMvc21pbGV5cy8nICsgc21pbGV5ICsgJy5zdmdcIj4nKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBib2R5O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBwcm9jZXNzUmVwbGFjZW1lbnRzOiBwcm9jZXNzUmVwbGFjZW1lbnRzLFxuICAgIGxpbmtpZnk6IGxpbmtpZnlcbn07XG4iLCJtb2R1bGUuZXhwb3J0cz17XG4gICAgXCJzbWlsZXlzXCI6IHtcbiAgICAgICAgXCJzbWlsZXkxXCI6IFwiOilcIixcbiAgICAgICAgXCJzbWlsZXkyXCI6IFwiOihcIixcbiAgICAgICAgXCJzbWlsZXkzXCI6IFwiOkRcIixcbiAgICAgICAgXCJzbWlsZXk0XCI6IFwiKHkpXCIsXG4gICAgICAgIFwic21pbGV5NVwiOiBcIiA6UFwiLFxuICAgICAgICBcInNtaWxleTZcIjogXCIod2F2ZSlcIixcbiAgICAgICAgXCJzbWlsZXk3XCI6IFwiKGJsdXNoKVwiLFxuICAgICAgICBcInNtaWxleThcIjogXCIoY2h1Y2tsZSlcIixcbiAgICAgICAgXCJzbWlsZXk5XCI6IFwiKHNob2NrZWQpXCIsXG4gICAgICAgIFwic21pbGV5MTBcIjogXCI6KlwiLFxuICAgICAgICBcInNtaWxleTExXCI6IFwiKG4pXCIsXG4gICAgICAgIFwic21pbGV5MTJcIjogXCIoc2VhcmNoKVwiLFxuICAgICAgICBcInNtaWxleTEzXCI6IFwiIDwzXCIsXG4gICAgICAgIFwic21pbGV5MTRcIjogXCIob29wcylcIixcbiAgICAgICAgXCJzbWlsZXkxNVwiOiBcIihhbmdyeSlcIixcbiAgICAgICAgXCJzbWlsZXkxNlwiOiBcIihhbmdlbClcIixcbiAgICAgICAgXCJzbWlsZXkxN1wiOiBcIihzaWNrKVwiLFxuICAgICAgICBcInNtaWxleTE4XCI6IFwiOyhcIixcbiAgICAgICAgXCJzbWlsZXkxOVwiOiBcIihib21iKVwiLFxuICAgICAgICBcInNtaWxleTIwXCI6IFwiKGNsYXApXCIsXG4gICAgICAgIFwic21pbGV5MjFcIjogXCIgOylcIlxuICAgIH0sXG4gICAgXCJyZWdleHNcIjoge1xuICAgICAgICBcInNtaWxleTJcIjogLyg6LVxcKFxcKHw6LVxcKHw6XFwoXFwofDpcXCh8XFwoc2FkXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkzXCI6IC8oOi1cXClcXCl8OlxcKVxcKXxcXChsb2xcXCl8Oi1EfDpEKS9naSxcbiAgICAgICAgXCJzbWlsZXkxXCI6IC8oOi1cXCl8OlxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5NFwiOiAvKFxcKHlcXCl8XFwoWVxcKXxcXChva1xcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5NVwiOiAvKDotUHw6UHw6LXB8OnApL2dpLFxuICAgICAgICBcInNtaWxleTZcIjogLyhcXCh3YXZlXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk3XCI6IC8oXFwoYmx1c2hcXCkpL2dpLFxuICAgICAgICBcInNtaWxleThcIjogLyhcXChjaHVja2xlXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXk5XCI6IC8oOi0wfFxcKHNob2NrZWRcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTEwXCI6IC8oOi1cXCp8OlxcKnxcXChraXNzXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxMVwiOiAvKFxcKG5cXCkpL2dpLFxuICAgICAgICBcInNtaWxleTEyXCI6IC8oXFwoc2VhcmNoXFwpKS9nLFxuICAgICAgICBcInNtaWxleTEzXCI6IC8oPDN8Jmx0OzN8JmFtcDtsdDszfFxcKExcXCl8XFwobFxcKXxcXChIXFwpfFxcKGhcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTE0XCI6IC8oXFwob29wc1xcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTVcIjogLyhcXChhbmdyeVxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTZcIjogLyhcXChhbmdlbFxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MTdcIjogLyhcXChzaWNrXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkxOFwiOiAvKDstXFwoXFwofDtcXChcXCh8Oy1cXCh8O1xcKHw6XCJcXCh8OlwiLVxcKHw6fi1cXCh8On5cXCh8XFwodXBzZXRcXCkpL2dpLFxuICAgICAgICBcInNtaWxleTE5XCI6IC8oXFwoYm9tYlxcKSkvZ2ksXG4gICAgICAgIFwic21pbGV5MjBcIjogLyhcXChjbGFwXFwpKS9naSxcbiAgICAgICAgXCJzbWlsZXkyMVwiOiAvKDstXFwpfDtcXCl8Oy1cXClcXCl8O1xcKVxcKXw7LUR8O0R8XFwod2lua1xcKSkvZ2lcbiAgICB9XG59XG4iLCJcbnZhciBudW1iZXJPZkNvbnRhY3RzID0gMDtcbnZhciBub3RpZmljYXRpb25JbnRlcnZhbDtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBudW1iZXIgb2YgcGFydGljaXBhbnRzIGluIHRoZSBjb250YWN0IGxpc3QgYnV0dG9uIGFuZCBzZXRzXG4gKiB0aGUgZ2xvd1xuICogQHBhcmFtIGRlbHRhIGluZGljYXRlcyB3aGV0aGVyIGEgbmV3IHVzZXIgaGFzIGpvaW5lZCAoMSkgb3Igc29tZW9uZSBoYXNcbiAqIGxlZnQoLTEpXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZU51bWJlck9mUGFydGljaXBhbnRzKGRlbHRhKSB7XG4gICAgLy93aGVuIHRoZSB1c2VyIGlzIGFsb25lIHdlIGRvbid0IHNob3cgdGhlIG51bWJlciBvZiBwYXJ0aWNpcGFudHNcbiAgICBpZihudW1iZXJPZkNvbnRhY3RzID09PSAwKSB7XG4gICAgICAgICQoXCIjbnVtYmVyT2ZQYXJ0aWNpcGFudHNcIikudGV4dCgnJyk7XG4gICAgICAgIG51bWJlck9mQ29udGFjdHMgKz0gZGVsdGE7XG4gICAgfSBlbHNlIGlmKG51bWJlck9mQ29udGFjdHMgIT09IDAgJiYgIUNvbnRhY3RMaXN0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgIENvbnRhY3RMaXN0LnNldFZpc3VhbE5vdGlmaWNhdGlvbih0cnVlKTtcbiAgICAgICAgbnVtYmVyT2ZDb250YWN0cyArPSBkZWx0YTtcbiAgICAgICAgJChcIiNudW1iZXJPZlBhcnRpY2lwYW50c1wiKS50ZXh0KG51bWJlck9mQ29udGFjdHMpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBhdmF0YXIgZWxlbWVudC5cbiAqXG4gKiBAcmV0dXJuIHRoZSBuZXdseSBjcmVhdGVkIGF2YXRhciBlbGVtZW50XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUF2YXRhcihpZCkge1xuICAgIHZhciBhdmF0YXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTtcbiAgICBhdmF0YXIuY2xhc3NOYW1lID0gXCJpY29uLWF2YXRhciBhdmF0YXJcIjtcbiAgICBhdmF0YXIuc3JjID0gXCJodHRwczovL3d3dy5ncmF2YXRhci5jb20vYXZhdGFyL1wiICsgaWQgKyBcIj9kPXdhdmF0YXImc2l6ZT0zMFwiO1xuXG4gICAgcmV0dXJuIGF2YXRhcjtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBkaXNwbGF5IG5hbWUgcGFyYWdyYXBoLlxuICpcbiAqIEBwYXJhbSBkaXNwbGF5TmFtZSB0aGUgZGlzcGxheSBuYW1lIHRvIHNldFxuICovXG5mdW5jdGlvbiBjcmVhdGVEaXNwbGF5TmFtZVBhcmFncmFwaChkaXNwbGF5TmFtZSkge1xuICAgIHZhciBwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgncCcpO1xuICAgIHAuaW5uZXJUZXh0ID0gZGlzcGxheU5hbWU7XG5cbiAgICByZXR1cm4gcDtcbn1cblxuXG5mdW5jdGlvbiBzdG9wR2xvd2luZyhnbG93ZXIpIHtcbiAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChub3RpZmljYXRpb25JbnRlcnZhbCk7XG4gICAgbm90aWZpY2F0aW9uSW50ZXJ2YWwgPSBmYWxzZTtcbiAgICBnbG93ZXIucmVtb3ZlQ2xhc3MoJ2dsb3dpbmcnKTtcbiAgICBpZiAoIUNvbnRhY3RMaXN0LmlzVmlzaWJsZSgpKSB7XG4gICAgICAgIGdsb3dlci5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgfVxufVxuXG5cbi8qKlxuICogQ29udGFjdCBsaXN0LlxuICovXG52YXIgQ29udGFjdExpc3QgPSB7XG4gICAgLyoqXG4gICAgICogSW5kaWNhdGVzIGlmIHRoZSBjaGF0IGlzIGN1cnJlbnRseSB2aXNpYmxlLlxuICAgICAqXG4gICAgICogQHJldHVybiA8dHQ+dHJ1ZTwvdHQ+IGlmIHRoZSBjaGF0IGlzIGN1cnJlbnRseSB2aXNpYmxlLCA8dHQ+ZmFsc2U8L3R0PiAtXG4gICAgICogb3RoZXJ3aXNlXG4gICAgICovXG4gICAgaXNWaXNpYmxlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiAkKCcjY29udGFjdGxpc3QnKS5pcyhcIjp2aXNpYmxlXCIpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgY29udGFjdCBmb3IgdGhlIGdpdmVuIHBlZXJKaWQgaWYgc3VjaCBkb2Vzbid0IHlldCBleGlzdC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBwZWVySmlkIHRoZSBwZWVySmlkIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNvbnRhY3RcbiAgICAgKiBAcGFyYW0gaWQgdGhlIHVzZXIncyBlbWFpbCBvciB1c2VySWQgdXNlZCB0byBnZXQgdGhlIHVzZXIncyBhdmF0YXJcbiAgICAgKi9cbiAgICBlbnN1cmVBZGRDb250YWN0OiBmdW5jdGlvbiAocGVlckppZCwgaWQpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCk7XG5cbiAgICAgICAgdmFyIGNvbnRhY3QgPSAkKCcjY29udGFjdGxpc3Q+dWw+bGlbaWQ9XCInICsgcmVzb3VyY2VKaWQgKyAnXCJdJyk7XG5cbiAgICAgICAgaWYgKCFjb250YWN0IHx8IGNvbnRhY3QubGVuZ3RoIDw9IDApXG4gICAgICAgICAgICBDb250YWN0TGlzdC5hZGRDb250YWN0KHBlZXJKaWQsIGlkKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQWRkcyBhIGNvbnRhY3QgZm9yIHRoZSBnaXZlbiBwZWVyIGppZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBwZWVySmlkIHRoZSBqaWQgb2YgdGhlIGNvbnRhY3QgdG8gYWRkXG4gICAgICogQHBhcmFtIGlkIHRoZSBlbWFpbCBvciB1c2VySWQgb2YgdGhlIHVzZXJcbiAgICAgKi9cbiAgICBhZGRDb250YWN0OiBmdW5jdGlvbiAocGVlckppZCwgaWQpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCk7XG5cbiAgICAgICAgdmFyIGNvbnRhY3RsaXN0ID0gJCgnI2NvbnRhY3RsaXN0PnVsJyk7XG5cbiAgICAgICAgdmFyIG5ld0NvbnRhY3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuICAgICAgICBuZXdDb250YWN0LmlkID0gcmVzb3VyY2VKaWQ7XG4gICAgICAgIG5ld0NvbnRhY3QuY2xhc3NOYW1lID0gXCJjbGlja2FibGVcIjtcbiAgICAgICAgbmV3Q29udGFjdC5vbmNsaWNrID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQuY3VycmVudFRhcmdldC5jbGFzc05hbWUgPT09IFwiY2xpY2thYmxlXCIpIHtcbiAgICAgICAgICAgICAgICAkKENvbnRhY3RMaXN0KS50cmlnZ2VyKCdjb250YWN0Y2xpY2tlZCcsIFtwZWVySmlkXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgbmV3Q29udGFjdC5hcHBlbmRDaGlsZChjcmVhdGVBdmF0YXIoaWQpKTtcbiAgICAgICAgbmV3Q29udGFjdC5hcHBlbmRDaGlsZChjcmVhdGVEaXNwbGF5TmFtZVBhcmFncmFwaChcIlBhcnRpY2lwYW50XCIpKTtcblxuICAgICAgICB2YXIgY2xFbGVtZW50ID0gY29udGFjdGxpc3QuZ2V0KDApO1xuXG4gICAgICAgIGlmIChyZXNvdXJjZUppZCA9PT0gQVBQLnhtcHAubXlSZXNvdXJjZSgpXG4gICAgICAgICAgICAmJiAkKCcjY29udGFjdGxpc3Q+dWwgLnRpdGxlJylbMF0ubmV4dFNpYmxpbmcubmV4dFNpYmxpbmcpIHtcbiAgICAgICAgICAgIGNsRWxlbWVudC5pbnNlcnRCZWZvcmUobmV3Q29udGFjdCxcbiAgICAgICAgICAgICAgICAkKCcjY29udGFjdGxpc3Q+dWwgLnRpdGxlJylbMF0ubmV4dFNpYmxpbmcubmV4dFNpYmxpbmcpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY2xFbGVtZW50LmFwcGVuZENoaWxkKG5ld0NvbnRhY3QpO1xuICAgICAgICB9XG4gICAgICAgIHVwZGF0ZU51bWJlck9mUGFydGljaXBhbnRzKDEpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgY29udGFjdCBmb3IgdGhlIGdpdmVuIHBlZXIgamlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHBlZXJKaWQgdGhlIHBlZXJKaWQgY29ycmVzcG9uZGluZyB0byB0aGUgY29udGFjdCB0byByZW1vdmVcbiAgICAgKi9cbiAgICByZW1vdmVDb250YWN0OiBmdW5jdGlvbiAocGVlckppZCkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChwZWVySmlkKTtcblxuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyNjb250YWN0bGlzdD51bD5saVtpZD1cIicgKyByZXNvdXJjZUppZCArICdcIl0nKTtcblxuICAgICAgICBpZiAoY29udGFjdCAmJiBjb250YWN0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciBjb250YWN0bGlzdCA9ICQoJyNjb250YWN0bGlzdD51bCcpO1xuXG4gICAgICAgICAgICBjb250YWN0bGlzdC5nZXQoMCkucmVtb3ZlQ2hpbGQoY29udGFjdC5nZXQoMCkpO1xuXG4gICAgICAgICAgICB1cGRhdGVOdW1iZXJPZlBhcnRpY2lwYW50cygtMSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc2V0VmlzdWFsTm90aWZpY2F0aW9uOiBmdW5jdGlvbiAoc2hvdywgc3RvcEdsb3dpbmdJbikge1xuICAgICAgICB2YXIgZ2xvd2VyID0gJCgnI2NvbnRhY3RMaXN0QnV0dG9uJyk7XG5cbiAgICAgICAgaWYgKHNob3cgJiYgIW5vdGlmaWNhdGlvbkludGVydmFsKSB7XG4gICAgICAgICAgICBub3RpZmljYXRpb25JbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZ2xvd2VyLnRvZ2dsZUNsYXNzKCdhY3RpdmUgZ2xvd2luZycpO1xuICAgICAgICAgICAgfSwgODAwKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmICghc2hvdyAmJiBub3RpZmljYXRpb25JbnRlcnZhbCkge1xuICAgICAgICAgICAgc3RvcEdsb3dpbmcoZ2xvd2VyKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc3RvcEdsb3dpbmdJbikge1xuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgc3RvcEdsb3dpbmcoZ2xvd2VyKTtcbiAgICAgICAgICAgIH0sIHN0b3BHbG93aW5nSW4pO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIHNldENsaWNrYWJsZTogZnVuY3Rpb24gKHJlc291cmNlSmlkLCBpc0NsaWNrYWJsZSkge1xuICAgICAgICB2YXIgY29udGFjdCA9ICQoJyNjb250YWN0bGlzdD51bD5saVtpZD1cIicgKyByZXNvdXJjZUppZCArICdcIl0nKTtcbiAgICAgICAgaWYgKGlzQ2xpY2thYmxlKSB7XG4gICAgICAgICAgICBjb250YWN0LmFkZENsYXNzKCdjbGlja2FibGUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnRhY3QucmVtb3ZlQ2xhc3MoJ2NsaWNrYWJsZScpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIG9uRGlzcGxheU5hbWVDaGFuZ2U6IGZ1bmN0aW9uIChwZWVySmlkLCBkaXNwbGF5TmFtZSkge1xuICAgICAgICBpZiAocGVlckppZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInKVxuICAgICAgICAgICAgcGVlckppZCA9IEFQUC54bXBwLm15SmlkKCk7XG5cbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCk7XG5cbiAgICAgICAgdmFyIGNvbnRhY3ROYW1lID0gJCgnI2NvbnRhY3RsaXN0ICMnICsgcmVzb3VyY2VKaWQgKyAnPnAnKTtcblxuICAgICAgICBpZiAoY29udGFjdE5hbWUgJiYgZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgIGNvbnRhY3ROYW1lLmh0bWwoZGlzcGxheU5hbWUpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29udGFjdExpc3Q7IiwidmFyIGVtYWlsID0gJyc7XG52YXIgZGlzcGxheU5hbWUgPSAnJztcbnZhciB1c2VySWQ7XG5cblxuZnVuY3Rpb24gc3VwcG9ydHNMb2NhbFN0b3JhZ2UoKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuICdsb2NhbFN0b3JhZ2UnIGluIHdpbmRvdyAmJiB3aW5kb3cubG9jYWxTdG9yYWdlICE9PSBudWxsO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJsb2NhbHN0b3JhZ2UgaXMgbm90IHN1cHBvcnRlZFwiKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuXG5mdW5jdGlvbiBnZW5lcmF0ZVVuaXF1ZUlkKCkge1xuICAgIGZ1bmN0aW9uIF9wOCgpIHtcbiAgICAgICAgcmV0dXJuIChNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDE2KStcIjAwMDAwMDAwMFwiKS5zdWJzdHIoMiw4KTtcbiAgICB9XG4gICAgcmV0dXJuIF9wOCgpICsgX3A4KCkgKyBfcDgoKSArIF9wOCgpO1xufVxuXG5pZihzdXBwb3J0c0xvY2FsU3RvcmFnZSgpKSB7XG4gICAgaWYoIXdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQpIHtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCA9IGdlbmVyYXRlVW5pcXVlSWQoKTtcbiAgICAgICAgY29uc29sZS5sb2coXCJnZW5lcmF0ZWQgaWRcIiwgd2luZG93LmxvY2FsU3RvcmFnZS5qaXRzaU1lZXRJZCk7XG4gICAgfVxuICAgIHVzZXJJZCA9IHdpbmRvdy5sb2NhbFN0b3JhZ2Uuaml0c2lNZWV0SWQgfHwgJyc7XG4gICAgZW1haWwgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmVtYWlsIHx8ICcnO1xuICAgIGRpc3BsYXlOYW1lID0gd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSB8fCAnJztcbn0gZWxzZSB7XG4gICAgY29uc29sZS5sb2coXCJsb2NhbCBzdG9yYWdlIGlzIG5vdCBzdXBwb3J0ZWRcIik7XG4gICAgdXNlcklkID0gZ2VuZXJhdGVVbmlxdWVJZCgpO1xufVxuXG52YXIgU2V0dGluZ3MgPVxue1xuICAgIHNldERpc3BsYXlOYW1lOiBmdW5jdGlvbiAobmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgZGlzcGxheU5hbWUgPSBuZXdEaXNwbGF5TmFtZTtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSA9IGRpc3BsYXlOYW1lO1xuICAgICAgICByZXR1cm4gZGlzcGxheU5hbWU7XG4gICAgfSxcbiAgICBzZXRFbWFpbDogZnVuY3Rpb24obmV3RW1haWwpXG4gICAge1xuICAgICAgICBlbWFpbCA9IG5ld0VtYWlsO1xuICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLmVtYWlsID0gbmV3RW1haWw7XG4gICAgICAgIHJldHVybiBlbWFpbDtcbiAgICB9LFxuICAgIGdldFNldHRpbmdzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBlbWFpbDogZW1haWwsXG4gICAgICAgICAgICBkaXNwbGF5TmFtZTogZGlzcGxheU5hbWUsXG4gICAgICAgICAgICB1aWQ6IHVzZXJJZFxuICAgICAgICB9O1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2V0dGluZ3M7XG4iLCJ2YXIgQXZhdGFyID0gcmVxdWlyZShcIi4uLy4uL2F2YXRhci9BdmF0YXJcIik7XG52YXIgU2V0dGluZ3MgPSByZXF1aXJlKFwiLi9TZXR0aW5nc1wiKTtcbnZhciBVSVV0aWwgPSByZXF1aXJlKFwiLi4vLi4vdXRpbC9VSVV0aWxcIik7XG52YXIgbGFuZ3VhZ2VzID0gcmVxdWlyZShcIi4uLy4uLy4uLy4uL3NlcnZpY2UvdHJhbnNsYXRpb24vbGFuZ3VhZ2VzXCIpO1xuXG5mdW5jdGlvbiBnZW5lcmF0ZUxhbmd1YWdlc1NlbGVjdEJveCgpXG57XG4gICAgdmFyIGN1cnJlbnRMYW5nID0gQVBQLnRyYW5zbGF0aW9uLmdldEN1cnJlbnRMYW5ndWFnZSgpO1xuICAgIHZhciBodG1sID0gXCI8c2VsZWN0IGlkPVxcXCJsYW5ndWFnZXNfc2VsZWN0Ym94XFxcIj5cIjtcbiAgICB2YXIgbGFuZ0FycmF5ID0gbGFuZ3VhZ2VzLmdldExhbmd1YWdlcygpO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBsYW5nQXJyYXkubGVuZ3RoOyBpKyspXG4gICAge1xuICAgICAgICB2YXIgbGFuZyA9IGxhbmdBcnJheVtpXTtcbiAgICAgICAgaHRtbCArPSBcIjxvcHRpb24gXCI7XG4gICAgICAgIGlmKGxhbmcgPT09IGN1cnJlbnRMYW5nKVxuICAgICAgICAgICAgaHRtbCArPSBcInNlbGVjdGVkIFwiO1xuICAgICAgICBodG1sICs9IFwidmFsdWU9XFxcIlwiICsgbGFuZyArIFwiXFxcIiBkYXRhLWkxOG49J2xhbmd1YWdlczpcIiArIGxhbmcgKyBcIic+XCI7XG4gICAgICAgIGh0bWwgKz0gXCI8L29wdGlvbj5cIjtcblxuICAgIH1cblxuICAgIHJldHVybiBodG1sICsgXCI8L3NlbGVjdD5cIjtcbn1cblxuXG52YXIgU2V0dGluZ3NNZW51ID0ge1xuXG4gICAgaW5pdDogZnVuY3Rpb24gKCkge1xuICAgICAgICAkKFwiI3VwZGF0ZVNldHRpbmdzXCIpLmJlZm9yZShnZW5lcmF0ZUxhbmd1YWdlc1NlbGVjdEJveCgpKTtcbiAgICAgICAgJCgnI3NldHRpbmdzbWVudT5pbnB1dCcpLmtleXVwKGZ1bmN0aW9uKGV2ZW50KXtcbiAgICAgICAgICAgIGlmKGV2ZW50LmtleUNvZGUgPT09IDEzKSB7Ly9lbnRlclxuICAgICAgICAgICAgICAgIFNldHRpbmdzTWVudS51cGRhdGUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgJChcIiN1cGRhdGVTZXR0aW5nc1wiKS5jbGljayhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBTZXR0aW5nc01lbnUudXBkYXRlKCk7XG4gICAgICAgIH0pO1xuICAgIH0sXG5cbiAgICB1cGRhdGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgbmV3RGlzcGxheU5hbWUgPSBVSVV0aWwuZXNjYXBlSHRtbCgkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUpO1xuICAgICAgICB2YXIgbmV3RW1haWwgPSBVSVV0aWwuZXNjYXBlSHRtbCgkKCcjc2V0RW1haWwnKS5nZXQoMCkudmFsdWUpO1xuXG4gICAgICAgIGlmKG5ld0Rpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSBTZXR0aW5ncy5zZXREaXNwbGF5TmFtZShuZXdEaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICBBUFAueG1wcC5hZGRUb1ByZXNlbmNlKFwiZGlzcGxheU5hbWVcIiwgZGlzcGxheU5hbWUsIHRydWUpO1xuICAgICAgICB9XG5cbiAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnNldExhbmd1YWdlKCQoXCIjbGFuZ3VhZ2VzX3NlbGVjdGJveFwiKS52YWwoKSk7XG5cbiAgICAgICAgQVBQLnhtcHAuYWRkVG9QcmVzZW5jZShcImVtYWlsXCIsIG5ld0VtYWlsKTtcbiAgICAgICAgdmFyIGVtYWlsID0gU2V0dGluZ3Muc2V0RW1haWwobmV3RW1haWwpO1xuXG5cbiAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIoQVBQLnhtcHAubXlKaWQoKSwgZW1haWwpO1xuICAgIH0sXG5cbiAgICBpc1Zpc2libGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJCgnI3NldHRpbmdzbWVudScpLmlzKCc6dmlzaWJsZScpO1xuICAgIH0sXG5cbiAgICBzZXREaXNwbGF5TmFtZTogZnVuY3Rpb24obmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgdmFyIGRpc3BsYXlOYW1lID0gU2V0dGluZ3Muc2V0RGlzcGxheU5hbWUobmV3RGlzcGxheU5hbWUpO1xuICAgICAgICAkKCcjc2V0RGlzcGxheU5hbWUnKS5nZXQoMCkudmFsdWUgPSBkaXNwbGF5TmFtZTtcbiAgICB9LFxuXG4gICAgb25EaXNwbGF5TmFtZUNoYW5nZTogZnVuY3Rpb24ocGVlckppZCwgbmV3RGlzcGxheU5hbWUpIHtcbiAgICAgICAgaWYocGVlckppZCA9PT0gJ2xvY2FsVmlkZW9Db250YWluZXInIHx8XG4gICAgICAgICAgICBwZWVySmlkID09PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICB0aGlzLnNldERpc3BsYXlOYW1lKG5ld0Rpc3BsYXlOYW1lKTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cblxubW9kdWxlLmV4cG9ydHMgPSBTZXR0aW5nc01lbnU7IiwidmFyIFBhbmVsVG9nZ2xlciA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvU2lkZVBhbmVsVG9nZ2xlclwiKTtcblxudmFyIGJ1dHRvbkhhbmRsZXJzID0ge1xuICAgIFwiYm90dG9tX3Rvb2xiYXJfY29udGFjdF9saXN0XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQm90dG9tVG9vbGJhci50b2dnbGVDb250YWN0TGlzdCgpO1xuICAgIH0sXG4gICAgXCJib3R0b21fdG9vbGJhcl9maWxtX3N0cmlwXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQm90dG9tVG9vbGJhci50b2dnbGVGaWxtU3RyaXAoKTtcbiAgICB9LFxuICAgIFwiYm90dG9tX3Rvb2xiYXJfY2hhdFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEJvdHRvbVRvb2xiYXIudG9nZ2xlQ2hhdCgpO1xuICAgIH1cbn07XG5cbnZhciBCb3R0b21Ub29sYmFyID0gKGZ1bmN0aW9uIChteSkge1xuICAgIG15LmluaXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvcih2YXIgayBpbiBidXR0b25IYW5kbGVycylcbiAgICAgICAgICAgICQoXCIjXCIgKyBrKS5jbGljayhidXR0b25IYW5kbGVyc1trXSk7XG4gICAgfTtcblxuICAgIG15LnRvZ2dsZUNoYXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZUNoYXQoKTtcbiAgICB9O1xuXG4gICAgbXkudG9nZ2xlQ29udGFjdExpc3QgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZUNvbnRhY3RMaXN0KCk7XG4gICAgfTtcblxuICAgIG15LnRvZ2dsZUZpbG1TdHJpcCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgZmlsbXN0cmlwID0gJChcIiNyZW1vdGVWaWRlb3NcIik7XG4gICAgICAgIGZpbG1zdHJpcC50b2dnbGVDbGFzcyhcImhpZGRlblwiKTtcbiAgICB9O1xuXG4gICAgJChkb2N1bWVudCkuYmluZChcInJlbW90ZXZpZGVvLnJlc2l6ZWRcIiwgZnVuY3Rpb24gKGV2ZW50LCB3aWR0aCwgaGVpZ2h0KSB7XG4gICAgICAgIHZhciBib3R0b20gPSAoaGVpZ2h0IC0gJCgnI2JvdHRvbVRvb2xiYXInKS5vdXRlckhlaWdodCgpKS8yICsgMTg7XG5cbiAgICAgICAgJCgnI2JvdHRvbVRvb2xiYXInKS5jc3Moe2JvdHRvbTogYm90dG9tICsgJ3B4J30pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIG15O1xufShCb3R0b21Ub29sYmFyIHx8IHt9KSk7XG5cbm1vZHVsZS5leHBvcnRzID0gQm90dG9tVG9vbGJhcjtcbiIsIi8qIGdsb2JhbCAkLCBidXR0b25DbGljaywgY29uZmlnLCBsb2NrUm9vbSxcbiAgIHNldFNoYXJlZEtleSwgVXRpbCAqL1xudmFyIG1lc3NhZ2VIYW5kbGVyID0gcmVxdWlyZShcIi4uL3V0aWwvTWVzc2FnZUhhbmRsZXJcIik7XG52YXIgQm90dG9tVG9vbGJhciA9IHJlcXVpcmUoXCIuL0JvdHRvbVRvb2xiYXJcIik7XG52YXIgUHJlemkgPSByZXF1aXJlKFwiLi4vcHJlemkvUHJlemlcIik7XG52YXIgRXRoZXJwYWQgPSByZXF1aXJlKFwiLi4vZXRoZXJwYWQvRXRoZXJwYWRcIik7XG52YXIgUGFuZWxUb2dnbGVyID0gcmVxdWlyZShcIi4uL3NpZGVfcGFubmVscy9TaWRlUGFuZWxUb2dnbGVyXCIpO1xudmFyIEF1dGhlbnRpY2F0aW9uID0gcmVxdWlyZShcIi4uL2F1dGhlbnRpY2F0aW9uL0F1dGhlbnRpY2F0aW9uXCIpO1xudmFyIFVJVXRpbCA9IHJlcXVpcmUoXCIuLi91dGlsL1VJVXRpbFwiKTtcblxudmFyIHJvb21VcmwgPSBudWxsO1xudmFyIHNoYXJlZEtleSA9ICcnO1xudmFyIFVJID0gbnVsbDtcblxudmFyIGJ1dHRvbkhhbmRsZXJzID1cbntcbiAgICBcInRvb2xiYXJfYnV0dG9uX211dGVcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gQVBQLlVJLnRvZ2dsZUF1ZGlvKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2NhbWVyYVwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBBUFAuVUkudG9nZ2xlVmlkZW8oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fYXV0aGVudGljYXRpb25cIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gVG9vbGJhci5hdXRoZW50aWNhdGVDbGlja2VkKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3JlY29yZFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0b2dnbGVSZWNvcmRpbmcoKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fc2VjdXJpdHlcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gVG9vbGJhci5vcGVuTG9ja0RpYWxvZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9saW5rXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIFRvb2xiYXIub3BlbkxpbmtEaWFsb2coKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fY2hhdFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBCb3R0b21Ub29sYmFyLnRvZ2dsZUNoYXQoKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fcHJlemlcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gUHJlemkub3BlblByZXppRGlhbG9nKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX2V0aGVycGFkXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIEV0aGVycGFkLnRvZ2dsZUV0aGVycGFkKDApO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9kZXNrdG9wc2hhcmluZ1wiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBBUFAuZGVza3RvcHNoYXJpbmcudG9nZ2xlU2NyZWVuU2hhcmluZygpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9mdWxsU2NyZWVuXCI6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIFVJVXRpbC5idXR0b25DbGljayhcIiNmdWxsU2NyZWVuXCIsIFwiaWNvbi1mdWxsLXNjcmVlbiBpY29uLWV4aXQtZnVsbC1zY3JlZW5cIik7XG4gICAgICAgIHJldHVybiBUb29sYmFyLnRvZ2dsZUZ1bGxTY3JlZW4oKTtcbiAgICB9LFxuICAgIFwidG9vbGJhcl9idXR0b25fc2lwXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNhbGxTaXBCdXR0b25DbGlja2VkKCk7XG4gICAgfSxcbiAgICBcInRvb2xiYXJfYnV0dG9uX3NldHRpbmdzXCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgUGFuZWxUb2dnbGVyLnRvZ2dsZVNldHRpbmdzTWVudSgpO1xuICAgIH0sXG4gICAgXCJ0b29sYmFyX2J1dHRvbl9oYW5ndXBcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gaGFuZ3VwKCk7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gaGFuZ3VwKCkge1xuICAgIEFQUC54bXBwLmRpc3Bvc2VDb25mZXJlbmNlKCk7XG4gICAgaWYoY29uZmlnLmVuYWJsZVdlbGNvbWVQYWdlKVxuICAgIHtcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2Uud2VsY29tZVBhZ2VEaXNhYmxlZCA9IGZhbHNlO1xuICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLnBhdGhuYW1lID0gXCIvXCI7XG4gICAgICAgIH0sIDEwMDAwKTtcblxuICAgIH1cblxuICAgIFVJLm1lc3NhZ2VIYW5kbGVyLm9wZW5EaWFsb2coXG4gICAgICAgIFwiU2Vzc2lvbiBUZXJtaW5hdGVkXCIsXG4gICAgICAgIFwiWW91IGh1bmcgdXAgdGhlIGNhbGxcIixcbiAgICAgICAgdHJ1ZSxcbiAgICAgICAgeyBcIkpvaW4gYWdhaW5cIjogdHJ1ZSB9LFxuICAgICAgICBmdW5jdGlvbihldmVudCwgdmFsdWUsIG1lc3NhZ2UsIGZvcm1WYWxzKVxuICAgICAgICB7XG4gICAgICAgICAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICApO1xufVxuXG4vKipcbiAqIFN0YXJ0cyBvciBzdG9wcyB0aGUgcmVjb3JkaW5nIGZvciB0aGUgY29uZmVyZW5jZS5cbiAqL1xuXG5mdW5jdGlvbiB0b2dnbGVSZWNvcmRpbmcoKSB7XG4gICAgQVBQLnhtcHAudG9nZ2xlUmVjb3JkaW5nKGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIub3BlblR3b0J1dHRvbkRpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICc8aDI+RW50ZXIgcmVjb3JkaW5nIHRva2VuPC9oMj4nICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwicmVjb3JkaW5nVG9rZW5cIiB0eXBlPVwidGV4dFwiICcgK1xuICAgICAgICAgICAgICAgICdwbGFjZWhvbGRlcj1cInRva2VuXCIgYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgIFwiU2F2ZVwiLFxuICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYsIG0sIGYpIHtcbiAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdG9rZW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVjb3JkaW5nVG9rZW4nKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAodG9rZW4udmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKFVJVXRpbC5lc2NhcGVIdG1sKHRva2VuLnZhbHVlKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3JlY29yZGluZ1Rva2VuJykuZm9jdXMoKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSwgVG9vbGJhci5zZXRSZWNvcmRpbmdCdXR0b25TdGF0ZSwgVG9vbGJhci5zZXRSZWNvcmRpbmdCdXR0b25TdGF0ZSk7XG59XG5cbi8qKlxuICogTG9ja3MgLyB1bmxvY2tzIHRoZSByb29tLlxuICovXG5mdW5jdGlvbiBsb2NrUm9vbShsb2NrKSB7XG4gICAgdmFyIGN1cnJlbnRTaGFyZWRLZXkgPSAnJztcbiAgICBpZiAobG9jaylcbiAgICAgICAgY3VycmVudFNoYXJlZEtleSA9IHNoYXJlZEtleTtcblxuICAgIEFQUC54bXBwLmxvY2tSb29tKGN1cnJlbnRTaGFyZWRLZXksIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgLy8gcGFzc3dvcmQgaXMgcmVxdWlyZWRcbiAgICAgICAgaWYgKHNoYXJlZEtleSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldCByb29tIHBhc3N3b3JkJyk7XG4gICAgICAgICAgICBUb29sYmFyLmxvY2tMb2NrQnV0dG9uKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygncmVtb3ZlZCByb29tIHBhc3N3b3JkJyk7XG4gICAgICAgICAgICBUb29sYmFyLnVubG9ja0xvY2tCdXR0b24oKTtcbiAgICAgICAgfVxuICAgIH0sIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdzZXR0aW5nIHBhc3N3b3JkIGZhaWxlZCcsIGVycik7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcignTG9jayBmYWlsZWQnLFxuICAgICAgICAgICAgJ0ZhaWxlZCB0byBsb2NrIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgIGVycik7XG4gICAgICAgIFRvb2xiYXIuc2V0U2hhcmVkS2V5KCcnKTtcbiAgICB9LCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGNvbnNvbGUud2Fybigncm9vbSBwYXNzd29yZHMgbm90IHN1cHBvcnRlZCcpO1xuICAgICAgICBtZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ1dhcm5pbmcnLFxuICAgICAgICAgICAgJ1Jvb20gcGFzc3dvcmRzIGFyZSBjdXJyZW50bHkgbm90IHN1cHBvcnRlZC4nKTtcbiAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkoJycpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBJbnZpdGUgcGFydGljaXBhbnRzIHRvIGNvbmZlcmVuY2UuXG4gKi9cbmZ1bmN0aW9uIGludml0ZVBhcnRpY2lwYW50cygpIHtcbiAgICBpZiAocm9vbVVybCA9PT0gbnVsbClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgdmFyIHNoYXJlZEtleVRleHQgPSBcIlwiO1xuICAgIGlmIChzaGFyZWRLZXkgJiYgc2hhcmVkS2V5Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgc2hhcmVkS2V5VGV4dCA9XG4gICAgICAgICAgICBcIlRoaXMgY29uZmVyZW5jZSBpcyBwYXNzd29yZCBwcm90ZWN0ZWQuIFBsZWFzZSB1c2UgdGhlIFwiICtcbiAgICAgICAgICAgIFwiZm9sbG93aW5nIHBpbiB3aGVuIGpvaW5pbmc6JTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICAgICAgc2hhcmVkS2V5ICsgXCIlMEQlMEElMEQlMEFcIjtcbiAgICB9XG5cbiAgICB2YXIgY29uZmVyZW5jZU5hbWUgPSByb29tVXJsLnN1YnN0cmluZyhyb29tVXJsLmxhc3RJbmRleE9mKCcvJykgKyAxKTtcbiAgICB2YXIgc3ViamVjdCA9IFwiSW52aXRhdGlvbiB0byBhIFwiICsgaW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FICsgXCIgKFwiICsgY29uZmVyZW5jZU5hbWUgKyBcIilcIjtcbiAgICB2YXIgYm9keSA9IFwiSGV5IHRoZXJlLCBJJTI3ZCBsaWtlIHRvIGludml0ZSB5b3UgdG8gYSBcIiArIGludGVyZmFjZUNvbmZpZy5BUFBfTkFNRSArXG4gICAgICAgIFwiIGNvbmZlcmVuY2UgSSUyN3ZlIGp1c3Qgc2V0IHVwLiUwRCUwQSUwRCUwQVwiICtcbiAgICAgICAgXCJQbGVhc2UgY2xpY2sgb24gdGhlIGZvbGxvd2luZyBsaW5rIGluIG9yZGVyXCIgK1xuICAgICAgICBcIiB0byBqb2luIHRoZSBjb25mZXJlbmNlLiUwRCUwQSUwRCUwQVwiICtcbiAgICAgICAgcm9vbVVybCArXG4gICAgICAgIFwiJTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICBzaGFyZWRLZXlUZXh0ICtcbiAgICAgICAgXCJOb3RlIHRoYXQgXCIgKyBpbnRlcmZhY2VDb25maWcuQVBQX05BTUUgKyBcIiBpcyBjdXJyZW50bHlcIiArXG4gICAgICAgIFwiIG9ubHkgc3VwcG9ydGVkIGJ5IENocm9taXVtLFwiICtcbiAgICAgICAgXCIgR29vZ2xlIENocm9tZSBhbmQgT3BlcmEsIHNvIHlvdSBuZWVkXCIgK1xuICAgICAgICBcIiB0byBiZSB1c2luZyBvbmUgb2YgdGhlc2UgYnJvd3NlcnMuJTBEJTBBJTBEJTBBXCIgK1xuICAgICAgICBcIlRhbGsgdG8geW91IGluIGEgc2VjIVwiO1xuXG4gICAgaWYgKHdpbmRvdy5sb2NhbFN0b3JhZ2UuZGlzcGxheW5hbWUpIHtcbiAgICAgICAgYm9keSArPSBcIiUwRCUwQSUwRCUwQVwiICsgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZTtcbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLklOVklUQVRJT05fUE9XRVJFRF9CWSkge1xuICAgICAgICBib2R5ICs9IFwiJTBEJTBBJTBEJTBBLS0lMEQlMEFwb3dlcmVkIGJ5IGppdHNpLm9yZ1wiO1xuICAgIH1cblxuICAgIHdpbmRvdy5vcGVuKFwibWFpbHRvOj9zdWJqZWN0PVwiICsgc3ViamVjdCArIFwiJmJvZHk9XCIgKyBib2R5LCAnX2JsYW5rJyk7XG59XG5cbmZ1bmN0aW9uIGNhbGxTaXBCdXR0b25DbGlja2VkKClcbntcbiAgICB2YXIgZGVmYXVsdE51bWJlclxuICAgICAgICA9IGNvbmZpZy5kZWZhdWx0U2lwTnVtYmVyID8gY29uZmlnLmRlZmF1bHRTaXBOdW1iZXIgOiAnJztcblxuICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCxcbiAgICAgICAgJzxoMj5FbnRlciBTSVAgbnVtYmVyPC9oMj4nICtcbiAgICAgICAgJzxpbnB1dCBpZD1cInNpcE51bWJlclwiIHR5cGU9XCJ0ZXh0XCInICtcbiAgICAgICAgJyB2YWx1ZT1cIicgKyBkZWZhdWx0TnVtYmVyICsgJ1wiIGF1dG9mb2N1cz4nLFxuICAgICAgICBmYWxzZSxcbiAgICAgICAgXCJEaWFsXCIsXG4gICAgICAgIGZ1bmN0aW9uIChlLCB2LCBtLCBmKSB7XG4gICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgIHZhciBudW1iZXJJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzaXBOdW1iZXInKTtcbiAgICAgICAgICAgICAgICBpZiAobnVtYmVySW5wdXQudmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLnhtcHAuZGlhbChudW1iZXJJbnB1dC52YWx1ZSwgJ2Zyb21udW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgVUkuZ2V0Um9vbU5hbWUoKSwgc2hhcmVkS2V5KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3NpcE51bWJlcicpLmZvY3VzKCk7XG4gICAgICAgIH1cbiAgICApO1xufVxuXG52YXIgVG9vbGJhciA9IChmdW5jdGlvbiAobXkpIHtcblxuICAgIG15LmluaXQgPSBmdW5jdGlvbiAodWkpIHtcbiAgICAgICAgZm9yKHZhciBrIGluIGJ1dHRvbkhhbmRsZXJzKVxuICAgICAgICAgICAgJChcIiNcIiArIGspLmNsaWNrKGJ1dHRvbkhhbmRsZXJzW2tdKTtcbiAgICAgICAgVUkgPSB1aTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHNoYXJlZCBrZXlcbiAgICAgKiBAcGFyYW0gc0tleSB0aGUgc2hhcmVkIGtleVxuICAgICAqL1xuICAgIG15LnNldFNoYXJlZEtleSA9IGZ1bmN0aW9uIChzS2V5KSB7XG4gICAgICAgIHNoYXJlZEtleSA9IHNLZXk7XG4gICAgfTtcblxuICAgIG15LmF1dGhlbnRpY2F0ZUNsaWNrZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIEF1dGhlbnRpY2F0aW9uLmZvY3VzQXV0aGVudGljYXRpb25XaW5kb3coKTtcbiAgICAgICAgLy8gR2V0IGF1dGhlbnRpY2F0aW9uIFVSTFxuICAgICAgICBBUFAueG1wcC5nZXRBdXRoVXJsKEFQUC5VSS5nZXRSb29tTmFtZSgpLCBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgICAgICAvLyBPcGVuIHBvcHVwIHdpdGggYXV0aGVudGljYXRpb24gVVJMXG4gICAgICAgICAgICB2YXIgYXV0aGVudGljYXRpb25XaW5kb3cgPSBBdXRoZW50aWNhdGlvbi5jcmVhdGVBdXRoZW50aWNhdGlvbldpbmRvdyhmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgLy8gT24gcG9wdXAgY2xvc2VkIC0gcmV0cnkgcm9vbSBhbGxvY2F0aW9uXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoQVBQLlVJLmdldFJvb21OYW1lKCksIEFQUC5VSS5jaGVja0Zvck5pY2tuYW1lQW5kSm9pbik7XG4gICAgICAgICAgICB9LCB1cmwpO1xuICAgICAgICAgICAgaWYgKCFhdXRoZW50aWNhdGlvbldpbmRvdykge1xuICAgICAgICAgICAgICAgIFRvb2xiYXIuc2hvd0F1dGhlbnRpY2F0ZUJ1dHRvbih0cnVlKTtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhcbiAgICAgICAgICAgICAgICAgICAgbnVsbCwgXCJZb3VyIGJyb3dzZXIgaXMgYmxvY2tpbmcgcG9wdXAgd2luZG93cyBmcm9tIHRoaXMgc2l0ZS5cIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBQbGVhc2UgZW5hYmxlIHBvcHVwcyBpbiB5b3VyIGJyb3dzZXIgc2VjdXJpdHkgc2V0dGluZ3NcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBhbmQgdHJ5IGFnYWluLlwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIHJvb20gaW52aXRlIHVybC5cbiAgICAgKi9cbiAgICBteS51cGRhdGVSb29tVXJsID0gZnVuY3Rpb24gKG5ld1Jvb21VcmwpIHtcbiAgICAgICAgcm9vbVVybCA9IG5ld1Jvb21Vcmw7XG5cbiAgICAgICAgLy8gSWYgdGhlIGludml0ZSBkaWFsb2cgaGFzIGJlZW4gYWxyZWFkeSBvcGVuZWQgd2UgdXBkYXRlIHRoZSBpbmZvcm1hdGlvbi5cbiAgICAgICAgdmFyIGludml0ZUxpbmsgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52aXRlTGlua1JlZicpO1xuICAgICAgICBpZiAoaW52aXRlTGluaykge1xuICAgICAgICAgICAgaW52aXRlTGluay52YWx1ZSA9IHJvb21Vcmw7XG4gICAgICAgICAgICBpbnZpdGVMaW5rLnNlbGVjdCgpO1xuICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2pxaV9zdGF0ZTBfYnV0dG9uSW52aXRlJykuZGlzYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlcyBhbmQgZW5hYmxlcyBzb21lIG9mIHRoZSBidXR0b25zLlxuICAgICAqL1xuICAgIG15LnNldHVwQnV0dG9uc0Zyb21Db25maWcgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChjb25maWcuZGlzYWJsZVByZXppKVxuICAgICAgICB7XG4gICAgICAgICAgICAkKFwiI3ByZXppX2J1dHRvblwiKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIGxvY2sgcm9vbSBkaWFsb2cuXG4gICAgICovXG4gICAgbXkub3BlbkxvY2tEaWFsb2cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIE9ubHkgdGhlIGZvY3VzIGlzIGFibGUgdG8gc2V0IGEgc2hhcmVkIGtleS5cbiAgICAgICAgaWYgKCFBUFAueG1wcC5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICBpZiAoc2hhcmVkS2V5KSB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2cobnVsbCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiVGhpcyBjb252ZXJzYXRpb24gaXMgY3VycmVudGx5IHByb3RlY3RlZCBieVwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGEgcGFzc3dvcmQuIE9ubHkgdGhlIG93bmVyIG9mIHRoZSBjb25mZXJlbmNlXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgY291bGQgc2V0IGEgcGFzc3dvcmQuXCIsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBcIlBhc3N3b3JkXCIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuTWVzc2FnZURpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIlRoaXMgY29udmVyc2F0aW9uIGlzbid0IGN1cnJlbnRseSBwcm90ZWN0ZWQgYnlcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBhIHBhc3N3b3JkLiBPbmx5IHRoZSBvd25lciBvZiB0aGUgY29uZmVyZW5jZVwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGNvdWxkIHNldCBhIHBhc3N3b3JkLlwiLFxuICAgICAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgXCJQYXNzd29yZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChzaGFyZWRLZXkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKG51bGwsXG4gICAgICAgICAgICAgICAgICAgIFwiQXJlIHlvdSBzdXJlIHlvdSB3b3VsZCBsaWtlIHRvIHJlbW92ZSB5b3VyIHBhc3N3b3JkP1wiLFxuICAgICAgICAgICAgICAgICAgICBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgXCJSZW1vdmVcIixcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVG9vbGJhci5zZXRTaGFyZWRLZXkoJycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2tSb29tKGZhbHNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2cobnVsbCxcbiAgICAgICAgICAgICAgICAgICAgJzxoMj5TZXQgYSBwYXNzd29yZCB0byBsb2NrIHlvdXIgcm9vbTwvaDI+JyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwibG9ja0tleVwiIHR5cGU9XCJ0ZXh0XCInICtcbiAgICAgICAgICAgICAgICAgICAgICAgICdwbGFjZWhvbGRlcj1cInlvdXIgcGFzc3dvcmRcIiBhdXRvZm9jdXM+JyxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIFwiU2F2ZVwiLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZSwgdikge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgbG9ja0tleSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NrS2V5Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9ja0tleS52YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUb29sYmFyLnNldFNoYXJlZEtleShVSVV0aWwuZXNjYXBlSHRtbChsb2NrS2V5LnZhbHVlKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2tSb29tKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvY2tLZXknKS5mb2N1cygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB0aGUgaW52aXRlIGxpbmsgZGlhbG9nLlxuICAgICAqL1xuICAgIG15Lm9wZW5MaW5rRGlhbG9nID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW52aXRlTGluaztcbiAgICAgICAgaWYgKHJvb21VcmwgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGludml0ZUxpbmsgPSBcIllvdXIgY29uZmVyZW5jZSBpcyBjdXJyZW50bHkgYmVpbmcgY3JlYXRlZC4uLlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW52aXRlTGluayA9IGVuY29kZVVSSShyb29tVXJsKTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlSGFuZGxlci5vcGVuVHdvQnV0dG9uRGlhbG9nKFxuICAgICAgICAgICAgXCJTaGFyZSB0aGlzIGxpbmsgd2l0aCBldmVyeW9uZSB5b3Ugd2FudCB0byBpbnZpdGVcIixcbiAgICAgICAgICAgICc8aW5wdXQgaWQ9XCJpbnZpdGVMaW5rUmVmXCIgdHlwZT1cInRleHRcIiB2YWx1ZT1cIicgK1xuICAgICAgICAgICAgICAgIGludml0ZUxpbmsgKyAnXCIgb25jbGljaz1cInRoaXMuc2VsZWN0KCk7XCIgcmVhZG9ubHk+JyxcbiAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgXCJJbnZpdGVcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlLCB2KSB7XG4gICAgICAgICAgICAgICAgaWYgKHYpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJvb21VcmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGludml0ZVBhcnRpY2lwYW50cygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocm9vbVVybCkge1xuICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnaW52aXRlTGlua1JlZicpLnNlbGVjdCgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdqcWlfc3RhdGUwX2J1dHRvbkludml0ZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogT3BlbnMgdGhlIHNldHRpbmdzIGRpYWxvZy5cbiAgICAgKi9cbiAgICBteS5vcGVuU2V0dGluZ3NEaWFsb2cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lc3NhZ2VIYW5kbGVyLm9wZW5Ud29CdXR0b25EaWFsb2coXG4gICAgICAgICAgICAnPGgyPkNvbmZpZ3VyZSB5b3VyIGNvbmZlcmVuY2U8L2gyPicgK1xuICAgICAgICAgICAgICAgICc8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgaWQ9XCJpbml0TXV0ZWRcIj4nICtcbiAgICAgICAgICAgICAgICAnUGFydGljaXBhbnRzIGpvaW4gbXV0ZWQ8YnIvPicgK1xuICAgICAgICAgICAgICAgICc8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgaWQ9XCJyZXF1aXJlTmlja25hbWVzXCI+JyArXG4gICAgICAgICAgICAgICAgJ1JlcXVpcmUgbmlja25hbWVzPGJyLz48YnIvPicgK1xuICAgICAgICAgICAgICAgICdTZXQgYSBwYXNzd29yZCB0byBsb2NrIHlvdXIgcm9vbTonICtcbiAgICAgICAgICAgICAgICAnPGlucHV0IGlkPVwibG9ja0tleVwiIHR5cGU9XCJ0ZXh0XCIgcGxhY2Vob2xkZXI9XCJ5b3VyIHBhc3N3b3JkXCInICtcbiAgICAgICAgICAgICAgICAnYXV0b2ZvY3VzPicsXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICBcIlNhdmVcIixcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9ja0tleScpLmZvY3VzKCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGUsIHYpIHtcbiAgICAgICAgICAgICAgICBpZiAodikge1xuICAgICAgICAgICAgICAgICAgICBpZiAoJCgnI2luaXRNdXRlZCcpLmlzKFwiOmNoZWNrZWRcIikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGl0IGlzIGNoZWNrZWRcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICgkKCcjcmVxdWlyZU5pY2tuYW1lcycpLmlzKFwiOmNoZWNrZWRcIikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGl0IGlzIGNoZWNrZWRcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAvKlxuICAgICAgICAgICAgICAgICAgICB2YXIgbG9ja0tleSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NrS2V5Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGxvY2tLZXkudmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldFNoYXJlZEtleShsb2NrS2V5LnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2tSb29tKHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBUb2dnbGVzIHRoZSBhcHBsaWNhdGlvbiBpbiBhbmQgb3V0IG9mIGZ1bGwgc2NyZWVuIG1vZGVcbiAgICAgKiAoYS5rLmEuIHByZXNlbnRhdGlvbiBtb2RlIGluIENocm9tZSkuXG4gICAgICovXG4gICAgbXkudG9nZ2xlRnVsbFNjcmVlbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGZzRWxlbWVudCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcblxuICAgICAgICBpZiAoIWRvY3VtZW50Lm1vekZ1bGxTY3JlZW4gJiYgIWRvY3VtZW50LndlYmtpdElzRnVsbFNjcmVlbikge1xuICAgICAgICAgICAgLy9FbnRlciBGdWxsIFNjcmVlblxuICAgICAgICAgICAgaWYgKGZzRWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbikge1xuICAgICAgICAgICAgICAgIGZzRWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgZnNFbGVtZW50LndlYmtpdFJlcXVlc3RGdWxsU2NyZWVuKEVsZW1lbnQuQUxMT1dfS0VZQk9BUkRfSU5QVVQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy9FeGl0IEZ1bGwgU2NyZWVuXG4gICAgICAgICAgICBpZiAoZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbikge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50Lm1vekNhbmNlbEZ1bGxTY3JlZW4oKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQud2Via2l0Q2FuY2VsRnVsbFNjcmVlbigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgICAvKipcbiAgICAgKiBVbmxvY2tzIHRoZSBsb2NrIGJ1dHRvbiBzdGF0ZS5cbiAgICAgKi9cbiAgICBteS51bmxvY2tMb2NrQnV0dG9uID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoJChcIiNsb2NrSWNvblwiKS5oYXNDbGFzcyhcImljb24tc2VjdXJpdHktbG9ja2VkXCIpKVxuICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI2xvY2tJY29uXCIsIFwiaWNvbi1zZWN1cml0eSBpY29uLXNlY3VyaXR5LWxvY2tlZFwiKTtcbiAgICB9O1xuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGxvY2sgYnV0dG9uIHN0YXRlIHRvIGxvY2tlZC5cbiAgICAgKi9cbiAgICBteS5sb2NrTG9ja0J1dHRvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKCQoXCIjbG9ja0ljb25cIikuaGFzQ2xhc3MoXCJpY29uLXNlY3VyaXR5XCIpKVxuICAgICAgICAgICAgVUlVdGlsLmJ1dHRvbkNsaWNrKFwiI2xvY2tJY29uXCIsIFwiaWNvbi1zZWN1cml0eSBpY29uLXNlY3VyaXR5LWxvY2tlZFwiKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3Mgb3IgaGlkZXMgYXV0aGVudGljYXRpb24gYnV0dG9uXG4gICAgICogQHBhcmFtIHNob3cgPHR0PnRydWU8L3R0PiB0byBzaG93IG9yIDx0dD5mYWxzZTwvdHQ+IHRvIGhpZGVcbiAgICAgKi9cbiAgICBteS5zaG93QXV0aGVudGljYXRlQnV0dG9uID0gZnVuY3Rpb24gKHNob3cpIHtcbiAgICAgICAgaWYgKHNob3cpIHtcbiAgICAgICAgICAgICQoJyNhdXRoZW50aWNhdGlvbicpLmNzcyh7ZGlzcGxheTogXCJpbmxpbmVcIn0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgJCgnI2F1dGhlbnRpY2F0aW9uJykuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8vIFNob3dzIG9yIGhpZGVzIHRoZSAncmVjb3JkaW5nJyBidXR0b24uXG4gICAgbXkuc2hvd1JlY29yZGluZ0J1dHRvbiA9IGZ1bmN0aW9uIChzaG93KSB7XG4gICAgICAgIGlmICghY29uZmlnLmVuYWJsZVJlY29yZGluZykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHNob3cpIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRpbmcnKS5jc3Moe2Rpc3BsYXk6IFwiaW5saW5lXCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRpbmcnKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLy8gU2V0cyB0aGUgc3RhdGUgb2YgdGhlIHJlY29yZGluZyBidXR0b25cbiAgICBteS5zZXRSZWNvcmRpbmdCdXR0b25TdGF0ZSA9IGZ1bmN0aW9uIChpc1JlY29yZGluZykge1xuICAgICAgICBpZiAoaXNSZWNvcmRpbmcpIHtcbiAgICAgICAgICAgICQoJyNyZWNvcmRCdXR0b24nKS5yZW1vdmVDbGFzcyhcImljb24tcmVjRW5hYmxlXCIpO1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLmFkZENsYXNzKFwiaWNvbi1yZWNFbmFibGUgYWN0aXZlXCIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLnJlbW92ZUNsYXNzKFwiaWNvbi1yZWNFbmFibGUgYWN0aXZlXCIpO1xuICAgICAgICAgICAgJCgnI3JlY29yZEJ1dHRvbicpLmFkZENsYXNzKFwiaWNvbi1yZWNFbmFibGVcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLy8gU2hvd3Mgb3IgaGlkZXMgU0lQIGNhbGxzIGJ1dHRvblxuICAgIG15LnNob3dTaXBDYWxsQnV0dG9uID0gZnVuY3Rpb24gKHNob3cpIHtcbiAgICAgICAgaWYgKEFQUC54bXBwLmlzU2lwR2F0ZXdheUVuYWJsZWQoKSAmJiBzaG93KSB7XG4gICAgICAgICAgICAkKCcjc2lwQ2FsbEJ1dHRvbicpLmNzcyh7ZGlzcGxheTogXCJpbmxpbmVcIn0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgJCgnI3NpcENhbGxCdXR0b24nKS5jc3Moe2Rpc3BsYXk6IFwibm9uZVwifSk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgc3RhdGUgb2YgdGhlIGJ1dHRvbi4gVGhlIGJ1dHRvbiBoYXMgYmx1ZSBnbG93IGlmIGRlc2t0b3BcbiAgICAgKiBzdHJlYW1pbmcgaXMgYWN0aXZlLlxuICAgICAqIEBwYXJhbSBhY3RpdmUgdGhlIHN0YXRlIG9mIHRoZSBkZXNrdG9wIHN0cmVhbWluZy5cbiAgICAgKi9cbiAgICBteS5jaGFuZ2VEZXNrdG9wU2hhcmluZ0J1dHRvblN0YXRlID0gZnVuY3Rpb24gKGFjdGl2ZSkge1xuICAgICAgICB2YXIgYnV0dG9uID0gJChcIiNkZXNrdG9wc2hhcmluZyA+IGFcIik7XG4gICAgICAgIGlmIChhY3RpdmUpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ1dHRvbi5hZGRDbGFzcyhcImdsb3dcIik7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBidXR0b24ucmVtb3ZlQ2xhc3MoXCJnbG93XCIpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHJldHVybiBteTtcbn0oVG9vbGJhciB8fCB7fSkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFRvb2xiYXI7IiwiLyogZ2xvYmFsICQsIGludGVyZmFjZUNvbmZpZywgTW9kZXJhdG9yLCBEZXNrdG9wU3RyZWFtaW5nLnNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbiAqL1xuXG52YXIgdG9vbGJhclRpbWVvdXRPYmplY3QsXG4gICAgdG9vbGJhclRpbWVvdXQgPSBpbnRlcmZhY2VDb25maWcuSU5JVElBTF9UT09MQkFSX1RJTUVPVVQ7XG5cbmZ1bmN0aW9uIHNob3dEZXNrdG9wU2hhcmluZ0J1dHRvbigpIHtcbiAgICBpZiAoQVBQLmRlc2t0b3BzaGFyaW5nLmlzRGVza3RvcFNoYXJpbmdFbmFibGVkKCkpIHtcbiAgICAgICAgJCgnI2Rlc2t0b3BzaGFyaW5nJykuY3NzKHtkaXNwbGF5OiBcImlubGluZVwifSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgJCgnI2Rlc2t0b3BzaGFyaW5nJykuY3NzKHtkaXNwbGF5OiBcIm5vbmVcIn0pO1xuICAgIH1cbn1cblxuLyoqXG4gKiBIaWRlcyB0aGUgdG9vbGJhci5cbiAqL1xuZnVuY3Rpb24gaGlkZVRvb2xiYXIoKSB7XG4gICAgdmFyIGhlYWRlciA9ICQoXCIjaGVhZGVyXCIpLFxuICAgICAgICBib3R0b21Ub29sYmFyID0gJChcIiNib3R0b21Ub29sYmFyXCIpO1xuICAgIHZhciBpc1Rvb2xiYXJIb3ZlciA9IGZhbHNlO1xuICAgIGhlYWRlci5maW5kKCcqJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBpZCA9ICQodGhpcykuYXR0cignaWQnKTtcbiAgICAgICAgaWYgKCQoXCIjXCIgKyBpZCArIFwiOmhvdmVyXCIpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGlzVG9vbGJhckhvdmVyID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIGlmICgkKFwiI2JvdHRvbVRvb2xiYXI6aG92ZXJcIikubGVuZ3RoID4gMCkge1xuICAgICAgICBpc1Rvb2xiYXJIb3ZlciA9IHRydWU7XG4gICAgfVxuXG4gICAgY2xlYXJUaW1lb3V0KHRvb2xiYXJUaW1lb3V0T2JqZWN0KTtcbiAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IG51bGw7XG5cbiAgICBpZiAoIWlzVG9vbGJhckhvdmVyKSB7XG4gICAgICAgIGhlYWRlci5oaWRlKFwic2xpZGVcIiwgeyBkaXJlY3Rpb246IFwidXBcIiwgZHVyYXRpb246IDMwMH0pO1xuICAgICAgICAkKCcjc3ViamVjdCcpLmFuaW1hdGUoe3RvcDogXCItPTQwXCJ9LCAzMDApO1xuICAgICAgICBpZiAoJChcIiNyZW1vdGVWaWRlb3NcIikuaGFzQ2xhc3MoXCJoaWRkZW5cIikpIHtcbiAgICAgICAgICAgIGJvdHRvbVRvb2xiYXIuaGlkZShcbiAgICAgICAgICAgICAgICBcInNsaWRlXCIsIHtkaXJlY3Rpb246IFwicmlnaHRcIiwgZHVyYXRpb246IDMwMH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IHNldFRpbWVvdXQoaGlkZVRvb2xiYXIsIHRvb2xiYXJUaW1lb3V0KTtcbiAgICB9XG59XG5cbnZhciBUb29sYmFyVG9nZ2xlciA9IHtcbiAgICAvKipcbiAgICAgKiBTaG93cyB0aGUgbWFpbiB0b29sYmFyLlxuICAgICAqL1xuICAgIHNob3dUb29sYmFyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBoZWFkZXIgPSAkKFwiI2hlYWRlclwiKSxcbiAgICAgICAgICAgIGJvdHRvbVRvb2xiYXIgPSAkKFwiI2JvdHRvbVRvb2xiYXJcIik7XG4gICAgICAgIGlmICghaGVhZGVyLmlzKCc6dmlzaWJsZScpIHx8ICFib3R0b21Ub29sYmFyLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgICAgIGhlYWRlci5zaG93KFwic2xpZGVcIiwgeyBkaXJlY3Rpb246IFwidXBcIiwgZHVyYXRpb246IDMwMH0pO1xuICAgICAgICAgICAgJCgnI3N1YmplY3QnKS5hbmltYXRlKHt0b3A6IFwiKz00MFwifSwgMzAwKTtcbiAgICAgICAgICAgIGlmICghYm90dG9tVG9vbGJhci5pcyhcIjp2aXNpYmxlXCIpKSB7XG4gICAgICAgICAgICAgICAgYm90dG9tVG9vbGJhci5zaG93KFxuICAgICAgICAgICAgICAgICAgICBcInNsaWRlXCIsIHtkaXJlY3Rpb246IFwicmlnaHRcIiwgZHVyYXRpb246IDMwMH0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodG9vbGJhclRpbWVvdXRPYmplY3QpIHtcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodG9vbGJhclRpbWVvdXRPYmplY3QpO1xuICAgICAgICAgICAgICAgIHRvb2xiYXJUaW1lb3V0T2JqZWN0ID0gbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRvb2xiYXJUaW1lb3V0T2JqZWN0ID0gc2V0VGltZW91dChoaWRlVG9vbGJhciwgdG9vbGJhclRpbWVvdXQpO1xuICAgICAgICAgICAgdG9vbGJhclRpbWVvdXQgPSBpbnRlcmZhY2VDb25maWcuVE9PTEJBUl9USU1FT1VUO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKEFQUC54bXBwLmlzTW9kZXJhdG9yKCkpXG4gICAgICAgIHtcbi8vICAgICAgICAgICAgVE9ETzogRW5hYmxlIHNldHRpbmdzIGZ1bmN0aW9uYWxpdHkuXG4vLyAgICAgICAgICAgICAgICAgIE5lZWQgdG8gdW5jb21tZW50IHRoZSBzZXR0aW5ncyBidXR0b24gaW4gaW5kZXguaHRtbC5cbi8vICAgICAgICAgICAgJCgnI3NldHRpbmdzQnV0dG9uJykuY3NzKHt2aXNpYmlsaXR5OlwidmlzaWJsZVwifSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTaG93L2hpZGUgZGVza3RvcCBzaGFyaW5nIGJ1dHRvblxuICAgICAgICBzaG93RGVza3RvcFNoYXJpbmdCdXR0b24oKTtcbiAgICB9LFxuXG5cbiAgICAvKipcbiAgICAgKiBEb2Nrcy91bmRvY2tzIHRoZSB0b29sYmFyLlxuICAgICAqXG4gICAgICogQHBhcmFtIGlzRG9jayBpbmRpY2F0ZXMgd2hhdCBvcGVyYXRpb24gdG8gcGVyZm9ybVxuICAgICAqL1xuICAgIGRvY2tUb29sYmFyOiBmdW5jdGlvbiAoaXNEb2NrKSB7XG4gICAgICAgIGlmIChpc0RvY2spIHtcbiAgICAgICAgICAgIC8vIEZpcnN0IG1ha2Ugc3VyZSB0aGUgdG9vbGJhciBpcyBzaG93bi5cbiAgICAgICAgICAgIGlmICghJCgnI2hlYWRlcicpLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaG93VG9vbGJhcigpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBUaGVuIGNsZWFyIHRoZSB0aW1lIG91dCwgdG8gZG9jayB0aGUgdG9vbGJhci5cbiAgICAgICAgICAgIGlmICh0b29sYmFyVGltZW91dE9iamVjdCkge1xuICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0b29sYmFyVGltZW91dE9iamVjdCk7XG4gICAgICAgICAgICAgICAgdG9vbGJhclRpbWVvdXRPYmplY3QgPSBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKCEkKCcjaGVhZGVyJykuaXMoJzp2aXNpYmxlJykpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNob3dUb29sYmFyKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB0b29sYmFyVGltZW91dE9iamVjdCA9IHNldFRpbWVvdXQoaGlkZVRvb2xiYXIsIHRvb2xiYXJUaW1lb3V0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBzaG93RGVza3RvcFNoYXJpbmdCdXR0b246IHNob3dEZXNrdG9wU2hhcmluZ0J1dHRvblxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFRvb2xiYXJUb2dnbGVyOyIsInZhciBKaXRzaVBvcG92ZXIgPSAoZnVuY3Rpb24gKCkge1xuICAgIC8qKlxuICAgICAqIENvbnN0cnVjdHMgbmV3IEppdHNpUG9wb3ZlciBhbmQgYXR0YWNoZXMgaXQgdG8gdGhlIGVsZW1lbnRcbiAgICAgKiBAcGFyYW0gZWxlbWVudCBqcXVlcnkgc2VsZWN0b3JcbiAgICAgKiBAcGFyYW0gb3B0aW9ucyB0aGUgb3B0aW9ucyBmb3IgdGhlIHBvcG92ZXIuXG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICovXG4gICAgZnVuY3Rpb24gSml0c2lQb3BvdmVyKGVsZW1lbnQsIG9wdGlvbnMpXG4gICAge1xuICAgICAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICAgICAgICBza2luOiBcIndoaXRlXCIsXG4gICAgICAgICAgICBjb250ZW50OiBcIlwiXG4gICAgICAgIH07XG4gICAgICAgIGlmKG9wdGlvbnMpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlmKG9wdGlvbnMuc2tpbilcbiAgICAgICAgICAgICAgICB0aGlzLm9wdGlvbnMuc2tpbiA9IG9wdGlvbnMuc2tpbjtcblxuICAgICAgICAgICAgaWYob3B0aW9ucy5jb250ZW50KVxuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucy5jb250ZW50ID0gb3B0aW9ucy5jb250ZW50O1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5lbGVtZW50SXNIb3ZlcmVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMucG9wb3ZlcklzSG92ZXJlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnBvcG92ZXJTaG93biA9IGZhbHNlO1xuXG4gICAgICAgIGVsZW1lbnQuZGF0YShcImppdHNpX3BvcG92ZXJcIiwgdGhpcyk7XG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnQ7XG4gICAgICAgIHRoaXMudGVtcGxhdGUgPSAnIDxkaXYgY2xhc3M9XCJqaXRzaXBvcG92ZXIgJyArIHRoaXMub3B0aW9ucy5za2luICtcbiAgICAgICAgICAgICdcIj48ZGl2IGNsYXNzPVwiYXJyb3dcIj48L2Rpdj48ZGl2IGNsYXNzPVwiaml0c2lwb3BvdmVyLWNvbnRlbnRcIj48L2Rpdj4nICtcbiAgICAgICAgICAgICc8ZGl2IGNsYXNzPVwiaml0c2lQb3B1cG1lbnVQYWRkaW5nXCI+PC9kaXY+PC9kaXY+JztcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICB0aGlzLmVsZW1lbnQub24oXCJtb3VzZWVudGVyXCIsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYuZWxlbWVudElzSG92ZXJlZCA9IHRydWU7XG4gICAgICAgICAgICBzZWxmLnNob3coKTtcbiAgICAgICAgfSkub24oXCJtb3VzZWxlYXZlXCIsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYuZWxlbWVudElzSG92ZXJlZCA9IGZhbHNlO1xuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5oaWRlKCk7XG4gICAgICAgICAgICB9LCAxMCk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBwb3BvdmVyXG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS5zaG93ID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB0aGlzLmNyZWF0ZVBvcG92ZXIoKTtcbiAgICAgICAgdGhpcy5wb3BvdmVyU2hvd24gPSB0cnVlO1xuXG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIHRoZSBwb3BvdmVyXG4gICAgICovXG4gICAgSml0c2lQb3BvdmVyLnByb3RvdHlwZS5oaWRlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBpZighdGhpcy5lbGVtZW50SXNIb3ZlcmVkICYmICF0aGlzLnBvcG92ZXJJc0hvdmVyZWQgJiYgdGhpcy5wb3BvdmVyU2hvd24pXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuZm9yY2VIaWRlKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIHBvcG92ZXJcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLmZvcmNlSGlkZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgJChcIi5qaXRzaXBvcG92ZXJcIikucmVtb3ZlKCk7XG4gICAgICAgIHRoaXMucG9wb3ZlclNob3duID0gZmFsc2U7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgdGhlIHBvcG92ZXIgaHRtbFxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUuY3JlYXRlUG9wb3ZlciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgJChcImJvZHlcIikuYXBwZW5kKHRoaXMudGVtcGxhdGUpO1xuICAgICAgICAkKFwiLmppdHNpcG9wb3ZlciA+IC5qaXRzaXBvcG92ZXItY29udGVudFwiKS5odG1sKHRoaXMub3B0aW9ucy5jb250ZW50KTtcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAkKFwiLmppdHNpcG9wb3ZlclwiKS5vbihcIm1vdXNlZW50ZXJcIiwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5wb3BvdmVySXNIb3ZlcmVkID0gdHJ1ZTtcbiAgICAgICAgfSkub24oXCJtb3VzZWxlYXZlXCIsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYucG9wb3ZlcklzSG92ZXJlZCA9IGZhbHNlO1xuICAgICAgICAgICAgc2VsZi5oaWRlKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmVmcmVzaFBvc2l0aW9uKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlZnJlc2hlcyB0aGUgcG9zaXRpb24gb2YgdGhlIHBvcG92ZXJcbiAgICAgKi9cbiAgICBKaXRzaVBvcG92ZXIucHJvdG90eXBlLnJlZnJlc2hQb3NpdGlvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgJChcIi5qaXRzaXBvcG92ZXJcIikucG9zaXRpb24oe1xuICAgICAgICAgICAgbXk6IFwiYm90dG9tXCIsXG4gICAgICAgICAgICBhdDogXCJ0b3BcIixcbiAgICAgICAgICAgIGNvbGxpc2lvbjogXCJmaXRcIixcbiAgICAgICAgICAgIG9mOiB0aGlzLmVsZW1lbnQsXG4gICAgICAgICAgICB1c2luZzogZnVuY3Rpb24gKHBvc2l0aW9uLCBlbGVtZW50cykge1xuICAgICAgICAgICAgICAgIHZhciBjYWxjTGVmdCA9IGVsZW1lbnRzLnRhcmdldC5sZWZ0IC0gZWxlbWVudHMuZWxlbWVudC5sZWZ0ICsgZWxlbWVudHMudGFyZ2V0LndpZHRoLzI7XG4gICAgICAgICAgICAgICAgJChcIi5qaXRzaXBvcG92ZXJcIikuY3NzKHt0b3A6IHBvc2l0aW9uLnRvcCwgbGVmdDogcG9zaXRpb24ubGVmdCwgZGlzcGxheTogXCJ0YWJsZVwifSk7XG4gICAgICAgICAgICAgICAgJChcIi5qaXRzaXBvcG92ZXIgPiAuYXJyb3dcIikuY3NzKHtsZWZ0OiBjYWxjTGVmdH0pO1xuICAgICAgICAgICAgICAgICQoXCIuaml0c2lwb3BvdmVyID4gLmppdHNpUG9wdXBtZW51UGFkZGluZ1wiKS5jc3Moe2xlZnQ6IGNhbGNMZWZ0IC0gNTB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGNvbnRlbnQgb2YgcG9wb3Zlci5cbiAgICAgKiBAcGFyYW0gY29udGVudCBuZXcgY29udGVudFxuICAgICAqL1xuICAgIEppdHNpUG9wb3Zlci5wcm90b3R5cGUudXBkYXRlQ29udGVudCA9IGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgICAgIHRoaXMub3B0aW9ucy5jb250ZW50ID0gY29udGVudDtcbiAgICAgICAgaWYoIXRoaXMucG9wb3ZlclNob3duKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAkKFwiLmppdHNpcG9wb3ZlclwiKS5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5jcmVhdGVQb3BvdmVyKCk7XG4gICAgfTtcblxuICAgIHJldHVybiBKaXRzaVBvcG92ZXI7XG5cblxufSkoKTtcblxubW9kdWxlLmV4cG9ydHMgPSBKaXRzaVBvcG92ZXI7IiwiLyogZ2xvYmFsICQsIGpRdWVyeSAqL1xudmFyIG1lc3NhZ2VIYW5kbGVyID0gKGZ1bmN0aW9uKG15KSB7XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyBhIG1lc3NhZ2UgdG8gdGhlIHVzZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGl0bGVTdHJpbmcgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1lc3NhZ2VTdHJpbmcgdGhlIHRleHQgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKi9cbiAgICBteS5vcGVuTWVzc2FnZURpYWxvZyA9IGZ1bmN0aW9uKHRpdGxlU3RyaW5nLCBtZXNzYWdlU3RyaW5nKSB7XG4gICAgICAgICQucHJvbXB0KG1lc3NhZ2VTdHJpbmcsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdGl0bGU6IHRpdGxlU3RyaW5nLFxuICAgICAgICAgICAgICAgIHBlcnNpc3RlbnQ6IGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgbWVzc2FnZSB0byB0aGUgdXNlciB3aXRoIHR3byBidXR0b25zOiBmaXJzdCBpcyBnaXZlbiBhcyBhIHBhcmFtZXRlciBhbmQgdGhlIHNlY29uZCBpcyBDYW5jZWwuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGl0bGVTdHJpbmcgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1zZ1N0cmluZyB0aGUgdGV4dCBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBwZXJzaXN0ZW50IGJvb2xlYW4gdmFsdWUgd2hpY2ggZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBtZXNzYWdlIGlzIHBlcnNpc3RlbnQgb3Igbm90XG4gICAgICogQHBhcmFtIGxlZnRCdXR0b24gdGhlIGZpc3QgYnV0dG9uJ3MgdGV4dFxuICAgICAqIEBwYXJhbSBzdWJtaXRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgb24gc3VibWl0XG4gICAgICogQHBhcmFtIGxvYWRlZEZ1bmN0aW9uIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBhZnRlciB0aGUgcHJvbXB0IGlzIGZ1bGx5IGxvYWRlZFxuICAgICAqIEBwYXJhbSBjbG9zZUZ1bmN0aW9uIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBhZnRlciB0aGUgcHJvbXB0IGlzIGNsb3NlZFxuICAgICAqL1xuICAgIG15Lm9wZW5Ud29CdXR0b25EaWFsb2cgPSBmdW5jdGlvbih0aXRsZVN0cmluZywgbXNnU3RyaW5nLCBwZXJzaXN0ZW50LCBsZWZ0QnV0dG9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJtaXRGdW5jdGlvbiwgbG9hZGVkRnVuY3Rpb24sIGNsb3NlRnVuY3Rpb24pIHtcbiAgICAgICAgdmFyIGJ1dHRvbnMgPSB7fTtcbiAgICAgICAgYnV0dG9uc1tsZWZ0QnV0dG9uXSA9IHRydWU7XG4gICAgICAgIGJ1dHRvbnMuQ2FuY2VsID0gZmFsc2U7XG4gICAgICAgICQucHJvbXB0KG1zZ1N0cmluZywge1xuICAgICAgICAgICAgdGl0bGU6IHRpdGxlU3RyaW5nLFxuICAgICAgICAgICAgcGVyc2lzdGVudDogZmFsc2UsXG4gICAgICAgICAgICBidXR0b25zOiBidXR0b25zLFxuICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMSxcbiAgICAgICAgICAgIGxvYWRlZDogbG9hZGVkRnVuY3Rpb24sXG4gICAgICAgICAgICBzdWJtaXQ6IHN1Ym1pdEZ1bmN0aW9uLFxuICAgICAgICAgICAgY2xvc2U6IGNsb3NlRnVuY3Rpb25cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgbWVzc2FnZSB0byB0aGUgdXNlciB3aXRoIHR3byBidXR0b25zOiBmaXJzdCBpcyBnaXZlbiBhcyBhIHBhcmFtZXRlciBhbmQgdGhlIHNlY29uZCBpcyBDYW5jZWwuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGl0bGVTdHJpbmcgdGhlIHRpdGxlIG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIG1zZ1N0cmluZyB0aGUgdGV4dCBvZiB0aGUgbWVzc2FnZVxuICAgICAqIEBwYXJhbSBwZXJzaXN0ZW50IGJvb2xlYW4gdmFsdWUgd2hpY2ggZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBtZXNzYWdlIGlzIHBlcnNpc3RlbnQgb3Igbm90XG4gICAgICogQHBhcmFtIGJ1dHRvbnMgb2JqZWN0IHdpdGggdGhlIGJ1dHRvbnMuIFRoZSBrZXlzIG11c3QgYmUgdGhlIG5hbWUgb2YgdGhlIGJ1dHRvbiBhbmQgdmFsdWUgaXMgdGhlIHZhbHVlXG4gICAgICogdGhhdCB3aWxsIGJlIHBhc3NlZCB0byBzdWJtaXRGdW5jdGlvblxuICAgICAqIEBwYXJhbSBzdWJtaXRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgb24gc3VibWl0XG4gICAgICogQHBhcmFtIGxvYWRlZEZ1bmN0aW9uIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBhZnRlciB0aGUgcHJvbXB0IGlzIGZ1bGx5IGxvYWRlZFxuICAgICAqL1xuICAgIG15Lm9wZW5EaWFsb2cgPSBmdW5jdGlvbiAodGl0bGVTdHJpbmcsICAgIG1zZ1N0cmluZywgcGVyc2lzdGVudCwgYnV0dG9ucyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Ym1pdEZ1bmN0aW9uLCBsb2FkZWRGdW5jdGlvbikge1xuICAgICAgICB2YXIgYXJncyA9IHtcbiAgICAgICAgICAgIHRpdGxlOiB0aXRsZVN0cmluZyxcbiAgICAgICAgICAgIHBlcnNpc3RlbnQ6IHBlcnNpc3RlbnQsXG4gICAgICAgICAgICBidXR0b25zOiBidXR0b25zLFxuICAgICAgICAgICAgZGVmYXVsdEJ1dHRvbjogMSxcbiAgICAgICAgICAgIGxvYWRlZDogbG9hZGVkRnVuY3Rpb24sXG4gICAgICAgICAgICBzdWJtaXQ6IHN1Ym1pdEZ1bmN0aW9uXG4gICAgICAgIH07XG4gICAgICAgIGlmIChwZXJzaXN0ZW50KSB7XG4gICAgICAgICAgICBhcmdzLmNsb3NlVGV4dCA9ICcnO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiAkLnByb21wdChtc2dTdHJpbmcsIGFyZ3MpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZXMgY3VycmVudGx5IG9wZW5lZCBkaWFsb2cuXG4gICAgICovXG4gICAgbXkuY2xvc2VEaWFsb2cgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICQucHJvbXB0LmNsb3NlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgZGlhbG9nIHdpdGggZGlmZmVyZW50IHN0YXRlcyB0byB0aGUgdXNlci5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBzdGF0ZXNPYmplY3Qgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIHRoZSBzdGF0ZXMgb2YgdGhlIGRpYWxvZ1xuICAgICAqIEBwYXJhbSBsb2FkZWRGdW5jdGlvbiBmdW5jdGlvbiB0byBiZSBjYWxsZWQgYWZ0ZXIgdGhlIHByb21wdCBpcyBmdWxseSBsb2FkZWRcbiAgICAgKiBAcGFyYW0gc3RhdGVDaGFuZ2VkRnVuY3Rpb24gZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW4gdGhlIHN0YXRlIG9mIHRoZSBkaWFsb2cgaXMgY2hhbmdlZFxuICAgICAqL1xuICAgIG15Lm9wZW5EaWFsb2dXaXRoU3RhdGVzID0gZnVuY3Rpb24oc3RhdGVzT2JqZWN0LCBsb2FkZWRGdW5jdGlvbiwgc3RhdGVDaGFuZ2VkRnVuY3Rpb24pIHtcblxuXG4gICAgICAgIHZhciBteVByb21wdCA9ICQucHJvbXB0KHN0YXRlc09iamVjdCk7XG5cbiAgICAgICAgbXlQcm9tcHQub24oJ2ltcHJvbXB0dTpsb2FkZWQnLCBsb2FkZWRGdW5jdGlvbik7XG4gICAgICAgIG15UHJvbXB0Lm9uKCdpbXByb21wdHU6c3RhdGVjaGFuZ2VkJywgc3RhdGVDaGFuZ2VkRnVuY3Rpb24pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyBuZXcgcG9wdXAgd2luZG93IGZvciBnaXZlbiA8dHQ+dXJsPC90dD4gY2VudGVyZWQgb3ZlciBjdXJyZW50XG4gICAgICogd2luZG93LlxuICAgICAqXG4gICAgICogQHBhcmFtIHVybCB0aGUgVVJMIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgcG9wdXAgd2luZG93XG4gICAgICogQHBhcmFtIHcgdGhlIHdpZHRoIG9mIHRoZSBwb3B1cCB3aW5kb3dcbiAgICAgKiBAcGFyYW0gaCB0aGUgaGVpZ2h0IG9mIHRoZSBwb3B1cCB3aW5kb3dcbiAgICAgKiBAcGFyYW0gb25Qb3B1cENsb3NlZCBvcHRpb25hbCBjYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiBwb3B1cCB3aW5kb3dcbiAgICAgKiAgICAgICAgaGFzIGJlZW4gY2xvc2VkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgcG9wdXAgd2luZG93IG9iamVjdCBpZiBvcGVuZWQgc3VjY2Vzc2Z1bGx5IG9yIHVuZGVmaW5lZFxuICAgICAqICAgICAgICAgIGluIGNhc2Ugd2UgZmFpbGVkIHRvIG9wZW4gaXQocG9wdXAgYmxvY2tlZClcbiAgICAgKi9cbiAgICBteS5vcGVuQ2VudGVyZWRQb3B1cCA9IGZ1bmN0aW9uICh1cmwsIHcsIGgsIG9uUG9wdXBDbG9zZWQpIHtcbiAgICAgICAgdmFyIGwgPSB3aW5kb3cuc2NyZWVuWCArICh3aW5kb3cuaW5uZXJXaWR0aCAvIDIpIC0gKHcgLyAyKTtcbiAgICAgICAgdmFyIHQgPSB3aW5kb3cuc2NyZWVuWSArICh3aW5kb3cuaW5uZXJIZWlnaHQgLyAyKSAtIChoIC8gMik7XG4gICAgICAgIHZhciBwb3B1cCA9IHdpbmRvdy5vcGVuKFxuICAgICAgICAgICAgdXJsLCAnX2JsYW5rJyxcbiAgICAgICAgICAgICd0b3A9JyArIHQgKyAnLCBsZWZ0PScgKyBsICsgJywgd2lkdGg9JyArIHcgKyAnLCBoZWlnaHQ9JyArIGggKyAnJyk7XG4gICAgICAgIGlmIChwb3B1cCAmJiBvblBvcHVwQ2xvc2VkKSB7XG4gICAgICAgICAgICB2YXIgcG9sbFRpbWVyID0gd2luZG93LnNldEludGVydmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocG9wdXAuY2xvc2VkICE9PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbChwb2xsVGltZXIpO1xuICAgICAgICAgICAgICAgICAgICBvblBvcHVwQ2xvc2VkKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgMjAwKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcG9wdXA7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIGEgZGlhbG9nIHByb21wdGluZyB0aGUgdXNlciB0byBzZW5kIGFuIGVycm9yIHJlcG9ydC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB0aXRsZVN0cmluZyB0aGUgdGl0bGUgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gbXNnU3RyaW5nIHRoZSB0ZXh0IG9mIHRoZSBtZXNzYWdlXG4gICAgICogQHBhcmFtIGVycm9yIHRoZSBlcnJvciB0aGF0IGlzIGJlaW5nIHJlcG9ydGVkXG4gICAgICovXG4gICAgbXkub3BlblJlcG9ydERpYWxvZyA9IGZ1bmN0aW9uKHRpdGxlU3RyaW5nLCBtc2dTdHJpbmcsIGVycm9yKSB7XG4gICAgICAgIG15Lm9wZW5NZXNzYWdlRGlhbG9nKHRpdGxlU3RyaW5nLCBtc2dTdHJpbmcpO1xuICAgICAgICBjb25zb2xlLmxvZyhlcnJvcik7XG4gICAgICAgIC8vRklYTUUgc2VuZCB0aGUgZXJyb3IgdG8gdGhlIHNlcnZlclxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiAgU2hvd3MgYW4gZXJyb3IgZGlhbG9nIHRvIHRoZSB1c2VyLlxuICAgICAqIEBwYXJhbSB0aXRsZSB0aGUgdGl0bGUgb2YgdGhlIG1lc3NhZ2VcbiAgICAgKiBAcGFyYW0gbWVzc2FnZSB0aGUgdGV4dCBvZiB0aGUgbWVzc2FmZVxuICAgICAqL1xuICAgIG15LnNob3dFcnJvciA9IGZ1bmN0aW9uKHRpdGxlLCBtZXNzYWdlKSB7XG4gICAgICAgIGlmKCEodGl0bGUgfHwgbWVzc2FnZSkpIHtcbiAgICAgICAgICAgIHRpdGxlID0gdGl0bGUgfHwgXCJPb3BzIVwiO1xuICAgICAgICAgICAgbWVzc2FnZSA9IG1lc3NhZ2UgfHwgXCJUaGVyZSB3YXMgc29tZSBraW5kIG9mIGVycm9yXCI7XG4gICAgICAgIH1cbiAgICAgICAgbWVzc2FnZUhhbmRsZXIub3Blbk1lc3NhZ2VEaWFsb2codGl0bGUsIG1lc3NhZ2UpO1xuICAgIH07XG5cbiAgICBteS5ub3RpZnkgPSBmdW5jdGlvbihkaXNwbGF5TmFtZSwgY2xzLCBtZXNzYWdlKSB7XG4gICAgICAgIHRvYXN0ci5pbmZvKFxuICAgICAgICAgICAgJzxzcGFuIGNsYXNzPVwibmlja25hbWVcIj4nICtcbiAgICAgICAgICAgICAgICBkaXNwbGF5TmFtZSArXG4gICAgICAgICAgICAnPC9zcGFuPjxicj4nICtcbiAgICAgICAgICAgICc8c3BhbiBjbGFzcz0nICsgY2xzICsgJz4nICtcbiAgICAgICAgICAgICAgICBtZXNzYWdlICtcbiAgICAgICAgICAgICc8L3NwYW4+Jyk7XG4gICAgfTtcblxuICAgIHJldHVybiBteTtcbn0obWVzc2FnZUhhbmRsZXIgfHwge30pKTtcblxubW9kdWxlLmV4cG9ydHMgPSBtZXNzYWdlSGFuZGxlcjtcblxuXG4iLCJ2YXIgVUlFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vLi4vc2VydmljZS9VSS9VSUV2ZW50c1wiKTtcblxudmFyIG5pY2tuYW1lID0gbnVsbDtcbnZhciBldmVudEVtaXR0ZXIgPSBudWxsO1xuXG52YXIgTmlja2FuYW1lSGFuZGxlciA9IHtcbiAgICBpbml0OiBmdW5jdGlvbiAoZW1pdHRlcikge1xuICAgICAgICBldmVudEVtaXR0ZXIgPSBlbWl0dGVyO1xuICAgICAgICB2YXIgc3RvcmVkRGlzcGxheU5hbWUgPSB3aW5kb3cubG9jYWxTdG9yYWdlLmRpc3BsYXluYW1lO1xuICAgICAgICBpZiAoc3RvcmVkRGlzcGxheU5hbWUpIHtcbiAgICAgICAgICAgIG5pY2tuYW1lID0gc3RvcmVkRGlzcGxheU5hbWU7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHNldE5pY2tuYW1lOiBmdW5jdGlvbiAobmV3Tmlja25hbWUpIHtcbiAgICAgICAgaWYgKCFuZXdOaWNrbmFtZSB8fCBuaWNrbmFtZSA9PT0gbmV3Tmlja25hbWUpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgbmlja25hbWUgPSBuZXdOaWNrbmFtZTtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5kaXNwbGF5bmFtZSA9IG5pY2tuYW1lO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5OSUNLTkFNRV9DSEFOR0VELCBuZXdOaWNrbmFtZSk7XG4gICAgfSxcbiAgICBnZXROaWNrbmFtZTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmlja25hbWU7XG4gICAgfSxcbiAgICBhZGRMaXN0ZW5lcjogZnVuY3Rpb24gKHR5cGUsIGxpc3RlbmVyKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbih0eXBlLCBsaXN0ZW5lcik7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBOaWNrYW5hbWVIYW5kbGVyOyIsIi8qKlxuICogQ3JlYXRlZCBieSBocmlzdG8gb24gMTIvMjIvMTQuXG4gKi9cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGF2YWlsYWJsZSB2aWRlbyB3aWR0aC5cbiAgICAgKi9cbiAgICBnZXRBdmFpbGFibGVWaWRlb1dpZHRoOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBQYW5lbFRvZ2dsZXIgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL1NpZGVQYW5lbFRvZ2dsZXJcIik7XG4gICAgICAgIHZhciByaWdodFBhbmVsV2lkdGhcbiAgICAgICAgICAgID0gUGFuZWxUb2dnbGVyLmlzVmlzaWJsZSgpID8gUGFuZWxUb2dnbGVyLmdldFBhbmVsU2l6ZSgpWzBdIDogMDtcblxuICAgICAgICByZXR1cm4gd2luZG93LmlubmVyV2lkdGggLSByaWdodFBhbmVsV2lkdGg7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBDaGFuZ2VzIHRoZSBzdHlsZSBjbGFzcyBvZiB0aGUgZWxlbWVudCBnaXZlbiBieSBpZC5cbiAgICAgKi9cbiAgICBidXR0b25DbGljazogZnVuY3Rpb24oaWQsIGNsYXNzbmFtZSkge1xuICAgICAgICAkKGlkKS50b2dnbGVDbGFzcyhjbGFzc25hbWUpOyAvLyBhZGQgdGhlIGNsYXNzIHRvIHRoZSBjbGlja2VkIGVsZW1lbnRcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHRleHQgd2lkdGggZm9yIHRoZSBnaXZlbiBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGVsIHRoZSBlbGVtZW50XG4gICAgICovXG4gICAgZ2V0VGV4dFdpZHRoOiBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgcmV0dXJuIChlbC5jbGllbnRXaWR0aCArIDEpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSB0ZXh0IGhlaWdodCBmb3IgdGhlIGdpdmVuIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZWwgdGhlIGVsZW1lbnRcbiAgICAgKi9cbiAgICBnZXRUZXh0SGVpZ2h0OiBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgcmV0dXJuIChlbC5jbGllbnRIZWlnaHQgKyAxKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUGxheXMgdGhlIHNvdW5kIGdpdmVuIGJ5IGlkLlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIHRoZSBpZGVudGlmaWVyIG9mIHRoZSBhdWRpbyBlbGVtZW50LlxuICAgICAqL1xuICAgIHBsYXlTb3VuZE5vdGlmaWNhdGlvbjogZnVuY3Rpb24gKGlkKSB7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKS5wbGF5KCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEVzY2FwZXMgdGhlIGdpdmVuIHRleHQuXG4gICAgICovXG4gICAgZXNjYXBlSHRtbDogZnVuY3Rpb24gKHVuc2FmZVRleHQpIHtcbiAgICAgICAgcmV0dXJuICQoJzxkaXYvPicpLnRleHQodW5zYWZlVGV4dCkuaHRtbCgpO1xuICAgIH0sXG5cbiAgICBpbWFnZVRvR3JheVNjYWxlOiBmdW5jdGlvbiAoY2FudmFzKSB7XG4gICAgICAgIHZhciBjb250ZXh0ID0gY2FudmFzLmdldENvbnRleHQoJzJkJyk7XG4gICAgICAgIHZhciBpbWdEYXRhID0gY29udGV4dC5nZXRJbWFnZURhdGEoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KTtcbiAgICAgICAgdmFyIHBpeGVscyAgPSBpbWdEYXRhLmRhdGE7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIG4gPSBwaXhlbHMubGVuZ3RoOyBpIDwgbjsgaSArPSA0KSB7XG4gICAgICAgICAgICB2YXIgZ3JheXNjYWxlXG4gICAgICAgICAgICAgICAgPSBwaXhlbHNbaV0gKiAuMyArIHBpeGVsc1tpKzFdICogLjU5ICsgcGl4ZWxzW2krMl0gKiAuMTE7XG4gICAgICAgICAgICBwaXhlbHNbaSAgXSA9IGdyYXlzY2FsZTsgICAgICAgIC8vIHJlZFxuICAgICAgICAgICAgcGl4ZWxzW2krMV0gPSBncmF5c2NhbGU7ICAgICAgICAvLyBncmVlblxuICAgICAgICAgICAgcGl4ZWxzW2krMl0gPSBncmF5c2NhbGU7ICAgICAgICAvLyBibHVlXG4gICAgICAgICAgICAvLyBwaXhlbHNbaSszXSAgICAgICAgICAgICAgaXMgYWxwaGFcbiAgICAgICAgfVxuICAgICAgICAvLyByZWRyYXcgdGhlIGltYWdlIGluIGJsYWNrICYgd2hpdGVcbiAgICAgICAgY29udGV4dC5wdXRJbWFnZURhdGEoaW1nRGF0YSwgMCwgMCk7XG4gICAgfSxcblxuICAgIHNldFRvb2x0aXA6IGZ1bmN0aW9uIChlbGVtZW50LCBrZXksIHBvc2l0aW9uKSB7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS1pMThuXCIsIFwiW2RhdGEtY29udGVudF1cIiArIGtleSk7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS10b2dnbGVcIiwgXCJwb3BvdmVyXCIpO1xuICAgICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShcImRhdGEtcGxhY2VtZW50XCIsIHBvc2l0aW9uKTtcbiAgICAgICAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJkYXRhLWh0bWxcIiwgdHJ1ZSk7XG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZGF0YS1jb250YWluZXJcIiwgXCJib2R5XCIpO1xuICAgIH1cblxuXG59OyIsInZhciBKaXRzaVBvcG92ZXIgPSByZXF1aXJlKFwiLi4vdXRpbC9KaXRzaVBvcG92ZXJcIik7XG5cbi8qKlxuICogQ29uc3RydWN0cyBuZXcgY29ubmVjdGlvbiBpbmRpY2F0b3IuXG4gKiBAcGFyYW0gdmlkZW9Db250YWluZXIgdGhlIHZpZGVvIGNvbnRhaW5lciBhc3NvY2lhdGVkIHdpdGggdGhlIGluZGljYXRvci5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBDb25uZWN0aW9uSW5kaWNhdG9yKHZpZGVvQ29udGFpbmVyLCBqaWQsIFZpZGVvTGF5b3V0KVxue1xuICAgIHRoaXMudmlkZW9Db250YWluZXIgPSB2aWRlb0NvbnRhaW5lcjtcbiAgICB0aGlzLmJhbmR3aWR0aCA9IG51bGw7XG4gICAgdGhpcy5wYWNrZXRMb3NzID0gbnVsbDtcbiAgICB0aGlzLmJpdHJhdGUgPSBudWxsO1xuICAgIHRoaXMuc2hvd01vcmVWYWx1ZSA9IGZhbHNlO1xuICAgIHRoaXMucmVzb2x1dGlvbiA9IG51bGw7XG4gICAgdGhpcy50cmFuc3BvcnQgPSBbXTtcbiAgICB0aGlzLnBvcG92ZXIgPSBudWxsO1xuICAgIHRoaXMuamlkID0gamlkO1xuICAgIHRoaXMuY3JlYXRlKCk7XG4gICAgdGhpcy52aWRlb0xheW91dCA9IFZpZGVvTGF5b3V0O1xufVxuXG4vKipcbiAqIFZhbHVlcyBmb3IgdGhlIGNvbm5lY3Rpb24gcXVhbGl0eVxuICogQHR5cGUge3s5ODogc3RyaW5nLFxuICogICAgICAgICA4MTogc3RyaW5nLFxuICogICAgICAgICA2NDogc3RyaW5nLFxuICogICAgICAgICA0Nzogc3RyaW5nLFxuICogICAgICAgICAzMDogc3RyaW5nLFxuICogICAgICAgICAwOiBzdHJpbmd9fVxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLmNvbm5lY3Rpb25RdWFsaXR5VmFsdWVzID0ge1xuICAgIDk4OiBcIjE4cHhcIiwgLy9mdWxsXG4gICAgODE6IFwiMTVweFwiLC8vNCBiYXJzXG4gICAgNjQ6IFwiMTFweFwiLC8vMyBiYXJzXG4gICAgNDc6IFwiN3B4XCIsLy8yIGJhcnNcbiAgICAzMDogXCIzcHhcIiwvLzEgYmFyXG4gICAgMDogXCIwcHhcIi8vZW1wdHlcbn07XG5cbkNvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0SVAgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKDAsIHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQgPSBmdW5jdGlvbih2YWx1ZSlcbntcbiAgICByZXR1cm4gdmFsdWUuc3Vic3RyaW5nKHZhbHVlLmxhc3RJbmRleE9mKFwiOlwiKSArIDEsIHZhbHVlLmxlbmd0aCk7XG59O1xuXG5Db25uZWN0aW9uSW5kaWNhdG9yLmdldFN0cmluZ0Zyb21BcnJheSA9IGZ1bmN0aW9uIChhcnJheSkge1xuICAgIHZhciByZXMgPSBcIlwiO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKylcbiAgICB7XG4gICAgICAgIHJlcyArPSAoaSA9PT0gMD8gXCJcIiA6IFwiLCBcIikgKyBhcnJheVtpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlcztcbn07XG5cbi8qKlxuICogR2VuZXJhdGVzIHRoZSBodG1sIGNvbnRlbnQuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSB0aGUgaHRtbCBjb250ZW50LlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5nZW5lcmF0ZVRleHQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGRvd25sb2FkQml0cmF0ZSwgdXBsb2FkQml0cmF0ZSwgcGFja2V0TG9zcywgcmVzb2x1dGlvbiwgaTtcblxuICAgIHZhciB0cmFuc2xhdGUgPSBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlU3RyaW5nO1xuXG4gICAgaWYodGhpcy5iaXRyYXRlID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgZG93bmxvYWRCaXRyYXRlID0gXCJOL0FcIjtcbiAgICAgICAgdXBsb2FkQml0cmF0ZSA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIGRvd25sb2FkQml0cmF0ZSA9XG4gICAgICAgICAgICB0aGlzLmJpdHJhdGUuZG93bmxvYWQ/IHRoaXMuYml0cmF0ZS5kb3dubG9hZCArIFwiIEticHNcIiA6IFwiTi9BXCI7XG4gICAgICAgIHVwbG9hZEJpdHJhdGUgPVxuICAgICAgICAgICAgdGhpcy5iaXRyYXRlLnVwbG9hZD8gdGhpcy5iaXRyYXRlLnVwbG9hZCArIFwiIEticHNcIiA6IFwiTi9BXCI7XG4gICAgfVxuXG4gICAgaWYodGhpcy5wYWNrZXRMb3NzID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgcGFja2V0TG9zcyA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG5cbiAgICAgICAgcGFja2V0TG9zcyA9IFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ncmVlbic+JmRhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgICh0aGlzLnBhY2tldExvc3MuZG93bmxvYWQgIT09IG51bGw/IHRoaXMucGFja2V0TG9zcy5kb3dubG9hZCA6IFwiTi9BXCIpICtcbiAgICAgICAgICAgIFwiJSA8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX29yYW5nZSc+JnVhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgICh0aGlzLnBhY2tldExvc3MudXBsb2FkICE9PSBudWxsPyB0aGlzLnBhY2tldExvc3MudXBsb2FkIDogXCJOL0FcIikgKyBcIiVcIjtcbiAgICB9XG5cbiAgICB2YXIgcmVzb2x1dGlvblZhbHVlID0gbnVsbDtcbiAgICBpZih0aGlzLnJlc29sdXRpb24gJiYgdGhpcy5qaWQgIT0gbnVsbClcbiAgICB7XG4gICAgICAgIHZhciBrZXlzID0gT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKTtcbiAgICAgICAgaWYoa2V5cy5sZW5ndGggPT0gMSlcbiAgICAgICAge1xuICAgICAgICAgICAgZm9yKHZhciBzc3JjIGluIHRoaXMucmVzb2x1dGlvbilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uVmFsdWUgPSB0aGlzLnJlc29sdXRpb25bc3NyY107XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZihrZXlzLmxlbmd0aCA+IDEpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBkaXNwbGF5ZWRTc3JjID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdTU1JDKHRoaXMuamlkKTtcbiAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZSA9IHRoaXMucmVzb2x1dGlvbltkaXNwbGF5ZWRTc3JjXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmKHRoaXMuamlkID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiXCI7XG4gICAgICAgIGlmKHRoaXMucmVzb2x1dGlvbiA9PT0gbnVsbCB8fCAhT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKSB8fFxuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5yZXNvbHV0aW9uKS5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHJlc29sdXRpb24gPSBcIk4vQVwiO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGZvcihpIGluIHRoaXMucmVzb2x1dGlvbilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uVmFsdWUgPSB0aGlzLnJlc29sdXRpb25baV07XG4gICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgaWYocmVzb2x1dGlvblZhbHVlLmhlaWdodCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uICs9IChyZXNvbHV0aW9uID09PSBcIlwiPyBcIlwiIDogXCIsIFwiKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvblZhbHVlLndpZHRoICsgXCJ4XCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgfVxuICAgIGVsc2UgaWYoIXJlc29sdXRpb25WYWx1ZSB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLmhlaWdodCB8fFxuICAgICAgICAhcmVzb2x1dGlvblZhbHVlLndpZHRoKVxuICAgIHtcbiAgICAgICAgcmVzb2x1dGlvbiA9IFwiTi9BXCI7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIHJlc29sdXRpb24gPSByZXNvbHV0aW9uVmFsdWUud2lkdGggKyBcInhcIiArIHJlc29sdXRpb25WYWx1ZS5oZWlnaHQ7XG4gICAgfVxuXG4gICAgdmFyIHJlc3VsdCA9IFwiPHRhYmxlIHN0eWxlPSd3aWR0aDoxMDAlJz5cIiArXG4gICAgICAgIFwiPHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IuYml0cmF0ZSc+XCIgK1xuICAgICAgICB0cmFuc2xhdGUoXCJjb25uZWN0aW9uaW5kaWNhdG9yLmJpdHJhdGVcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ncmVlbic+JmRhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgZG93bmxvYWRCaXRyYXRlICsgXCIgPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9vcmFuZ2UnPiZ1YXJyOzwvc3Bhbj5cIiArXG4gICAgICAgIHVwbG9hZEJpdHJhdGUgKyBcIjwvdGQ+XCIgK1xuICAgICAgICBcIjwvdHI+PHRyPlwiICtcbiAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IucGFja2V0bG9zcyc+XCIgK1xuICAgICAgICB0cmFuc2xhdGUoXCJjb25uZWN0aW9uaW5kaWNhdG9yLnBhY2tldGxvc3NcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgXCI8dGQ+XCIgKyBwYWNrZXRMb3NzICArIFwiPC90ZD5cIiArXG4gICAgICAgIFwiPC90cj48dHI+XCIgK1xuICAgICAgICBcIjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5yZXNvbHV0aW9uJz5cIiArXG4gICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IucmVzb2x1dGlvblwiKSArIFwiPC9zcGFuPjwvdGQ+XCIgK1xuICAgICAgICBcIjx0ZD5cIiArIHJlc29sdXRpb24gKyBcIjwvdGQ+PC90cj48L3RhYmxlPlwiO1xuXG4gICAgaWYodGhpcy52aWRlb0NvbnRhaW5lci5pZCA9PSBcImxvY2FsVmlkZW9Db250YWluZXJcIikge1xuICAgICAgICByZXN1bHQgKz0gXCI8ZGl2IGNsYXNzPVxcXCJqaXRzaXBvcG92ZXJfc2hvd21vcmVcXFwiIFwiICtcbiAgICAgICAgICAgIFwib25jbGljayA9IFxcXCJBUFAuVUkuY29ubmVjdGlvbkluZGljYXRvclNob3dNb3JlKCdcIiArXG4gICAgICAgICAgICB0aGlzLnZpZGVvQ29udGFpbmVyLmlkICsgXCInKVxcXCIgIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5cIiArXG4gICAgICAgICAgICAgICAgKHRoaXMuc2hvd01vcmVWYWx1ZSA/IFwibGVzc1wiIDogXCJtb3JlXCIpICsgXCInPlwiICtcbiAgICAgICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IuXCIgKyAodGhpcy5zaG93TW9yZVZhbHVlID8gXCJsZXNzXCIgOiBcIm1vcmVcIikpICtcbiAgICAgICAgICAgIFwiPC9kaXY+PGJyIC8+XCI7XG4gICAgfVxuXG4gICAgaWYodGhpcy5zaG93TW9yZVZhbHVlKVxuICAgIHtcbiAgICAgICAgdmFyIGRvd25sb2FkQmFuZHdpZHRoLCB1cGxvYWRCYW5kd2lkdGgsIHRyYW5zcG9ydDtcbiAgICAgICAgaWYodGhpcy5iYW5kd2lkdGggPT09IG51bGwpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGRvd25sb2FkQmFuZHdpZHRoID0gXCJOL0FcIjtcbiAgICAgICAgICAgIHVwbG9hZEJhbmR3aWR0aCA9IFwiTi9BXCI7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZVxuICAgICAgICB7XG4gICAgICAgICAgICBkb3dubG9hZEJhbmR3aWR0aCA9IHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLmRvd25sb2FkICsgXCIgS2Jwc1wiIDpcbiAgICAgICAgICAgICAgICBcIk4vQVwiO1xuICAgICAgICAgICAgdXBsb2FkQmFuZHdpZHRoID0gdGhpcy5iYW5kd2lkdGgudXBsb2FkP1xuICAgICAgICAgICAgICAgIHRoaXMuYmFuZHdpZHRoLnVwbG9hZCArIFwiIEticHNcIiA6XG4gICAgICAgICAgICAgICAgXCJOL0FcIjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCF0aGlzLnRyYW5zcG9ydCB8fCB0aGlzLnRyYW5zcG9ydC5sZW5ndGggPT09IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRyYW5zcG9ydCA9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD48c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIFwiICtcbiAgICAgICAgICAgICAgICBcImRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5hZGRyZXNzJz5cIiArXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlKFwiY29ubmVjdGlvbmluZGljYXRvci5hZGRyZXNzXCIpICsgXCI8L3NwYW4+PC90ZD5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+IE4vQTwvdGQ+PC90cj5cIjtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBkYXRhID0ge3JlbW90ZUlQOiBbXSwgbG9jYWxJUDpbXSwgcmVtb3RlUG9ydDpbXSwgbG9jYWxQb3J0OltdfTtcbiAgICAgICAgICAgIGZvcihpID0gMDsgaSA8IHRoaXMudHJhbnNwb3J0Lmxlbmd0aDsgaSsrKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHZhciBpcCA9ICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldElQKHRoaXMudHJhbnNwb3J0W2ldLmlwKTtcbiAgICAgICAgICAgICAgICB2YXIgcG9ydCA9IENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0UG9ydCh0aGlzLnRyYW5zcG9ydFtpXS5pcCk7XG4gICAgICAgICAgICAgICAgdmFyIGxvY2FsSVAgPVxuICAgICAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldElQKHRoaXMudHJhbnNwb3J0W2ldLmxvY2FsaXApO1xuICAgICAgICAgICAgICAgIHZhciBsb2NhbFBvcnQgPVxuICAgICAgICAgICAgICAgICAgICBDb25uZWN0aW9uSW5kaWNhdG9yLmdldFBvcnQodGhpcy50cmFuc3BvcnRbaV0ubG9jYWxpcCk7XG4gICAgICAgICAgICAgICAgaWYoZGF0YS5yZW1vdGVJUC5pbmRleE9mKGlwKSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEucmVtb3RlSVAucHVzaChpcCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYoZGF0YS5yZW1vdGVQb3J0LmluZGV4T2YocG9ydCkgPT0gLTEpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnJlbW90ZVBvcnQucHVzaChwb3J0KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZihkYXRhLmxvY2FsSVAuaW5kZXhPZihsb2NhbElQKSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEubG9jYWxJUC5wdXNoKGxvY2FsSVApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmKGRhdGEubG9jYWxQb3J0LmluZGV4T2YobG9jYWxQb3J0KSA9PSAtMSlcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEubG9jYWxQb3J0LnB1c2gobG9jYWxQb3J0KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGxvY2FsX2FkZHJlc3Nfa2V5ID0gXCJjb25uZWN0aW9uaW5kaWNhdG9yLlwiICtcbiAgICAgICAgICAgICAgICAoZGF0YS5sb2NhbElQLmxlbmd0aCA+IDE/IFwibG9jYWxhZGRyZXNzZXNcIiA6IFwibG9jYWxhZGRyZXNzXCIpO1xuICAgICAgICAgICAgdmFyIHJlbW90ZV9hZGRyZXNzX2tleSA9IFwiY29ubmVjdGlvbmluZGljYXRvci5cIiArXG4gICAgICAgICAgICAgICAgKGRhdGEucmVtb3RlSVAubGVuZ3RoID4gMT8gXCJyZW1vdGVhZGRyZXNzZXNcIiA6IFwicmVtb3RlYWRkcmVzc1wiKTtcbiAgICAgICAgICAgIHZhciBsb2NhbFRyYW5zcG9ydCA9XG4gICAgICAgICAgICAgICAgXCI8dHI+PHRkPjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfYmx1ZScgZGF0YS1pMThuPSdcIiArXG4gICAgICAgICAgICAgICAgbG9jYWxfYWRkcmVzc19rZXkgK1wiJz5cIiArXG4gICAgICAgICAgICAgICAgICAgIHRyYW5zbGF0ZShsb2NhbF9hZGRyZXNzX2tleSkgKyBcIjwvc3Bhbj48L3RkPjx0ZD4gXCIgK1xuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuZ2V0U3RyaW5nRnJvbUFycmF5KGRhdGEubG9jYWxJUCkgK1xuICAgICAgICAgICAgICAgIFwiPC90ZD48L3RyPlwiO1xuICAgICAgICAgICAgdHJhbnNwb3J0ID1cbiAgICAgICAgICAgICAgICBcIjx0cj48dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J1wiICtcbiAgICAgICAgICAgICAgICByZW1vdGVfYWRkcmVzc19rZXkgKyBcIic+XCIgK1xuICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGUocmVtb3RlX2FkZHJlc3Nfa2V5KSArIFwiPC9zcGFuPjwvdGQ+PHRkPiBcIiArXG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRTdHJpbmdGcm9tQXJyYXkoZGF0YS5yZW1vdGVJUCkgK1xuICAgICAgICAgICAgICAgIFwiPC90ZD48L3RyPlwiO1xuXG4gICAgICAgICAgICB2YXIga2V5X3JlbW90ZSA9IFwiY29ubmVjdGlvbmluZGljYXRvci5cIixcbiAgICAgICAgICAgICAgICBrZXlfbG9jYWwgPSBcImNvbm5lY3Rpb25pbmRpY2F0b3IuXCI7XG5cbiAgICAgICAgICAgIGlmKHRoaXMudHJhbnNwb3J0Lmxlbmd0aCA+IDEpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAga2V5X3JlbW90ZSArPSBcInJlbW90ZXBvcnRzXCI7XG4gICAgICAgICAgICAgICAga2V5X2xvY2FsICs9IFwibG9jYWxwb3J0c1wiO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGtleV9yZW1vdGUgKz0gXCJyZW1vdGVwb3J0XCI7XG4gICAgICAgICAgICAgICAga2V5X2xvY2FsICs9IFwibG9jYWxwb3J0XCI7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRyYW5zcG9ydCArPSBcIjx0cj5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgICAgIFwiPHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J1wiICsga2V5X3JlbW90ZSArXG4gICAgICAgICAgICAgICAgXCInPlwiICtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGUoa2V5X3JlbW90ZSkgKyBcIjwvc3Bhbj48L3RkPjx0ZD5cIjtcbiAgICAgICAgICAgIGxvY2FsVHJhbnNwb3J0ICs9IFwiPHRyPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD5cIiArXG4gICAgICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nXCIgKyBrZXlfbG9jYWwgK1xuICAgICAgICAgICAgICAgIFwiJz5cIiArXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlKGtleV9sb2NhbCkgKyBcIjwvc3Bhbj48L3RkPjx0ZD5cIjtcblxuICAgICAgICAgICAgdHJhbnNwb3J0ICs9XG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRTdHJpbmdGcm9tQXJyYXkoZGF0YS5yZW1vdGVQb3J0KTtcbiAgICAgICAgICAgIGxvY2FsVHJhbnNwb3J0ICs9XG4gICAgICAgICAgICAgICAgQ29ubmVjdGlvbkluZGljYXRvci5nZXRTdHJpbmdGcm9tQXJyYXkoZGF0YS5sb2NhbFBvcnQpO1xuICAgICAgICAgICAgdHJhbnNwb3J0ICs9IFwiPC90ZD48L3RyPlwiO1xuICAgICAgICAgICAgdHJhbnNwb3J0ICs9IGxvY2FsVHJhbnNwb3J0ICsgXCI8L3RkPjwvdHI+XCI7XG4gICAgICAgICAgICB0cmFuc3BvcnQgKz1cIjx0cj5cIiArXG4gICAgICAgICAgICAgICAgXCI8dGQ+PHNwYW4gY2xhc3M9J2ppdHNpcG9wb3Zlcl9ibHVlJyBkYXRhLWkxOG49J2Nvbm5lY3Rpb25pbmRpY2F0b3IudHJhbnNwb3J0Jz5cIiArXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlKFwiY29ubmVjdGlvbmluZGljYXRvci50cmFuc3BvcnRcIikgKyBcIjwvc3Bhbj48L3RkPlwiICtcbiAgICAgICAgICAgICAgICBcIjx0ZD5cIiArIHRoaXMudHJhbnNwb3J0WzBdLnR5cGUgKyBcIjwvdGQ+PC90cj5cIjtcblxuICAgICAgICB9XG5cbiAgICAgICAgcmVzdWx0ICs9IFwiPHRhYmxlICBzdHlsZT0nd2lkdGg6MTAwJSc+XCIgK1xuICAgICAgICAgICAgXCI8dHI+XCIgK1xuICAgICAgICAgICAgXCI8dGQ+XCIgK1xuICAgICAgICAgICAgXCI8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX2JsdWUnIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5iYW5kd2lkdGgnPlwiICtcbiAgICAgICAgICAgIHRyYW5zbGF0ZShcImNvbm5lY3Rpb25pbmRpY2F0b3IuYmFuZHdpZHRoXCIpICsgXCI8L3NwYW4+XCIgK1xuICAgICAgICAgICAgXCI8L3RkPjx0ZD5cIiArXG4gICAgICAgICAgICBcIjxzcGFuIGNsYXNzPSdqaXRzaXBvcG92ZXJfZ3JlZW4nPiZkYXJyOzwvc3Bhbj5cIiArXG4gICAgICAgICAgICBkb3dubG9hZEJhbmR3aWR0aCArXG4gICAgICAgICAgICBcIiA8c3BhbiBjbGFzcz0naml0c2lwb3BvdmVyX29yYW5nZSc+JnVhcnI7PC9zcGFuPlwiICtcbiAgICAgICAgICAgIHVwbG9hZEJhbmR3aWR0aCArIFwiPC90ZD48L3RyPlwiO1xuXG4gICAgICAgIHJlc3VsdCArPSB0cmFuc3BvcnQgKyBcIjwvdGFibGU+XCI7XG5cbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyoqXG4gKiBTaG93cyBvciBoaWRlIHRoZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uLlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5zaG93TW9yZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnNob3dNb3JlVmFsdWUgPSAhdGhpcy5zaG93TW9yZVZhbHVlO1xuICAgIHRoaXMudXBkYXRlUG9wb3ZlckRhdGEoKTtcbn07XG5cblxuZnVuY3Rpb24gY3JlYXRlSWNvbihjbGFzc2VzKVxue1xuICAgIHZhciBpY29uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIik7XG4gICAgZm9yKHZhciBpIGluIGNsYXNzZXMpXG4gICAge1xuICAgICAgICBpY29uLmNsYXNzTGlzdC5hZGQoY2xhc3Nlc1tpXSk7XG4gICAgfVxuICAgIGljb24uYXBwZW5kQ2hpbGQoXG4gICAgICAgIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJpXCIpKS5jbGFzc0xpc3QuYWRkKFwiaWNvbi1jb25uZWN0aW9uXCIpO1xuICAgIHJldHVybiBpY29uO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGluZGljYXRvclxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5jcmVhdGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuY2xhc3NOYW1lID0gXCJjb25uZWN0aW9uaW5kaWNhdG9yXCI7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICB0aGlzLnZpZGVvQ29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lcik7XG4gICAgdGhpcy5wb3BvdmVyID0gbmV3IEppdHNpUG9wb3ZlcihcbiAgICAgICAgJChcIiNcIiArIHRoaXMudmlkZW9Db250YWluZXIuaWQgKyBcIiA+IC5jb25uZWN0aW9uaW5kaWNhdG9yXCIpLFxuICAgICAgICB7Y29udGVudDogXCI8ZGl2IGNsYXNzPVxcXCJjb25uZWN0aW9uX2luZm9cXFwiIGRhdGEtaTE4bj0nY29ubmVjdGlvbmluZGljYXRvci5uYSc+XCIgK1xuICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZVN0cmluZyhcImNvbm5lY3Rpb25pbmRpY2F0b3IubmFcIikgKyBcIjwvZGl2PlwiLFxuICAgICAgICAgICAgc2tpbjogXCJibGFja1wifSk7XG5cbiAgICB0aGlzLmVtcHR5SWNvbiA9IHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5hcHBlbmRDaGlsZChcbiAgICAgICAgY3JlYXRlSWNvbihbXCJjb25uZWN0aW9uXCIsIFwiY29ubmVjdGlvbl9lbXB0eVwiXSkpO1xuICAgIHRoaXMuZnVsbEljb24gPSB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuYXBwZW5kQ2hpbGQoXG4gICAgICAgIGNyZWF0ZUljb24oW1wiY29ubmVjdGlvblwiLCBcImNvbm5lY3Rpb25fZnVsbFwiXSkpO1xuXG59O1xuXG4vKipcbiAqIFJlbW92ZXMgdGhlIGluZGljYXRvclxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS5yZW1vdmUgPSBmdW5jdGlvbigpXG57XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnJlbW92ZSgpO1xuICAgIHRoaXMucG9wb3Zlci5mb3JjZUhpZGUoKTtcblxufTtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBkYXRhIG9mIHRoZSBpbmRpY2F0b3JcbiAqIEBwYXJhbSBwZXJjZW50IHRoZSBwZXJjZW50IG9mIGNvbm5lY3Rpb24gcXVhbGl0eVxuICogQHBhcmFtIG9iamVjdCB0aGUgc3RhdGlzdGljcyBkYXRhLlxuICovXG5Db25uZWN0aW9uSW5kaWNhdG9yLnByb3RvdHlwZS51cGRhdGVDb25uZWN0aW9uUXVhbGl0eSA9XG5mdW5jdGlvbiAocGVyY2VudCwgb2JqZWN0KSB7XG5cbiAgICBpZihwZXJjZW50ID09PSBudWxsKVxuICAgIHtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICAgICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIGlmKHRoaXMuY29ubmVjdGlvbkluZGljYXRvckNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID09IFwibm9uZVwiKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb25JbmRpY2F0b3JDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwiYmxvY2tcIjtcbiAgICAgICAgICAgIHRoaXMudmlkZW9MYXlvdXQudXBkYXRlTXV0ZVBvc2l0aW9uKHRoaXMudmlkZW9Db250YWluZXIuaWQpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRoaXMuYmFuZHdpZHRoID0gb2JqZWN0LmJhbmR3aWR0aDtcbiAgICB0aGlzLmJpdHJhdGUgPSBvYmplY3QuYml0cmF0ZTtcbiAgICB0aGlzLnBhY2tldExvc3MgPSBvYmplY3QucGFja2V0TG9zcztcbiAgICB0aGlzLnRyYW5zcG9ydCA9IG9iamVjdC50cmFuc3BvcnQ7XG4gICAgaWYob2JqZWN0LnJlc29sdXRpb24pXG4gICAge1xuICAgICAgICB0aGlzLnJlc29sdXRpb24gPSBvYmplY3QucmVzb2x1dGlvbjtcbiAgICB9XG4gICAgZm9yKHZhciBxdWFsaXR5IGluIENvbm5lY3Rpb25JbmRpY2F0b3IuY29ubmVjdGlvblF1YWxpdHlWYWx1ZXMpXG4gICAge1xuICAgICAgICBpZihwZXJjZW50ID49IHF1YWxpdHkpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuZnVsbEljb24uc3R5bGUud2lkdGggPVxuICAgICAgICAgICAgICAgIENvbm5lY3Rpb25JbmRpY2F0b3IuY29ubmVjdGlvblF1YWxpdHlWYWx1ZXNbcXVhbGl0eV07XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhpcy51cGRhdGVQb3BvdmVyRGF0YSgpO1xufTtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSByZXNvbHV0aW9uXG4gKiBAcGFyYW0gcmVzb2x1dGlvbiB0aGUgbmV3IHJlc29sdXRpb25cbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUudXBkYXRlUmVzb2x1dGlvbiA9IGZ1bmN0aW9uIChyZXNvbHV0aW9uKSB7XG4gICAgdGhpcy5yZXNvbHV0aW9uID0gcmVzb2x1dGlvbjtcbiAgICB0aGlzLnVwZGF0ZVBvcG92ZXJEYXRhKCk7XG59O1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGNvbnRlbnQgb2YgdGhlIHBvcG92ZXJcbiAqL1xuQ29ubmVjdGlvbkluZGljYXRvci5wcm90b3R5cGUudXBkYXRlUG9wb3ZlckRhdGEgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5wb3BvdmVyLnVwZGF0ZUNvbnRlbnQoXG4gICAgICAgIFwiPGRpdiBjbGFzcz1cXFwiY29ubmVjdGlvbl9pbmZvXFxcIj5cIiArIHRoaXMuZ2VuZXJhdGVUZXh0KCkgKyBcIjwvZGl2PlwiKTtcbiAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlRWxlbWVudCgkKFwiLmNvbm5lY3Rpb25faW5mb1wiKSk7XG59O1xuXG4vKipcbiAqIEhpZGVzIHRoZSBwb3BvdmVyXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLmhpZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5wb3BvdmVyLmZvcmNlSGlkZSgpO1xufTtcblxuLyoqXG4gKiBIaWRlcyB0aGUgaW5kaWNhdG9yXG4gKi9cbkNvbm5lY3Rpb25JbmRpY2F0b3IucHJvdG90eXBlLmhpZGVJbmRpY2F0b3IgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5jb25uZWN0aW9uSW5kaWNhdG9yQ29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICBpZih0aGlzLnBvcG92ZXIpXG4gICAgICAgIHRoaXMucG9wb3Zlci5mb3JjZUhpZGUoKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29ubmVjdGlvbkluZGljYXRvcjsiLCJ2YXIgQXVkaW9MZXZlbHMgPSByZXF1aXJlKFwiLi4vYXVkaW9fbGV2ZWxzL0F1ZGlvTGV2ZWxzXCIpO1xudmFyIEF2YXRhciA9IHJlcXVpcmUoXCIuLi9hdmF0YXIvQXZhdGFyXCIpO1xudmFyIENoYXQgPSByZXF1aXJlKFwiLi4vc2lkZV9wYW5uZWxzL2NoYXQvQ2hhdFwiKTtcbnZhciBDb250YWN0TGlzdCA9IHJlcXVpcmUoXCIuLi9zaWRlX3Bhbm5lbHMvY29udGFjdGxpc3QvQ29udGFjdExpc3RcIik7XG52YXIgVUlVdGlsID0gcmVxdWlyZShcIi4uL3V0aWwvVUlVdGlsXCIpO1xudmFyIENvbm5lY3Rpb25JbmRpY2F0b3IgPSByZXF1aXJlKFwiLi9Db25uZWN0aW9uSW5kaWNhdG9yXCIpO1xudmFyIE5pY2tuYW1lSGFuZGxlciA9IHJlcXVpcmUoXCIuLi91dGlsL05pY2tuYW1lSGFuZGxlclwiKTtcbnZhciBNZWRpYVN0cmVhbVR5cGUgPSByZXF1aXJlKFwiLi4vLi4vLi4vc2VydmljZS9SVEMvTWVkaWFTdHJlYW1UeXBlc1wiKTtcbnZhciBVSUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi8uLi9zZXJ2aWNlL1VJL1VJRXZlbnRzXCIpO1xuXG52YXIgY3VycmVudERvbWluYW50U3BlYWtlciA9IG51bGw7XG52YXIgbGFzdE5Db3VudCA9IGNvbmZpZy5jaGFubmVsTGFzdE47XG52YXIgbG9jYWxMYXN0TkNvdW50ID0gY29uZmlnLmNoYW5uZWxMYXN0TjtcbnZhciBsb2NhbExhc3ROU2V0ID0gW107XG52YXIgbGFzdE5FbmRwb2ludHNDYWNoZSA9IFtdO1xudmFyIGxhc3ROUGlja3VwSmlkID0gbnVsbDtcbnZhciBsYXJnZVZpZGVvU3RhdGUgPSB7XG4gICAgdXBkYXRlSW5Qcm9ncmVzczogZmFsc2UsXG4gICAgbmV3U3JjOiAnJ1xufTtcblxudmFyIGV2ZW50RW1pdHRlciA9IG51bGw7XG5cbi8qKlxuICogQ3VycmVudGx5IGZvY3VzZWQgdmlkZW8gXCJzcmNcIihkaXNwbGF5ZWQgaW4gbGFyZ2UgdmlkZW8pLlxuICogQHR5cGUge1N0cmluZ31cbiAqL1xudmFyIGZvY3VzZWRWaWRlb0luZm8gPSBudWxsO1xuXG4vKipcbiAqIEluZGljYXRlcyBpZiB3ZSBoYXZlIG11dGVkIG91ciBhdWRpbyBiZWZvcmUgdGhlIGNvbmZlcmVuY2UgaGFzIHN0YXJ0ZWQuXG4gKiBAdHlwZSB7Ym9vbGVhbn1cbiAqL1xudmFyIHByZU11dGVkID0gZmFsc2U7XG5cbnZhciBtdXRlZEF1ZGlvcyA9IHt9O1xuXG52YXIgZmxpcFhMb2NhbFZpZGVvID0gdHJ1ZTtcbnZhciBjdXJyZW50VmlkZW9XaWR0aCA9IG51bGw7XG52YXIgY3VycmVudFZpZGVvSGVpZ2h0ID0gbnVsbDtcblxudmFyIGxvY2FsVmlkZW9TcmMgPSBudWxsO1xuXG5mdW5jdGlvbiB2aWRlb2FjdGl2ZSggdmlkZW9lbGVtKSB7XG4gICAgaWYgKHZpZGVvZWxlbS5hdHRyKCdpZCcpLmluZGV4T2YoJ21peGVkbXNsYWJlbCcpID09PSAtMSkge1xuICAgICAgICAvLyBpZ25vcmUgbWl4ZWRtc2xhYmVsYTAgYW5kIHYwXG5cbiAgICAgICAgdmlkZW9lbGVtLnNob3coKTtcbiAgICAgICAgVmlkZW9MYXlvdXQucmVzaXplVGh1bWJuYWlscygpO1xuXG4gICAgICAgIHZhciB2aWRlb1BhcmVudCA9IHZpZGVvZWxlbS5wYXJlbnQoKTtcbiAgICAgICAgdmFyIHBhcmVudFJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgaWYgKHZpZGVvUGFyZW50KVxuICAgICAgICAgICAgcGFyZW50UmVzb3VyY2VKaWRcbiAgICAgICAgICAgICAgICA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZCh2aWRlb1BhcmVudFswXSk7XG5cbiAgICAgICAgLy8gVXBkYXRlIHRoZSBsYXJnZSB2aWRlbyB0byB0aGUgbGFzdCBhZGRlZCB2aWRlbyBvbmx5IGlmIHRoZXJlJ3Mgbm9cbiAgICAgICAgLy8gY3VycmVudCBkb21pbmFudCwgZm9jdXNlZCBzcGVha2VyIG9yIHByZXppIHBsYXlpbmcgb3IgdXBkYXRlIGl0IHRvXG4gICAgICAgIC8vIHRoZSBjdXJyZW50IGRvbWluYW50IHNwZWFrZXIuXG4gICAgICAgIGlmICgoIWZvY3VzZWRWaWRlb0luZm8gJiZcbiAgICAgICAgICAgICFWaWRlb0xheW91dC5nZXREb21pbmFudFNwZWFrZXJSZXNvdXJjZUppZCgpICYmXG4gICAgICAgICAgICAhcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpKSB8fFxuICAgICAgICAgICAgKHBhcmVudFJlc291cmNlSmlkICYmXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZ2V0RG9taW5hbnRTcGVha2VyUmVzb3VyY2VKaWQoKSA9PT0gcGFyZW50UmVzb3VyY2VKaWQpKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKFxuICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmModmlkZW9lbGVtWzBdKSxcbiAgICAgICAgICAgICAgICAxLFxuICAgICAgICAgICAgICAgIHBhcmVudFJlc291cmNlSmlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFZpZGVvTGF5b3V0LnNob3dNb2RlcmF0b3JJbmRpY2F0b3IoKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWxlY3Rvciwgc3NyYywgc3RyZWFtLCBqaWQpIHtcbiAgICAvLyBYWFgoZ3ApIHNvLCBldmVyeSBjYWxsIHRvIHRoaXMgZnVuY3Rpb24gaXMgKmFsd2F5cyogcHJlY2VkZWQgYnkgYSBjYWxsXG4gICAgLy8gdG8gdGhlIFJUQy5hdHRhY2hNZWRpYVN0cmVhbSgpIGZ1bmN0aW9uIGJ1dCB0aGF0IGNhbGwgaXMgKm5vdCogZm9sbG93ZWRcbiAgICAvLyBieSBhbiB1cGRhdGUgdG8gdGhlIHZpZGVvU3JjVG9Tc3JjIG1hcCFcbiAgICAvL1xuICAgIC8vIFRoZSBhYm92ZSB3YXkgb2YgZG9pbmcgdGhpbmdzIHJlc3VsdHMgaW4gdmlkZW8gU1JDcyB0aGF0IGRvbid0IGNvcnJlc3BvbmRcbiAgICAvLyB0byBhbnkgU1NSQyBmb3IgYSBzaG9ydCBwZXJpb2Qgb2YgdGltZSAodG8gYmUgbW9yZSBwcmVjaXNlLCBmb3IgYXMgbG9uZ1xuICAgIC8vIHRoZSB3YWl0Rm9yUmVtb3RlVmlkZW8gdGFrZXMgdG8gY29tcGxldGUpLiBUaGlzIGNhdXNlcyBwcm9ibGVtcyAoc2VlXG4gICAgLy8gYmVsbG93KS5cbiAgICAvL1xuICAgIC8vIEknbSB3b25kZXJpbmcgd2h5IHdlIG5lZWQgdG8gZG8gdGhhdDsgaS5lLiB3aHkgY2FsbCBSVEMuYXR0YWNoTWVkaWFTdHJlYW0oKVxuICAgIC8vIGEgc2Vjb25kIHRpbWUgaW4gaGVyZSBhbmQgb25seSB0aGVuIHVwZGF0ZSB0aGUgdmlkZW9TcmNUb1NzcmMgbWFwPyBXaHlcbiAgICAvLyBub3Qgc2ltcGx5IHVwZGF0ZSB0aGUgdmlkZW9TcmNUb1NzcmMgbWFwIHdoZW4gdGhlIFJUQy5hdHRhY2hNZWRpYVN0cmVhbSgpXG4gICAgLy8gaXMgY2FsbGVkIHRoZSBmaXJzdCB0aW1lPyBJIGFjdHVhbGx5IGRvIHRoYXQgaW4gdGhlIGxhc3ROIGNoYW5nZWQgZXZlbnRcbiAgICAvLyBoYW5kbGVyIGJlY2F1c2UgdGhlIFwib3JwaGFuXCIgdmlkZW8gU1JDIGlzIGNhdXNpbmcgdHJvdWJsZXMgdGhlcmUuIFRoZVxuICAgIC8vIHB1cnBvc2Ugb2YgdGhpcyBtZXRob2Qgd291bGQgdGhlbiBiZSB0byBmaXJlIHRoZSBcInZpZGVvYWN0aXZlLmppbmdsZVwiLlxuICAgIC8vXG4gICAgLy8gRm9vZCBmb3IgdGhvdWdoIEkgZ3Vlc3MgOi0pXG5cbiAgICBpZiAoc2VsZWN0b3IucmVtb3ZlZCB8fCAhc2VsZWN0b3IucGFyZW50KCkuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJNZWRpYSByZW1vdmVkIGJlZm9yZSBoYWQgc3RhcnRlZFwiLCBzZWxlY3Rvcik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoc3RyZWFtLmlkID09PSAnbWl4ZWRtc2xhYmVsJykgcmV0dXJuO1xuXG4gICAgaWYgKHNlbGVjdG9yWzBdLmN1cnJlbnRUaW1lID4gMCkge1xuICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBBUFAuc2ltdWxjYXN0LmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKHN0cmVhbSk7XG4gICAgICAgIEFQUC5SVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsZWN0b3IsIHZpZGVvU3RyZWFtKTsgLy8gRklYTUU6IHdoeSBkbyBpIGhhdmUgdG8gZG8gdGhpcyBmb3IgRkY/XG4gICAgICAgIHZpZGVvYWN0aXZlKHNlbGVjdG9yKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWxlY3Rvciwgc3NyYywgc3RyZWFtLCBqaWQpO1xuICAgICAgICB9LCAyNTApO1xuICAgIH1cbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIHRoZSB2aWRlbyBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBpbmRlbnRzLFxuICogc28gdGhhdCBpZiBmaXRzIGl0cyBwYXJlbnQuXG4gKlxuICogQHJldHVybiBhbiBhcnJheSB3aXRoIDIgZWxlbWVudHMsIHRoZSBob3Jpem9udGFsIGluZGVudCBhbmQgdGhlIHZlcnRpY2FsXG4gKiBpbmRlbnRcbiAqL1xuZnVuY3Rpb24gZ2V0Q2FtZXJhVmlkZW9Qb3NpdGlvbih2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG4gICAgLy8gUGFyZW50IGhlaWdodCBpc24ndCBjb21wbGV0ZWx5IGNhbGN1bGF0ZWQgd2hlbiB3ZSBwb3NpdGlvbiB0aGUgdmlkZW8gaW5cbiAgICAvLyBmdWxsIHNjcmVlbiBtb2RlIGFuZCB0aGlzIGlzIHdoeSB3ZSB1c2UgdGhlIHNjcmVlbiBoZWlnaHQgaW4gdGhpcyBjYXNlLlxuICAgIC8vIE5lZWQgdG8gdGhpbmsgaXQgZnVydGhlciBhdCBzb21lIHBvaW50IGFuZCBpbXBsZW1lbnQgaXQgcHJvcGVybHkuXG4gICAgdmFyIGlzRnVsbFNjcmVlbiA9IGRvY3VtZW50LmZ1bGxTY3JlZW4gfHxcbiAgICAgICAgZG9jdW1lbnQubW96RnVsbFNjcmVlbiB8fFxuICAgICAgICBkb2N1bWVudC53ZWJraXRJc0Z1bGxTY3JlZW47XG4gICAgaWYgKGlzRnVsbFNjcmVlbilcbiAgICAgICAgdmlkZW9TcGFjZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcblxuICAgIHZhciBob3Jpem9udGFsSW5kZW50ID0gKHZpZGVvU3BhY2VXaWR0aCAtIHZpZGVvV2lkdGgpIC8gMjtcbiAgICB2YXIgdmVydGljYWxJbmRlbnQgPSAodmlkZW9TcGFjZUhlaWdodCAtIHZpZGVvSGVpZ2h0KSAvIDI7XG5cbiAgICByZXR1cm4gW2hvcml6b250YWxJbmRlbnQsIHZlcnRpY2FsSW5kZW50XTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIHRoZSB2aWRlbyBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBpbmRlbnRzLlxuICogQ2VudGVycyBob3Jpem9udGFsbHkgYW5kIHRvcCBhbGlnbnMgdmVydGljYWxseS5cbiAqXG4gKiBAcmV0dXJuIGFuIGFycmF5IHdpdGggMiBlbGVtZW50cywgdGhlIGhvcml6b250YWwgaW5kZW50IGFuZCB0aGUgdmVydGljYWxcbiAqIGluZGVudFxuICovXG5mdW5jdGlvbiBnZXREZXNrdG9wVmlkZW9Qb3NpdGlvbih2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG5cbiAgICB2YXIgaG9yaXpvbnRhbEluZGVudCA9ICh2aWRlb1NwYWNlV2lkdGggLSB2aWRlb1dpZHRoKSAvIDI7XG5cbiAgICB2YXIgdmVydGljYWxJbmRlbnQgPSAwOy8vIFRvcCBhbGlnbmVkXG5cbiAgICByZXR1cm4gW2hvcml6b250YWxJbmRlbnQsIHZlcnRpY2FsSW5kZW50XTtcbn1cblxuXG4vKipcbiAqIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHZpZGVvIGRpbWVuc2lvbnMsIHNvIHRoYXQgaXQgY292ZXJzIHRoZSBzY3JlZW4uXG4gKiBJdCBsZWF2ZXMgbm8gZW1wdHkgYXJlYXMsIGJ1dCBzb21lIHBhcnRzIG9mIHRoZSB2aWRlbyBtaWdodCBub3QgYmUgdmlzaWJsZS5cbiAqXG4gKiBAcmV0dXJuIGFuIGFycmF5IHdpdGggMiBlbGVtZW50cywgdGhlIHZpZGVvIHdpZHRoIGFuZCB0aGUgdmlkZW8gaGVpZ2h0XG4gKi9cbmZ1bmN0aW9uIGdldENhbWVyYVZpZGVvU2l6ZSh2aWRlb1dpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlSGVpZ2h0KSB7XG4gICAgaWYgKCF2aWRlb1dpZHRoKVxuICAgICAgICB2aWRlb1dpZHRoID0gY3VycmVudFZpZGVvV2lkdGg7XG4gICAgaWYgKCF2aWRlb0hlaWdodClcbiAgICAgICAgdmlkZW9IZWlnaHQgPSBjdXJyZW50VmlkZW9IZWlnaHQ7XG5cbiAgICB2YXIgYXNwZWN0UmF0aW8gPSB2aWRlb1dpZHRoIC8gdmlkZW9IZWlnaHQ7XG5cbiAgICB2YXIgYXZhaWxhYmxlV2lkdGggPSBNYXRoLm1heCh2aWRlb1dpZHRoLCB2aWRlb1NwYWNlV2lkdGgpO1xuICAgIHZhciBhdmFpbGFibGVIZWlnaHQgPSBNYXRoLm1heCh2aWRlb0hlaWdodCwgdmlkZW9TcGFjZUhlaWdodCk7XG5cbiAgICBpZiAoYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyA8IHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gdmlkZW9TcGFjZUhlaWdodDtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8gPCB2aWRlb1NwYWNlV2lkdGgpIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSB2aWRlb1NwYWNlV2lkdGg7XG4gICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW87XG4gICAgfVxuXG4gICAgcmV0dXJuIFthdmFpbGFibGVXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBkaXNwbGF5IG5hbWUgZm9yIHRoZSBnaXZlbiB2aWRlbyBzcGFuIGlkLlxuICovXG5mdW5jdGlvbiBzZXREaXNwbGF5TmFtZSh2aWRlb1NwYW5JZCwgZGlzcGxheU5hbWUpIHtcbiAgICB2YXIgbmFtZVNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLmRpc3BsYXluYW1lJyk7XG4gICAgdmFyIGRlZmF1bHRMb2NhbERpc3BsYXlOYW1lID0gaW50ZXJmYWNlQ29uZmlnLkRFRkFVTFRfTE9DQUxfRElTUExBWV9OQU1FO1xuXG4gICAgLy8gSWYgd2UgYWxyZWFkeSBoYXZlIGEgZGlzcGxheSBuYW1lIGZvciB0aGlzIHZpZGVvLlxuICAgIGlmIChuYW1lU3Bhbi5sZW5ndGggPiAwKSB7XG4gICAgICAgIHZhciBuYW1lU3BhbkVsZW1lbnQgPSBuYW1lU3Bhbi5nZXQoMCk7XG5cbiAgICAgICAgaWYgKG5hbWVTcGFuRWxlbWVudC5pZCA9PT0gJ2xvY2FsRGlzcGxheU5hbWUnICYmXG4gICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLnRleHQoKSAhPT0gZGlzcGxheU5hbWUpIHtcbiAgICAgICAgICAgIGlmIChkaXNwbGF5TmFtZSAmJiBkaXNwbGF5TmFtZS5sZW5ndGggPiAwKVxuICAgICAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJykuaHRtbChkaXNwbGF5TmFtZSArICcgKG1lKScpO1xuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJykudGV4dChkZWZhdWx0TG9jYWxEaXNwbGF5TmFtZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkICsgJ19uYW1lJykuaHRtbChkaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZCArICdfbmFtZScpLnRleHQoaW50ZXJmYWNlQ29uZmlnLkRFRkFVTFRfUkVNT1RFX0RJU1BMQVlfTkFNRSk7XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgZWRpdEJ1dHRvbiA9IG51bGw7XG5cbiAgICAgICAgbmFtZVNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgIG5hbWVTcGFuLmNsYXNzTmFtZSA9ICdkaXNwbGF5bmFtZSc7XG4gICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKG5hbWVTcGFuKTtcblxuICAgICAgICBpZiAodmlkZW9TcGFuSWQgPT09ICdsb2NhbFZpZGVvQ29udGFpbmVyJykge1xuICAgICAgICAgICAgZWRpdEJ1dHRvbiA9IGNyZWF0ZUVkaXREaXNwbGF5TmFtZUJ1dHRvbigpO1xuICAgICAgICAgICAgbmFtZVNwYW4uaW5uZXJUZXh0ID0gZGVmYXVsdExvY2FsRGlzcGxheU5hbWU7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBuYW1lU3Bhbi5pbm5lclRleHQgPSBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9SRU1PVEVfRElTUExBWV9OQU1FO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIG5hbWVTcGFuLmlubmVyVGV4dCA9IGRpc3BsYXlOYW1lO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFlZGl0QnV0dG9uKSB7XG4gICAgICAgICAgICBuYW1lU3Bhbi5pZCA9IHZpZGVvU3BhbklkICsgJ19uYW1lJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG5hbWVTcGFuLmlkID0gJ2xvY2FsRGlzcGxheU5hbWUnO1xuICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQoZWRpdEJ1dHRvbik7XG4gICAgICAgICAgICAvL3RyYW5zbGF0ZXMgcG9wb3ZlciBvZiBlZGl0IGJ1dHRvblxuICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJChcImEuZGlzcGxheW5hbWVcIikpO1xuXG4gICAgICAgICAgICB2YXIgZWRpdGFibGVUZXh0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW5wdXQnKTtcbiAgICAgICAgICAgIGVkaXRhYmxlVGV4dC5jbGFzc05hbWUgPSAnZGlzcGxheW5hbWUnO1xuICAgICAgICAgICAgZWRpdGFibGVUZXh0LnR5cGUgPSAndGV4dCc7XG4gICAgICAgICAgICBlZGl0YWJsZVRleHQuaWQgPSAnZWRpdERpc3BsYXlOYW1lJztcblxuICAgICAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGVkaXRhYmxlVGV4dC52YWx1ZVxuICAgICAgICAgICAgICAgICAgICA9IGRpc3BsYXlOYW1lLnN1YnN0cmluZygwLCBkaXNwbGF5TmFtZS5pbmRleE9mKCcgKG1lKScpKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZWRpdGFibGVUZXh0LnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAnZGlzcGxheTpub25lOycpO1xuICAgICAgICAgICAgZWRpdGFibGVUZXh0LnNldEF0dHJpYnV0ZSgncGxhY2Vob2xkZXInLCAnZXguIEphbmUgUGluaycpO1xuICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZClbMF0uYXBwZW5kQ2hpbGQoZWRpdGFibGVUZXh0KTtcblxuICAgICAgICAgICAgJCgnI2xvY2FsVmlkZW9Db250YWluZXIgLmRpc3BsYXluYW1lJylcbiAgICAgICAgICAgICAgICAuYmluZChcImNsaWNrXCIsIGZ1bmN0aW9uIChlKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICAkKCcjbG9jYWxEaXNwbGF5TmFtZScpLmhpZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLnNob3coKTtcbiAgICAgICAgICAgICAgICAgICAgJCgnI2VkaXREaXNwbGF5TmFtZScpLmZvY3VzKCk7XG4gICAgICAgICAgICAgICAgICAgICQoJyNlZGl0RGlzcGxheU5hbWUnKS5zZWxlY3QoKTtcblxuICAgICAgICAgICAgICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykub25lKFwiZm9jdXNvdXRcIiwgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmlucHV0RGlzcGxheU5hbWVIYW5kbGVyKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykub24oJ2tleWRvd24nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUua2V5Q29kZSA9PT0gMTMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaW5wdXREaXNwbGF5TmFtZUhhbmRsZXIodGhpcy52YWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIEdldHMgdGhlIHNlbGVjdG9yIG9mIHZpZGVvIHRodW1ibmFpbCBjb250YWluZXIgZm9yIHRoZSB1c2VyIGlkZW50aWZpZWQgYnlcbiAqIGdpdmVuIDx0dD51c2VySmlkPC90dD5cbiAqIEBwYXJhbSByZXNvdXJjZUppZCB1c2VyJ3MgSmlkIGZvciB3aG9tIHdlIHdhbnQgdG8gZ2V0IHRoZSB2aWRlbyBjb250YWluZXIuXG4gKi9cbmZ1bmN0aW9uIGdldFBhcnRpY2lwYW50Q29udGFpbmVyKHJlc291cmNlSmlkKVxue1xuICAgIGlmICghcmVzb3VyY2VKaWQpXG4gICAgICAgIHJldHVybiBudWxsO1xuXG4gICAgaWYgKHJlc291cmNlSmlkID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpXG4gICAgICAgIHJldHVybiAkKFwiI2xvY2FsVmlkZW9Db250YWluZXJcIik7XG4gICAgZWxzZVxuICAgICAgICByZXR1cm4gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkKTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBzaXplIGFuZCBwb3NpdGlvbiBvZiB0aGUgZ2l2ZW4gdmlkZW8gZWxlbWVudC5cbiAqXG4gKiBAcGFyYW0gdmlkZW8gdGhlIHZpZGVvIGVsZW1lbnQgdG8gcG9zaXRpb25cbiAqIEBwYXJhbSB3aWR0aCB0aGUgZGVzaXJlZCB2aWRlbyB3aWR0aFxuICogQHBhcmFtIGhlaWdodCB0aGUgZGVzaXJlZCB2aWRlbyBoZWlnaHRcbiAqIEBwYXJhbSBob3Jpem9udGFsSW5kZW50IHRoZSBsZWZ0IGFuZCByaWdodCBpbmRlbnRcbiAqIEBwYXJhbSB2ZXJ0aWNhbEluZGVudCB0aGUgdG9wIGFuZCBib3R0b20gaW5kZW50XG4gKi9cbmZ1bmN0aW9uIHBvc2l0aW9uVmlkZW8odmlkZW8sXG4gICAgICAgICAgICAgICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgIGhvcml6b250YWxJbmRlbnQsXG4gICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2FsSW5kZW50KSB7XG4gICAgdmlkZW8ud2lkdGgod2lkdGgpO1xuICAgIHZpZGVvLmhlaWdodChoZWlnaHQpO1xuICAgIHZpZGVvLmNzcyh7ICB0b3A6IHZlcnRpY2FsSW5kZW50ICsgJ3B4JyxcbiAgICAgICAgYm90dG9tOiB2ZXJ0aWNhbEluZGVudCArICdweCcsXG4gICAgICAgIGxlZnQ6IGhvcml6b250YWxJbmRlbnQgKyAncHgnLFxuICAgICAgICByaWdodDogaG9yaXpvbnRhbEluZGVudCArICdweCd9KTtcbn1cblxuLyoqXG4gKiBBZGRzIHRoZSByZW1vdGUgdmlkZW8gbWVudSBlbGVtZW50IGZvciB0aGUgZ2l2ZW4gPHR0PmppZDwvdHQ+IGluIHRoZVxuICogZ2l2ZW4gPHR0PnBhcmVudEVsZW1lbnQ8L3R0Pi5cbiAqXG4gKiBAcGFyYW0gamlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZm9yIHdoaWNoIHdlJ3JlIGFkZGluZyBhIG1lbnUuXG4gKiBAcGFyYW0gcGFyZW50RWxlbWVudCB0aGUgcGFyZW50IGVsZW1lbnQgd2hlcmUgdGhpcyBtZW51IHdpbGwgYmUgYWRkZWRcbiAqL1xuZnVuY3Rpb24gYWRkUmVtb3RlVmlkZW9NZW51KGppZCwgcGFyZW50RWxlbWVudCkge1xuICAgIHZhciBzcGFuRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICBzcGFuRWxlbWVudC5jbGFzc05hbWUgPSAncmVtb3RldmlkZW9tZW51JztcblxuICAgIHBhcmVudEVsZW1lbnQuYXBwZW5kQ2hpbGQoc3BhbkVsZW1lbnQpO1xuXG4gICAgdmFyIG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgIG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdmYSBmYS1hbmdsZS1kb3duJztcbiAgICBtZW51RWxlbWVudC50aXRsZSA9ICdSZW1vdGUgdXNlciBjb250cm9scyc7XG4gICAgc3BhbkVsZW1lbnQuYXBwZW5kQ2hpbGQobWVudUVsZW1lbnQpO1xuXG4vLyAgICAgICAgPHVsIGNsYXNzPVwicG9wdXBtZW51XCI+XG4vLyAgICAgICAgPGxpPjxhIGhyZWY9XCIjXCI+TXV0ZTwvYT48L2xpPlxuLy8gICAgICAgIDxsaT48YSBocmVmPVwiI1wiPkVqZWN0PC9hPjwvbGk+XG4vLyAgICAgICAgPC91bD5cblxuICAgIHZhciBwb3B1cG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndWwnKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmNsYXNzTmFtZSA9ICdwb3B1cG1lbnUnO1xuICAgIHBvcHVwbWVudUVsZW1lbnQuaWRcbiAgICAgICAgPSAncmVtb3RlX3BvcHVwbWVudV8nICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICBzcGFuRWxlbWVudC5hcHBlbmRDaGlsZChwb3B1cG1lbnVFbGVtZW50KTtcblxuICAgIHZhciBtdXRlTWVudUl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuICAgIHZhciBtdXRlTGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cbiAgICB2YXIgbXV0ZWRJbmRpY2F0b3IgPSBcIjxpIHN0eWxlPSdmbG9hdDpsZWZ0OycgY2xhc3M9J2ljb24tbWljLWRpc2FibGVkJz48L2k+XCI7XG5cbiAgICBpZiAoIW11dGVkQXVkaW9zW2ppZF0pIHtcbiAgICAgICAgbXV0ZUxpbmtJdGVtLmlubmVySFRNTCA9IG11dGVkSW5kaWNhdG9yICtcbiAgICAgICAgICAgIFwiIDxkaXYgc3R5bGU9J3dpZHRoOiA5MHB4O21hcmdpbi1sZWZ0OiAyMHB4OycgZGF0YS1pMThuPSd2aWRlb3RodW1ibmFpbC5kb211dGUnPjwvZGl2PlwiO1xuICAgICAgICBtdXRlTGlua0l0ZW0uY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIG11dGVMaW5rSXRlbS5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArXG4gICAgICAgICAgICBcIiA8ZGl2IHN0eWxlPSd3aWR0aDogOTBweDttYXJnaW4tbGVmdDogMjBweDsnIGRhdGEtaTE4bj0ndmlkZW90aHVtYm5haWwubXV0ZWQnPjwvZGl2PlwiO1xuICAgICAgICBtdXRlTGlua0l0ZW0uY2xhc3NOYW1lID0gJ211dGVsaW5rIGRpc2FibGVkJztcbiAgICB9XG5cbiAgICBtdXRlTGlua0l0ZW0ub25jbGljayA9IGZ1bmN0aW9uKCl7XG4gICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ2Rpc2FibGVkJykgIT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBpc011dGUgPSBtdXRlZEF1ZGlvc1tqaWRdID09IHRydWU7XG4gICAgICAgIEFQUC54bXBwLnNldE11dGUoamlkLCAhaXNNdXRlKTtcblxuICAgICAgICBwb3B1cG1lbnVFbGVtZW50LnNldEF0dHJpYnV0ZSgnc3R5bGUnLCAnZGlzcGxheTpub25lOycpO1xuXG4gICAgICAgIGlmIChpc011dGUpIHtcbiAgICAgICAgICAgIHRoaXMuaW5uZXJIVE1MID0gbXV0ZWRJbmRpY2F0b3IgK1xuICAgICAgICAgICAgICAgIFwiIDxkaXYgc3R5bGU9J3dpZHRoOiA5MHB4O21hcmdpbi1sZWZ0OiAyMHB4OycgZGF0YS1pMThuPSd2aWRlb3RodW1ibmFpbC5tdXRlZCc+PC9kaXY+XCI7XG4gICAgICAgICAgICB0aGlzLmNsYXNzTmFtZSA9ICdtdXRlbGluayBkaXNhYmxlZCc7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmlubmVySFRNTCA9IG11dGVkSW5kaWNhdG9yICtcbiAgICAgICAgICAgICAgICBcIiA8ZGl2IHN0eWxlPSd3aWR0aDogOTBweDttYXJnaW4tbGVmdDogMjBweDsnIGRhdGEtaTE4bj0ndmlkZW90aHVtYm5haWwuZG9tdXRlJz48L2Rpdj5cIjtcbiAgICAgICAgICAgIHRoaXMuY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBtdXRlTWVudUl0ZW0uYXBwZW5kQ2hpbGQobXV0ZUxpbmtJdGVtKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKG11dGVNZW51SXRlbSk7XG5cbiAgICB2YXIgZWplY3RJbmRpY2F0b3IgPSBcIjxpIHN0eWxlPSdmbG9hdDpsZWZ0OycgY2xhc3M9J2ZhIGZhLWVqZWN0Jz48L2k+XCI7XG5cbiAgICB2YXIgZWplY3RNZW51SXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7XG4gICAgdmFyIGVqZWN0TGlua0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgdmFyIGVqZWN0VGV4dCA9IFwiPGRpdiBzdHlsZT0nd2lkdGg6IDkwcHg7bWFyZ2luLWxlZnQ6IDIwcHg7JyBkYXRhLWkxOG49J3ZpZGVvdGh1bWJuYWlsLmtpY2snPiZuYnNwOzwvZGl2PlwiO1xuICAgIGVqZWN0TGlua0l0ZW0uaW5uZXJIVE1MID0gZWplY3RJbmRpY2F0b3IgKyAnICcgKyBlamVjdFRleHQ7XG4gICAgZWplY3RMaW5rSXRlbS5vbmNsaWNrID0gZnVuY3Rpb24oKXtcbiAgICAgICAgQVBQLnhtcHAuZWplY3QoamlkKTtcbiAgICAgICAgcG9wdXBtZW51RWxlbWVudC5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgJ2Rpc3BsYXk6bm9uZTsnKTtcbiAgICB9O1xuXG4gICAgZWplY3RNZW51SXRlbS5hcHBlbmRDaGlsZChlamVjdExpbmtJdGVtKTtcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKGVqZWN0TWVudUl0ZW0pO1xuXG4gICAgdmFyIHBhZGRpbmdTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgIHBhZGRpbmdTcGFuLmNsYXNzTmFtZSA9ICdwb3B1cG1lbnVQYWRkaW5nJztcbiAgICBwb3B1cG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKHBhZGRpbmdTcGFuKTtcbiAgICBBUFAudHJhbnNsYXRpb24udHJhbnNsYXRlRWxlbWVudCgkKFwiI1wiICsgcG9wdXBtZW51RWxlbWVudC5pZCArIFwiID4gbGkgPiBhID4gZGl2XCIpKTtcbn1cblxuLyoqXG4gKiBSZW1vdmVzIHJlbW90ZSB2aWRlbyBtZW51IGVsZW1lbnQgZnJvbSB2aWRlbyBlbGVtZW50IGlkZW50aWZpZWQgYnlcbiAqIGdpdmVuIDx0dD52aWRlb0VsZW1lbnRJZDwvdHQ+LlxuICpcbiAqIEBwYXJhbSB2aWRlb0VsZW1lbnRJZCB0aGUgaWQgb2YgbG9jYWwgb3IgcmVtb3RlIHZpZGVvIGVsZW1lbnQuXG4gKi9cbmZ1bmN0aW9uIHJlbW92ZVJlbW90ZVZpZGVvTWVudSh2aWRlb0VsZW1lbnRJZCkge1xuICAgIHZhciBtZW51U3BhbiA9ICQoJyMnICsgdmlkZW9FbGVtZW50SWQgKyAnPnNwYW4ucmVtb3RldmlkZW9tZW51Jyk7XG4gICAgaWYgKG1lbnVTcGFuLmxlbmd0aCkge1xuICAgICAgICBtZW51U3Bhbi5yZW1vdmUoKTtcbiAgICB9XG59XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgZGF0YSBmb3IgdGhlIGluZGljYXRvclxuICogQHBhcmFtIGlkIHRoZSBpZCBvZiB0aGUgaW5kaWNhdG9yXG4gKiBAcGFyYW0gcGVyY2VudCB0aGUgcGVyY2VudCBmb3IgY29ubmVjdGlvbiBxdWFsaXR5XG4gKiBAcGFyYW0gb2JqZWN0IHRoZSBkYXRhXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZVN0YXRzSW5kaWNhdG9yKGlkLCBwZXJjZW50LCBvYmplY3QpIHtcbiAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tpZF0pXG4gICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2lkXS51cGRhdGVDb25uZWN0aW9uUXVhbGl0eShwZXJjZW50LCBvYmplY3QpO1xufVxuXG5cbi8qKlxuICogUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgdmlkZW8gZGltZW5zaW9ucywgc28gdGhhdCBpdCBrZWVwcyBpdCdzIGFzcGVjdFxuICogcmF0aW8gYW5kIGZpdHMgYXZhaWxhYmxlIGFyZWEgd2l0aCBpdCdzIGxhcmdlciBkaW1lbnNpb24uIFRoaXMgbWV0aG9kXG4gKiBlbnN1cmVzIHRoYXQgd2hvbGUgdmlkZW8gd2lsbCBiZSB2aXNpYmxlIGFuZCBjYW4gbGVhdmUgZW1wdHkgYXJlYXMuXG4gKlxuICogQHJldHVybiBhbiBhcnJheSB3aXRoIDIgZWxlbWVudHMsIHRoZSB2aWRlbyB3aWR0aCBhbmQgdGhlIHZpZGVvIGhlaWdodFxuICovXG5mdW5jdGlvbiBnZXREZXNrdG9wVmlkZW9TaXplKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1NwYWNlV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpIHtcbiAgICBpZiAoIXZpZGVvV2lkdGgpXG4gICAgICAgIHZpZGVvV2lkdGggPSBjdXJyZW50VmlkZW9XaWR0aDtcbiAgICBpZiAoIXZpZGVvSGVpZ2h0KVxuICAgICAgICB2aWRlb0hlaWdodCA9IGN1cnJlbnRWaWRlb0hlaWdodDtcblxuICAgIHZhciBhc3BlY3RSYXRpbyA9IHZpZGVvV2lkdGggLyB2aWRlb0hlaWdodDtcblxuICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KHZpZGVvV2lkdGgsIHZpZGVvU3BhY2VXaWR0aCk7XG4gICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IE1hdGgubWF4KHZpZGVvSGVpZ2h0LCB2aWRlb1NwYWNlSGVpZ2h0KTtcblxuICAgIHZpZGVvU3BhY2VIZWlnaHQgLT0gJCgnI3JlbW90ZVZpZGVvcycpLm91dGVySGVpZ2h0KCk7XG5cbiAgICBpZiAoYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbyA+PSB2aWRlb1NwYWNlSGVpZ2h0KVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gdmlkZW9TcGFjZUhlaWdodDtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSBhdmFpbGFibGVIZWlnaHQgKiBhc3BlY3RSYXRpbztcbiAgICB9XG5cbiAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8gPj0gdmlkZW9TcGFjZVdpZHRoKVxuICAgIHtcbiAgICAgICAgYXZhaWxhYmxlV2lkdGggPSB2aWRlb1NwYWNlV2lkdGg7XG4gICAgICAgIGF2YWlsYWJsZUhlaWdodCA9IGF2YWlsYWJsZVdpZHRoIC8gYXNwZWN0UmF0aW87XG4gICAgfVxuXG4gICAgcmV0dXJuIFthdmFpbGFibGVXaWR0aCwgYXZhaWxhYmxlSGVpZ2h0XTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBlZGl0IGRpc3BsYXkgbmFtZSBidXR0b24uXG4gKlxuICogQHJldHVybnMgdGhlIGVkaXQgYnV0dG9uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUVkaXREaXNwbGF5TmFtZUJ1dHRvbigpIHtcbiAgICB2YXIgZWRpdEJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICBlZGl0QnV0dG9uLmNsYXNzTmFtZSA9ICdkaXNwbGF5bmFtZSc7XG4gICAgVUlVdGlsLnNldFRvb2x0aXAoZWRpdEJ1dHRvbixcbiAgICAgICAgXCJ2aWRlb3RodW1ibmFpbC5lZGl0bmlja25hbWVcIixcbiAgICAgICAgXCJ0b3BcIik7XG4gICAgZWRpdEJ1dHRvbi5pbm5lckhUTUwgPSAnPGkgY2xhc3M9XCJmYSBmYS1wZW5jaWxcIj48L2k+JztcblxuICAgIHJldHVybiBlZGl0QnV0dG9uO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGVsZW1lbnQgaW5kaWNhdGluZyB0aGUgbW9kZXJhdG9yKG93bmVyKSBvZiB0aGUgY29uZmVyZW5jZS5cbiAqXG4gKiBAcGFyYW0gcGFyZW50RWxlbWVudCB0aGUgcGFyZW50IGVsZW1lbnQgd2hlcmUgdGhlIG93bmVyIGluZGljYXRvciB3aWxsXG4gKiBiZSBhZGRlZFxuICovXG5mdW5jdGlvbiBjcmVhdGVNb2RlcmF0b3JJbmRpY2F0b3JFbGVtZW50KHBhcmVudEVsZW1lbnQpIHtcbiAgICB2YXIgbW9kZXJhdG9ySW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgIG1vZGVyYXRvckluZGljYXRvci5jbGFzc05hbWUgPSAnZmEgZmEtc3Rhcic7XG4gICAgcGFyZW50RWxlbWVudC5hcHBlbmRDaGlsZChtb2RlcmF0b3JJbmRpY2F0b3IpO1xuXG4gICAgVUlVdGlsLnNldFRvb2x0aXAocGFyZW50RWxlbWVudCxcbiAgICAgICAgXCJ2aWRlb3RodW1ibmFpbC5tb2RlcmF0b3JcIixcbiAgICAgICAgXCJ0b3BcIik7XG59XG5cblxudmFyIFZpZGVvTGF5b3V0ID0gKGZ1bmN0aW9uIChteSkge1xuICAgIG15LmNvbm5lY3Rpb25JbmRpY2F0b3JzID0ge307XG5cbiAgICAvLyBCeSBkZWZhdWx0IHdlIHVzZSBjYW1lcmFcbiAgICBteS5nZXRWaWRlb1NpemUgPSBnZXRDYW1lcmFWaWRlb1NpemU7XG4gICAgbXkuZ2V0VmlkZW9Qb3NpdGlvbiA9IGdldENhbWVyYVZpZGVvUG9zaXRpb247XG5cbiAgICBteS5pbml0ID0gZnVuY3Rpb24gKGVtaXR0ZXIpIHtcbiAgICAgICAgLy8gTGlzdGVuIGZvciBsYXJnZSB2aWRlbyBzaXplIHVwZGF0ZXNcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKVxuICAgICAgICAgICAgLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWRlZG1ldGFkYXRhJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9XaWR0aCA9IHRoaXMudmlkZW9XaWR0aDtcbiAgICAgICAgICAgICAgICBjdXJyZW50VmlkZW9IZWlnaHQgPSB0aGlzLnZpZGVvSGVpZ2h0O1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnBvc2l0aW9uTGFyZ2UoY3VycmVudFZpZGVvV2lkdGgsIGN1cnJlbnRWaWRlb0hlaWdodCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgZXZlbnRFbWl0dGVyID0gZW1pdHRlcjtcbiAgICB9O1xuXG4gICAgbXkuaXNJbkxhc3ROID0gZnVuY3Rpb24ocmVzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIGxhc3ROQ291bnQgPCAwIC8vIGxhc3ROIGlzIGRpc2FibGVkLCByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfHwgKGxhc3ROQ291bnQgPiAwICYmIGxhc3RORW5kcG9pbnRzQ2FjaGUubGVuZ3RoID09IDApIC8vIGxhc3RORW5kcG9pbnRzIGNhY2hlIG5vdCBidWlsdCB5ZXQsIHJldHVybiB0cnVlXG4gICAgICAgICAgICB8fCAobGFzdE5FbmRwb2ludHNDYWNoZSAmJiBsYXN0TkVuZHBvaW50c0NhY2hlLmluZGV4T2YocmVzb3VyY2UpICE9PSAtMSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICBWaWRlb0xheW91dC5jaGFuZ2VMb2NhbFZpZGVvKHN0cmVhbSk7XG4gICAgfTtcblxuICAgIG15LmNoYW5nZUxvY2FsQXVkaW8gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbSgkKCcjbG9jYWxBdWRpbycpLCBzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKSk7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NhbEF1ZGlvJykuYXV0b3BsYXkgPSB0cnVlO1xuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbG9jYWxBdWRpbycpLnZvbHVtZSA9IDA7XG4gICAgICAgIGlmIChwcmVNdXRlZCkge1xuICAgICAgICAgICAgaWYoIUFQUC5VSS5zZXRBdWRpb011dGVkKHRydWUpKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHByZU11dGVkID0gbXV0ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHByZU11dGVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkuY2hhbmdlTG9jYWxWaWRlbyA9IGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgICB2YXIgZmxpcFggPSB0cnVlO1xuICAgICAgICBpZihzdHJlYW0udmlkZW9UeXBlID09IFwic2NyZWVuXCIpXG4gICAgICAgICAgICBmbGlwWCA9IGZhbHNlO1xuICAgICAgICB2YXIgbG9jYWxWaWRlbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJyk7XG4gICAgICAgIGxvY2FsVmlkZW8uaWQgPSAnbG9jYWxWaWRlb18nICtcbiAgICAgICAgICAgIEFQUC5SVEMuZ2V0U3RyZWFtSUQoc3RyZWFtLmdldE9yaWdpbmFsU3RyZWFtKCkpO1xuICAgICAgICBsb2NhbFZpZGVvLmF1dG9wbGF5ID0gdHJ1ZTtcbiAgICAgICAgbG9jYWxWaWRlby52b2x1bWUgPSAwOyAvLyBpcyBpdCByZXF1aXJlZCBpZiBhdWRpbyBpcyBzZXBhcmF0ZWQgP1xuICAgICAgICBsb2NhbFZpZGVvLm9uY29udGV4dG1lbnUgPSBmdW5jdGlvbiAoKSB7IHJldHVybiBmYWxzZTsgfTtcblxuICAgICAgICB2YXIgbG9jYWxWaWRlb0NvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2NhbFZpZGVvV3JhcHBlcicpO1xuICAgICAgICBsb2NhbFZpZGVvQ29udGFpbmVyLmFwcGVuZENoaWxkKGxvY2FsVmlkZW8pO1xuXG4gICAgICAgIC8vIFNldCBkZWZhdWx0IGRpc3BsYXkgbmFtZS5cbiAgICAgICAgc2V0RGlzcGxheU5hbWUoJ2xvY2FsVmlkZW9Db250YWluZXInKTtcblxuICAgICAgICBpZighVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbXCJsb2NhbFZpZGVvQ29udGFpbmVyXCJdKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tcImxvY2FsVmlkZW9Db250YWluZXJcIl1cbiAgICAgICAgICAgICAgICA9IG5ldyBDb25uZWN0aW9uSW5kaWNhdG9yKCQoXCIjbG9jYWxWaWRlb0NvbnRhaW5lclwiKVswXSwgbnVsbCwgVmlkZW9MYXlvdXQpO1xuICAgICAgICB9XG5cbiAgICAgICAgQXVkaW9MZXZlbHMudXBkYXRlQXVkaW9MZXZlbENhbnZhcyhudWxsLCBWaWRlb0xheW91dCk7XG5cbiAgICAgICAgdmFyIGxvY2FsVmlkZW9TZWxlY3RvciA9ICQoJyMnICsgbG9jYWxWaWRlby5pZCk7XG5cbiAgICAgICAgZnVuY3Rpb24gbG9jYWxWaWRlb0NsaWNrKGV2ZW50KSB7XG4gICAgICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgIEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbyksXG4gICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAubXlSZXNvdXJjZSgpKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBZGQgY2xpY2sgaGFuZGxlciB0byBib3RoIHZpZGVvIGFuZCB2aWRlbyB3cmFwcGVyIGVsZW1lbnRzIGluIGNhc2VcbiAgICAgICAgLy8gdGhlcmUncyBubyB2aWRlby5cbiAgICAgICAgbG9jYWxWaWRlb1NlbGVjdG9yLmNsaWNrKGxvY2FsVmlkZW9DbGljayk7XG4gICAgICAgICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyJykuY2xpY2sobG9jYWxWaWRlb0NsaWNrKTtcblxuICAgICAgICAvLyBBZGQgaG92ZXIgaGFuZGxlclxuICAgICAgICAkKCcjbG9jYWxWaWRlb0NvbnRhaW5lcicpLmhvdmVyKFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd0Rpc3BsYXlOYW1lKCdsb2NhbFZpZGVvQ29udGFpbmVyJywgdHJ1ZSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9WaXNpYmxlKClcbiAgICAgICAgICAgICAgICAgICAgICAgIHx8IEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbykgIT09IEFQUC5SVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpXG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSgnbG9jYWxWaWRlb0NvbnRhaW5lcicsIGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgICAgLy8gQWRkIHN0cmVhbSBlbmRlZCBoYW5kbGVyXG4gICAgICAgIHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLm9uZW5kZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBsb2NhbFZpZGVvQ29udGFpbmVyLnJlbW92ZUNoaWxkKGxvY2FsVmlkZW8pO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3ZlZFZpZGVvKEFQUC5SVEMuZ2V0VmlkZW9TcmMobG9jYWxWaWRlbykpO1xuICAgICAgICB9O1xuICAgICAgICAvLyBGbGlwIHZpZGVvIHggYXhpcyBpZiBuZWVkZWRcbiAgICAgICAgZmxpcFhMb2NhbFZpZGVvID0gZmxpcFg7XG4gICAgICAgIGlmIChmbGlwWCkge1xuICAgICAgICAgICAgbG9jYWxWaWRlb1NlbGVjdG9yLmFkZENsYXNzKFwiZmxpcFZpZGVvWFwiKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBdHRhY2ggV2ViUlRDIHN0cmVhbVxuICAgICAgICB2YXIgdmlkZW9TdHJlYW0gPSBBUFAuc2ltdWxjYXN0LmdldExvY2FsVmlkZW9TdHJlYW0oKTtcbiAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShsb2NhbFZpZGVvU2VsZWN0b3IsIHZpZGVvU3RyZWFtKTtcblxuICAgICAgICBsb2NhbFZpZGVvU3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYyhsb2NhbFZpZGVvKTtcblxuICAgICAgICB2YXIgbXlSZXNvdXJjZUppZCA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcblxuICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKGxvY2FsVmlkZW9TcmMsIDAsXG4gICAgICAgICAgICBteVJlc291cmNlSmlkKTtcblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgcmVtb3ZlZCB2aWRlbyBpcyBjdXJyZW50bHkgZGlzcGxheWVkIGFuZCB0cmllcyB0byBkaXNwbGF5XG4gICAgICogYW5vdGhlciBvbmUgaW5zdGVhZC5cbiAgICAgKiBAcGFyYW0gcmVtb3ZlZFZpZGVvU3JjIHNyYyBzdHJlYW0gaWRlbnRpZmllciBvZiB0aGUgdmlkZW8uXG4gICAgICovXG4gICAgbXkudXBkYXRlUmVtb3ZlZFZpZGVvID0gZnVuY3Rpb24ocmVtb3ZlZFZpZGVvU3JjKSB7XG4gICAgICAgIGlmIChyZW1vdmVkVmlkZW9TcmMgPT09IEFQUC5SVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkpIHtcbiAgICAgICAgICAgIC8vIHRoaXMgaXMgY3VycmVudGx5IGRpc3BsYXllZCBhcyBsYXJnZVxuICAgICAgICAgICAgLy8gcGljayB0aGUgbGFzdCB2aXNpYmxlIHZpZGVvIGluIHRoZSByb3dcbiAgICAgICAgICAgIC8vIGlmIG5vYm9keSBlbHNlIGlzIGxlZnQsIHRoaXMgcGlja3MgdGhlIGxvY2FsIHZpZGVvXG4gICAgICAgICAgICB2YXIgcGlja1xuICAgICAgICAgICAgICAgID0gJCgnI3JlbW90ZVZpZGVvcz5zcGFuW2lkIT1cIm1peGVkc3RyZWFtXCJdOnZpc2libGU6bGFzdD52aWRlbycpXG4gICAgICAgICAgICAgICAgICAgIC5nZXQoMCk7XG5cbiAgICAgICAgICAgIGlmICghcGljaykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkxhc3QgdmlzaWJsZSB2aWRlbyBubyBsb25nZXIgZXhpc3RzXCIpO1xuICAgICAgICAgICAgICAgIHBpY2sgPSAkKCcjcmVtb3RlVmlkZW9zPnNwYW5baWQhPVwibWl4ZWRzdHJlYW1cIl0+dmlkZW8nKS5nZXQoMCk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIXBpY2sgfHwgIUFQUC5SVEMuZ2V0VmlkZW9TcmMocGljaykpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVHJ5IGxvY2FsIHZpZGVvXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkZhbGxiYWNrIHRvIGxvY2FsIHZpZGVvLi4uXCIpO1xuICAgICAgICAgICAgICAgICAgICBwaWNrID0gJCgnI3JlbW90ZVZpZGVvcz5zcGFuPnNwYW4+dmlkZW8nKS5nZXQoMCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBtdXRlIGlmIGxvY2FsdmlkZW9cbiAgICAgICAgICAgIGlmIChwaWNrKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNvbnRhaW5lciA9IHBpY2sucGFyZW50Tm9kZTtcbiAgICAgICAgICAgICAgICB2YXIgamlkID0gbnVsbDtcbiAgICAgICAgICAgICAgICBpZihjb250YWluZXIpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBpZihjb250YWluZXIuaWQgPT0gXCJsb2NhbFZpZGVvV3JhcHBlclwiKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBqaWQgPSBBUFAueG1wcC5teVJlc291cmNlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBqaWQgPSBWaWRlb0xheW91dC5nZXRQZWVyQ29udGFpbmVyUmVzb3VyY2VKaWQoY29udGFpbmVyKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oQVBQLlJUQy5nZXRWaWRlb1NyYyhwaWNrKSwgcGljay52b2x1bWUsIGppZCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkZhaWxlZCB0byBlbGVjdCBsYXJnZSB2aWRlb1wiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG4gICAgXG4gICAgbXkub25SZW1vdGVTdHJlYW1BZGRlZCA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgdmFyIGNvbnRhaW5lcjtcbiAgICAgICAgdmFyIHJlbW90ZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVtb3RlVmlkZW9zJyk7XG5cbiAgICAgICAgaWYgKHN0cmVhbS5wZWVyamlkKSB7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5lbnN1cmVQZWVyQ29udGFpbmVyRXhpc3RzKHN0cmVhbS5wZWVyamlkKTtcblxuICAgICAgICAgICAgY29udGFpbmVyICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFxuICAgICAgICAgICAgICAgICAgICAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHN0cmVhbS5wZWVyamlkKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgaWQgPSBzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKS5pZDtcbiAgICAgICAgICAgIGlmIChpZCAhPT0gJ21peGVkbXNsYWJlbCdcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogZGVmYXVsdCBzdHJlYW0gaXMgYWRkZWQgYWx3YXlzIHdpdGggbmV3IGZvY3VzXG4gICAgICAgICAgICAgICAgLy8gKHRvIGJlIGludmVzdGlnYXRlZClcbiAgICAgICAgICAgICAgICAmJiBpZCAhPT0gJ2RlZmF1bHQnKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignY2FuIG5vdCBhc3NvY2lhdGUgc3RyZWFtJyxcbiAgICAgICAgICAgICAgICAgICAgaWQsXG4gICAgICAgICAgICAgICAgICAgICd3aXRoIGEgcGFydGljaXBhbnQnKTtcbiAgICAgICAgICAgICAgICAvLyBXZSBkb24ndCB3YW50IHRvIGFkZCBpdCBoZXJlIHNpbmNlIGl0IHdpbGwgY2F1c2UgdHJvdWJsZXNcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGSVhNRTogZm9yIHRoZSBtaXhlZCBtcyB3ZSBkb250IG5lZWQgYSB2aWRlbyAtLSBjdXJyZW50bHlcbiAgICAgICAgICAgIGNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5pZCA9ICdtaXhlZHN0cmVhbSc7XG4gICAgICAgICAgICBjb250YWluZXIuY2xhc3NOYW1lID0gJ3ZpZGVvY29udGFpbmVyJztcbiAgICAgICAgICAgIHJlbW90ZXMuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcbiAgICAgICAgICAgIFVJVXRpbC5wbGF5U291bmROb3RpZmljYXRpb24oJ3VzZXJKb2luZWQnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb250YWluZXIpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmFkZFJlbW90ZVN0cmVhbUVsZW1lbnQoIGNvbnRhaW5lcixcbiAgICAgICAgICAgICAgICBzdHJlYW0uc2lkLFxuICAgICAgICAgICAgICAgIHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLFxuICAgICAgICAgICAgICAgIHN0cmVhbS5wZWVyamlkLFxuICAgICAgICAgICAgICAgIHN0cmVhbS5zc3JjKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG15LmdldExhcmdlVmlkZW9TdGF0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGxhcmdlVmlkZW9TdGF0ZTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgbGFyZ2UgdmlkZW8gd2l0aCB0aGUgZ2l2ZW4gbmV3IHZpZGVvIHNvdXJjZS5cbiAgICAgKi9cbiAgICBteS51cGRhdGVMYXJnZVZpZGVvID0gZnVuY3Rpb24obmV3U3JjLCB2b2wsIHJlc291cmNlSmlkKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdob3ZlciBpbicsIG5ld1NyYyk7XG5cbiAgICAgICAgaWYgKEFQUC5SVEMuZ2V0VmlkZW9TcmMoJCgnI2xhcmdlVmlkZW8nKVswXSkgIT09IG5ld1NyYykge1xuXG4gICAgICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcbiAgICAgICAgICAgIC8vIER1ZSB0byB0aGUgc2ltdWxjYXN0IHRoZSBsb2NhbFZpZGVvU3JjIG1heSBoYXZlIGNoYW5nZWQgd2hlbiB0aGVcbiAgICAgICAgICAgIC8vIGZhZGVPdXQgZXZlbnQgdHJpZ2dlcnMuIEluIHRoYXQgY2FzZSB0aGUgZ2V0SmlkRnJvbVZpZGVvU3JjIGFuZFxuICAgICAgICAgICAgLy8gaXNWaWRlb1NyY0Rlc2t0b3AgbWV0aG9kcyB3aWxsIG5vdCBmdW5jdGlvbiBjb3JyZWN0bHkuXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gQWxzbywgYWdhaW4gZHVlIHRvIHRoZSBzaW11bGNhc3QsIHRoZSB1cGRhdGVMYXJnZVZpZGVvIG1ldGhvZCBjYW5cbiAgICAgICAgICAgIC8vIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyBhbG1vc3Qgc2ltdWx0YW5lb3VzbHkuIFRoZXJlZm9yZSwgd2VcbiAgICAgICAgICAgIC8vIHN0b3JlIHRoZSBzdGF0ZSBoZXJlIGFuZCB1cGRhdGUgb25seSBvbmNlLlxuXG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUubmV3U3JjID0gbmV3U3JjO1xuICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLmlzVmlzaWJsZSA9ICQoJyNsYXJnZVZpZGVvJykuaXMoJzp2aXNpYmxlJyk7XG4gICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUuaXNEZXNrdG9wID0gQVBQLlJUQy5pc1ZpZGVvU3JjRGVza3RvcChyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICBpZihsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkID0gbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQgPSByZXNvdXJjZUppZDtcblxuICAgICAgICAgICAgLy8gU2NyZWVuIHN0cmVhbSBpcyBhbHJlYWR5IHJvdGF0ZWRcbiAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5mbGlwWCA9IChuZXdTcmMgPT09IGxvY2FsVmlkZW9TcmMpICYmIGZsaXBYTG9jYWxWaWRlbztcblxuICAgICAgICAgICAgdmFyIHVzZXJDaGFuZ2VkID0gZmFsc2U7XG4gICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLm9sZFJlc291cmNlSmlkICE9PSBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgdXNlckNoYW5nZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIC8vIHdlIHdhbnQgdGhlIG5vdGlmaWNhdGlvbiB0byB0cmlnZ2VyIGV2ZW4gaWYgdXNlckppZCBpcyB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgLy8gb3IgbnVsbC5cbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5TRUxFQ1RFRF9FTkRQT0lOVCxcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghbGFyZ2VWaWRlb1N0YXRlLnVwZGF0ZUluUHJvZ3Jlc3MpIHtcbiAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXBkYXRlSW5Qcm9ncmVzcyA9IHRydWU7XG5cbiAgICAgICAgICAgICAgICB2YXIgZG9VcGRhdGUgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgQXZhdGFyLnVwZGF0ZUFjdGl2ZVNwZWFrZXJBdmF0YXJTcmMoXG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAueG1wcC5maW5kSmlkRnJvbVJlc291cmNlKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIXVzZXJDaGFuZ2VkICYmIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkICYmXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZCAhPT0gbnVsbCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5nZXRWaWRlb1NyYygkKGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkKVswXSkgPT09IG5ld1NyYylcbiAgICAgICAgICAgICAgICAgICAge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ1N3aXRjaGluZyB0byBwcmVsb2FkZWQgdmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBhdHRyaWJ1dGVzID0gJCgnI2xhcmdlVmlkZW8nKS5wcm9wKFwiYXR0cmlidXRlc1wiKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gbG9vcCB0aHJvdWdoIGxhcmdlVmlkZW8gYXR0cmlidXRlcyBhbmQgYXBwbHkgdGhlbSBvblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJlbG9hZC5cbiAgICAgICAgICAgICAgICAgICAgICAgICQuZWFjaChhdHRyaWJ1dGVzLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubmFtZSAhPT0gJ2lkJyAmJiB0aGlzLm5hbWUgIT09ICdzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkLmF0dHIodGhpcy5uYW1lLCB0aGlzLnZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQuYXBwZW5kVG8oJCgnI2xhcmdlVmlkZW9Db250YWluZXInKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmF0dHIoJ2lkJywgJ3ByZXZpb3VzTGFyZ2VWaWRlbycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQuYXR0cignaWQnLCAnbGFyZ2VWaWRlbycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJCgnI3ByZXZpb3VzTGFyZ2VWaWRlbycpLnJlbW92ZSgpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZC5vbignbG9hZGVkbWV0YWRhdGEnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRWaWRlb1dpZHRoID0gdGhpcy52aWRlb1dpZHRoO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRWaWRlb0hlaWdodCA9IHRoaXMudmlkZW9IZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQucG9zaXRpb25MYXJnZShjdXJyZW50VmlkZW9XaWR0aCwgY3VycmVudFZpZGVvSGVpZ2h0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRfc3NyYyA9IDA7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAuUlRDLnNldFZpZGVvU3JjKCQoJyNsYXJnZVZpZGVvJylbMF0sIGxhcmdlVmlkZW9TdGF0ZS5uZXdTcmMpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHZpZGVvVHJhbnNmb3JtID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xhcmdlVmlkZW8nKVxuICAgICAgICAgICAgICAgICAgICAgICAgLnN0eWxlLndlYmtpdFRyYW5zZm9ybTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLmZsaXBYICYmIHZpZGVvVHJhbnNmb3JtICE9PSAnc2NhbGVYKC0xKScpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsYXJnZVZpZGVvJykuc3R5bGUud2Via2l0VHJhbnNmb3JtXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBcInNjYWxlWCgtMSlcIjtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmICghbGFyZ2VWaWRlb1N0YXRlLmZsaXBYICYmIHZpZGVvVHJhbnNmb3JtID09PSAnc2NhbGVYKC0xKScpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsYXJnZVZpZGVvJykuc3R5bGUud2Via2l0VHJhbnNmb3JtXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBcIm5vbmVcIjtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIENoYW5nZSB0aGUgd2F5IHdlJ2xsIGJlIG1lYXN1cmluZyBhbmQgcG9zaXRpb25pbmcgbGFyZ2UgdmlkZW9cblxuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5nZXRWaWRlb1NpemUgPSBsYXJnZVZpZGVvU3RhdGUuaXNEZXNrdG9wXG4gICAgICAgICAgICAgICAgICAgICAgICA/IGdldERlc2t0b3BWaWRlb1NpemVcbiAgICAgICAgICAgICAgICAgICAgICAgIDogZ2V0Q2FtZXJhVmlkZW9TaXplO1xuICAgICAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5nZXRWaWRlb1Bvc2l0aW9uID0gbGFyZ2VWaWRlb1N0YXRlLmlzRGVza3RvcFxuICAgICAgICAgICAgICAgICAgICAgICAgPyBnZXREZXNrdG9wVmlkZW9Qb3NpdGlvblxuICAgICAgICAgICAgICAgICAgICAgICAgOiBnZXRDYW1lcmFWaWRlb1Bvc2l0aW9uO1xuXG5cbiAgICAgICAgICAgICAgICAgICAgLy8gT25seSBpZiB0aGUgbGFyZ2UgdmlkZW8gaXMgY3VycmVudGx5IHZpc2libGUuXG4gICAgICAgICAgICAgICAgICAgIC8vIERpc2FibGUgcHJldmlvdXMgZG9taW5hbnQgc3BlYWtlciB2aWRlby5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGxhcmdlVmlkZW9TdGF0ZS5vbGRSZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5hYmxlRG9taW5hbnRTcGVha2VyKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS5vbGRSZXNvdXJjZUppZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBFbmFibGUgbmV3IGRvbWluYW50IHNwZWFrZXIgaW4gdGhlIHJlbW90ZSB2aWRlb3Mgc2VjdGlvbi5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmVuYWJsZURvbWluYW50U3BlYWtlcihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHVzZXJDaGFuZ2VkICYmIGxhcmdlVmlkZW9TdGF0ZS5pc1Zpc2libGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHVzaW5nIFwidGhpc1wiIHNob3VsZCBiZSBvayBiZWNhdXNlIHdlJ3JlIGNhbGxlZFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gZnJvbSB3aXRoaW4gdGhlIGZhZGVPdXQgZXZlbnQuXG4gICAgICAgICAgICAgICAgICAgICAgICAkKHRoaXMpLmZhZGVJbigzMDApO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYodXNlckNoYW5nZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBBUFAueG1wcC5maW5kSmlkRnJvbVJlc291cmNlKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUub2xkUmVzb3VyY2VKaWQpKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGxhcmdlVmlkZW9TdGF0ZS51cGRhdGVJblByb2dyZXNzID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIGlmICh1c2VyQ2hhbmdlZCkge1xuICAgICAgICAgICAgICAgICAgICAkKCcjbGFyZ2VWaWRlbycpLmZhZGVPdXQoMzAwLCBkb1VwZGF0ZSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZG9VcGRhdGUoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCkpO1xuICAgICAgICB9XG5cbiAgICB9O1xuXG4gICAgbXkuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQgPSBmdW5jdGlvbih2aWRlb1NyYyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vUGlubmVkRW5kcG9pbnRDaGFuZ2VkRXZlbnQsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb3VyY2VKaWQpIHtcbiAgICAgICAgLy8gUmVzdG9yZSBzdHlsZSBmb3IgcHJldmlvdXNseSBmb2N1c2VkIHZpZGVvXG4gICAgICAgIHZhciBvbGRDb250YWluZXIgPSBudWxsO1xuICAgICAgICBpZihmb2N1c2VkVmlkZW9JbmZvKSB7XG4gICAgICAgICAgICB2YXIgZm9jdXNSZXNvdXJjZUppZCA9IGZvY3VzZWRWaWRlb0luZm8ucmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICBvbGRDb250YWluZXIgPSBnZXRQYXJ0aWNpcGFudENvbnRhaW5lcihmb2N1c1Jlc291cmNlSmlkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChvbGRDb250YWluZXIpIHtcbiAgICAgICAgICAgIG9sZENvbnRhaW5lci5yZW1vdmVDbGFzcyhcInZpZGVvQ29udGFpbmVyRm9jdXNlZFwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFVubG9jayBjdXJyZW50IGZvY3VzZWQuXG4gICAgICAgIGlmIChmb2N1c2VkVmlkZW9JbmZvICYmIGZvY3VzZWRWaWRlb0luZm8uc3JjID09PSB2aWRlb1NyYylcbiAgICAgICAge1xuICAgICAgICAgICAgZm9jdXNlZFZpZGVvSW5mbyA9IG51bGw7XG4gICAgICAgICAgICB2YXIgZG9taW5hbnRTcGVha2VyVmlkZW8gPSBudWxsO1xuICAgICAgICAgICAgLy8gRW5hYmxlIHRoZSBjdXJyZW50bHkgc2V0IGRvbWluYW50IHNwZWFrZXIuXG4gICAgICAgICAgICBpZiAoY3VycmVudERvbWluYW50U3BlYWtlcikge1xuICAgICAgICAgICAgICAgIGRvbWluYW50U3BlYWtlclZpZGVvXG4gICAgICAgICAgICAgICAgICAgID0gJCgnI3BhcnRpY2lwYW50XycgKyBjdXJyZW50RG9taW5hbnRTcGVha2VyICsgJz52aWRlbycpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZ2V0KDApO1xuXG4gICAgICAgICAgICAgICAgaWYgKGRvbWluYW50U3BlYWtlclZpZGVvKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oXG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAuUlRDLmdldFZpZGVvU3JjKGRvbWluYW50U3BlYWtlclZpZGVvKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIDEsXG4gICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50RG9taW5hbnRTcGVha2VyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghbm9QaW5uZWRFbmRwb2ludENoYW5nZWRFdmVudCkge1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFVJRXZlbnRzLlBJTk5FRF9FTkRQT0lOVCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBMb2NrIG5ldyB2aWRlb1xuICAgICAgICBmb2N1c2VkVmlkZW9JbmZvID0ge1xuICAgICAgICAgICAgc3JjOiB2aWRlb1NyYyxcbiAgICAgICAgICAgIHJlc291cmNlSmlkOiByZXNvdXJjZUppZFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIFVwZGF0ZSBmb2N1c2VkL3Bpbm5lZCBpbnRlcmZhY2UuXG4gICAgICAgIGlmIChyZXNvdXJjZUppZClcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIGNvbnRhaW5lciA9IGdldFBhcnRpY2lwYW50Q29udGFpbmVyKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hZGRDbGFzcyhcInZpZGVvQ29udGFpbmVyRm9jdXNlZFwiKTtcblxuICAgICAgICAgICAgaWYgKCFub1Bpbm5lZEVuZHBvaW50Q2hhbmdlZEV2ZW50KSB7XG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoVUlFdmVudHMuUElOTkVEX0VORFBPSU5ULCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoJCgnI2xhcmdlVmlkZW8nKS5hdHRyKCdzcmMnKSA9PT0gdmlkZW9TcmMgJiZcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LmlzTGFyZ2VWaWRlb09uVG9wKCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRyaWdnZXJzIGEgXCJ2aWRlby5zZWxlY3RlZFwiIGV2ZW50LiBUaGUgXCJmYWxzZVwiIHBhcmFtZXRlciBpbmRpY2F0ZXNcbiAgICAgICAgLy8gdGhpcyBpc24ndCBhIHByZXppLlxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwidmlkZW8uc2VsZWN0ZWRcIiwgW2ZhbHNlXSk7XG5cbiAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTGFyZ2VWaWRlbyh2aWRlb1NyYywgMSwgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgICQoJ2F1ZGlvJykuZWFjaChmdW5jdGlvbiAoaWR4LCBlbCkge1xuICAgICAgICAgICAgaWYgKGVsLmlkLmluZGV4T2YoJ21peGVkbXNsYWJlbCcpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgIGVsLnZvbHVtZSA9IDA7XG4gICAgICAgICAgICAgICAgZWwudm9sdW1lID0gMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFBvc2l0aW9ucyB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdmlkZW9XaWR0aCB0aGUgc3RyZWFtIHZpZGVvIHdpZHRoXG4gICAgICogQHBhcmFtIHZpZGVvSGVpZ2h0IHRoZSBzdHJlYW0gdmlkZW8gaGVpZ2h0XG4gICAgICovXG4gICAgbXkucG9zaXRpb25MYXJnZSA9IGZ1bmN0aW9uICh2aWRlb1dpZHRoLCB2aWRlb0hlaWdodCkge1xuICAgICAgICB2YXIgdmlkZW9TcGFjZVdpZHRoID0gJCgnI3ZpZGVvc3BhY2UnKS53aWR0aCgpO1xuICAgICAgICB2YXIgdmlkZW9TcGFjZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcblxuICAgICAgICB2YXIgdmlkZW9TaXplID0gVmlkZW9MYXlvdXQuZ2V0VmlkZW9TaXplKHZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZGVvU3BhY2VIZWlnaHQpO1xuXG4gICAgICAgIHZhciBsYXJnZVZpZGVvV2lkdGggPSB2aWRlb1NpemVbMF07XG4gICAgICAgIHZhciBsYXJnZVZpZGVvSGVpZ2h0ID0gdmlkZW9TaXplWzFdO1xuXG4gICAgICAgIHZhciB2aWRlb1Bvc2l0aW9uID0gVmlkZW9MYXlvdXQuZ2V0VmlkZW9Qb3NpdGlvbihsYXJnZVZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZVdpZHRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFjZUhlaWdodCk7XG5cbiAgICAgICAgdmFyIGhvcml6b250YWxJbmRlbnQgPSB2aWRlb1Bvc2l0aW9uWzBdO1xuICAgICAgICB2YXIgdmVydGljYWxJbmRlbnQgPSB2aWRlb1Bvc2l0aW9uWzFdO1xuXG4gICAgICAgIHBvc2l0aW9uVmlkZW8oJCgnI2xhcmdlVmlkZW8nKSxcbiAgICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb0hlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgICBob3Jpem9udGFsSW5kZW50LCB2ZXJ0aWNhbEluZGVudCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzL2hpZGVzIHRoZSBsYXJnZSB2aWRlby5cbiAgICAgKi9cbiAgICBteS5zZXRMYXJnZVZpZGVvVmlzaWJsZSA9IGZ1bmN0aW9uKGlzVmlzaWJsZSkge1xuICAgICAgICB2YXIgcmVzb3VyY2VKaWQgPSBsYXJnZVZpZGVvU3RhdGUudXNlclJlc291cmNlSmlkO1xuXG4gICAgICAgIGlmIChpc1Zpc2libGUpIHtcbiAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuY3NzKHt2aXNpYmlsaXR5OiAndmlzaWJsZSd9KTtcbiAgICAgICAgICAgICQoJy53YXRlcm1hcmsnKS5jc3Moe3Zpc2liaWxpdHk6ICd2aXNpYmxlJ30pO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5hYmxlRG9taW5hbnRTcGVha2VyKHJlc291cmNlSmlkLCB0cnVlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICQoJyNsYXJnZVZpZGVvJykuY3NzKHt2aXNpYmlsaXR5OiAnaGlkZGVuJ30pO1xuICAgICAgICAgICAgJCgnI2FjdGl2ZVNwZWFrZXInKS5jc3MoJ3Zpc2liaWxpdHknLCAnaGlkZGVuJyk7XG4gICAgICAgICAgICAkKCcud2F0ZXJtYXJrJykuY3NzKHt2aXNpYmlsaXR5OiAnaGlkZGVuJ30pO1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5hYmxlRG9taW5hbnRTcGVha2VyKHJlc291cmNlSmlkLCBmYWxzZSk7XG4gICAgICAgICAgICBpZihmb2N1c2VkVmlkZW9JbmZvKSB7XG4gICAgICAgICAgICAgICAgdmFyIGZvY3VzUmVzb3VyY2VKaWQgPSBmb2N1c2VkVmlkZW9JbmZvLnJlc291cmNlSmlkO1xuICAgICAgICAgICAgICAgIHZhciBvbGRDb250YWluZXIgPSBnZXRQYXJ0aWNpcGFudENvbnRhaW5lcihmb2N1c1Jlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgICAgIGlmIChvbGRDb250YWluZXIgJiYgb2xkQ29udGFpbmVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgb2xkQ29udGFpbmVyLnJlbW92ZUNsYXNzKFwidmlkZW9Db250YWluZXJGb2N1c2VkXCIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBmb2N1c2VkVmlkZW9JbmZvID0gbnVsbDtcbiAgICAgICAgICAgICAgICBpZihmb2N1c1Jlc291cmNlSmlkKSB7XG4gICAgICAgICAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC54bXBwLmZpbmRKaWRGcm9tUmVzb3VyY2UoZm9jdXNSZXNvdXJjZUppZCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgaWYgdGhlIGxhcmdlIHZpZGVvIGlzIGN1cnJlbnRseSB2aXNpYmxlLlxuICAgICAqXG4gICAgICogQHJldHVybiA8dHQ+dHJ1ZTwvdHQ+IGlmIHZpc2libGUsIDx0dD5mYWxzZTwvdHQ+IC0gb3RoZXJ3aXNlXG4gICAgICovXG4gICAgbXkuaXNMYXJnZVZpZGVvVmlzaWJsZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJCgnI2xhcmdlVmlkZW8nKS5pcygnOnZpc2libGUnKTtcbiAgICB9O1xuXG4gICAgbXkuaXNMYXJnZVZpZGVvT25Ub3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBFdGhlcnBhZCA9IHJlcXVpcmUoXCIuLi9ldGhlcnBhZC9FdGhlcnBhZFwiKTtcbiAgICAgICAgdmFyIFByZXppID0gcmVxdWlyZShcIi4uL3ByZXppL1ByZXppXCIpO1xuICAgICAgICByZXR1cm4gIVByZXppLmlzUHJlc2VudGF0aW9uVmlzaWJsZSgpICYmICFFdGhlcnBhZC5pc1Zpc2libGUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIGlmIGNvbnRhaW5lciBmb3IgcGFydGljaXBhbnQgaWRlbnRpZmllZCBieSBnaXZlbiBwZWVySmlkIGV4aXN0c1xuICAgICAqIGluIHRoZSBkb2N1bWVudCBhbmQgY3JlYXRlcyBpdCBldmVudHVhbGx5LlxuICAgICAqIFxuICAgICAqIEBwYXJhbSBwZWVySmlkIHBlZXIgSmlkIHRvIGNoZWNrLlxuICAgICAqIEBwYXJhbSB1c2VySWQgdXNlciBlbWFpbCBvciBpZCBmb3Igc2V0dGluZyB0aGUgYXZhdGFyXG4gICAgICogXG4gICAgICogQHJldHVybiBSZXR1cm5zIDx0dD50cnVlPC90dD4gaWYgdGhlIHBlZXIgY29udGFpbmVyIGV4aXN0cyxcbiAgICAgKiA8dHQ+ZmFsc2U8L3R0PiAtIG90aGVyd2lzZVxuICAgICAqL1xuICAgIG15LmVuc3VyZVBlZXJDb250YWluZXJFeGlzdHMgPSBmdW5jdGlvbihwZWVySmlkLCB1c2VySWQpIHtcbiAgICAgICAgQ29udGFjdExpc3QuZW5zdXJlQWRkQ29udGFjdChwZWVySmlkLCB1c2VySWQpO1xuXG4gICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHBlZXJKaWQpO1xuXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG5cbiAgICAgICAgaWYgKCEkKCcjJyArIHZpZGVvU3BhbklkKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBjb250YWluZXIgPVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmFkZFJlbW90ZVZpZGVvQ29udGFpbmVyKHBlZXJKaWQsIHZpZGVvU3BhbklkLCB1c2VySWQpO1xuICAgICAgICAgICAgQXZhdGFyLnNldFVzZXJBdmF0YXIocGVlckppZCwgdXNlcklkKTtcbiAgICAgICAgICAgIC8vIFNldCBkZWZhdWx0IGRpc3BsYXkgbmFtZS5cbiAgICAgICAgICAgIHNldERpc3BsYXlOYW1lKHZpZGVvU3BhbklkKTtcblxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbdmlkZW9TcGFuSWRdID1cbiAgICAgICAgICAgICAgICBuZXcgQ29ubmVjdGlvbkluZGljYXRvcihjb250YWluZXIsIHBlZXJKaWQsIFZpZGVvTGF5b3V0KTtcblxuICAgICAgICAgICAgdmFyIG5pY2tmaWVsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgIG5pY2tmaWVsZC5jbGFzc05hbWUgPSBcIm5pY2tcIjtcbiAgICAgICAgICAgIG5pY2tmaWVsZC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShyZXNvdXJjZUppZCkpO1xuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKG5pY2tmaWVsZCk7XG5cbiAgICAgICAgICAgIC8vIEluIGNhc2UgdGhpcyBpcyBub3QgY3VycmVudGx5IGluIHRoZSBsYXN0IG4gd2UgZG9uJ3Qgc2hvdyBpdC5cbiAgICAgICAgICAgIGlmIChsb2NhbExhc3ROQ291bnRcbiAgICAgICAgICAgICAgICAmJiBsb2NhbExhc3ROQ291bnQgPiAwXG4gICAgICAgICAgICAgICAgJiYgJCgnI3JlbW90ZVZpZGVvcz5zcGFuJykubGVuZ3RoID49IGxvY2FsTGFzdE5Db3VudCArIDIpIHtcbiAgICAgICAgICAgICAgICBzaG93UGVlckNvbnRhaW5lcihyZXNvdXJjZUppZCwgJ2hpZGUnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkuYWRkUmVtb3RlVmlkZW9Db250YWluZXIgPSBmdW5jdGlvbihwZWVySmlkLCBzcGFuSWQpIHtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgY29udGFpbmVyLmlkID0gc3BhbklkO1xuICAgICAgICBjb250YWluZXIuY2xhc3NOYW1lID0gJ3ZpZGVvY29udGFpbmVyJztcbiAgICAgICAgdmFyIHJlbW90ZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncmVtb3RlVmlkZW9zJyk7XG4gICAgICAgIHJlbW90ZXMuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcbiAgICAgICAgLy8gSWYgdGhlIHBlZXJKaWQgaXMgbnVsbCB0aGVuIHRoaXMgdmlkZW8gc3BhbiBjb3VsZG4ndCBiZSBkaXJlY3RseVxuICAgICAgICAvLyBhc3NvY2lhdGVkIHdpdGggYSBwYXJ0aWNpcGFudCAodGhpcyBjb3VsZCBoYXBwZW4gaW4gdGhlIGNhc2Ugb2YgcHJlemkpLlxuICAgICAgICBpZiAoQVBQLnhtcHAuaXNNb2RlcmF0b3IoKSAmJiBwZWVySmlkICE9PSBudWxsKVxuICAgICAgICAgICAgYWRkUmVtb3RlVmlkZW9NZW51KHBlZXJKaWQsIGNvbnRhaW5lcik7XG4gICAgICAgIEF1ZGlvTGV2ZWxzLnVwZGF0ZUF1ZGlvTGV2ZWxDYW52YXMocGVlckppZCwgVmlkZW9MYXlvdXQpO1xuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gYXVkaW8gb3IgdmlkZW8gc3RyZWFtIGVsZW1lbnQuXG4gICAgICovXG4gICAgbXkuY3JlYXRlU3RyZWFtRWxlbWVudCA9IGZ1bmN0aW9uIChzaWQsIHN0cmVhbSkge1xuICAgICAgICB2YXIgaXNWaWRlbyA9IHN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG5cbiAgICAgICAgdmFyIGVsZW1lbnQgPSBpc1ZpZGVvXG4gICAgICAgICAgICAgICAgICAgICAgICA/IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJylcbiAgICAgICAgICAgICAgICAgICAgICAgIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYXVkaW8nKTtcbiAgICAgICAgdmFyIGlkID0gKGlzVmlkZW8gPyAncmVtb3RlVmlkZW9fJyA6ICdyZW1vdGVBdWRpb18nKVxuICAgICAgICAgICAgICAgICAgICArIHNpZCArICdfJyArIEFQUC5SVEMuZ2V0U3RyZWFtSUQoc3RyZWFtKTtcblxuICAgICAgICBlbGVtZW50LmlkID0gaWQ7XG4gICAgICAgIGVsZW1lbnQuYXV0b3BsYXkgPSB0cnVlO1xuICAgICAgICBlbGVtZW50Lm9uY29udGV4dG1lbnUgPSBmdW5jdGlvbiAoKSB7IHJldHVybiBmYWxzZTsgfTtcblxuICAgICAgICByZXR1cm4gZWxlbWVudDtcbiAgICB9O1xuXG4gICAgbXkuYWRkUmVtb3RlU3RyZWFtRWxlbWVudFxuICAgICAgICA9IGZ1bmN0aW9uIChjb250YWluZXIsIHNpZCwgc3RyZWFtLCBwZWVySmlkLCB0aGVzc3JjKSB7XG4gICAgICAgIHZhciBuZXdFbGVtZW50SWQgPSBudWxsO1xuXG4gICAgICAgIHZhciBpc1ZpZGVvID0gc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkubGVuZ3RoID4gMDtcblxuICAgICAgICBpZiAoY29udGFpbmVyKSB7XG4gICAgICAgICAgICB2YXIgc3RyZWFtRWxlbWVudCA9IFZpZGVvTGF5b3V0LmNyZWF0ZVN0cmVhbUVsZW1lbnQoc2lkLCBzdHJlYW0pO1xuICAgICAgICAgICAgbmV3RWxlbWVudElkID0gc3RyZWFtRWxlbWVudC5pZDtcblxuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKHN0cmVhbUVsZW1lbnQpO1xuXG4gICAgICAgICAgICB2YXIgc2VsID0gJCgnIycgKyBuZXdFbGVtZW50SWQpO1xuICAgICAgICAgICAgc2VsLmhpZGUoKTtcblxuICAgICAgICAgICAgLy8gSWYgdGhlIGNvbnRhaW5lciBpcyBjdXJyZW50bHkgdmlzaWJsZSB3ZSBhdHRhY2ggdGhlIHN0cmVhbS5cbiAgICAgICAgICAgIGlmICghaXNWaWRlb1xuICAgICAgICAgICAgICAgIHx8IChjb250YWluZXIub2Zmc2V0UGFyZW50ICE9PSBudWxsICYmIGlzVmlkZW8pKSB7XG4gICAgICAgICAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShzdHJlYW0pO1xuICAgICAgICAgICAgICAgIEFQUC5SVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsLCB2aWRlb1N0cmVhbSk7XG5cbiAgICAgICAgICAgICAgICBpZiAoaXNWaWRlbylcbiAgICAgICAgICAgICAgICAgICAgd2FpdEZvclJlbW90ZVZpZGVvKHNlbCwgdGhlc3NyYywgc3RyZWFtLCBwZWVySmlkKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc3RyZWFtLm9uZW5kZWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3N0cmVhbSBlbmRlZCcsIHRoaXMpO1xuXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQucmVtb3ZlUmVtb3RlU3RyZWFtRWxlbWVudChcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLCBpc1ZpZGVvLCBjb250YWluZXIpO1xuXG4gICAgICAgICAgICAgICAgLy8gTk9URShncCkgaXQgc2VlbXMgdGhhdCB1bmRlciBjZXJ0YWluIGNpcmN1bXN0YW5jZXMsIHRoZVxuICAgICAgICAgICAgICAgIC8vIG9uZW5kZWQgZXZlbnQgaXMgbm90IGZpcmVkIGFuZCB0aHVzIHRoZSBjb250YWN0IGxpc3QgaXMgbm90XG4gICAgICAgICAgICAgICAgLy8gdXBkYXRlZC5cbiAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgIC8vIFRoZSBvbmVuZGVkIGV2ZW50IG9mIGEgc3RyZWFtIHNob3VsZCBiZSBmaXJlZCB3aGVuIHRoZSBTU1JDc1xuICAgICAgICAgICAgICAgIC8vIGNvcnJlc3BvbmRpbmcgdG8gdGhhdCBzdHJlYW0gYXJlIHJlbW92ZWQgZnJvbSB0aGUgU0RQOyBidXRcbiAgICAgICAgICAgICAgICAvLyB0aGlzIGRvZXNuJ3Qgc2VlbSB0byBhbHdheXMgYmUgdGhlIGNhc2UsIHJlc3VsdGluZyBpbiBnaG9zdFxuICAgICAgICAgICAgICAgIC8vIGNvbnRhY3RzLlxuICAgICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgICAgLy8gSW4gYW4gYXR0ZW1wdCB0byBmaXggdGhlIGdob3N0IGNvbnRhY3RzIHByb2JsZW0sIEknbSBtb3ZpbmdcbiAgICAgICAgICAgICAgICAvLyB0aGUgcmVtb3ZlQ29udGFjdCgpIG1ldGhvZCBjYWxsIGluIGFwcC5qcywgaW5zaWRlIHRoZVxuICAgICAgICAgICAgICAgIC8vICdtdWMubGVmdCcgZXZlbnQgaGFuZGxlci5cblxuICAgICAgICAgICAgICAgIC8vaWYgKHBlZXJKaWQpXG4gICAgICAgICAgICAgICAgLy8gICAgQ29udGFjdExpc3QucmVtb3ZlQ29udGFjdChwZWVySmlkKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIEFkZCBjbGljayBoYW5kbGVyLlxuICAgICAgICAgICAgY29udGFpbmVyLm9uY2xpY2sgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICAvKlxuICAgICAgICAgICAgICAgICAqIEZJWE1FIEl0IHR1cm5zIG91dCB0aGF0IHZpZGVvVGh1bWIgbWF5IG5vdCBleGlzdCAoaWYgdGhlcmUgaXNcbiAgICAgICAgICAgICAgICAgKiBubyBhY3R1YWwgdmlkZW8pLlxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIHZhciB2aWRlb1RodW1iID0gJCgnIycgKyBjb250YWluZXIuaWQgKyAnPnZpZGVvJykuZ2V0KDApO1xuICAgICAgICAgICAgICAgIGlmICh2aWRlb1RodW1iKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmhhbmRsZVZpZGVvVGh1bWJDbGlja2VkKFxuICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5nZXRWaWRlb1NyYyh2aWRlb1RodW1iKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQocGVlckppZCkpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gQWRkIGhvdmVyIGhhbmRsZXJcbiAgICAgICAgICAgICQoY29udGFpbmVyKS5ob3ZlcihcbiAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuc2hvd0Rpc3BsYXlOYW1lKGNvbnRhaW5lci5pZCwgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHZpZGVvU3JjID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJiYgJCgnIycgKyBjb250YWluZXIuaWQgKyAnPnZpZGVvJykubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmlkZW9TcmMgPSBBUFAuUlRDLmdldFZpZGVvU3JjKCQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpLmdldCgwKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB0aGUgdmlkZW8gaGFzIGJlZW4gXCJwaW5uZWRcIiBieSB0aGUgdXNlciB3ZSB3YW50IHRvXG4gICAgICAgICAgICAgICAgICAgIC8vIGtlZXAgdGhlIGRpc3BsYXkgbmFtZSBvbiBwbGFjZS5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9WaXNpYmxlKClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCB2aWRlb1NyYyAhPT0gQVBQLlJUQy5nZXRWaWRlb1NyYygkKCcjbGFyZ2VWaWRlbycpWzBdKSlcbiAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZShjb250YWluZXIuaWQsIGZhbHNlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5ld0VsZW1lbnRJZDtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyB0aGUgcmVtb3RlIHN0cmVhbSBlbGVtZW50IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIHN0cmVhbSBhbmRcbiAgICAgKiBwYXJlbnQgY29udGFpbmVyLlxuICAgICAqIFxuICAgICAqIEBwYXJhbSBzdHJlYW0gdGhlIHN0cmVhbVxuICAgICAqIEBwYXJhbSBpc1ZpZGVvIDx0dD50cnVlPC90dD4gaWYgZ2l2ZW4gPHR0PnN0cmVhbTwvdHQ+IGlzIGEgdmlkZW8gb25lLlxuICAgICAqIEBwYXJhbSBjb250YWluZXJcbiAgICAgKi9cbiAgICBteS5yZW1vdmVSZW1vdGVTdHJlYW1FbGVtZW50ID0gZnVuY3Rpb24gKHN0cmVhbSwgaXNWaWRlbywgY29udGFpbmVyKSB7XG4gICAgICAgIGlmICghY29udGFpbmVyKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHZhciBzZWxlY3QgPSBudWxsO1xuICAgICAgICB2YXIgcmVtb3ZlZFZpZGVvU3JjID0gbnVsbDtcbiAgICAgICAgaWYgKGlzVmlkZW8pIHtcbiAgICAgICAgICAgIHNlbGVjdCA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz52aWRlbycpO1xuICAgICAgICAgICAgcmVtb3ZlZFZpZGVvU3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxlY3QuZ2V0KDApKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBzZWxlY3QgPSAkKCcjJyArIGNvbnRhaW5lci5pZCArICc+YXVkaW8nKTtcblxuXG4gICAgICAgIC8vIE1hcmsgdmlkZW8gYXMgcmVtb3ZlZCB0byBjYW5jZWwgd2FpdGluZyBsb29wKGlmIHZpZGVvIGlzIHJlbW92ZWRcbiAgICAgICAgLy8gYmVmb3JlIGhhcyBzdGFydGVkKVxuICAgICAgICBzZWxlY3QucmVtb3ZlZCA9IHRydWU7XG4gICAgICAgIHNlbGVjdC5yZW1vdmUoKTtcblxuICAgICAgICB2YXIgYXVkaW9Db3VudCA9ICQoJyMnICsgY29udGFpbmVyLmlkICsgJz5hdWRpbycpLmxlbmd0aDtcbiAgICAgICAgdmFyIHZpZGVvQ291bnQgPSAkKCcjJyArIGNvbnRhaW5lci5pZCArICc+dmlkZW8nKS5sZW5ndGg7XG5cbiAgICAgICAgaWYgKCFhdWRpb0NvdW50ICYmICF2aWRlb0NvdW50KSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIlJlbW92ZSB3aG9sZSB1c2VyXCIsIGNvbnRhaW5lci5pZCk7XG4gICAgICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1tjb250YWluZXIuaWRdKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2NvbnRhaW5lci5pZF0ucmVtb3ZlKCk7XG4gICAgICAgICAgICAvLyBSZW1vdmUgd2hvbGUgY29udGFpbmVyXG4gICAgICAgICAgICBjb250YWluZXIucmVtb3ZlKCk7XG5cbiAgICAgICAgICAgIFVJVXRpbC5wbGF5U291bmROb3RpZmljYXRpb24oJ3VzZXJMZWZ0Jyk7XG4gICAgICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocmVtb3ZlZFZpZGVvU3JjKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3ZlZFZpZGVvKHJlbW92ZWRWaWRlb1NyYyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3cvaGlkZSBwZWVyIGNvbnRhaW5lciBmb3IgdGhlIGdpdmVuIHJlc291cmNlSmlkLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHNob3dQZWVyQ29udGFpbmVyKHJlc291cmNlSmlkLCBzdGF0ZSkge1xuICAgICAgICB2YXIgcGVlckNvbnRhaW5lciA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpO1xuXG4gICAgICAgIGlmICghcGVlckNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICB2YXIgaXNIaWRlID0gc3RhdGUgPT09ICdoaWRlJztcbiAgICAgICAgdmFyIHJlc2l6ZVRodW1ibmFpbHMgPSBmYWxzZTtcblxuICAgICAgICBpZiAoIWlzSGlkZSkge1xuICAgICAgICAgICAgaWYgKCFwZWVyQ29udGFpbmVyLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgcmVzaXplVGh1bWJuYWlscyA9IHRydWU7XG4gICAgICAgICAgICAgICAgcGVlckNvbnRhaW5lci5zaG93KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBqaWQgPSBBUFAueG1wcC5maW5kSmlkRnJvbVJlc291cmNlKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgIGlmIChzdGF0ZSA9PSAnc2hvdycpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgLy8gcGVlckNvbnRhaW5lci5jc3MoJy13ZWJraXQtZmlsdGVyJywgJycpO1xuXG4gICAgICAgICAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKGppZCwgZmFsc2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSAvLyBpZiAoc3RhdGUgPT0gJ2F2YXRhcicpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgLy8gcGVlckNvbnRhaW5lci5jc3MoJy13ZWJraXQtZmlsdGVyJywgJ2dyYXlzY2FsZSgxMDAlKScpO1xuICAgICAgICAgICAgICAgIEF2YXRhci5zaG93VXNlckF2YXRhcihqaWQsIHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKHBlZXJDb250YWluZXIuaXMoJzp2aXNpYmxlJykgJiYgaXNIaWRlKVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNpemVUaHVtYm5haWxzID0gdHJ1ZTtcbiAgICAgICAgICAgIHBlZXJDb250YWluZXIuaGlkZSgpO1xuICAgICAgICAgICAgaWYoVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZF0pXG4gICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZF0uaGlkZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHJlc2l6ZVRodW1ibmFpbHMpIHtcbiAgICAgICAgICAgIFZpZGVvTGF5b3V0LnJlc2l6ZVRodW1ibmFpbHMoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFdlIHdhbnQgdG8gYmUgYWJsZSB0byBwaW4gYSBwYXJ0aWNpcGFudCBmcm9tIHRoZSBjb250YWN0IGxpc3QsIGV2ZW5cbiAgICAgICAgLy8gaWYgaGUncyBub3QgaW4gdGhlIGxhc3ROIHNldCFcbiAgICAgICAgLy8gQ29udGFjdExpc3Quc2V0Q2xpY2thYmxlKHJlc291cmNlSmlkLCAhaXNIaWRlKTtcblxuICAgIH07XG5cbiAgICBteS5pbnB1dERpc3BsYXlOYW1lSGFuZGxlciA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgIE5pY2tuYW1lSGFuZGxlci5zZXROaWNrbmFtZShuYW1lKTtcblxuICAgICAgICBpZiAoISQoJyNsb2NhbERpc3BsYXlOYW1lJykuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICAgICAgaWYgKE5pY2tuYW1lSGFuZGxlci5nZXROaWNrbmFtZSgpKVxuICAgICAgICAgICAgICAgICQoJyNsb2NhbERpc3BsYXlOYW1lJykudGV4dChOaWNrbmFtZUhhbmRsZXIuZ2V0Tmlja25hbWUoKSArIFwiIChtZSlcIik7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKVxuICAgICAgICAgICAgICAgICAgICAudGV4dChpbnRlcmZhY2VDb25maWcuREVGQVVMVF9MT0NBTF9ESVNQTEFZX05BTUUpO1xuICAgICAgICAgICAgJCgnI2xvY2FsRGlzcGxheU5hbWUnKS5zaG93KCk7XG4gICAgICAgIH1cblxuICAgICAgICAkKCcjZWRpdERpc3BsYXlOYW1lJykuaGlkZSgpO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cy9oaWRlcyB0aGUgZGlzcGxheSBuYW1lIG9uIHRoZSByZW1vdGUgdmlkZW8uXG4gICAgICogQHBhcmFtIHZpZGVvU3BhbklkIHRoZSBpZGVudGlmaWVyIG9mIHRoZSB2aWRlbyBzcGFuIGVsZW1lbnRcbiAgICAgKiBAcGFyYW0gaXNTaG93IGluZGljYXRlcyBpZiB0aGUgZGlzcGxheSBuYW1lIHNob3VsZCBiZSBzaG93biBvciBoaWRkZW5cbiAgICAgKi9cbiAgICBteS5zaG93RGlzcGxheU5hbWUgPSBmdW5jdGlvbih2aWRlb1NwYW5JZCwgaXNTaG93KSB7XG4gICAgICAgIHZhciBuYW1lU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uZGlzcGxheW5hbWUnKS5nZXQoMCk7XG4gICAgICAgIGlmIChpc1Nob3cpIHtcbiAgICAgICAgICAgIGlmIChuYW1lU3BhbiAmJiBuYW1lU3Bhbi5pbm5lckhUTUwgJiYgbmFtZVNwYW4uaW5uZXJIVE1MLmxlbmd0aCkgXG4gICAgICAgICAgICAgICAgbmFtZVNwYW4uc2V0QXR0cmlidXRlKFwic3R5bGVcIiwgXCJkaXNwbGF5OmlubGluZS1ibG9jaztcIik7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBpZiAobmFtZVNwYW4pXG4gICAgICAgICAgICAgICAgbmFtZVNwYW4uc2V0QXR0cmlidXRlKFwic3R5bGVcIiwgXCJkaXNwbGF5Om5vbmU7XCIpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBwcmVzZW5jZSBzdGF0dXMgbWVzc2FnZSBmb3IgdGhlIGdpdmVuIHZpZGVvLlxuICAgICAqL1xuICAgIG15LnNldFByZXNlbmNlU3RhdHVzID0gZnVuY3Rpb24gKHZpZGVvU3BhbklkLCBzdGF0dXNNc2cpIHtcblxuICAgICAgICBpZiAoISQoJyMnICsgdmlkZW9TcGFuSWQpLmxlbmd0aCkge1xuICAgICAgICAgICAgLy8gTm8gY29udGFpbmVyXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc3RhdHVzU3BhbiA9ICQoJyMnICsgdmlkZW9TcGFuSWQgKyAnPnNwYW4uc3RhdHVzJyk7XG4gICAgICAgIGlmICghc3RhdHVzU3Bhbi5sZW5ndGgpIHtcbiAgICAgICAgICAgIC8vQWRkIHN0YXR1cyBzcGFuXG4gICAgICAgICAgICBzdGF0dXNTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgICAgICAgICAgc3RhdHVzU3Bhbi5jbGFzc05hbWUgPSAnc3RhdHVzJztcbiAgICAgICAgICAgIHN0YXR1c1NwYW4uaWQgPSB2aWRlb1NwYW5JZCArICdfc3RhdHVzJztcbiAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKHN0YXR1c1NwYW4pO1xuXG4gICAgICAgICAgICBzdGF0dXNTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5zdGF0dXMnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIERpc3BsYXkgc3RhdHVzXG4gICAgICAgIGlmIChzdGF0dXNNc2cgJiYgc3RhdHVzTXNnLmxlbmd0aCkge1xuICAgICAgICAgICAgJCgnIycgKyB2aWRlb1NwYW5JZCArICdfc3RhdHVzJykudGV4dChzdGF0dXNNc2cpO1xuICAgICAgICAgICAgc3RhdHVzU3Bhbi5nZXQoMCkuc2V0QXR0cmlidXRlKFwic3R5bGVcIiwgXCJkaXNwbGF5OmlubGluZS1ibG9jaztcIik7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBIaWRlXG4gICAgICAgICAgICBzdGF0dXNTcGFuLmdldCgwKS5zZXRBdHRyaWJ1dGUoXCJzdHlsZVwiLCBcImRpc3BsYXk6bm9uZTtcIik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogU2hvd3MgYSB2aXN1YWwgaW5kaWNhdG9yIGZvciB0aGUgbW9kZXJhdG9yIG9mIHRoZSBjb25mZXJlbmNlLlxuICAgICAqL1xuICAgIG15LnNob3dNb2RlcmF0b3JJbmRpY2F0b3IgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgdmFyIGlzTW9kZXJhdG9yID0gQVBQLnhtcHAuaXNNb2RlcmF0b3IoKTtcbiAgICAgICAgaWYgKGlzTW9kZXJhdG9yKSB7XG4gICAgICAgICAgICB2YXIgaW5kaWNhdG9yU3BhbiA9ICQoJyNsb2NhbFZpZGVvQ29udGFpbmVyIC5mb2N1c2luZGljYXRvcicpO1xuXG4gICAgICAgICAgICBpZiAoaW5kaWNhdG9yU3Bhbi5jaGlsZHJlbigpLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBjcmVhdGVNb2RlcmF0b3JJbmRpY2F0b3JFbGVtZW50KGluZGljYXRvclNwYW5bMF0pO1xuICAgICAgICAgICAgICAgIC8vdHJhbnNsYXRlcyB0ZXh0IGluIGZvY3VzIGluZGljYXRvclxuICAgICAgICAgICAgICAgIEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVFbGVtZW50KCQoJyNsb2NhbFZpZGVvQ29udGFpbmVyIC5mb2N1c2luZGljYXRvcicpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBtZW1iZXJzID0gQVBQLnhtcHAuZ2V0TWVtYmVycygpO1xuXG4gICAgICAgIE9iamVjdC5rZXlzKG1lbWJlcnMpLmZvckVhY2goZnVuY3Rpb24gKGppZCkge1xuXG4gICAgICAgICAgICBpZiAoU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSA9PT0gJ2ZvY3VzJykge1xuICAgICAgICAgICAgICAgIC8vIFNraXAgc2VydmVyIHNpZGUgZm9jdXNcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciByZXNvdXJjZUppZCA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuICAgICAgICAgICAgdmFyIHZpZGVvQ29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodmlkZW9TcGFuSWQpO1xuXG4gICAgICAgICAgICBpZiAoIXZpZGVvQ29udGFpbmVyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIk5vIHZpZGVvIGNvbnRhaW5lciBmb3IgXCIgKyBqaWQpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIG1lbWJlciA9IG1lbWJlcnNbamlkXTtcblxuICAgICAgICAgICAgaWYgKG1lbWJlci5yb2xlID09PSAnbW9kZXJhdG9yJykge1xuICAgICAgICAgICAgICAgIC8vIFJlbW92ZSBtZW51IGlmIHBlZXIgaXMgbW9kZXJhdG9yXG4gICAgICAgICAgICAgICAgdmFyIG1lbnVTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5yZW1vdGV2aWRlb21lbnUnKTtcbiAgICAgICAgICAgICAgICBpZiAobWVudVNwYW4ubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlbW92ZVJlbW90ZVZpZGVvTWVudSh2aWRlb1NwYW5JZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIFNob3cgbW9kZXJhdG9yIGluZGljYXRvclxuICAgICAgICAgICAgICAgIHZhciBpbmRpY2F0b3JTcGFuXG4gICAgICAgICAgICAgICAgICAgID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICcgLmZvY3VzaW5kaWNhdG9yJyk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIWluZGljYXRvclNwYW4gfHwgaW5kaWNhdG9yU3Bhbi5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaW5kaWNhdG9yU3BhbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgICAgICAgICAgICAgICAgaW5kaWNhdG9yU3Bhbi5jbGFzc05hbWUgPSAnZm9jdXNpbmRpY2F0b3InO1xuXG4gICAgICAgICAgICAgICAgICAgIHZpZGVvQ29udGFpbmVyLmFwcGVuZENoaWxkKGluZGljYXRvclNwYW4pO1xuXG4gICAgICAgICAgICAgICAgICAgIGNyZWF0ZU1vZGVyYXRvckluZGljYXRvckVsZW1lbnQoaW5kaWNhdG9yU3Bhbik7XG4gICAgICAgICAgICAgICAgICAgIC8vdHJhbnNsYXRlcyB0ZXh0IGluIGZvY3VzIGluZGljYXRvcnNcbiAgICAgICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJCgnIycgKyB2aWRlb1NwYW5JZCArICcgLmZvY3VzaW5kaWNhdG9yJykpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNNb2RlcmF0b3IpIHtcbiAgICAgICAgICAgICAgICAvLyBXZSBhcmUgbW9kZXJhdG9yLCBidXQgdXNlciBpcyBub3QgLSBhZGQgbWVudVxuICAgICAgICAgICAgICAgIGlmICgkKCcjcmVtb3RlX3BvcHVwbWVudV8nICsgcmVzb3VyY2VKaWQpLmxlbmd0aCA8PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGFkZFJlbW90ZVZpZGVvTWVudShcbiAgICAgICAgICAgICAgICAgICAgICAgIGppZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB2aWRlbyBtdXRlZCBpbmRpY2F0b3Igb3ZlciBzbWFsbCB2aWRlb3MuXG4gICAgICovXG4gICAgbXkuc2hvd1ZpZGVvSW5kaWNhdG9yID0gZnVuY3Rpb24odmlkZW9TcGFuSWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIHZpZGVvTXV0ZWRTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi52aWRlb011dGVkJyk7XG5cbiAgICAgICAgaWYgKGlzTXV0ZWQgPT09ICdmYWxzZScpIHtcbiAgICAgICAgICAgIGlmICh2aWRlb011dGVkU3Bhbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgdmlkZW9NdXRlZFNwYW4ucmVtb3ZlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBpZih2aWRlb011dGVkU3Bhbi5sZW5ndGggPT0gMCkge1xuICAgICAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xuICAgICAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmNsYXNzTmFtZSA9ICd2aWRlb011dGVkJztcblxuICAgICAgICAgICAgICAgICQoJyMnICsgdmlkZW9TcGFuSWQpWzBdLmFwcGVuZENoaWxkKHZpZGVvTXV0ZWRTcGFuKTtcblxuICAgICAgICAgICAgICAgIHZhciBtdXRlZEluZGljYXRvciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2knKTtcbiAgICAgICAgICAgICAgICBtdXRlZEluZGljYXRvci5jbGFzc05hbWUgPSAnaWNvbi1jYW1lcmEtZGlzYWJsZWQnO1xuICAgICAgICAgICAgICAgIFVJVXRpbC5zZXRUb29sdGlwKG11dGVkSW5kaWNhdG9yLFxuICAgICAgICAgICAgICAgICAgICBcInZpZGVvdGh1bWJuYWlsLnZpZGVvbXV0ZVwiLFxuICAgICAgICAgICAgICAgICAgICBcInRvcFwiKTtcbiAgICAgICAgICAgICAgICB2aWRlb011dGVkU3Bhbi5hcHBlbmRDaGlsZChtdXRlZEluZGljYXRvcik7XG4gICAgICAgICAgICAgICAgLy90cmFuc2xhdGUgdGV4dHMgZm9yIG11dGVkIGluZGljYXRvclxuICAgICAgICAgICAgICAgIEFQUC50cmFuc2xhdGlvbi50cmFuc2xhdGVFbGVtZW50KCQoJyMnICsgdmlkZW9TcGFuSWQgICsgXCIgPiBpXCIpKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlTXV0ZVBvc2l0aW9uKHZpZGVvU3BhbklkKTtcblxuICAgICAgICB9XG4gICAgfTtcblxuICAgIG15LnVwZGF0ZU11dGVQb3NpdGlvbiA9IGZ1bmN0aW9uICh2aWRlb1NwYW5JZCkge1xuICAgICAgICB2YXIgYXVkaW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLmF1ZGlvTXV0ZWQnKTtcbiAgICAgICAgdmFyIGNvbm5lY3Rpb25JbmRpY2F0b3IgPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5kaXYuY29ubmVjdGlvbmluZGljYXRvcicpO1xuICAgICAgICB2YXIgdmlkZW9NdXRlZFNwYW4gPSAkKCcjJyArIHZpZGVvU3BhbklkICsgJz5zcGFuLnZpZGVvTXV0ZWQnKTtcbiAgICAgICAgaWYoY29ubmVjdGlvbkluZGljYXRvci5sZW5ndGggPiAwXG4gICAgICAgICAgICAmJiBjb25uZWN0aW9uSW5kaWNhdG9yWzBdLnN0eWxlLmRpc3BsYXkgIT0gXCJub25lXCIpIHtcbiAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IFwiMjNweFwifSk7XG4gICAgICAgICAgICB2aWRlb011dGVkU3Bhbi5jc3Moe3JpZ2h0OiAoKGF1ZGlvTXV0ZWRTcGFuLmxlbmd0aCA+IDA/IDIzIDogMCkgKyAzMCkgKyBcInB4XCJ9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IFwiMHB4XCJ9KTtcbiAgICAgICAgICAgIHZpZGVvTXV0ZWRTcGFuLmNzcyh7cmlnaHQ6IChhdWRpb011dGVkU3Bhbi5sZW5ndGggPiAwPyAzMCA6IDApICsgXCJweFwifSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogU2hvd3MgYXVkaW8gbXV0ZWQgaW5kaWNhdG9yIG92ZXIgc21hbGwgdmlkZW9zLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBpc011dGVkXG4gICAgICovXG4gICAgbXkuc2hvd0F1ZGlvSW5kaWNhdG9yID0gZnVuY3Rpb24odmlkZW9TcGFuSWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIGF1ZGlvTXV0ZWRTcGFuID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+c3Bhbi5hdWRpb011dGVkJyk7XG5cbiAgICAgICAgaWYgKGlzTXV0ZWQgPT09ICdmYWxzZScpIHtcbiAgICAgICAgICAgIGlmIChhdWRpb011dGVkU3Bhbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4ucG9wb3ZlcignaGlkZScpO1xuICAgICAgICAgICAgICAgIGF1ZGlvTXV0ZWRTcGFuLnJlbW92ZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYoYXVkaW9NdXRlZFNwYW4ubGVuZ3RoID09IDAgKSB7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uY2xhc3NOYW1lID0gJ2F1ZGlvTXV0ZWQnO1xuICAgICAgICAgICAgICAgIFVJVXRpbC5zZXRUb29sdGlwKGF1ZGlvTXV0ZWRTcGFuLFxuICAgICAgICAgICAgICAgICAgICBcInZpZGVvdGh1bWJuYWlsLm11dGVcIixcbiAgICAgICAgICAgICAgICAgICAgXCJ0b3BcIik7XG5cbiAgICAgICAgICAgICAgICAkKCcjJyArIHZpZGVvU3BhbklkKVswXS5hcHBlbmRDaGlsZChhdWRpb011dGVkU3Bhbik7XG4gICAgICAgICAgICAgICAgQVBQLnRyYW5zbGF0aW9uLnRyYW5zbGF0ZUVsZW1lbnQoJCgnIycgKyB2aWRlb1NwYW5JZCArIFwiID4gc3BhblwiKSk7XG4gICAgICAgICAgICAgICAgdmFyIG11dGVkSW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaScpO1xuICAgICAgICAgICAgICAgIG11dGVkSW5kaWNhdG9yLmNsYXNzTmFtZSA9ICdpY29uLW1pYy1kaXNhYmxlZCc7XG4gICAgICAgICAgICAgICAgYXVkaW9NdXRlZFNwYW4uYXBwZW5kQ2hpbGQobXV0ZWRJbmRpY2F0b3IpO1xuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVNdXRlUG9zaXRpb24odmlkZW9TcGFuSWQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qXG4gICAgICogU2hvd3Mgb3IgaGlkZXMgdGhlIGF1ZGlvIG11dGVkIGluZGljYXRvciBvdmVyIHRoZSBsb2NhbCB0aHVtYm5haWwgdmlkZW8uXG4gICAgICogQHBhcmFtIHtib29sZWFufSBpc011dGVkXG4gICAgICovXG4gICAgbXkuc2hvd0xvY2FsQXVkaW9JbmRpY2F0b3IgPSBmdW5jdGlvbihpc011dGVkKSB7XG4gICAgICAgIFZpZGVvTGF5b3V0LnNob3dBdWRpb0luZGljYXRvcignbG9jYWxWaWRlb0NvbnRhaW5lcicsIGlzTXV0ZWQudG9TdHJpbmcoKSk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIGxhcmdlIHZpZGVvIGNvbnRhaW5lci5cbiAgICAgKi9cbiAgICBteS5yZXNpemVMYXJnZVZpZGVvQ29udGFpbmVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBDaGF0LnJlc2l6ZUNoYXQoKTtcbiAgICAgICAgdmFyIGF2YWlsYWJsZUhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcbiAgICAgICAgdmFyIGF2YWlsYWJsZVdpZHRoID0gVUlVdGlsLmdldEF2YWlsYWJsZVZpZGVvV2lkdGgoKTtcblxuICAgICAgICBpZiAoYXZhaWxhYmxlV2lkdGggPCAwIHx8IGF2YWlsYWJsZUhlaWdodCA8IDApIHJldHVybjtcblxuICAgICAgICAkKCcjdmlkZW9zcGFjZScpLndpZHRoKGF2YWlsYWJsZVdpZHRoKTtcbiAgICAgICAgJCgnI3ZpZGVvc3BhY2UnKS5oZWlnaHQoYXZhaWxhYmxlSGVpZ2h0KTtcbiAgICAgICAgJCgnI2xhcmdlVmlkZW9Db250YWluZXInKS53aWR0aChhdmFpbGFibGVXaWR0aCk7XG4gICAgICAgICQoJyNsYXJnZVZpZGVvQ29udGFpbmVyJykuaGVpZ2h0KGF2YWlsYWJsZUhlaWdodCk7XG5cbiAgICAgICAgdmFyIGF2YXRhclNpemUgPSBpbnRlcmZhY2VDb25maWcuQUNUSVZFX1NQRUFLRVJfQVZBVEFSX1NJWkU7XG4gICAgICAgIHZhciB0b3AgPSBhdmFpbGFibGVIZWlnaHQgLyAyIC0gYXZhdGFyU2l6ZSAvIDQgKiAzO1xuICAgICAgICAkKCcjYWN0aXZlU3BlYWtlcicpLmNzcygndG9wJywgdG9wKTtcblxuICAgICAgICBWaWRlb0xheW91dC5yZXNpemVUaHVtYm5haWxzKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGh1bWJuYWlscy5cbiAgICAgKi9cbiAgICBteS5yZXNpemVUaHVtYm5haWxzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB2aWRlb1NwYWNlV2lkdGggPSAkKCcjcmVtb3RlVmlkZW9zJykud2lkdGgoKTtcblxuICAgICAgICB2YXIgdGh1bWJuYWlsU2l6ZSA9IFZpZGVvTGF5b3V0LmNhbGN1bGF0ZVRodW1ibmFpbFNpemUodmlkZW9TcGFjZVdpZHRoKTtcbiAgICAgICAgdmFyIHdpZHRoID0gdGh1bWJuYWlsU2l6ZVswXTtcbiAgICAgICAgdmFyIGhlaWdodCA9IHRodW1ibmFpbFNpemVbMV07XG5cbiAgICAgICAgLy8gc2l6ZSB2aWRlb3Mgc28gdGhhdCB3aGlsZSBrZWVwaW5nIEFSIGFuZCBtYXggaGVpZ2h0LCB3ZSBoYXZlIGFcbiAgICAgICAgLy8gbmljZSBmaXRcbiAgICAgICAgJCgnI3JlbW90ZVZpZGVvcycpLmhlaWdodChoZWlnaHQpO1xuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS53aWR0aCh3aWR0aCk7XG4gICAgICAgICQoJyNyZW1vdGVWaWRlb3M+c3BhbicpLmhlaWdodChoZWlnaHQpO1xuXG4gICAgICAgICQoJy51c2VyQXZhdGFyJykuY3NzKCdsZWZ0JywgKHdpZHRoIC0gaGVpZ2h0KSAvIDIpO1xuXG5cblxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKFwicmVtb3RldmlkZW8ucmVzaXplZFwiLCBbd2lkdGgsIGhlaWdodF0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBkb21pbmFudCBzcGVha2VyIFVJLlxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSmlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZWxlbWVudCB0b1xuICAgICAqIGFjdGl2YXRlL2RlYWN0aXZhdGVcbiAgICAgKiBAcGFyYW0gaXNFbmFibGUgaW5kaWNhdGVzIGlmIHRoZSBkb21pbmFudCBzcGVha2VyIHNob3VsZCBiZSBlbmFibGVkIG9yXG4gICAgICogZGlzYWJsZWRcbiAgICAgKi9cbiAgICBteS5lbmFibGVEb21pbmFudFNwZWFrZXIgPSBmdW5jdGlvbihyZXNvdXJjZUppZCwgaXNFbmFibGUpIHtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSBudWxsO1xuICAgICAgICB2YXIgdmlkZW9Db250YWluZXJJZCA9IG51bGw7XG4gICAgICAgIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgID09PSBBUFAueG1wcC5teVJlc291cmNlKCkpIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ2xvY2FsVmlkZW9XcmFwcGVyJztcbiAgICAgICAgICAgIHZpZGVvQ29udGFpbmVySWQgPSAnbG9jYWxWaWRlb0NvbnRhaW5lcic7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB2aWRlb1NwYW5JZCA9ICdwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB2aWRlb0NvbnRhaW5lcklkID0gdmlkZW9TcGFuSWQ7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSByZXNvdXJjZUppZDtcbiAgICAgICAgdmFyIG5hbWVTcGFuID0gJCgnIycgKyB2aWRlb0NvbnRhaW5lcklkICsgJz5zcGFuLmRpc3BsYXluYW1lJyk7XG4gICAgICAgIGlmIChuYW1lU3Bhbi5sZW5ndGggPiAwKVxuICAgICAgICAgICAgZGlzcGxheU5hbWUgPSBuYW1lU3Bhbi5odG1sKCk7XG5cbiAgICAgICAgY29uc29sZS5sb2coXCJVSSBlbmFibGUgZG9taW5hbnQgc3BlYWtlclwiLFxuICAgICAgICAgICAgZGlzcGxheU5hbWUsXG4gICAgICAgICAgICByZXNvdXJjZUppZCxcbiAgICAgICAgICAgIGlzRW5hYmxlKTtcblxuICAgICAgICB2aWRlb1NwYW4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh2aWRlb0NvbnRhaW5lcklkKTtcblxuICAgICAgICBpZiAoIXZpZGVvU3Bhbikge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHZpZGVvID0gJCgnIycgKyB2aWRlb1NwYW5JZCArICc+dmlkZW8nKTtcblxuICAgICAgICBpZiAodmlkZW8gJiYgdmlkZW8ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgaWYgKGlzRW5hYmxlKSB7XG4gICAgICAgICAgICAgICAgdmFyIGlzTGFyZ2VWaWRlb1Zpc2libGUgPSBWaWRlb0xheW91dC5pc0xhcmdlVmlkZW9PblRvcCgpO1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnNob3dEaXNwbGF5TmFtZSh2aWRlb0NvbnRhaW5lcklkLCBpc0xhcmdlVmlkZW9WaXNpYmxlKTtcblxuICAgICAgICAgICAgICAgIGlmICghdmlkZW9TcGFuLmNsYXNzTGlzdC5jb250YWlucyhcImRvbWluYW50c3BlYWtlclwiKSlcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9TcGFuLmNsYXNzTGlzdC5hZGQoXCJkb21pbmFudHNwZWFrZXJcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC5zaG93RGlzcGxheU5hbWUodmlkZW9Db250YWluZXJJZCwgZmFsc2UpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHZpZGVvU3Bhbi5jbGFzc0xpc3QuY29udGFpbnMoXCJkb21pbmFudHNwZWFrZXJcIikpXG4gICAgICAgICAgICAgICAgICAgIHZpZGVvU3Bhbi5jbGFzc0xpc3QucmVtb3ZlKFwiZG9taW5hbnRzcGVha2VyXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBBdmF0YXIuc2hvd1VzZXJBdmF0YXIoXG4gICAgICAgICAgICAgICAgQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCkpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgdGhlIHRodW1ibmFpbCBzaXplLlxuICAgICAqXG4gICAgICogQHBhcmFtIHZpZGVvU3BhY2VXaWR0aCB0aGUgd2lkdGggb2YgdGhlIHZpZGVvIHNwYWNlXG4gICAgICovXG4gICAgbXkuY2FsY3VsYXRlVGh1bWJuYWlsU2l6ZSA9IGZ1bmN0aW9uICh2aWRlb1NwYWNlV2lkdGgpIHtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIHRoZSBhdmFpbGFibGUgaGVpZ2h0LCB3aGljaCBpcyB0aGUgaW5uZXIgd2luZG93IGhlaWdodCBtaW51c1xuICAgICAgIC8vIDM5cHggZm9yIHRoZSBoZWFkZXIgbWludXMgMnB4IGZvciB0aGUgZGVsaW1pdGVyIGxpbmVzIG9uIHRoZSB0b3AgYW5kXG4gICAgICAgLy8gYm90dG9tIG9mIHRoZSBsYXJnZSB2aWRlbywgbWludXMgdGhlIDM2cHggc3BhY2UgaW5zaWRlIHRoZSByZW1vdGVWaWRlb3NcbiAgICAgICAvLyBjb250YWluZXIgdXNlZCBmb3IgaGlnaGxpZ2h0aW5nIHNoYWRvdy5cbiAgICAgICB2YXIgYXZhaWxhYmxlSGVpZ2h0ID0gMTAwO1xuXG4gICAgICAgIHZhciBudW12aWRzID0gJCgnI3JlbW90ZVZpZGVvcz5zcGFuOnZpc2libGUnKS5sZW5ndGg7XG4gICAgICAgIGlmIChsb2NhbExhc3ROQ291bnQgJiYgbG9jYWxMYXN0TkNvdW50ID4gMCkge1xuICAgICAgICAgICAgbnVtdmlkcyA9IE1hdGgubWluKGxvY2FsTGFzdE5Db3VudCArIDEsIG51bXZpZHMpO1xuICAgICAgICB9XG5cbiAgICAgICAvLyBSZW1vdmUgdGhlIDNweCBib3JkZXJzIGFycm91bmQgdmlkZW9zIGFuZCBib3JkZXIgYXJvdW5kIHRoZSByZW1vdGVcbiAgICAgICAvLyB2aWRlb3MgYXJlYSBhbmQgdGhlIDQgcGl4ZWxzIGJldHdlZW4gdGhlIGxvY2FsIHZpZGVvIGFuZCB0aGUgb3RoZXJzXG4gICAgICAgLy9UT0RPOiBGaW5kIG91dCB3aGVyZSB0aGUgNCBwaXhlbHMgY29tZSBmcm9tIGFuZCByZW1vdmUgdGhlbVxuICAgICAgIHZhciBhdmFpbGFibGVXaW5XaWR0aCA9IHZpZGVvU3BhY2VXaWR0aCAtIDIgKiAzICogbnVtdmlkcyAtIDcwIC0gNDtcblxuICAgICAgIHZhciBhdmFpbGFibGVXaWR0aCA9IGF2YWlsYWJsZVdpbldpZHRoIC8gbnVtdmlkcztcbiAgICAgICB2YXIgYXNwZWN0UmF0aW8gPSAxNi4wIC8gOS4wO1xuICAgICAgIHZhciBtYXhIZWlnaHQgPSBNYXRoLm1pbigxNjAsIGF2YWlsYWJsZUhlaWdodCk7XG4gICAgICAgYXZhaWxhYmxlSGVpZ2h0ID0gTWF0aC5taW4obWF4SGVpZ2h0LCBhdmFpbGFibGVXaWR0aCAvIGFzcGVjdFJhdGlvKTtcbiAgICAgICBpZiAoYXZhaWxhYmxlSGVpZ2h0IDwgYXZhaWxhYmxlV2lkdGggLyBhc3BlY3RSYXRpbykge1xuICAgICAgICAgICBhdmFpbGFibGVXaWR0aCA9IE1hdGguZmxvb3IoYXZhaWxhYmxlSGVpZ2h0ICogYXNwZWN0UmF0aW8pO1xuICAgICAgIH1cblxuICAgICAgIHJldHVybiBbYXZhaWxhYmxlV2lkdGgsIGF2YWlsYWJsZUhlaWdodF07XG4gICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgcmVtb3RlIHZpZGVvIG1lbnUuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gamlkIHRoZSBqaWQgaW5kaWNhdGluZyB0aGUgdmlkZW8gZm9yIHdoaWNoIHdlJ3JlIGFkZGluZyBhIG1lbnUuXG4gICAgICogQHBhcmFtIGlzTXV0ZWQgaW5kaWNhdGVzIHRoZSBjdXJyZW50IG11dGUgc3RhdGVcbiAgICAgKi9cbiAgICBteS51cGRhdGVSZW1vdGVWaWRlb01lbnUgPSBmdW5jdGlvbihqaWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgdmFyIG11dGVNZW51SXRlbVxuICAgICAgICAgICAgPSAkKCcjcmVtb3RlX3BvcHVwbWVudV8nXG4gICAgICAgICAgICAgICAgICAgICsgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKVxuICAgICAgICAgICAgICAgICAgICArICc+bGk+YS5tdXRlbGluaycpO1xuXG4gICAgICAgIHZhciBtdXRlZEluZGljYXRvciA9IFwiPGkgY2xhc3M9J2ljb24tbWljLWRpc2FibGVkJz48L2k+XCI7XG5cbiAgICAgICAgaWYgKG11dGVNZW51SXRlbS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBtdXRlTGluayA9IG11dGVNZW51SXRlbS5nZXQoMCk7XG5cbiAgICAgICAgICAgIGlmIChpc011dGVkID09PSAndHJ1ZScpIHtcbiAgICAgICAgICAgICAgICBtdXRlTGluay5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArICcgTXV0ZWQnO1xuICAgICAgICAgICAgICAgIG11dGVMaW5rLmNsYXNzTmFtZSA9ICdtdXRlbGluayBkaXNhYmxlZCc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBtdXRlTGluay5pbm5lckhUTUwgPSBtdXRlZEluZGljYXRvciArICcgTXV0ZSc7XG4gICAgICAgICAgICAgICAgbXV0ZUxpbmsuY2xhc3NOYW1lID0gJ211dGVsaW5rJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IGRvbWluYW50IHNwZWFrZXIgcmVzb3VyY2UgamlkLlxuICAgICAqL1xuICAgIG15LmdldERvbWluYW50U3BlYWtlclJlc291cmNlSmlkID0gZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY3VycmVudERvbWluYW50U3BlYWtlcjtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyByZXNvdXJjZSBqaWQgdG8gdGhlIGdpdmVuIHBlZXIgY29udGFpbmVyXG4gICAgICogRE9NIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHRoZSBjb3JyZXNwb25kaW5nIHJlc291cmNlIGppZCB0byB0aGUgZ2l2ZW4gcGVlciBjb250YWluZXJcbiAgICAgKiBET00gZWxlbWVudFxuICAgICAqL1xuICAgIG15LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZCA9IGZ1bmN0aW9uIChjb250YWluZXJFbGVtZW50KSB7XG4gICAgICAgIHZhciBpID0gY29udGFpbmVyRWxlbWVudC5pZC5pbmRleE9mKCdwYXJ0aWNpcGFudF8nKTtcblxuICAgICAgICBpZiAoaSA+PSAwKVxuICAgICAgICAgICAgcmV0dXJuIGNvbnRhaW5lckVsZW1lbnQuaWQuc3Vic3RyaW5nKGkgKyAxMik7IFxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBjb250YWN0IGxpc3QgaXRlbSBjbGlja2VkLlxuICAgICAqL1xuICAgICQoQ29udGFjdExpc3QpLmJpbmQoJ2NvbnRhY3RjbGlja2VkJywgZnVuY3Rpb24oZXZlbnQsIGppZCkge1xuICAgICAgICBpZiAoIWppZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHJlc291cmNlID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcbiAgICAgICAgdmFyIHZpZGVvQ29udGFpbmVyID0gJChcIiNwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlKTtcbiAgICAgICAgaWYgKHZpZGVvQ29udGFpbmVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciB2aWRlb1RodW1iID0gJCgndmlkZW8nLCB2aWRlb0NvbnRhaW5lcikuZ2V0KDApO1xuICAgICAgICAgICAgLy8gSXQgaXMgbm90IGFsd2F5cyB0aGUgY2FzZSB0aGF0IGEgdmlkZW9UaHVtYiBleGlzdHMgKGlmIHRoZXJlIGlzXG4gICAgICAgICAgICAvLyBubyBhY3R1YWwgdmlkZW8pLlxuICAgICAgICAgICAgaWYgKHZpZGVvVGh1bWIpIHtcbiAgICAgICAgICAgICAgICBpZiAodmlkZW9UaHVtYi5zcmMgJiYgdmlkZW9UaHVtYi5zcmMgIT0gJycpIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBXZSBoYXZlIGEgdmlkZW8gc3JjLCBncmVhdCEgTGV0J3MgdXBkYXRlIHRoZSBsYXJnZSB2aWRlb1xuICAgICAgICAgICAgICAgICAgICAvLyBub3cuXG5cbiAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQoXG4gICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1RodW1iLnNyYyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSBkb24ndCBoYXZlIGEgdmlkZW8gc3JjIGZvciBqaWQsIHRoZXJlJ3MgYWJzb2x1dGVseVxuICAgICAgICAgICAgICAgICAgICAvLyBubyBwb2ludCBpbiBjYWxsaW5nIGhhbmRsZVZpZGVvVGh1bWJDbGlja2VkOyBRdWl0ZVxuICAgICAgICAgICAgICAgICAgICAvLyBzaW1wbHksIGl0IHdvbid0IHdvcmsgYmVjYXVzZSBpdCBuZWVkcyBhbiBzcmMgdG8gYXR0YWNoXG4gICAgICAgICAgICAgICAgICAgIC8vIHRvIHRoZSBsYXJnZSB2aWRlby5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gSW5zdGVhZCwgd2UgdHJpZ2dlciB0aGUgcGlubmVkIGVuZHBvaW50IGNoYW5nZWQgZXZlbnQgdG9cbiAgICAgICAgICAgICAgICAgICAgLy8gbGV0IHRoZSBicmlkZ2UgYWRqdXN0IGl0cyBsYXN0TiBzZXQgZm9yIG15amlkIGFuZCBzdG9yZVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgcGlubmVkIHVzZXIgaW4gdGhlIGxhc3ROUGlja3VwSmlkIHZhcmlhYmxlIHRvIGJlXG4gICAgICAgICAgICAgICAgICAgIC8vIHBpY2tlZCB1cCBsYXRlciBieSB0aGUgbGFzdE4gY2hhbmdlZCBldmVudCBoYW5kbGVyLlxuXG4gICAgICAgICAgICAgICAgICAgIGxhc3ROUGlja3VwSmlkID0gamlkO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChVSUV2ZW50cy5QSU5ORURfRU5EUE9JTlQsXG4gICAgICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGppZCA9PSBBUFAueG1wcC5teUppZCgpKSB7XG4gICAgICAgICAgICAgICAgJChcIiNsb2NhbFZpZGVvQ29udGFpbmVyXCIpLmNsaWNrKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIE9uIGF1ZGlvIG11dGVkIGV2ZW50LlxuICAgICAqL1xuICAgICQoZG9jdW1lbnQpLmJpbmQoJ2F1ZGlvbXV0ZWQubXVjJywgZnVuY3Rpb24gKGV2ZW50LCBqaWQsIGlzTXV0ZWQpIHtcbiAgICAgICAgLypcbiAgICAgICAgIC8vIEZJWE1FOiBidXQgZm9jdXMgY2FuIG5vdCBtdXRlIGluIHRoaXMgY2FzZSA/IC0gY2hlY2tcbiAgICAgICAgaWYgKGppZCA9PT0geG1wcC5teUppZCgpKSB7XG5cbiAgICAgICAgICAgIC8vIFRoZSBsb2NhbCBtdXRlIGluZGljYXRvciBpcyBjb250cm9sbGVkIGxvY2FsbHlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSovXG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChqaWQgPT09IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ2xvY2FsVmlkZW9Db250YWluZXInO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQpO1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBtdXRlZEF1ZGlvc1tqaWRdID0gaXNNdXRlZDtcblxuICAgICAgICBpZiAoQVBQLnhtcHAuaXNNb2RlcmF0b3IoKSkge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQudXBkYXRlUmVtb3RlVmlkZW9NZW51KGppZCwgaXNNdXRlZCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodmlkZW9TcGFuSWQpXG4gICAgICAgICAgICBWaWRlb0xheW91dC5zaG93QXVkaW9JbmRpY2F0b3IodmlkZW9TcGFuSWQsIGlzTXV0ZWQpO1xuICAgIH0pO1xuXG4gICAgLyoqXG4gICAgICogT24gdmlkZW8gbXV0ZWQgZXZlbnQuXG4gICAgICovXG4gICAgJChkb2N1bWVudCkuYmluZCgndmlkZW9tdXRlZC5tdWMnLCBmdW5jdGlvbiAoZXZlbnQsIGppZCwgdmFsdWUpIHtcbiAgICAgICAgdmFyIGlzTXV0ZWQgPSAodmFsdWUgPT09IFwidHJ1ZVwiKTtcbiAgICAgICAgaWYoIUFQUC5SVEMubXV0ZVJlbW90ZVZpZGVvU3RyZWFtKGppZCwgaXNNdXRlZCkpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgQXZhdGFyLnNob3dVc2VyQXZhdGFyKGppZCwgaXNNdXRlZCk7XG4gICAgICAgIHZhciB2aWRlb1NwYW5JZCA9IG51bGw7XG4gICAgICAgIGlmIChqaWQgPT09IEFQUC54bXBwLm15SmlkKCkpIHtcbiAgICAgICAgICAgIHZpZGVvU3BhbklkID0gJ2xvY2FsVmlkZW9Db250YWluZXInO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQpO1xuICAgICAgICAgICAgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodmlkZW9TcGFuSWQpXG4gICAgICAgICAgICBWaWRlb0xheW91dC5zaG93VmlkZW9JbmRpY2F0b3IodmlkZW9TcGFuSWQsIHZhbHVlKTtcbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIERpc3BsYXkgbmFtZSBjaGFuZ2VkLlxuICAgICAqL1xuICAgIG15Lm9uRGlzcGxheU5hbWVDaGFuZ2VkID1cbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGppZCwgZGlzcGxheU5hbWUsIHN0YXR1cykge1xuICAgICAgICBpZiAoamlkID09PSAnbG9jYWxWaWRlb0NvbnRhaW5lcidcbiAgICAgICAgICAgIHx8IGppZCA9PT0gQVBQLnhtcHAubXlKaWQoKSkge1xuICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUoJ2xvY2FsVmlkZW9Db250YWluZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheU5hbWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuZW5zdXJlUGVlckNvbnRhaW5lckV4aXN0cyhqaWQpO1xuICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUoXG4gICAgICAgICAgICAgICAgJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpLFxuICAgICAgICAgICAgICAgIGRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgICAgIHN0YXR1cyk7XG4gICAgICAgIH1cblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBkb21pbmFudCBzcGVha2VyIGNoYW5nZWQgZXZlbnQuXG4gICAgICovXG4gICAgbXkub25Eb21pbmFudFNwZWFrZXJDaGFuZ2VkID0gZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG4gICAgICAgIC8vIFdlIGlnbm9yZSBsb2NhbCB1c2VyIGV2ZW50cy5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkXG4gICAgICAgICAgICAgICAgPT09IEFQUC54bXBwLm15UmVzb3VyY2UoKSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGN1cnJlbnQgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgaWYgKHJlc291cmNlSmlkICE9PSBjdXJyZW50RG9taW5hbnRTcGVha2VyKSB7XG4gICAgICAgICAgICB2YXIgb2xkU3BlYWtlclZpZGVvU3BhbklkID0gXCJwYXJ0aWNpcGFudF9cIiArIGN1cnJlbnREb21pbmFudFNwZWFrZXIsXG4gICAgICAgICAgICAgICAgbmV3U3BlYWtlclZpZGVvU3BhbklkID0gXCJwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlSmlkO1xuICAgICAgICAgICAgaWYoJChcIiNcIiArIG9sZFNwZWFrZXJWaWRlb1NwYW5JZCArIFwiPnNwYW4uZGlzcGxheW5hbWVcIikudGV4dCgpID09PVxuICAgICAgICAgICAgICAgIGludGVyZmFjZUNvbmZpZy5ERUZBVUxUX0RPTUlOQU5UX1NQRUFLRVJfRElTUExBWV9OQU1FKSB7XG4gICAgICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUob2xkU3BlYWtlclZpZGVvU3BhbklkLCBudWxsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmKCQoXCIjXCIgKyBuZXdTcGVha2VyVmlkZW9TcGFuSWQgKyBcIj5zcGFuLmRpc3BsYXluYW1lXCIpLnRleHQoKSA9PT1cbiAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9SRU1PVEVfRElTUExBWV9OQU1FKSB7XG4gICAgICAgICAgICAgICAgc2V0RGlzcGxheU5hbWUobmV3U3BlYWtlclZpZGVvU3BhbklkLFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmZhY2VDb25maWcuREVGQVVMVF9ET01JTkFOVF9TUEVBS0VSX0RJU1BMQVlfTkFNRSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjdXJyZW50RG9taW5hbnRTcGVha2VyID0gcmVzb3VyY2VKaWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPYnRhaW4gY29udGFpbmVyIGZvciBuZXcgZG9taW5hbnQgc3BlYWtlci5cbiAgICAgICAgdmFyIGNvbnRhaW5lciAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcbiAgICAgICAgICAgICAgICAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkKTtcblxuICAgICAgICAvLyBMb2NhbCB2aWRlbyB3aWxsIG5vdCBoYXZlIGNvbnRhaW5lciBmb3VuZCwgYnV0IHRoYXQncyBva1xuICAgICAgICAvLyBzaW5jZSB3ZSBkb24ndCB3YW50IHRvIHN3aXRjaCB0byBsb2NhbCB2aWRlby5cbiAgICAgICAgaWYgKGNvbnRhaW5lciAmJiAhZm9jdXNlZFZpZGVvSW5mbylcbiAgICAgICAge1xuICAgICAgICAgICAgdmFyIHZpZGVvID0gY29udGFpbmVyLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwidmlkZW9cIik7XG5cbiAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgbGFyZ2UgdmlkZW8gaWYgdGhlIHZpZGVvIHNvdXJjZSBpcyBhbHJlYWR5IGF2YWlsYWJsZSxcbiAgICAgICAgICAgIC8vIG90aGVyd2lzZSB3YWl0IGZvciB0aGUgXCJ2aWRlb2FjdGl2ZS5qaW5nbGVcIiBldmVudC5cbiAgICAgICAgICAgIGlmICh2aWRlby5sZW5ndGggJiYgdmlkZW9bMF0uY3VycmVudFRpbWUgPiAwKVxuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oQVBQLlJUQy5nZXRWaWRlb1NyYyh2aWRlb1swXSksIHJlc291cmNlSmlkKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBsYXN0IE4gY2hhbmdlIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGxhc3RORW5kcG9pbnRzIHRoZSBsaXN0IG9mIGxhc3QgTiBlbmRwb2ludHNcbiAgICAgKiBAcGFyYW0gZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB0aGUgbGlzdCBjdXJyZW50bHkgZW50ZXJpbmcgbGFzdCBOXG4gICAgICogZW5kcG9pbnRzXG4gICAgICovXG4gICAgbXkub25MYXN0TkVuZHBvaW50c0NoYW5nZWQgPSBmdW5jdGlvbiAoIGxhc3RORW5kcG9pbnRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kcG9pbnRzRW50ZXJpbmdMYXN0TixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSkge1xuICAgICAgICBpZiAobGFzdE5Db3VudCAhPT0gbGFzdE5FbmRwb2ludHMubGVuZ3RoKVxuICAgICAgICAgICAgbGFzdE5Db3VudCA9IGxhc3RORW5kcG9pbnRzLmxlbmd0aDtcblxuICAgICAgICBsYXN0TkVuZHBvaW50c0NhY2hlID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgLy8gU2F5IEEsIEIsIEMsIEQsIEUsIGFuZCBGIGFyZSBpbiBhIGNvbmZlcmVuY2UgYW5kIExhc3ROID0gMy5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gSWYgTGFzdE4gZHJvcHMgdG8sIHNheSwgMiwgYmVjYXVzZSBvZiBhZGFwdGl2aXR5LCB0aGVuIEUgc2hvdWxkIHNlZVxuICAgICAgICAvLyB0aHVtYm5haWxzIGZvciBBLCBCIGFuZCBDLiBBIGFuZCBCIGFyZSBpbiBFJ3Mgc2VydmVyIHNpZGUgTGFzdE4gc2V0LFxuICAgICAgICAvLyBzbyBFIHNlZXMgdGhlbS4gQyBpcyBvbmx5IGluIEUncyBsb2NhbCBMYXN0TiBzZXQuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIElmIEYgc3RhcnRzIHRhbGtpbmcgYW5kIExhc3ROID0gMywgdGhlbiBFIHNob3VsZCBzZWUgdGh1bWJuYWlscyBmb3JcbiAgICAgICAgLy8gRiwgQSwgQi4gQiBnZXRzIFwiZWplY3RlZFwiIGZyb20gRSdzIHNlcnZlciBzaWRlIExhc3ROIHNldCwgYnV0IGl0XG4gICAgICAgIC8vIGVudGVycyBFJ3MgbG9jYWwgTGFzdE4gZWplY3RpbmcgQy5cblxuICAgICAgICAvLyBJbmNyZWFzZSB0aGUgbG9jYWwgTGFzdE4gc2V0IHNpemUsIGlmIG5lY2Vzc2FyeS5cbiAgICAgICAgaWYgKGxhc3ROQ291bnQgPiBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgIGxvY2FsTGFzdE5Db3VudCA9IGxhc3ROQ291bnQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGxvY2FsIExhc3ROIHNldCBwcmVzZXJ2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGVcbiAgICAgICAgLy8gZW5kcG9pbnRzIGFwcGVhcmVkIGluIHRoZSBMYXN0Ti9sb2NhbCBMYXN0TiBzZXQuXG5cbiAgICAgICAgdmFyIG5leHRMb2NhbExhc3ROU2V0ID0gbGFzdE5FbmRwb2ludHMuc2xpY2UoMCk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbG9jYWxMYXN0TlNldC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKG5leHRMb2NhbExhc3ROU2V0Lmxlbmd0aCA+PSBsb2NhbExhc3ROQ291bnQpIHtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHJlc291cmNlSmlkID0gbG9jYWxMYXN0TlNldFtpXTtcbiAgICAgICAgICAgIGlmIChuZXh0TG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICBuZXh0TG9jYWxMYXN0TlNldC5wdXNoKHJlc291cmNlSmlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxvY2FsTGFzdE5TZXQgPSBuZXh0TG9jYWxMYXN0TlNldDtcblxuICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IGZhbHNlO1xuXG4gICAgICAgIC8vIEhhbmRsZSBMYXN0Ti9sb2NhbCBMYXN0TiBjaGFuZ2VzLlxuICAgICAgICAkKCcjcmVtb3RlVmlkZW9zPnNwYW4nKS5lYWNoKGZ1bmN0aW9uKCBpbmRleCwgZWxlbWVudCApIHtcbiAgICAgICAgICAgIHZhciByZXNvdXJjZUppZCA9IFZpZGVvTGF5b3V0LmdldFBlZXJDb250YWluZXJSZXNvdXJjZUppZChlbGVtZW50KTtcblxuICAgICAgICAgICAgdmFyIGlzUmVjZWl2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA8IDApIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlJlbW92ZSBmcm9tIGxhc3QgTlwiLCByZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdoaWRlJyk7XG4gICAgICAgICAgICAgICAgaXNSZWNlaXZlZCA9IGZhbHNlO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChyZXNvdXJjZUppZFxuICAgICAgICAgICAgICAgICYmICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQpLmlzKCc6dmlzaWJsZScpXG4gICAgICAgICAgICAgICAgJiYgbGFzdE5FbmRwb2ludHMuaW5kZXhPZihyZXNvdXJjZUppZCkgPCAwXG4gICAgICAgICAgICAgICAgJiYgbG9jYWxMYXN0TlNldC5pbmRleE9mKHJlc291cmNlSmlkKSA+PSAwKSB7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdhdmF0YXInKTtcbiAgICAgICAgICAgICAgICBpc1JlY2VpdmVkID0gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghaXNSZWNlaXZlZCkge1xuICAgICAgICAgICAgICAgIC8vIHJlc291cmNlSmlkIGhhcyBkcm9wcGVkIG91dCBvZiB0aGUgc2VydmVyIHNpZGUgbGFzdE4gc2V0LCBzb1xuICAgICAgICAgICAgICAgIC8vIGl0IGlzIG5vIGxvbmdlciBiZWluZyByZWNlaXZlZC4gSWYgcmVzb3VyY2VKaWQgd2FzIGJlaW5nXG4gICAgICAgICAgICAgICAgLy8gZGlzcGxheWVkIGluIHRoZSBsYXJnZSB2aWRlbyB3ZSBoYXZlIHRvIHN3aXRjaCB0byBhbm90aGVyXG4gICAgICAgICAgICAgICAgLy8gdXNlci5cbiAgICAgICAgICAgICAgICB2YXIgbGFyZ2VWaWRlb1Jlc291cmNlID0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZDtcbiAgICAgICAgICAgICAgICBpZiAoIXVwZGF0ZUxhcmdlVmlkZW8gJiYgcmVzb3VyY2VKaWQgPT09IGxhcmdlVmlkZW9SZXNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICB1cGRhdGVMYXJnZVZpZGVvID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghZW5kcG9pbnRzRW50ZXJpbmdMYXN0TiB8fCBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmxlbmd0aCA8IDApXG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROID0gbGFzdE5FbmRwb2ludHM7XG5cbiAgICAgICAgaWYgKGVuZHBvaW50c0VudGVyaW5nTGFzdE4gJiYgZW5kcG9pbnRzRW50ZXJpbmdMYXN0Ti5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBlbmRwb2ludHNFbnRlcmluZ0xhc3ROLmZvckVhY2goZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG5cbiAgICAgICAgICAgICAgICB2YXIgaXNWaXNpYmxlID0gJCgnI3BhcnRpY2lwYW50XycgKyByZXNvdXJjZUppZCkuaXMoJzp2aXNpYmxlJyk7XG4gICAgICAgICAgICAgICAgc2hvd1BlZXJDb250YWluZXIocmVzb3VyY2VKaWQsICdzaG93Jyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc1Zpc2libGUpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJBZGQgdG8gbGFzdCBOXCIsIHJlc291cmNlSmlkKTtcblxuICAgICAgICAgICAgICAgICAgICB2YXIgamlkID0gQVBQLnhtcHAuZmluZEppZEZyb21SZXNvdXJjZShyZXNvdXJjZUppZCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBtZWRpYVN0cmVhbSA9IEFQUC5SVEMucmVtb3RlU3RyZWFtc1tqaWRdW01lZGlhU3RyZWFtVHlwZS5WSURFT19UWVBFXTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNlbCA9ICQoJyNwYXJ0aWNpcGFudF8nICsgcmVzb3VyY2VKaWQgKyAnPnZpZGVvJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHZpZGVvU3RyZWFtID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbShcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhU3RyZWFtLnN0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5SVEMuYXR0YWNoTWVkaWFTdHJlYW0oc2VsLCB2aWRlb1N0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChsYXN0TlBpY2t1cEppZCA9PSBtZWRpYVN0cmVhbS5wZWVyamlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDbGVhbiB1cCB0aGUgbGFzdE4gcGlja3VwIGppZC5cbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3ROUGlja3VwSmlkID0gbnVsbDtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRG9uJ3QgZmlyZSB0aGUgZXZlbnRzIGFnYWluLCB0aGV5J3ZlIGFscmVhZHlcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGJlZW4gZmlyZWQgaW4gdGhlIGNvbnRhY3QgbGlzdCBjbGljayBoYW5kbGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW9MYXlvdXQuaGFuZGxlVmlkZW9UaHVtYkNsaWNrZWQoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJChzZWwpLmF0dHIoJ3NyYycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKG1lZGlhU3RyZWFtLnBlZXJqaWQpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlTGFyZ2VWaWRlbyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHdhaXRGb3JSZW1vdGVWaWRlbyhzZWwsIG1lZGlhU3RyZWFtLnNzcmMsIG1lZGlhU3RyZWFtLnN0cmVhbSwgcmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBUaGUgZW5kcG9pbnQgdGhhdCB3YXMgYmVpbmcgc2hvd24gaW4gdGhlIGxhcmdlIHZpZGVvIGhhcyBkcm9wcGVkIG91dFxuICAgICAgICAvLyBvZiB0aGUgbGFzdE4gc2V0IGFuZCB0aGVyZSB3YXMgbm8gbGFzdE4gcGlja3VwIGppZC4gV2UgbmVlZCB0byB1cGRhdGVcbiAgICAgICAgLy8gdGhlIGxhcmdlIHZpZGVvIG5vdy5cblxuICAgICAgICBpZiAodXBkYXRlTGFyZ2VWaWRlbykge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UsIGNvbnRhaW5lciwgc3JjO1xuICAgICAgICAgICAgdmFyIG15UmVzb3VyY2VcbiAgICAgICAgICAgICAgICA9IEFQUC54bXBwLm15UmVzb3VyY2UoKTtcblxuICAgICAgICAgICAgLy8gRmluZCBvdXQgd2hpY2ggZW5kcG9pbnQgdG8gc2hvdyBpbiB0aGUgbGFyZ2UgdmlkZW8uXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxhc3RORW5kcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgcmVzb3VyY2UgPSBsYXN0TkVuZHBvaW50c1tpXTtcbiAgICAgICAgICAgICAgICBpZiAoIXJlc291cmNlIHx8IHJlc291cmNlID09PSBteVJlc291cmNlKVxuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgICAgIGNvbnRhaW5lciA9ICQoXCIjcGFydGljaXBhbnRfXCIgKyByZXNvdXJjZSk7XG4gICAgICAgICAgICAgICAgaWYgKGNvbnRhaW5lci5sZW5ndGggPT0gMClcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICAgICAgICBzcmMgPSAkKCd2aWRlbycsIGNvbnRhaW5lcikuYXR0cignc3JjJyk7XG4gICAgICAgICAgICAgICAgaWYgKCFzcmMpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICAgICAgLy8gdmlkZW9TcmNUb1NzcmMgbmVlZHMgdG8gYmUgdXBkYXRlIGZvciB0aGlzIGNhbGwgdG8gc3VjY2VlZC5cbiAgICAgICAgICAgICAgICBWaWRlb0xheW91dC51cGRhdGVMYXJnZVZpZGVvKHNyYyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBteS5vblNpbXVsY2FzdExheWVyc0NoYW5naW5nID0gZnVuY3Rpb24gKGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKSB7XG4gICAgICAgIGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzLmZvckVhY2goZnVuY3Rpb24gKGVzbCkge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSBlc2wuZW5kcG9pbnQ7XG5cbiAgICAgICAgICAgIC8vIGlmIGxhc3ROIGlzIGVuYWJsZWQgKmFuZCogdGhlIGVuZHBvaW50IGlzICpub3QqIGluIHRoZSBsYXN0TiBzZXQsXG4gICAgICAgICAgICAvLyB0aGVuIGlnbm9yZSB0aGUgZXZlbnQgKD0gZG8gbm90IHByZWxvYWQgYW55dGhpbmcpLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIFRoZSBicmlkZ2UgY291bGQgcHJvYmFibHkgc3RvcCBzZW5kaW5nIHRoaXMgbWVzc2FnZSBpZiBpdCdzIGZvclxuICAgICAgICAgICAgLy8gYW4gZW5kcG9pbnQgdGhhdCdzIG5vdCBpbiBsYXN0Ti5cblxuICAgICAgICAgICAgaWYgKGxhc3ROQ291bnQgIT0gLTFcbiAgICAgICAgICAgICAgICAmJiAobGFzdE5Db3VudCA8IDEgfHwgbGFzdE5FbmRwb2ludHNDYWNoZS5pbmRleE9mKHJlc291cmNlKSA9PT0gLTEpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcHJpbWFyeVNTUkMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgIC8vIEdldCBzZXNzaW9uIGFuZCBzdHJlYW0gZnJvbSBwcmltYXJ5IHNzcmMuXG4gICAgICAgICAgICB2YXIgcmVzID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhwcmltYXJ5U1NSQyk7XG4gICAgICAgICAgICB2YXIgc2lkID0gcmVzLnNpZDtcbiAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtID0gcmVzLnN0cmVhbTtcblxuICAgICAgICAgICAgaWYgKHNpZCAmJiBlbGVjdGVkU3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBBUFAuc2ltdWxjYXN0LmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMocHJpbWFyeVNTUkMpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFtlc2wsIHByaW1hcnlTU1JDLCBtc2lkLCBzaWQsIGVsZWN0ZWRTdHJlYW1dKTtcblxuICAgICAgICAgICAgICAgIHZhciBwcmVsb2FkID0gKFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKEFQUC54bXBwLmdldEppZEZyb21TU1JDKHByaW1hcnlTU1JDKSkgPT0gbGFyZ2VWaWRlb1N0YXRlLnVzZXJSZXNvdXJjZUppZCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocHJlbG9hZCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnUHJlbG9hZGluZyByZW1vdGUgdmlkZW8nKTtcbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQgPSAkKCc8dmlkZW8gYXV0b3BsYXk+PC92aWRlbz4nKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gc3NyY3MgYXJlIHVuaXF1ZSBpbiBhbiBydHAgc2Vzc2lvblxuICAgICAgICAgICAgICAgICAgICBsYXJnZVZpZGVvU3RhdGUucHJlbG9hZF9zc3JjID0gcHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShsYXJnZVZpZGVvU3RhdGUucHJlbG9hZCwgZWxlY3RlZFN0cmVhbSlcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignQ291bGQgbm90IGZpbmQgYSBzdHJlYW0gb3IgYSBzZXNzaW9uLicsIHNpZCwgZWxlY3RlZFN0cmVhbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBPbiBzaW11bGNhc3QgbGF5ZXJzIGNoYW5nZWQgZXZlbnQuXG4gICAgICovXG4gICAgbXkub25TaW11bGNhc3RMYXllcnNDaGFuZ2VkID0gZnVuY3Rpb24gKGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKSB7XG4gICAgICAgIGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzLmZvckVhY2goZnVuY3Rpb24gKGVzbCkge1xuXG4gICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSBlc2wuZW5kcG9pbnQ7XG5cbiAgICAgICAgICAgIC8vIGlmIGxhc3ROIGlzIGVuYWJsZWQgKmFuZCogdGhlIGVuZHBvaW50IGlzICpub3QqIGluIHRoZSBsYXN0TiBzZXQsXG4gICAgICAgICAgICAvLyB0aGVuIGlnbm9yZSB0aGUgZXZlbnQgKD0gZG8gbm90IGNoYW5nZSBsYXJnZSB2aWRlby90aHVtYm5haWxcbiAgICAgICAgICAgIC8vIFNSQ3MpLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBldmVuIGlmIHdlIGlnbm9yZSB0aGUgXCJjaGFuZ2VkXCIgZXZlbnQgaW4gdGhpcyBldmVudFxuICAgICAgICAgICAgLy8gaGFuZGxlciwgdGhlIGJyaWRnZSBtdXN0IGNvbnRpbnVlIHNlbmRpbmcgdGhlc2UgZXZlbnRzIGJlY2F1c2VcbiAgICAgICAgICAgIC8vIHRoZSBzaW11bGNhc3QgY29kZSBpbiBzaW11bGNhc3QuanMgdXNlcyBpdCB0byBrbm93IHdoYXQncyBnb2luZ1xuICAgICAgICAgICAgLy8gdG8gYmUgc3RyZWFtZWQgYnkgdGhlIGJyaWRnZSB3aGVuL2lmIHRoZSBlbmRwb2ludCBnZXRzIGJhY2sgaW50b1xuICAgICAgICAgICAgLy8gdGhlIGxhc3ROIHNldC5cblxuICAgICAgICAgICAgaWYgKGxhc3ROQ291bnQgIT0gLTFcbiAgICAgICAgICAgICAgICAmJiAobGFzdE5Db3VudCA8IDEgfHwgbGFzdE5FbmRwb2ludHNDYWNoZS5pbmRleE9mKHJlc291cmNlKSA9PT0gLTEpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcHJpbWFyeVNTUkMgPSBlc2wuc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG5cbiAgICAgICAgICAgIC8vIEdldCBzZXNzaW9uIGFuZCBzdHJlYW0gZnJvbSBwcmltYXJ5IHNzcmMuXG4gICAgICAgICAgICB2YXIgcmVzID0gQVBQLnNpbXVsY2FzdC5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyhwcmltYXJ5U1NSQyk7XG4gICAgICAgICAgICB2YXIgc2lkID0gcmVzLnNpZDtcbiAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtID0gcmVzLnN0cmVhbTtcblxuICAgICAgICAgICAgaWYgKHNpZCAmJiBlbGVjdGVkU3RyZWFtKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1zaWQgPSBBUFAuc2ltdWxjYXN0LmdldFJlbW90ZVZpZGVvU3RyZWFtSWRCeVNTUkMocHJpbWFyeVNTUkMpO1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdTd2l0Y2hpbmcgc2ltdWxjYXN0IHN1YnN0cmVhbS4nKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oW2VzbCwgcHJpbWFyeVNTUkMsIG1zaWQsIHNpZCwgZWxlY3RlZFN0cmVhbV0pO1xuXG4gICAgICAgICAgICAgICAgdmFyIG1zaWRQYXJ0cyA9IG1zaWQuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsUmVtb3RlVmlkZW8gPSAkKFsnIycsICdyZW1vdGVWaWRlb18nLCBzaWQsICdfJywgbXNpZFBhcnRzWzBdXS5qb2luKCcnKSk7XG5cbiAgICAgICAgICAgICAgICB2YXIgdXBkYXRlTGFyZ2VWaWRlbyA9IChTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChBUFAueG1wcC5nZXRKaWRGcm9tU1NSQyhwcmltYXJ5U1NSQykpXG4gICAgICAgICAgICAgICAgICAgID09IGxhcmdlVmlkZW9TdGF0ZS51c2VyUmVzb3VyY2VKaWQpO1xuICAgICAgICAgICAgICAgIHZhciB1cGRhdGVGb2N1c2VkVmlkZW9TcmMgPSAoZm9jdXNlZFZpZGVvSW5mbyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAmJiBmb2N1c2VkVmlkZW9JbmZvLnNyYyAhPSAnJyAmJlxuICAgICAgICAgICAgICAgICAgICAoQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSkgPT0gZm9jdXNlZFZpZGVvSW5mby5zcmMpKTtcblxuICAgICAgICAgICAgICAgIHZhciBlbGVjdGVkU3RyZWFtVXJsO1xuICAgICAgICAgICAgICAgIGlmIChsYXJnZVZpZGVvU3RhdGUucHJlbG9hZF9zc3JjID09IHByaW1hcnlTU1JDKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5zZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSwgQVBQLlJUQy5nZXRWaWRlb1NyYyhsYXJnZVZpZGVvU3RhdGUucHJlbG9hZFswXSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBpZiAobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRcbiAgICAgICAgICAgICAgICAgICAgICAgICYmIGxhcmdlVmlkZW9TdGF0ZS5wcmVsb2FkICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQobGFyZ2VWaWRlb1N0YXRlLnByZWxvYWQpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgbGFyZ2VWaWRlb1N0YXRlLnByZWxvYWRfc3NyYyA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgQVBQLlJUQy5hdHRhY2hNZWRpYVN0cmVhbShzZWxSZW1vdGVWaWRlbywgZWxlY3RlZFN0cmVhbSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIGppZCA9IEFQUC54bXBwLmdldEppZEZyb21TU1JDKHByaW1hcnlTU1JDKTtcblxuICAgICAgICAgICAgICAgIGlmICh1cGRhdGVMYXJnZVZpZGVvKSB7XG4gICAgICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LnVwZGF0ZUxhcmdlVmlkZW8oQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSksIG51bGwsXG4gICAgICAgICAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAodXBkYXRlRm9jdXNlZFZpZGVvU3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvY3VzZWRWaWRlb0luZm8uc3JjID0gQVBQLlJUQy5nZXRWaWRlb1NyYyhzZWxSZW1vdGVWaWRlb1swXSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIHZpZGVvSWQ7XG4gICAgICAgICAgICAgICAgaWYocmVzb3VyY2UgPT0gQVBQLnhtcHAubXlSZXNvdXJjZSgpKVxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9JZCA9IFwibG9jYWxWaWRlb0NvbnRhaW5lclwiO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2aWRlb0lkID0gXCJwYXJ0aWNpcGFudF9cIiArIHJlc291cmNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgY29ubmVjdGlvbkluZGljYXRvciA9IFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW3ZpZGVvSWRdO1xuICAgICAgICAgICAgICAgIGlmKGNvbm5lY3Rpb25JbmRpY2F0b3IpXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25JbmRpY2F0b3IudXBkYXRlUG9wb3ZlckRhdGEoKTtcblxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBhIHN0cmVhbSBvciBhIHNpZC4nLCBzaWQsIGVsZWN0ZWRTdHJlYW0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyBsb2NhbCBzdGF0c1xuICAgICAqIEBwYXJhbSBwZXJjZW50XG4gICAgICogQHBhcmFtIG9iamVjdFxuICAgICAqL1xuICAgIG15LnVwZGF0ZUxvY2FsQ29ubmVjdGlvblN0YXRzID0gZnVuY3Rpb24gKHBlcmNlbnQsIG9iamVjdCkge1xuICAgICAgICB2YXIgcmVzb2x1dGlvbiA9IG51bGw7XG4gICAgICAgIGlmKG9iamVjdC5yZXNvbHV0aW9uICE9PSBudWxsKVxuICAgICAgICB7XG4gICAgICAgICAgICByZXNvbHV0aW9uID0gb2JqZWN0LnJlc29sdXRpb247XG4gICAgICAgICAgICBvYmplY3QucmVzb2x1dGlvbiA9IHJlc29sdXRpb25bQVBQLnhtcHAubXlKaWQoKV07XG4gICAgICAgICAgICBkZWxldGUgcmVzb2x1dGlvbltBUFAueG1wcC5teUppZCgpXTtcbiAgICAgICAgfVxuICAgICAgICB1cGRhdGVTdGF0c0luZGljYXRvcihcImxvY2FsVmlkZW9Db250YWluZXJcIiwgcGVyY2VudCwgb2JqZWN0KTtcbiAgICAgICAgZm9yKHZhciBqaWQgaW4gcmVzb2x1dGlvbilcbiAgICAgICAge1xuICAgICAgICAgICAgaWYocmVzb2x1dGlvbltqaWRdID09PSBudWxsKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgdmFyIGlkID0gJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgaWYoVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbaWRdKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFZpZGVvTGF5b3V0LmNvbm5lY3Rpb25JbmRpY2F0b3JzW2lkXS51cGRhdGVSZXNvbHV0aW9uKHJlc29sdXRpb25bamlkXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBVcGRhdGVzIHJlbW90ZSBzdGF0cy5cbiAgICAgKiBAcGFyYW0gamlkIHRoZSBqaWQgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdGF0c1xuICAgICAqIEBwYXJhbSBwZXJjZW50IHRoZSBjb25uZWN0aW9uIHF1YWxpdHkgcGVyY2VudFxuICAgICAqIEBwYXJhbSBvYmplY3QgdGhlIHN0YXRzIGRhdGFcbiAgICAgKi9cbiAgICBteS51cGRhdGVDb25uZWN0aW9uU3RhdHMgPSBmdW5jdGlvbiAoamlkLCBwZXJjZW50LCBvYmplY3QpIHtcbiAgICAgICAgdmFyIHJlc291cmNlSmlkID0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKTtcblxuICAgICAgICB2YXIgdmlkZW9TcGFuSWQgPSAncGFydGljaXBhbnRfJyArIHJlc291cmNlSmlkO1xuICAgICAgICB1cGRhdGVTdGF0c0luZGljYXRvcih2aWRlb1NwYW5JZCwgcGVyY2VudCwgb2JqZWN0KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyB0aGUgY29ubmVjdGlvblxuICAgICAqIEBwYXJhbSBqaWRcbiAgICAgKi9cbiAgICBteS5yZW1vdmVDb25uZWN0aW9uSW5kaWNhdG9yID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpXS5yZW1vdmUoKTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIGNvbm5lY3Rpb24gaW5kaWNhdG9yXG4gICAgICogQHBhcmFtIGppZFxuICAgICAqL1xuICAgIG15LmhpZGVDb25uZWN0aW9uSW5kaWNhdG9yID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBpZihWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9yc1sncGFydGljaXBhbnRfJyArIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCldKVxuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbJ3BhcnRpY2lwYW50XycgKyBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpXS5oaWRlKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEhpZGVzIGFsbCB0aGUgaW5kaWNhdG9yc1xuICAgICAqL1xuICAgIG15Lm9uU3RhdHNTdG9wID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBmb3IodmFyIGluZGljYXRvciBpbiBWaWRlb0xheW91dC5jb25uZWN0aW9uSW5kaWNhdG9ycylcbiAgICAgICAge1xuICAgICAgICAgICAgVmlkZW9MYXlvdXQuY29ubmVjdGlvbkluZGljYXRvcnNbaW5kaWNhdG9yXS5oaWRlSW5kaWNhdG9yKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgbXkucGFydGljaXBhbnRMZWZ0ID0gZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAvLyBVbmxvY2sgbGFyZ2UgdmlkZW9cbiAgICAgICAgaWYgKGZvY3VzZWRWaWRlb0luZm8gJiYgZm9jdXNlZFZpZGVvSW5mby5qaWQgPT09IGppZClcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiRm9jdXNlZCB2aWRlbyBvd25lciBoYXMgbGVmdCB0aGUgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgICAgIGZvY3VzZWRWaWRlb0luZm8gPSBudWxsO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG15O1xufShWaWRlb0xheW91dCB8fCB7fSkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFZpZGVvTGF5b3V0OyIsIi8vdmFyIG5vdW5zID0gW1xuLy9dO1xudmFyIHBsdXJhbE5vdW5zID0gW1xuICAgIFwiQWxpZW5zXCIsIFwiQW5pbWFsc1wiLCBcIkFudGVsb3Blc1wiLCBcIkFudHNcIiwgXCJBcGVzXCIsIFwiQXBwbGVzXCIsIFwiQmFib29uc1wiLCBcIkJhY3RlcmlhXCIsIFwiQmFkZ2Vyc1wiLCBcIkJhbmFuYXNcIiwgXCJCYXRzXCIsXG4gICAgXCJCZWFyc1wiLCBcIkJpcmRzXCIsIFwiQm9ub2Jvc1wiLCBcIkJyaWRlc1wiLCBcIkJ1Z3NcIiwgXCJCdWxsc1wiLCBcIkJ1dHRlcmZsaWVzXCIsIFwiQ2hlZXRhaHNcIixcbiAgICBcIkNoZXJyaWVzXCIsIFwiQ2hpY2tlblwiLCBcIkNoaWxkcmVuXCIsIFwiQ2hpbXBzXCIsIFwiQ2xvd25zXCIsIFwiQ293c1wiLCBcIkNyZWF0dXJlc1wiLCBcIkRpbm9zYXVyc1wiLCBcIkRvZ3NcIiwgXCJEb2xwaGluc1wiLFxuICAgIFwiRG9ua2V5c1wiLCBcIkRyYWdvbnNcIiwgXCJEdWNrc1wiLCBcIkR3YXJmc1wiLCBcIkVhZ2xlc1wiLCBcIkVsZXBoYW50c1wiLCBcIkVsdmVzXCIsIFwiRkFJTFwiLCBcIkZhdGhlcnNcIixcbiAgICBcIkZpc2hcIiwgXCJGbG93ZXJzXCIsIFwiRnJvZ3NcIiwgXCJGcnVpdFwiLCBcIkZ1bmdpXCIsIFwiR2FsYXhpZXNcIiwgXCJHZWVzZVwiLCBcIkdvYXRzXCIsXG4gICAgXCJHb3JpbGxhc1wiLCBcIkhlZGdlaG9nc1wiLCBcIkhpcHBvc1wiLCBcIkhvcnNlc1wiLCBcIkh1bnRlcnNcIiwgXCJJbnNlY3RzXCIsIFwiS2lkc1wiLCBcIktuaWdodHNcIixcbiAgICBcIkxlbW9uc1wiLCBcIkxlbXVyc1wiLCBcIkxlb3BhcmRzXCIsIFwiTGlmZUZvcm1zXCIsIFwiTGlvbnNcIiwgXCJMaXphcmRzXCIsIFwiTWljZVwiLCBcIk1vbmtleXNcIiwgXCJNb25zdGVyc1wiLFxuICAgIFwiTXVzaHJvb21zXCIsIFwiT2N0b3BvZGVzXCIsIFwiT3Jhbmdlc1wiLCBcIk9yYW5ndXRhbnNcIiwgXCJPcmdhbmlzbXNcIiwgXCJQYW50c1wiLCBcIlBhcnJvdHNcIiwgXCJQZW5ndWluc1wiLFxuICAgIFwiUGVvcGxlXCIsIFwiUGlnZW9uc1wiLCBcIlBpZ3NcIiwgXCJQaW5lYXBwbGVzXCIsIFwiUGxhbnRzXCIsIFwiUG90YXRvZXNcIiwgXCJQcmllc3RzXCIsIFwiUmF0c1wiLCBcIlJlcHRpbGVzXCIsIFwiUmVwdGlsaWFuc1wiLFxuICAgIFwiUmhpbm9zXCIsIFwiU2VhZ3VsbHNcIiwgXCJTaGVlcFwiLCBcIlNpYmxpbmdzXCIsIFwiU25ha2VzXCIsIFwiU3BhZ2hldHRpXCIsIFwiU3BpZGVyc1wiLCBcIlNxdWlkXCIsIFwiU3F1aXJyZWxzXCIsXG4gICAgXCJTdGFyc1wiLCBcIlN0dWRlbnRzXCIsIFwiVGVhY2hlcnNcIiwgXCJUaWdlcnNcIiwgXCJUb21hdG9lc1wiLCBcIlRyZWVzXCIsIFwiVmFtcGlyZXNcIiwgXCJWZWdldGFibGVzXCIsIFwiVmlydXNlc1wiLCBcIlZ1bGNhbnNcIixcbiAgICBcIldhcmV3b2x2ZXNcIiwgXCJXZWFzZWxzXCIsIFwiV2hhbGVzXCIsIFwiV2l0Y2hlc1wiLCBcIldpemFyZHNcIiwgXCJXb2x2ZXNcIiwgXCJXb3JrZXJzXCIsIFwiV29ybXNcIiwgXCJaZWJyYXNcIlxuXTtcbi8vdmFyIHBsYWNlcyA9IFtcbi8vXCJQdWJcIiwgXCJVbml2ZXJzaXR5XCIsIFwiQWlycG9ydFwiLCBcIkxpYnJhcnlcIiwgXCJNYWxsXCIsIFwiVGhlYXRlclwiLCBcIlN0YWRpdW1cIiwgXCJPZmZpY2VcIiwgXCJTaG93XCIsIFwiR2FsbG93c1wiLCBcIkJlYWNoXCIsXG4vLyBcIkNlbWV0ZXJ5XCIsIFwiSG9zcGl0YWxcIiwgXCJSZWNlcHRpb25cIiwgXCJSZXN0YXVyYW50XCIsIFwiQmFyXCIsIFwiQ2h1cmNoXCIsIFwiSG91c2VcIiwgXCJTY2hvb2xcIiwgXCJTcXVhcmVcIiwgXCJWaWxsYWdlXCIsXG4vLyBcIkNpbmVtYVwiLCBcIk1vdmllc1wiLCBcIlBhcnR5XCIsIFwiUmVzdHJvb21cIiwgXCJFbmRcIiwgXCJKYWlsXCIsIFwiUG9zdE9mZmljZVwiLCBcIlN0YXRpb25cIiwgXCJDaXJjdXNcIiwgXCJHYXRlc1wiLCBcIkVudHJhbmNlXCIsXG4vLyBcIkJyaWRnZVwiXG4vL107XG52YXIgdmVyYnMgPSBbXG4gICAgXCJBYmFuZG9uXCIsIFwiQWRhcHRcIiwgXCJBZHZlcnRpc2VcIiwgXCJBbnN3ZXJcIiwgXCJBbnRpY2lwYXRlXCIsIFwiQXBwcmVjaWF0ZVwiLFxuICAgIFwiQXBwcm9hY2hcIiwgXCJBcmd1ZVwiLCBcIkFza1wiLCBcIkJpdGVcIiwgXCJCbG9zc29tXCIsIFwiQmx1c2hcIiwgXCJCcmVhdGhlXCIsIFwiQnJlZWRcIiwgXCJCcmliZVwiLCBcIkJ1cm5cIiwgXCJDYWxjdWxhdGVcIixcbiAgICBcIkNsZWFuXCIsIFwiQ29kZVwiLCBcIkNvbW11bmljYXRlXCIsIFwiQ29tcHV0ZVwiLCBcIkNvbmZlc3NcIiwgXCJDb25maXNjYXRlXCIsIFwiQ29uanVnYXRlXCIsIFwiQ29uanVyZVwiLCBcIkNvbnN1bWVcIixcbiAgICBcIkNvbnRlbXBsYXRlXCIsIFwiQ3Jhd2xcIiwgXCJEYW5jZVwiLCBcIkRlbGVnYXRlXCIsIFwiRGV2b3VyXCIsIFwiRGV2ZWxvcFwiLCBcIkRpZmZlclwiLCBcIkRpc2N1c3NcIixcbiAgICBcIkRpc3NvbHZlXCIsIFwiRHJpbmtcIiwgXCJFYXRcIiwgXCJFbGFib3JhdGVcIiwgXCJFbWFuY2lwYXRlXCIsIFwiRXN0aW1hdGVcIiwgXCJFeHBpcmVcIiwgXCJFeHRpbmd1aXNoXCIsXG4gICAgXCJFeHRyYWN0XCIsIFwiRkFJTFwiLCBcIkZhY2lsaXRhdGVcIiwgXCJGYWxsXCIsIFwiRmVlZFwiLCBcIkZpbmlzaFwiLCBcIkZsb3NzXCIsIFwiRmx5XCIsIFwiRm9sbG93XCIsIFwiRnJhZ21lbnRcIiwgXCJGcmVlemVcIixcbiAgICBcIkdhdGhlclwiLCBcIkdsb3dcIiwgXCJHcm93XCIsIFwiSGV4XCIsIFwiSGlkZVwiLCBcIkh1Z1wiLCBcIkh1cnJ5XCIsIFwiSW1wcm92ZVwiLCBcIkludGVyc2VjdFwiLCBcIkludmVzdGlnYXRlXCIsIFwiSmlueFwiLFxuICAgIFwiSm9rZVwiLCBcIkp1YmlsYXRlXCIsIFwiS2lzc1wiLCBcIkxhdWdoXCIsIFwiTWFuYWdlXCIsIFwiTWVldFwiLCBcIk1lcmdlXCIsIFwiTW92ZVwiLCBcIk9iamVjdFwiLCBcIk9ic2VydmVcIiwgXCJPZmZlclwiLFxuICAgIFwiUGFpbnRcIiwgXCJQYXJ0aWNpcGF0ZVwiLCBcIlBhcnR5XCIsIFwiUGVyZm9ybVwiLCBcIlBsYW5cIiwgXCJQdXJzdWVcIiwgXCJQaWVyY2VcIiwgXCJQbGF5XCIsIFwiUG9zdHBvbmVcIiwgXCJQcmF5XCIsIFwiUHJvY2xhaW1cIixcbiAgICBcIlF1ZXN0aW9uXCIsIFwiUmVhZFwiLCBcIlJlY2tvblwiLCBcIlJlam9pY2VcIiwgXCJSZXByZXNlbnRcIiwgXCJSZXNpemVcIiwgXCJSaHltZVwiLCBcIlNjcmVhbVwiLCBcIlNlYXJjaFwiLCBcIlNlbGVjdFwiLCBcIlNoYXJlXCIsIFwiU2hvb3RcIixcbiAgICBcIlNob3V0XCIsIFwiU2lnbmFsXCIsIFwiU2luZ1wiLCBcIlNrYXRlXCIsIFwiU2xlZXBcIiwgXCJTbWlsZVwiLCBcIlNtb2tlXCIsIFwiU29sdmVcIiwgXCJTcGVsbFwiLCBcIlN0ZWVyXCIsIFwiU3RpbmtcIixcbiAgICBcIlN1YnN0aXR1dGVcIiwgXCJTd2ltXCIsIFwiVGFzdGVcIiwgXCJUZWFjaFwiLCBcIlRlcm1pbmF0ZVwiLCBcIlRoaW5rXCIsIFwiVHlwZVwiLCBcIlVuaXRlXCIsIFwiVmFuaXNoXCIsIFwiV29yc2hpcFwiXG5dO1xudmFyIGFkdmVyYnMgPSBbXG4gICAgXCJBYnNlbnRseVwiLCBcIkFjY3VyYXRlbHlcIiwgXCJBY2N1c2luZ2x5XCIsIFwiQWRvcmFibHlcIiwgXCJBbGxUaGVUaW1lXCIsIFwiQWxvbmVcIiwgXCJBbHdheXNcIiwgXCJBbWF6aW5nbHlcIiwgXCJBbmdyaWx5XCIsXG4gICAgXCJBbnhpb3VzbHlcIiwgXCJBbnl3aGVyZVwiLCBcIkFwcGFsbGluZ2x5XCIsIFwiQXBwYXJlbnRseVwiLCBcIkFydGljdWxhdGVseVwiLCBcIkFzdG9uaXNoaW5nbHlcIiwgXCJCYWRseVwiLCBcIkJhcmVseVwiLFxuICAgIFwiQmVhdXRpZnVsbHlcIiwgXCJCbGluZGx5XCIsIFwiQnJhdmVseVwiLCBcIkJyaWdodGx5XCIsIFwiQnJpc2tseVwiLCBcIkJydXRhbGx5XCIsIFwiQ2FsbWx5XCIsIFwiQ2FyZWZ1bGx5XCIsIFwiQ2FzdWFsbHlcIixcbiAgICBcIkNhdXRpb3VzbHlcIiwgXCJDbGV2ZXJseVwiLCBcIkNvbnN0YW50bHlcIiwgXCJDb3JyZWN0bHlcIiwgXCJDcmF6aWx5XCIsIFwiQ3VyaW91c2x5XCIsIFwiQ3luaWNhbGx5XCIsIFwiRGFpbHlcIixcbiAgICBcIkRhbmdlcm91c2x5XCIsIFwiRGVsaWJlcmF0ZWx5XCIsIFwiRGVsaWNhdGVseVwiLCBcIkRlc3BlcmF0ZWx5XCIsIFwiRGlzY3JlZXRseVwiLCBcIkVhZ2VybHlcIiwgXCJFYXNpbHlcIiwgXCJFdXBob3JpY2x5XCIsXG4gICAgXCJFdmVubHlcIiwgXCJFdmVyeXdoZXJlXCIsIFwiRXhhY3RseVwiLCBcIkV4cGVjdGFudGx5XCIsIFwiRXh0ZW5zaXZlbHlcIiwgXCJGQUlMXCIsIFwiRmVyb2Npb3VzbHlcIiwgXCJGaWVyY2VseVwiLCBcIkZpbmVseVwiLFxuICAgIFwiRmxhdGx5XCIsIFwiRnJlcXVlbnRseVwiLCBcIkZyaWdodGVuaW5nbHlcIiwgXCJHZW50bHlcIiwgXCJHbG9yaW91c2x5XCIsIFwiR3JpbWx5XCIsIFwiR3VpbHRpbHlcIiwgXCJIYXBwaWx5XCIsXG4gICAgXCJIYXJkXCIsIFwiSGFzdGlseVwiLCBcIkhlcm9pY2FsbHlcIiwgXCJIaWdoXCIsIFwiSGlnaGx5XCIsIFwiSG91cmx5XCIsIFwiSHVtYmx5XCIsIFwiSHlzdGVyaWNhbGx5XCIsIFwiSW1tZW5zZWx5XCIsXG4gICAgXCJJbXBhcnRpYWxseVwiLCBcIkltcG9saXRlbHlcIiwgXCJJbmRpZmZlcmVudGx5XCIsIFwiSW50ZW5zZWx5XCIsIFwiSmVhbG91c2x5XCIsIFwiSm92aWFsbHlcIiwgXCJLaW5kbHlcIiwgXCJMYXppbHlcIixcbiAgICBcIkxpZ2h0bHlcIiwgXCJMb3VkbHlcIiwgXCJMb3ZpbmdseVwiLCBcIkxveWFsbHlcIiwgXCJNYWduaWZpY2VudGx5XCIsIFwiTWFsZXZvbGVudGx5XCIsIFwiTWVycmlseVwiLCBcIk1pZ2h0aWx5XCIsIFwiTWlzZXJhYmx5XCIsXG4gICAgXCJNeXN0ZXJpb3VzbHlcIiwgXCJOT1RcIiwgXCJOZXJ2b3VzbHlcIiwgXCJOaWNlbHlcIiwgXCJOb3doZXJlXCIsIFwiT2JqZWN0aXZlbHlcIiwgXCJPYm5veGlvdXNseVwiLCBcIk9ic2Vzc2l2ZWx5XCIsXG4gICAgXCJPYnZpb3VzbHlcIiwgXCJPZnRlblwiLCBcIlBhaW5mdWxseVwiLCBcIlBhdGllbnRseVwiLCBcIlBsYXlmdWxseVwiLCBcIlBvbGl0ZWx5XCIsIFwiUG9vcmx5XCIsIFwiUHJlY2lzZWx5XCIsIFwiUHJvbXB0bHlcIixcbiAgICBcIlF1aWNrbHlcIiwgXCJRdWlldGx5XCIsIFwiUmFuZG9tbHlcIiwgXCJSYXBpZGx5XCIsIFwiUmFyZWx5XCIsIFwiUmVja2xlc3NseVwiLCBcIlJlZ3VsYXJseVwiLCBcIlJlbW9yc2VmdWxseVwiLCBcIlJlc3BvbnNpYmx5XCIsXG4gICAgXCJSdWRlbHlcIiwgXCJSdXRobGVzc2x5XCIsIFwiU2FkbHlcIiwgXCJTY29ybmZ1bGx5XCIsIFwiU2VhbWxlc3NseVwiLCBcIlNlbGRvbVwiLCBcIlNlbGZpc2hseVwiLCBcIlNlcmlvdXNseVwiLCBcIlNoYWtpbHlcIixcbiAgICBcIlNoYXJwbHlcIiwgXCJTaWRld2F5c1wiLCBcIlNpbGVudGx5XCIsIFwiU2xlZXBpbHlcIiwgXCJTbGlnaHRseVwiLCBcIlNsb3dseVwiLCBcIlNseWx5XCIsIFwiU21vb3RobHlcIiwgXCJTb2Z0bHlcIiwgXCJTb2xlbW5seVwiLCBcIlN0ZWFkaWx5XCIsIFwiU3Rlcm5seVwiLCBcIlN0cmFuZ2VseVwiLCBcIlN0cm9uZ2x5XCIsIFwiU3R1bm5pbmdseVwiLCBcIlN1cmVseVwiLCBcIlRlbmRlcmx5XCIsIFwiVGhvdWdodGZ1bGx5XCIsXG4gICAgXCJUaWdodGx5XCIsIFwiVW5lYXNpbHlcIiwgXCJWYW5pc2hpbmdseVwiLCBcIlZpb2xlbnRseVwiLCBcIldhcm1seVwiLCBcIldlYWtseVwiLCBcIldlYXJpbHlcIiwgXCJXZWVrbHlcIiwgXCJXZWlyZGx5XCIsIFwiV2VsbFwiLFxuICAgIFwiV2VsbFwiLCBcIldpY2tlZGx5XCIsIFwiV2lsZGx5XCIsIFwiV2lzZWx5XCIsIFwiV29uZGVyZnVsbHlcIiwgXCJZZWFybHlcIlxuXTtcbnZhciBhZGplY3RpdmVzID0gW1xuICAgIFwiQWJvbWluYWJsZVwiLCBcIkFjY3VyYXRlXCIsIFwiQWRvcmFibGVcIiwgXCJBbGxcIiwgXCJBbGxlZ2VkXCIsIFwiQW5jaWVudFwiLCBcIkFuZ3J5XCIsIFwiQW5ncnlcIiwgXCJBbnhpb3VzXCIsIFwiQXBwYWxsaW5nXCIsXG4gICAgXCJBcHBhcmVudFwiLCBcIkFzdG9uaXNoaW5nXCIsIFwiQXR0cmFjdGl2ZVwiLCBcIkF3ZXNvbWVcIiwgXCJCYWJ5XCIsIFwiQmFkXCIsIFwiQmVhdXRpZnVsXCIsIFwiQmVuaWduXCIsIFwiQmlnXCIsIFwiQml0dGVyXCIsXG4gICAgXCJCbGluZFwiLCBcIkJsdWVcIiwgXCJCb2xkXCIsIFwiQnJhdmVcIiwgXCJCcmlnaHRcIiwgXCJCcmlza1wiLCBcIkNhbG1cIiwgXCJDYW1vdWZsYWdlZFwiLCBcIkNhc3VhbFwiLCBcIkNhdXRpb3VzXCIsXG4gICAgXCJDaG9wcHlcIiwgXCJDaG9zZW5cIiwgXCJDbGV2ZXJcIiwgXCJDb2xkXCIsIFwiQ29vbFwiLCBcIkNyYXdseVwiLCBcIkNyYXp5XCIsIFwiQ3JlZXB5XCIsIFwiQ3J1ZWxcIiwgXCJDdXJpb3VzXCIsIFwiQ3luaWNhbFwiLFxuICAgIFwiRGFuZ2Vyb3VzXCIsIFwiRGFya1wiLCBcIkRlbGljYXRlXCIsIFwiRGVzcGVyYXRlXCIsIFwiRGlmZmljdWx0XCIsIFwiRGlzY3JlZXRcIiwgXCJEaXNndWlzZWRcIiwgXCJEaXp6eVwiLFxuICAgIFwiRHVtYlwiLCBcIkVhZ2VyXCIsIFwiRWFzeVwiLCBcIkVkZ3lcIiwgXCJFbGVjdHJpY1wiLCBcIkVsZWdhbnRcIiwgXCJFbWFuY2lwYXRlZFwiLCBcIkVub3Jtb3VzXCIsIFwiRXVwaG9yaWNcIiwgXCJFdmlsXCIsXG4gICAgXCJGQUlMXCIsIFwiRmFzdFwiLCBcIkZlcm9jaW91c1wiLCBcIkZpZXJjZVwiLCBcIkZpbmVcIiwgXCJGbGF3ZWRcIiwgXCJGbHlpbmdcIiwgXCJGb29saXNoXCIsIFwiRm94eVwiLFxuICAgIFwiRnJlZXppbmdcIiwgXCJGdW5ueVwiLCBcIkZ1cmlvdXNcIiwgXCJHZW50bGVcIiwgXCJHbG9yaW91c1wiLCBcIkdvbGRlblwiLCBcIkdvb2RcIiwgXCJHcmVlblwiLCBcIkdyZWVuXCIsIFwiR3VpbHR5XCIsXG4gICAgXCJIYWlyeVwiLCBcIkhhcHB5XCIsIFwiSGFyZFwiLCBcIkhhc3R5XCIsIFwiSGF6eVwiLCBcIkhlcm9pY1wiLCBcIkhvc3RpbGVcIiwgXCJIb3RcIiwgXCJIdW1ibGVcIiwgXCJIdW1vbmdvdXNcIixcbiAgICBcIkh1bW9yb3VzXCIsIFwiSHlzdGVyaWNhbFwiLCBcIklkZWFsaXN0aWNcIiwgXCJJZ25vcmFudFwiLCBcIkltbWVuc2VcIiwgXCJJbXBhcnRpYWxcIiwgXCJJbXBvbGl0ZVwiLCBcIkluZGlmZmVyZW50XCIsXG4gICAgXCJJbmZ1cmlhdGVkXCIsIFwiSW5zaWdodGZ1bFwiLCBcIkludGVuc2VcIiwgXCJJbnRlcmVzdGluZ1wiLCBcIkludGltaWRhdGVkXCIsIFwiSW50cmlndWluZ1wiLCBcIkplYWxvdXNcIiwgXCJKb2xseVwiLCBcIkpvdmlhbFwiLFxuICAgIFwiSnVtcHlcIiwgXCJLaW5kXCIsIFwiTGF1Z2hpbmdcIiwgXCJMYXp5XCIsIFwiTGlxdWlkXCIsIFwiTG9uZWx5XCIsIFwiTG9uZ2luZ1wiLCBcIkxvdWRcIiwgXCJMb3ZpbmdcIiwgXCJMb3lhbFwiLCBcIk1hY2FicmVcIiwgXCJNYWRcIixcbiAgICBcIk1hZ2ljYWxcIiwgXCJNYWduaWZpY2VudFwiLCBcIk1hbGV2b2xlbnRcIiwgXCJNZWRpZXZhbFwiLCBcIk1lbW9yYWJsZVwiLCBcIk1lcmVcIiwgXCJNZXJyeVwiLCBcIk1pZ2h0eVwiLFxuICAgIFwiTWlzY2hpZXZvdXNcIiwgXCJNaXNlcmFibGVcIiwgXCJNb2RpZmllZFwiLCBcIk1vb2R5XCIsIFwiTW9zdFwiLCBcIk15c3RlcmlvdXNcIiwgXCJNeXN0aWNhbFwiLCBcIk5lZWR5XCIsXG4gICAgXCJOZXJ2b3VzXCIsIFwiTmljZVwiLCBcIk9iamVjdGl2ZVwiLCBcIk9ibm94aW91c1wiLCBcIk9ic2Vzc2l2ZVwiLCBcIk9idmlvdXNcIiwgXCJPcGluaW9uYXRlZFwiLCBcIk9yYW5nZVwiLFxuICAgIFwiUGFpbmZ1bFwiLCBcIlBhc3Npb25hdGVcIiwgXCJQZXJmZWN0XCIsIFwiUGlua1wiLCBcIlBsYXlmdWxcIiwgXCJQb2lzb25vdXNcIiwgXCJQb2xpdGVcIiwgXCJQb29yXCIsIFwiUG9wdWxhclwiLCBcIlBvd2VyZnVsXCIsXG4gICAgXCJQcmVjaXNlXCIsIFwiUHJlc2VydmVkXCIsIFwiUHJldHR5XCIsIFwiUHVycGxlXCIsIFwiUXVpY2tcIiwgXCJRdWlldFwiLCBcIlJhbmRvbVwiLCBcIlJhcGlkXCIsIFwiUmFyZVwiLCBcIlJlYWxcIixcbiAgICBcIlJlYXNzdXJpbmdcIiwgXCJSZWNrbGVzc1wiLCBcIlJlZFwiLCBcIlJlZ3VsYXJcIiwgXCJSZW1vcnNlZnVsXCIsIFwiUmVzcG9uc2libGVcIiwgXCJSaWNoXCIsIFwiUnVkZVwiLCBcIlJ1dGhsZXNzXCIsXG4gICAgXCJTYWRcIiwgXCJTY2FyZWRcIiwgXCJTY2FyeVwiLCBcIlNjb3JuZnVsXCIsIFwiU2NyZWFtaW5nXCIsIFwiU2VsZmlzaFwiLCBcIlNlcmlvdXNcIiwgXCJTaGFkeVwiLCBcIlNoYWt5XCIsIFwiU2hhcnBcIixcbiAgICBcIlNoaW55XCIsIFwiU2h5XCIsIFwiU2ltcGxlXCIsIFwiU2xlZXB5XCIsIFwiU2xvd1wiLCBcIlNseVwiLCBcIlNtYWxsXCIsIFwiU21hcnRcIiwgXCJTbWVsbHlcIiwgXCJTbWlsaW5nXCIsIFwiU21vb3RoXCIsXG4gICAgXCJTbXVnXCIsIFwiU29iZXJcIiwgXCJTb2Z0XCIsIFwiU29sZW1uXCIsIFwiU3F1YXJlXCIsIFwiU3F1YXJlXCIsIFwiU3RlYWR5XCIsIFwiU3RyYW5nZVwiLCBcIlN0cm9uZ1wiLFxuICAgIFwiU3R1bm5pbmdcIiwgXCJTdWJqZWN0aXZlXCIsIFwiU3VjY2Vzc2Z1bFwiLCBcIlN1cmx5XCIsIFwiU3dlZXRcIiwgXCJUYWN0ZnVsXCIsIFwiVGVuc2VcIixcbiAgICBcIlRob3VnaHRmdWxcIiwgXCJUaWdodFwiLCBcIlRpbnlcIiwgXCJUb2xlcmFudFwiLCBcIlVuZWFzeVwiLCBcIlVuaXF1ZVwiLCBcIlVuc2VlblwiLCBcIldhcm1cIiwgXCJXZWFrXCIsXG4gICAgXCJXZWlyZFwiLCBcIldlbGxDb29rZWRcIiwgXCJXaWxkXCIsIFwiV2lzZVwiLCBcIldpdHR5XCIsIFwiV29uZGVyZnVsXCIsIFwiV29ycmllZFwiLCBcIlllbGxvd1wiLCBcIllvdW5nXCIsXG4gICAgXCJaZWFsb3VzXCJcbiAgICBdO1xuLy92YXIgcHJvbm91bnMgPSBbXG4vL107XG4vL3ZhciBjb25qdW5jdGlvbnMgPSBbXG4vL1wiQW5kXCIsIFwiT3JcIiwgXCJGb3JcIiwgXCJBYm92ZVwiLCBcIkJlZm9yZVwiLCBcIkFnYWluc3RcIiwgXCJCZXR3ZWVuXCJcbi8vXTtcblxuLypcbiAqIE1hcHMgYSBzdHJpbmcgKGNhdGVnb3J5IG5hbWUpIHRvIHRoZSBhcnJheSBvZiB3b3JkcyBmcm9tIHRoYXQgY2F0ZWdvcnkuXG4gKi9cbnZhciBDQVRFR09SSUVTID1cbntcbiAgICAvL1wiX05PVU5fXCI6IG5vdW5zLFxuICAgIFwiX1BMVVJBTE5PVU5fXCI6IHBsdXJhbE5vdW5zLFxuICAgIC8vXCJfUExBQ0VfXCI6IHBsYWNlcyxcbiAgICBcIl9WRVJCX1wiOiB2ZXJicyxcbiAgICBcIl9BRFZFUkJfXCI6IGFkdmVyYnMsXG4gICAgXCJfQURKRUNUSVZFX1wiOiBhZGplY3RpdmVzXG4gICAgLy9cIl9QUk9OT1VOX1wiOiBwcm9ub3VucyxcbiAgICAvL1wiX0NPTkpVTkNUSU9OX1wiOiBjb25qdW5jdGlvbnMsXG59O1xuXG52YXIgUEFUVEVSTlMgPSBbXG4gICAgXCJfQURKRUNUSVZFX19QTFVSQUxOT1VOX19WRVJCX19BRFZFUkJfXCJcblxuICAgIC8vIEJlYXV0aWZ1bEZ1bmdpT3JTcGFnaGV0dGlcbiAgICAvL1wiX0FESkVDVElWRV9fUExVUkFMTk9VTl9fQ09OSlVOQ1RJT05fX1BMVVJBTE5PVU5fXCIsXG5cbiAgICAvLyBBbWF6aW5nbHlTY2FyeVRveVxuICAgIC8vXCJfQURWRVJCX19BREpFQ1RJVkVfX05PVU5fXCIsXG5cbiAgICAvLyBOZWl0aGVyVHJhc2hOb3JSaWZsZVxuICAgIC8vXCJOZWl0aGVyX05PVU5fTm9yX05PVU5fXCIsXG4gICAgLy9cIkVpdGhlcl9OT1VOX09yX05PVU5fXCIsXG5cbiAgICAvLyBFaXRoZXJDb3B1bGF0ZU9ySW52ZXN0aWdhdGVcbiAgICAvL1wiRWl0aGVyX1ZFUkJfT3JfVkVSQl9cIixcbiAgICAvL1wiTmVpdGhlcl9WRVJCX05vcl9WRVJCX1wiLFxuXG4gICAgLy9cIlRoZV9BREpFQ1RJVkVfX0FESkVDVElWRV9fTk9VTl9cIixcbiAgICAvL1wiVGhlX0FEVkVSQl9fQURKRUNUSVZFX19OT1VOX1wiLFxuICAgIC8vXCJUaGVfQURWRVJCX19BREpFQ1RJVkVfX05PVU5fc1wiLFxuICAgIC8vXCJUaGVfQURWRVJCX19BREpFQ1RJVkVfX1BMVVJBTE5PVU5fX1ZFUkJfXCIsXG5cbiAgICAvLyBXb2x2ZXNDb21wdXRlQmFkbHlcbiAgICAvL1wiX1BMVVJBTE5PVU5fX1ZFUkJfX0FEVkVSQl9cIixcblxuICAgIC8vIFVuaXRlRmFjaWxpdGF0ZUFuZE1lcmdlXG4gICAgLy9cIl9WRVJCX19WRVJCX0FuZF9WRVJCX1wiLFxuXG4gICAgLy9OYXN0eVdpdGNoZXNBdFRoZVB1YlxuICAgIC8vXCJfQURKRUNUSVZFX19QTFVSQUxOT1VOX0F0VGhlX1BMQUNFX1wiLFxuXTtcblxuXG4vKlxuICogUmV0dXJucyBhIHJhbmRvbSBlbGVtZW50IGZyb20gdGhlIGFycmF5ICdhcnInXG4gKi9cbmZ1bmN0aW9uIHJhbmRvbUVsZW1lbnQoYXJyKVxue1xuICAgIHJldHVybiBhcnJbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYXJyLmxlbmd0aCldO1xufVxuXG4vKlxuICogUmV0dXJucyB0cnVlIGlmIHRoZSBzdHJpbmcgJ3MnIGNvbnRhaW5zIG9uZSBvZiB0aGVcbiAqIHRlbXBsYXRlIHN0cmluZ3MuXG4gKi9cbmZ1bmN0aW9uIGhhc1RlbXBsYXRlKHMpXG57XG4gICAgZm9yICh2YXIgdGVtcGxhdGUgaW4gQ0FURUdPUklFUyl7XG4gICAgICAgIGlmIChzLmluZGV4T2YodGVtcGxhdGUpID49IDApe1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogR2VuZXJhdGVzIG5ldyByb29tIG5hbWUuXG4gKi9cbnZhciBSb29tTmFtZUdlbmVyYXRvciA9IHtcbiAgICBnZW5lcmF0ZVJvb21XaXRob3V0U2VwYXJhdG9yOiBmdW5jdGlvbigpXG4gICAge1xuICAgICAgICAvLyBOb3RlIHRoYXQgaWYgbW9yZSB0aGFuIG9uZSBwYXR0ZXJuIGlzIGF2YWlsYWJsZSwgdGhlIGNob2ljZSBvZiAnbmFtZScgd29uJ3QgYmUgcmFuZG9tIChuYW1lcyBmcm9tIHBhdHRlcm5zXG4gICAgICAgIC8vIHdpdGggZmV3ZXIgb3B0aW9ucyB3aWxsIGhhdmUgaGlnaGVyIHByb2JhYmlsaXR5IG9mIGJlaW5nIGNob3NlbiB0aGF0IG5hbWVzIGZyb20gcGF0dGVybnMgd2l0aCBtb3JlIG9wdGlvbnMpLlxuICAgICAgICB2YXIgbmFtZSA9IHJhbmRvbUVsZW1lbnQoUEFUVEVSTlMpO1xuICAgICAgICB2YXIgd29yZDtcbiAgICAgICAgd2hpbGUgKGhhc1RlbXBsYXRlKG5hbWUpKXtcbiAgICAgICAgICAgIGZvciAodmFyIHRlbXBsYXRlIGluIENBVEVHT1JJRVMpe1xuICAgICAgICAgICAgICAgIHdvcmQgPSByYW5kb21FbGVtZW50KENBVEVHT1JJRVNbdGVtcGxhdGVdKTtcbiAgICAgICAgICAgICAgICBuYW1lID0gbmFtZS5yZXBsYWNlKHRlbXBsYXRlLCB3b3JkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBuYW1lO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBSb29tTmFtZUdlbmVyYXRvcjtcbiIsInZhciBhbmltYXRlVGltZW91dCwgdXBkYXRlVGltZW91dDtcblxudmFyIFJvb21OYW1lR2VuZXJhdG9yID0gcmVxdWlyZShcIi4vUm9vbW5hbWVHZW5lcmF0b3JcIik7XG5cbmZ1bmN0aW9uIGVudGVyX3Jvb20oKVxue1xuICAgIHZhciB2YWwgPSAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikudmFsKCk7XG4gICAgaWYoIXZhbCkge1xuICAgICAgICB2YWwgPSAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikuYXR0cihcInJvb21fbmFtZVwiKTtcbiAgICB9XG4gICAgaWYgKHZhbCkge1xuICAgICAgICB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWUgPSBcIi9cIiArIHZhbDtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGFuaW1hdGUod29yZCkge1xuICAgIHZhciBjdXJyZW50VmFsID0gJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLmF0dHIoXCJwbGFjZWhvbGRlclwiKTtcbiAgICAkKFwiI2VudGVyX3Jvb21fZmllbGRcIikuYXR0cihcInBsYWNlaG9sZGVyXCIsIGN1cnJlbnRWYWwgKyB3b3JkLnN1YnN0cigwLCAxKSk7XG4gICAgYW5pbWF0ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICBhbmltYXRlKHdvcmQuc3Vic3RyaW5nKDEsIHdvcmQubGVuZ3RoKSlcbiAgICB9LCA3MCk7XG59XG5cbmZ1bmN0aW9uIHVwZGF0ZV9yb29tbmFtZSgpXG57XG4gICAgdmFyIHdvcmQgPSBSb29tTmFtZUdlbmVyYXRvci5nZW5lcmF0ZVJvb21XaXRob3V0U2VwYXJhdG9yKCk7XG4gICAgJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLmF0dHIoXCJyb29tX25hbWVcIiwgd29yZCk7XG4gICAgJChcIiNlbnRlcl9yb29tX2ZpZWxkXCIpLmF0dHIoXCJwbGFjZWhvbGRlclwiLCBcIlwiKTtcbiAgICBjbGVhclRpbWVvdXQoYW5pbWF0ZVRpbWVvdXQpO1xuICAgIGFuaW1hdGUod29yZCk7XG4gICAgdXBkYXRlVGltZW91dCA9IHNldFRpbWVvdXQodXBkYXRlX3Jvb21uYW1lLCAxMDAwMCk7XG59XG5cblxuZnVuY3Rpb24gc2V0dXBXZWxjb21lUGFnZSgpXG57XG4gICAgJChcIiN2aWRlb2NvbmZlcmVuY2VfcGFnZVwiKS5oaWRlKCk7XG4gICAgJChcIiNkb21haW5fbmFtZVwiKS50ZXh0KFxuICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLnByb3RvY29sICsgXCIvL1wiICsgd2luZG93LmxvY2F0aW9uLmhvc3QgKyBcIi9cIik7XG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0pJVFNJX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgbGVmdFdhdGVybWFya0RpdlxuICAgICAgICAgICAgPSAkKFwiI3dlbGNvbWVfcGFnZV9oZWFkZXIgZGl2W2NsYXNzPSd3YXRlcm1hcmsgbGVmdHdhdGVybWFyayddXCIpO1xuICAgICAgICBpZihsZWZ0V2F0ZXJtYXJrRGl2ICYmIGxlZnRXYXRlcm1hcmtEaXYubGVuZ3RoID4gMClcbiAgICAgICAge1xuICAgICAgICAgICAgbGVmdFdhdGVybWFya0Rpdi5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICAgICAgICAgIGxlZnRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5KSVRTSV9XQVRFUk1BUktfTElOSztcbiAgICAgICAgfVxuXG4gICAgfVxuXG4gICAgaWYgKGludGVyZmFjZUNvbmZpZy5TSE9XX0JSQU5EX1dBVEVSTUFSSykge1xuICAgICAgICB2YXIgcmlnaHRXYXRlcm1hcmtEaXZcbiAgICAgICAgICAgID0gJChcIiN3ZWxjb21lX3BhZ2VfaGVhZGVyIGRpdltjbGFzcz0nd2F0ZXJtYXJrIHJpZ2h0d2F0ZXJtYXJrJ11cIik7XG4gICAgICAgIGlmKHJpZ2h0V2F0ZXJtYXJrRGl2ICYmIHJpZ2h0V2F0ZXJtYXJrRGl2Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmNzcyh7ZGlzcGxheTogJ2Jsb2NrJ30pO1xuICAgICAgICAgICAgcmlnaHRXYXRlcm1hcmtEaXYucGFyZW50KCkuZ2V0KDApLmhyZWZcbiAgICAgICAgICAgICAgICA9IGludGVyZmFjZUNvbmZpZy5CUkFORF9XQVRFUk1BUktfTElOSztcbiAgICAgICAgICAgIHJpZ2h0V2F0ZXJtYXJrRGl2LmdldCgwKS5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2VcbiAgICAgICAgICAgICAgICA9IFwidXJsKGltYWdlcy9yaWdodHdhdGVybWFyay5wbmcpXCI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaW50ZXJmYWNlQ29uZmlnLlNIT1dfUE9XRVJFRF9CWSkge1xuICAgICAgICAkKFwiI3dlbGNvbWVfcGFnZV9oZWFkZXI+YVtjbGFzcz0ncG93ZXJlZGJ5J11cIilcbiAgICAgICAgICAgIC5jc3Moe2Rpc3BsYXk6ICdibG9jayd9KTtcbiAgICB9XG5cbiAgICAkKFwiI2VudGVyX3Jvb21fYnV0dG9uXCIpLmNsaWNrKGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIGVudGVyX3Jvb20oKTtcbiAgICB9KTtcblxuICAgICQoXCIjZW50ZXJfcm9vbV9maWVsZFwiKS5rZXlkb3duKGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBpZiAoZXZlbnQua2V5Q29kZSA9PT0gMTMgLyogZW50ZXIgKi8pIHtcbiAgICAgICAgICAgIGVudGVyX3Jvb20oKTtcbiAgICAgICAgfVxuICAgIH0pO1xuXG4gICAgaWYgKCEoaW50ZXJmYWNlQ29uZmlnLkdFTkVSQVRFX1JPT01OQU1FU19PTl9XRUxDT01FX1BBR0UgPT09IGZhbHNlKSl7XG4gICAgICAgIHZhciB1cGRhdGVUaW1lb3V0O1xuICAgICAgICB2YXIgYW5pbWF0ZVRpbWVvdXQ7XG4gICAgICAgICQoXCIjcmVsb2FkX3Jvb21uYW1lXCIpLmNsaWNrKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh1cGRhdGVUaW1lb3V0KTtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChhbmltYXRlVGltZW91dCk7XG4gICAgICAgICAgICB1cGRhdGVfcm9vbW5hbWUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgICQoXCIjcmVsb2FkX3Jvb21uYW1lXCIpLnNob3coKTtcblxuXG4gICAgICAgIHVwZGF0ZV9yb29tbmFtZSgpO1xuICAgIH1cblxuICAgICQoXCIjZGlzYWJsZV93ZWxjb21lXCIpLmNsaWNrKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS53ZWxjb21lUGFnZURpc2FibGVkXG4gICAgICAgICAgICA9ICQoXCIjZGlzYWJsZV93ZWxjb21lXCIpLmlzKFwiOmNoZWNrZWRcIik7XG4gICAgfSk7XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBzZXR1cFdlbGNvbWVQYWdlOyIsInZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIGV2ZW50RW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbnZhciBDUUV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL2Nvbm5lY3Rpb25xdWFsaXR5L0NRRXZlbnRzXCIpO1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cbi8qKlxuICogbG9jYWwgc3RhdHNcbiAqIEB0eXBlIHt7fX1cbiAqL1xudmFyIHN0YXRzID0ge307XG5cbi8qKlxuICogcmVtb3RlIHN0YXRzXG4gKiBAdHlwZSB7e319XG4gKi9cbnZhciByZW1vdGVTdGF0cyA9IHt9O1xuXG4vKipcbiAqIEludGVydmFsIGZvciBzZW5kaW5nIHN0YXRpc3RpY3MgdG8gb3RoZXIgcGFydGljaXBhbnRzXG4gKiBAdHlwZSB7bnVsbH1cbiAqL1xudmFyIHNlbmRJbnRlcnZhbElkID0gbnVsbDtcblxuXG4vKipcbiAqIFN0YXJ0IHN0YXRpc3RpY3Mgc2VuZGluZy5cbiAqL1xuZnVuY3Rpb24gc3RhcnRTZW5kaW5nU3RhdHMoKSB7XG4gICAgc2VuZFN0YXRzKCk7XG4gICAgc2VuZEludGVydmFsSWQgPSBzZXRJbnRlcnZhbChzZW5kU3RhdHMsIDEwMDAwKTtcbn1cblxuLyoqXG4gKiBTZW5kcyBzdGF0aXN0aWNzIHRvIG90aGVyIHBhcnRpY2lwYW50c1xuICovXG5mdW5jdGlvbiBzZW5kU3RhdHMoKSB7XG4gICAgQVBQLnhtcHAuYWRkVG9QcmVzZW5jZShcImNvbm5lY3Rpb25RdWFsaXR5XCIsIGNvbnZlcnRUb01VQ1N0YXRzKHN0YXRzKSk7XG59XG5cbi8qKlxuICogQ29udmVydHMgc3RhdGlzdGljcyB0byBmb3JtYXQgZm9yIHNlbmRpbmcgdGhyb3VnaCBYTVBQXG4gKiBAcGFyYW0gc3RhdHMgdGhlIHN0YXRpc3RpY3NcbiAqIEByZXR1cm5zIHt7Yml0cmF0ZV9kb253bG9hZDogKiwgYml0cmF0ZV91cGxwb2FkOiAqLCBwYWNrZXRMb3NzX3RvdGFsOiAqLCBwYWNrZXRMb3NzX2Rvd25sb2FkOiAqLCBwYWNrZXRMb3NzX3VwbG9hZDogKn19XG4gKi9cbmZ1bmN0aW9uIGNvbnZlcnRUb01VQ1N0YXRzKHN0YXRzKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgXCJiaXRyYXRlX2Rvd25sb2FkXCI6IHN0YXRzLmJpdHJhdGUuZG93bmxvYWQsXG4gICAgICAgIFwiYml0cmF0ZV91cGxvYWRcIjogc3RhdHMuYml0cmF0ZS51cGxvYWQsXG4gICAgICAgIFwicGFja2V0TG9zc190b3RhbFwiOiBzdGF0cy5wYWNrZXRMb3NzLnRvdGFsLFxuICAgICAgICBcInBhY2tldExvc3NfZG93bmxvYWRcIjogc3RhdHMucGFja2V0TG9zcy5kb3dubG9hZCxcbiAgICAgICAgXCJwYWNrZXRMb3NzX3VwbG9hZFwiOiBzdGF0cy5wYWNrZXRMb3NzLnVwbG9hZFxuICAgIH07XG59XG5cbi8qKlxuICogQ29udmVydHMgc3RhdGl0aXN0aWNzIHRvIGZvcm1hdCB1c2VkIGJ5IFZpZGVvTGF5b3V0XG4gKiBAcGFyYW0gc3RhdHNcbiAqIEByZXR1cm5zIHt7Yml0cmF0ZToge2Rvd25sb2FkOiAqLCB1cGxvYWQ6ICp9LCBwYWNrZXRMb3NzOiB7dG90YWw6ICosIGRvd25sb2FkOiAqLCB1cGxvYWQ6ICp9fX1cbiAqL1xuZnVuY3Rpb24gcGFyc2VNVUNTdGF0cyhzdGF0cykge1xuICAgIHJldHVybiB7XG4gICAgICAgIGJpdHJhdGU6IHtcbiAgICAgICAgICAgIGRvd25sb2FkOiBzdGF0cy5iaXRyYXRlX2Rvd25sb2FkLFxuICAgICAgICAgICAgdXBsb2FkOiBzdGF0cy5iaXRyYXRlX3VwbG9hZFxuICAgICAgICB9LFxuICAgICAgICBwYWNrZXRMb3NzOiB7XG4gICAgICAgICAgICB0b3RhbDogc3RhdHMucGFja2V0TG9zc190b3RhbCxcbiAgICAgICAgICAgIGRvd25sb2FkOiBzdGF0cy5wYWNrZXRMb3NzX2Rvd25sb2FkLFxuICAgICAgICAgICAgdXBsb2FkOiBzdGF0cy5wYWNrZXRMb3NzX3VwbG9hZFxuICAgICAgICB9XG4gICAgfTtcbn1cblxuXG52YXIgQ29ubmVjdGlvblF1YWxpdHkgPSB7XG4gICAgaW5pdDogZnVuY3Rpb24gKCkge1xuICAgICAgICBBUFAueG1wcC5hZGRMaXN0ZW5lcihYTVBQRXZlbnRzLlJFTU9URV9TVEFUUywgdGhpcy51cGRhdGVSZW1vdGVTdGF0cyk7XG4gICAgICAgIEFQUC5zdGF0aXN0aWNzLmFkZENvbm5lY3Rpb25TdGF0c0xpc3RlbmVyKHRoaXMudXBkYXRlTG9jYWxTdGF0cyk7XG4gICAgICAgIEFQUC5zdGF0aXN0aWNzLmFkZFJlbW90ZVN0YXRzU3RvcExpc3RlbmVyKHRoaXMuc3RvcFNlbmRpbmdTdGF0cyk7XG5cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyB0aGUgbG9jYWwgc3RhdGlzdGljc1xuICAgICAqIEBwYXJhbSBkYXRhIG5ldyBzdGF0aXN0aWNzXG4gICAgICovXG4gICAgdXBkYXRlTG9jYWxTdGF0czogZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgc3RhdHMgPSBkYXRhO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChDUUV2ZW50cy5MT0NBTFNUQVRTX1VQREFURUQsIDEwMCAtIHN0YXRzLnBhY2tldExvc3MudG90YWwsIHN0YXRzKTtcbiAgICAgICAgaWYgKHNlbmRJbnRlcnZhbElkID09IG51bGwpIHtcbiAgICAgICAgICAgIHN0YXJ0U2VuZGluZ1N0YXRzKCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlcyByZW1vdGUgc3RhdGlzdGljc1xuICAgICAqIEBwYXJhbSBqaWQgdGhlIGppZCBhc3NvY2lhdGVkIHdpdGggdGhlIHN0YXRpc3RpY3NcbiAgICAgKiBAcGFyYW0gZGF0YSB0aGUgc3RhdGlzdGljc1xuICAgICAqL1xuICAgIHVwZGF0ZVJlbW90ZVN0YXRzOiBmdW5jdGlvbiAoamlkLCBkYXRhKSB7XG4gICAgICAgIGlmIChkYXRhID09IG51bGwgfHwgZGF0YS5wYWNrZXRMb3NzX3RvdGFsID09IG51bGwpIHtcbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KENRRXZlbnRzLlJFTU9URVNUQVRTX1VQREFURUQsIGppZCwgbnVsbCwgbnVsbCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgcmVtb3RlU3RhdHNbamlkXSA9IHBhcnNlTVVDU3RhdHMoZGF0YSk7XG5cbiAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoQ1FFdmVudHMuUkVNT1RFU1RBVFNfVVBEQVRFRCxcbiAgICAgICAgICAgIGppZCwgMTAwIC0gZGF0YS5wYWNrZXRMb3NzX3RvdGFsLCByZW1vdGVTdGF0c1tqaWRdKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU3RvcHMgc3RhdGlzdGljcyBzZW5kaW5nLlxuICAgICAqL1xuICAgIHN0b3BTZW5kaW5nU3RhdHM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbChzZW5kSW50ZXJ2YWxJZCk7XG4gICAgICAgIHNlbmRJbnRlcnZhbElkID0gbnVsbDtcbiAgICAgICAgLy9ub3RpZnkgVUkgYWJvdXQgc3RvcHBpbmcgc3RhdGlzdGljcyBnYXRoZXJpbmdcbiAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoQ1FFdmVudHMuU1RPUCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGxvY2FsIHN0YXRpc3RpY3MuXG4gICAgICovXG4gICAgZ2V0U3RhdHM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHN0YXRzO1xuICAgIH0sXG4gICAgXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uICh0eXBlLCBsaXN0ZW5lcikge1xuICAgICAgICBldmVudEVtaXR0ZXIub24odHlwZSwgbGlzdGVuZXIpO1xuICAgIH1cblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBDb25uZWN0aW9uUXVhbGl0eTsiLCIvKiBnbG9iYWwgJCwgYWxlcnQsIGNoYW5nZUxvY2FsVmlkZW8sIGNocm9tZSwgY29uZmlnLCBnZXRDb25mZXJlbmNlSGFuZGxlciwgZ2V0VXNlck1lZGlhV2l0aENvbnN0cmFpbnRzICovXG4vKipcbiAqIEluZGljYXRlcyB0aGF0IGRlc2t0b3Agc3RyZWFtIGlzIGN1cnJlbnRseSBpbiB1c2UoZm9yIHRvZ2dsZSBwdXJwb3NlKS5cbiAqIEB0eXBlIHtib29sZWFufVxuICovXG52YXIgaXNVc2luZ1NjcmVlblN0cmVhbSA9IGZhbHNlO1xuLyoqXG4gKiBJbmRpY2F0ZXMgdGhhdCBzd2l0Y2ggc3RyZWFtIG9wZXJhdGlvbiBpcyBpbiBwcm9ncmVzcyBhbmQgcHJldmVudCBmcm9tIHRyaWdnZXJpbmcgbmV3IGV2ZW50cy5cbiAqIEB0eXBlIHtib29sZWFufVxuICovXG52YXIgc3dpdGNoSW5Qcm9ncmVzcyA9IGZhbHNlO1xuXG4vKipcbiAqIE1ldGhvZCB1c2VkIHRvIGdldCBzY3JlZW4gc2hhcmluZyBzdHJlYW0uXG4gKlxuICogQHR5cGUge2Z1bmN0aW9uIChzdHJlYW1fY2FsbGJhY2ssIGZhaWx1cmVfY2FsbGJhY2t9XG4gKi9cbnZhciBvYnRhaW5EZXNrdG9wU3RyZWFtID0gbnVsbDtcblxuLyoqXG4gKiBGbGFnIHVzZWQgdG8gY2FjaGUgZGVza3RvcCBzaGFyaW5nIGVuYWJsZWQgc3RhdGUuIERvIG5vdCB1c2UgZGlyZWN0bHkgYXMgaXQgY2FuIGJlIDx0dD5udWxsPC90dD4uXG4gKiBAdHlwZSB7bnVsbHxib29sZWFufVxuICovXG52YXIgX2Rlc2t0b3BTaGFyaW5nRW5hYmxlZCA9IG51bGw7XG5cbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xuXG52YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG52YXIgRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvZGVza3RvcHNoYXJpbmcvRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzXCIpO1xuXG4vKipcbiAqIE1ldGhvZCBvYnRhaW5zIGRlc2t0b3Agc3RyZWFtIGZyb20gV2ViUlRDICdzY3JlZW4nIHNvdXJjZS5cbiAqIEZsYWcgJ2Nocm9tZTovL2ZsYWdzLyNlbmFibGUtdXNlcm1lZGlhLXNjcmVlbi1jYXB0dXJlJyBtdXN0IGJlIGVuYWJsZWQuXG4gKi9cbmZ1bmN0aW9uIG9idGFpbldlYlJUQ1NjcmVlbihzdHJlYW1DYWxsYmFjaywgZmFpbENhbGxiYWNrKSB7XG4gICAgQVBQLlJUQy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMoXG4gICAgICAgIFsnc2NyZWVuJ10sXG4gICAgICAgIHN0cmVhbUNhbGxiYWNrLFxuICAgICAgICBmYWlsQ2FsbGJhY2tcbiAgICApO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdHMgaW5saW5lIGluc3RhbGwgVVJMIGZvciBDaHJvbWUgZGVza3RvcCBzdHJlYW1pbmcgZXh0ZW5zaW9uLlxuICogVGhlICdjaHJvbWVFeHRlbnNpb25JZCcgbXVzdCBiZSBkZWZpbmVkIGluIGNvbmZpZy5qcy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9XG4gKi9cbmZ1bmN0aW9uIGdldFdlYlN0b3JlSW5zdGFsbFVybCgpXG57XG4gICAgcmV0dXJuIFwiaHR0cHM6Ly9jaHJvbWUuZ29vZ2xlLmNvbS93ZWJzdG9yZS9kZXRhaWwvXCIgKyBjb25maWcuY2hyb21lRXh0ZW5zaW9uSWQ7XG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgZXh0ZW5zaW9uIHVwZGF0ZSBpcyByZXF1aXJlZC5cbiAqIEBwYXJhbSBtaW5WZXJzaW9uIG1pbmltYWwgcmVxdWlyZWQgdmVyc2lvblxuICogQHBhcmFtIGV4dFZlcnNpb24gY3VycmVudCBleHRlbnNpb24gdmVyc2lvblxuICogQHJldHVybnMge2Jvb2xlYW59XG4gKi9cbmZ1bmN0aW9uIGlzVXBkYXRlUmVxdWlyZWQobWluVmVyc2lvbiwgZXh0VmVyc2lvbilcbntcbiAgICB0cnlcbiAgICB7XG4gICAgICAgIHZhciBzMSA9IG1pblZlcnNpb24uc3BsaXQoJy4nKTtcbiAgICAgICAgdmFyIHMyID0gZXh0VmVyc2lvbi5zcGxpdCgnLicpO1xuXG4gICAgICAgIHZhciBsZW4gPSBNYXRoLm1heChzMS5sZW5ndGgsIHMyLmxlbmd0aCk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuOyBpKyspXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBuMSA9IDAsXG4gICAgICAgICAgICAgICAgbjIgPSAwO1xuXG4gICAgICAgICAgICBpZiAoaSA8IHMxLmxlbmd0aClcbiAgICAgICAgICAgICAgICBuMSA9IHBhcnNlSW50KHMxW2ldKTtcbiAgICAgICAgICAgIGlmIChpIDwgczIubGVuZ3RoKVxuICAgICAgICAgICAgICAgIG4yID0gcGFyc2VJbnQoczJbaV0pO1xuXG4gICAgICAgICAgICBpZiAoaXNOYU4objEpIHx8IGlzTmFOKG4yKSlcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKG4xICE9PSBuMilcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbjEgPiBuMjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHdpbGwgaGFwcGVuIGlmIGJvdGhzIHZlcnNpb24gaGFzIGlkZW50aWNhbCBudW1iZXJzIGluXG4gICAgICAgIC8vIHRoZWlyIGNvbXBvbmVudHMgKGV2ZW4gaWYgb25lIG9mIHRoZW0gaXMgbG9uZ2VyLCBoYXMgbW9yZSBjb21wb25lbnRzKVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNhdGNoIChlKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBwYXJzZSBleHRlbnNpb24gdmVyc2lvblwiLCBlKTtcbiAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcignRXJyb3InLFxuICAgICAgICAgICAgJ0Vycm9yIHdoZW4gdHJ5aW5nIHRvIGRldGVjdCBkZXNrdG9wc2hhcmluZyBleHRlbnNpb24uJyk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbn1cblxuXG5mdW5jdGlvbiBjaGVja0V4dEluc3RhbGxlZChpc0luc3RhbGxlZENhbGxiYWNrKSB7XG4gICAgaWYgKCFjaHJvbWUucnVudGltZSkge1xuICAgICAgICAvLyBObyBBUEksIHNvIG5vIGV4dGVuc2lvbiBmb3Igc3VyZVxuICAgICAgICBpc0luc3RhbGxlZENhbGxiYWNrKGZhbHNlKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjaHJvbWUucnVudGltZS5zZW5kTWVzc2FnZShcbiAgICAgICAgY29uZmlnLmNocm9tZUV4dGVuc2lvbklkLFxuICAgICAgICB7IGdldFZlcnNpb246IHRydWUgfSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlIHx8ICFyZXNwb25zZS52ZXJzaW9uKSB7XG4gICAgICAgICAgICAgICAgLy8gQ29tbXVuaWNhdGlvbiBmYWlsdXJlIC0gYXNzdW1lIHRoYXQgbm8gZW5kcG9pbnQgZXhpc3RzXG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiRXh0ZW5zaW9uIG5vdCBpbnN0YWxsZWQ/OiBcIiArIGNocm9tZS5ydW50aW1lLmxhc3RFcnJvcik7XG4gICAgICAgICAgICAgICAgaXNJbnN0YWxsZWRDYWxsYmFjayhmYWxzZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIENoZWNrIGluc3RhbGxlZCBleHRlbnNpb24gdmVyc2lvblxuICAgICAgICAgICAgICAgIHZhciBleHRWZXJzaW9uID0gcmVzcG9uc2UudmVyc2lvbjtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnRXh0ZW5zaW9uIHZlcnNpb24gaXM6ICcgKyBleHRWZXJzaW9uKTtcbiAgICAgICAgICAgICAgICB2YXIgdXBkYXRlUmVxdWlyZWQgPSBpc1VwZGF0ZVJlcXVpcmVkKGNvbmZpZy5taW5DaHJvbWVFeHRWZXJzaW9uLCBleHRWZXJzaW9uKTtcbiAgICAgICAgICAgICAgICBpZiAodXBkYXRlUmVxdWlyZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgYWxlcnQoXG4gICAgICAgICAgICAgICAgICAgICAgICAnSml0c2kgRGVza3RvcCBTdHJlYW1lciByZXF1aXJlcyB1cGRhdGUuICcgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJ0NoYW5nZXMgd2lsbCB0YWtlIGVmZmVjdCBhZnRlciBuZXh0IENocm9tZSByZXN0YXJ0LicpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpc0luc3RhbGxlZENhbGxiYWNrKCF1cGRhdGVSZXF1aXJlZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICApO1xufVxuXG5mdW5jdGlvbiBkb0dldFN0cmVhbUZyb21FeHRlbnNpb24oc3RyZWFtQ2FsbGJhY2ssIGZhaWxDYWxsYmFjaykge1xuICAgIC8vIFNlbmRzICdnZXRTdHJlYW0nIG1zZyB0byB0aGUgZXh0ZW5zaW9uLiBFeHRlbnNpb24gaWQgbXVzdCBiZSBkZWZpbmVkIGluIHRoZSBjb25maWcuXG4gICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoXG4gICAgICAgIGNvbmZpZy5jaHJvbWVFeHRlbnNpb25JZCxcbiAgICAgICAgeyBnZXRTdHJlYW06IHRydWUsIHNvdXJjZXM6IGNvbmZpZy5kZXNrdG9wU2hhcmluZ1NvdXJjZXMgfSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlKSB7XG4gICAgICAgICAgICAgICAgZmFpbENhbGxiYWNrKGNocm9tZS5ydW50aW1lLmxhc3RFcnJvcik7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJSZXNwb25zZSBmcm9tIGV4dGVuc2lvbjogXCIgKyByZXNwb25zZSk7XG4gICAgICAgICAgICBpZiAocmVzcG9uc2Uuc3RyZWFtSWQpIHtcbiAgICAgICAgICAgICAgICBBUFAuUlRDLmdldFVzZXJNZWRpYVdpdGhDb25zdHJhaW50cyhcbiAgICAgICAgICAgICAgICAgICAgWydkZXNrdG9wJ10sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbUNhbGxiYWNrKHN0cmVhbSk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZhaWxDYWxsYmFjayxcbiAgICAgICAgICAgICAgICAgICAgbnVsbCwgbnVsbCwgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RyZWFtSWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmYWlsQ2FsbGJhY2soXCJFeHRlbnNpb24gZmFpbGVkIHRvIGdldCB0aGUgc3RyZWFtXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgKTtcbn1cbi8qKlxuICogQXNrcyBDaHJvbWUgZXh0ZW5zaW9uIHRvIGNhbGwgY2hvb3NlRGVza3RvcE1lZGlhIGFuZCBnZXRzIGNocm9tZSAnZGVza3RvcCcgc3RyZWFtIGZvciByZXR1cm5lZCBzdHJlYW0gdG9rZW4uXG4gKi9cbmZ1bmN0aW9uIG9idGFpblNjcmVlbkZyb21FeHRlbnNpb24oc3RyZWFtQ2FsbGJhY2ssIGZhaWxDYWxsYmFjaykge1xuICAgIGNoZWNrRXh0SW5zdGFsbGVkKFxuICAgICAgICBmdW5jdGlvbiAoaXNJbnN0YWxsZWQpIHtcbiAgICAgICAgICAgIGlmIChpc0luc3RhbGxlZCkge1xuICAgICAgICAgICAgICAgIGRvR2V0U3RyZWFtRnJvbUV4dGVuc2lvbihzdHJlYW1DYWxsYmFjaywgZmFpbENhbGxiYWNrKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY2hyb21lLndlYnN0b3JlLmluc3RhbGwoXG4gICAgICAgICAgICAgICAgICAgIGdldFdlYlN0b3JlSW5zdGFsbFVybCgpLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoYXJnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIkV4dGVuc2lvbiBpbnN0YWxsZWQgc3VjY2Vzc2Z1bGx5XCIsIGFyZyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBXZSBuZWVkIHRvIHJlbG9hZCB0aGUgcGFnZSBpbiBvcmRlciB0byBnZXQgdGhlIGFjY2VzcyB0byBjaHJvbWUucnVudGltZVxuICAgICAgICAgICAgICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLnJlbG9hZChmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChhcmcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiRmFpbGVkIHRvIGluc3RhbGwgdGhlIGV4dGVuc2lvblwiLCBhcmcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZmFpbENhbGxiYWNrKGFyZyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCdFcnJvcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0ZhaWxlZCB0byBpbnN0YWxsIGRlc2t0b3Agc2hhcmluZyBleHRlbnNpb24nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICApO1xufVxuXG4vKipcbiAqIENhbGwgdGhpcyBtZXRob2QgdG8gdG9nZ2xlIGRlc2t0b3Agc2hhcmluZyBmZWF0dXJlLlxuICogQHBhcmFtIG1ldGhvZCBwYXNzIFwiZXh0XCIgdG8gdXNlIGNocm9tZSBleHRlbnNpb24gZm9yIGRlc2t0b3AgY2FwdHVyZShjaHJvbWUgZXh0ZW5zaW9uIHJlcXVpcmVkKSxcbiAqICAgICAgICBwYXNzIFwid2VicnRjXCIgdG8gdXNlIFdlYlJUQyBcInNjcmVlblwiIGRlc2t0b3Agc291cmNlKCdjaHJvbWU6Ly9mbGFncy8jZW5hYmxlLXVzZXJtZWRpYS1zY3JlZW4tY2FwdHVyZSdcbiAqICAgICAgICBtdXN0IGJlIGVuYWJsZWQpLCBwYXNzIGFueSBvdGhlciBzdHJpbmcgb3Igbm90aGluZyBpbiBvcmRlciB0byBkaXNhYmxlIHRoaXMgZmVhdHVyZSBjb21wbGV0ZWx5LlxuICovXG5mdW5jdGlvbiBzZXREZXNrdG9wU2hhcmluZyhtZXRob2QpIHtcbiAgICAvLyBDaGVjayBpZiB3ZSBhcmUgcnVubmluZyBjaHJvbWVcbiAgICBpZiAoIW5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEpIHtcbiAgICAgICAgb2J0YWluRGVza3RvcFN0cmVhbSA9IG51bGw7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIkRlc2t0b3Agc2hhcmluZyBkaXNhYmxlZFwiKTtcbiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSBcImV4dFwiKSB7XG4gICAgICAgIG9idGFpbkRlc2t0b3BTdHJlYW0gPSBvYnRhaW5TY3JlZW5Gcm9tRXh0ZW5zaW9uO1xuICAgICAgICBjb25zb2xlLmluZm8oXCJVc2luZyBDaHJvbWUgZXh0ZW5zaW9uIGZvciBkZXNrdG9wIHNoYXJpbmdcIik7XG4gICAgfSBlbHNlIGlmIChtZXRob2QgPT0gXCJ3ZWJydGNcIikge1xuICAgICAgICBvYnRhaW5EZXNrdG9wU3RyZWFtID0gb2J0YWluV2ViUlRDU2NyZWVuO1xuICAgICAgICBjb25zb2xlLmluZm8oXCJVc2luZyBDaHJvbWUgV2ViUlRDIGZvciBkZXNrdG9wIHNoYXJpbmdcIik7XG4gICAgfVxuXG4gICAgLy8gUmVzZXQgZW5hYmxlZCBjYWNoZVxuICAgIF9kZXNrdG9wU2hhcmluZ0VuYWJsZWQgPSBudWxsO1xufVxuXG4vKipcbiAqIEluaXRpYWxpemVzIDxsaW5rIHJlbD1jaHJvbWUtd2Vic3RvcmUtaXRlbSAvPiB3aXRoIGV4dGVuc2lvbiBpZCBzZXQgaW4gY29uZmlnLmpzIHRvIHN1cHBvcnQgaW5saW5lIGluc3RhbGxzLlxuICogSG9zdCBzaXRlIG11c3QgYmUgc2VsZWN0ZWQgYXMgbWFpbiB3ZWJzaXRlIG9mIHB1Ymxpc2hlZCBleHRlbnNpb24uXG4gKi9cbmZ1bmN0aW9uIGluaXRJbmxpbmVJbnN0YWxscygpXG57XG4gICAgJChcImxpbmtbcmVsPWNocm9tZS13ZWJzdG9yZS1pdGVtXVwiKS5hdHRyKFwiaHJlZlwiLCBnZXRXZWJTdG9yZUluc3RhbGxVcmwoKSk7XG59XG5cbmZ1bmN0aW9uIGdldFN3aXRjaFN0cmVhbUZhaWxlZChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gb2J0YWluIHRoZSBzdHJlYW0gdG8gc3dpdGNoIHRvXCIsIGVycm9yKTtcbiAgICBzd2l0Y2hJblByb2dyZXNzID0gZmFsc2U7XG59XG5cbmZ1bmN0aW9uIHN0cmVhbVN3aXRjaERvbmUoKSB7XG4gICAgc3dpdGNoSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgIGV2ZW50RW1pdHRlci5lbWl0KFxuICAgICAgICBEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuU1dJVENISU5HX0RPTkUsXG4gICAgICAgIGlzVXNpbmdTY3JlZW5TdHJlYW0pO1xufVxuXG5mdW5jdGlvbiBuZXdTdHJlYW1DcmVhdGVkKHN0cmVhbSlcbntcbiAgICBldmVudEVtaXR0ZXIuZW1pdChEZXNrdG9wU2hhcmluZ0V2ZW50VHlwZXMuTkVXX1NUUkVBTV9DUkVBVEVELFxuICAgICAgICBzdHJlYW0sIGlzVXNpbmdTY3JlZW5TdHJlYW0sIHN0cmVhbVN3aXRjaERvbmUpO1xufVxuXG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGlzVXNpbmdTY3JlZW5TdHJlYW06IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGlzVXNpbmdTY3JlZW5TdHJlYW07XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSA8dHQ+dHJ1ZTwvdHQ+IGlmIGRlc2t0b3Agc2hhcmluZyBmZWF0dXJlIGlzIGF2YWlsYWJsZSBhbmQgZW5hYmxlZC5cbiAgICAgKi9cbiAgICBpc0Rlc2t0b3BTaGFyaW5nRW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoX2Rlc2t0b3BTaGFyaW5nRW5hYmxlZCA9PT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKG9idGFpbkRlc2t0b3BTdHJlYW0gPT09IG9idGFpblNjcmVlbkZyb21FeHRlbnNpb24pIHtcbiAgICAgICAgICAgICAgICAvLyBQYXJzZSBjaHJvbWUgdmVyc2lvblxuICAgICAgICAgICAgICAgIHZhciB1c2VyQWdlbnQgPSBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICAgICAgLy8gV2UgY2FuIGFzc3VtZSB0aGF0IHVzZXIgYWdlbnQgaXMgY2hyb21lLCBiZWNhdXNlIGl0J3MgZW5mb3JjZWQgd2hlbiAnZXh0JyBzdHJlYW1pbmcgbWV0aG9kIGlzIHNldFxuICAgICAgICAgICAgICAgIHZhciB2ZXIgPSBwYXJzZUludCh1c2VyQWdlbnQubWF0Y2goL2Nocm9tZVxcLyhcXGQrKVxcLi8pWzFdLCAxMCk7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJDaHJvbWUgdmVyc2lvblwiICsgdXNlckFnZW50LCB2ZXIpO1xuICAgICAgICAgICAgICAgIF9kZXNrdG9wU2hhcmluZ0VuYWJsZWQgPSB2ZXIgPj0gMzQ7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIF9kZXNrdG9wU2hhcmluZ0VuYWJsZWQgPSBvYnRhaW5EZXNrdG9wU3RyZWFtID09PSBvYnRhaW5XZWJSVENTY3JlZW47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIF9kZXNrdG9wU2hhcmluZ0VuYWJsZWQ7XG4gICAgfSxcbiAgICBcbiAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHNldERlc2t0b3BTaGFyaW5nKGNvbmZpZy5kZXNrdG9wU2hhcmluZyk7XG5cbiAgICAgICAgLy8gSW5pdGlhbGl6ZSBDaHJvbWUgZXh0ZW5zaW9uIGlubGluZSBpbnN0YWxsc1xuICAgICAgICBpZiAoY29uZmlnLmNocm9tZUV4dGVuc2lvbklkKSB7XG4gICAgICAgICAgICBpbml0SW5saW5lSW5zdGFsbHMoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcy5JTklUKTtcbiAgICB9LFxuXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKGxpc3RlbmVyLCB0eXBlKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuXG4gICAgcmVtb3ZlTGlzdGVuZXI6IGZ1bmN0aW9uIChsaXN0ZW5lcix0eXBlKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICogVG9nZ2xlcyBzY3JlZW4gc2hhcmluZy5cbiAgICAgKi9cbiAgICB0b2dnbGVTY3JlZW5TaGFyaW5nOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChzd2l0Y2hJblByb2dyZXNzIHx8ICFvYnRhaW5EZXNrdG9wU3RyZWFtKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJTd2l0Y2ggaW4gcHJvZ3Jlc3Mgb3Igbm8gbWV0aG9kIGRlZmluZWRcIik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgc3dpdGNoSW5Qcm9ncmVzcyA9IHRydWU7XG5cbiAgICAgICAgaWYgKCFpc1VzaW5nU2NyZWVuU3RyZWFtKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBTd2l0Y2ggdG8gZGVza3RvcCBzdHJlYW1cbiAgICAgICAgICAgIG9idGFpbkRlc2t0b3BTdHJlYW0oXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBXZSBub3cgdXNlIHNjcmVlbiBzdHJlYW1cbiAgICAgICAgICAgICAgICAgICAgaXNVc2luZ1NjcmVlblN0cmVhbSA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIC8vIEhvb2sgJ2VuZGVkJyBldmVudCB0byByZXN0b3JlIGNhbWVyYSB3aGVuIHNjcmVlbiBzdHJlYW0gc3RvcHNcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLmFkZEV2ZW50TGlzdGVuZXIoJ2VuZGVkJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFzd2l0Y2hJblByb2dyZXNzICYmIGlzVXNpbmdTY3JlZW5TdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlU2NyZWVuU2hhcmluZygpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgbmV3U3RyZWFtQ3JlYXRlZChzdHJlYW0pO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZ2V0U3dpdGNoU3RyZWFtRmFpbGVkKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIERpc2FibGUgc2NyZWVuIHN0cmVhbVxuICAgICAgICAgICAgQVBQLlJUQy5nZXRVc2VyTWVkaWFXaXRoQ29uc3RyYWludHMoXG4gICAgICAgICAgICAgICAgWyd2aWRlbyddLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gV2UgYXJlIG5vdyB1c2luZyBjYW1lcmEgc3RyZWFtXG4gICAgICAgICAgICAgICAgICAgIGlzVXNpbmdTY3JlZW5TdHJlYW0gPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgbmV3U3RyZWFtQ3JlYXRlZChzdHJlYW0pO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZ2V0U3dpdGNoU3RyZWFtRmFpbGVkLCBjb25maWcucmVzb2x1dGlvbiB8fCAnMzYwJ1xuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cbiIsIi8vbWFwcyBrZXljb2RlIHRvIGNoYXJhY3RlciwgaWQgb2YgcG9wb3ZlciBmb3IgZ2l2ZW4gZnVuY3Rpb24gYW5kIGZ1bmN0aW9uXG52YXIgc2hvcnRjdXRzID0ge1xuICAgIDY3OiB7XG4gICAgICAgIGNoYXJhY3RlcjogXCJDXCIsXG4gICAgICAgIGlkOiBcInRvZ2dsZUNoYXRQb3BvdmVyXCIsXG4gICAgICAgIGZ1bmN0aW9uOiBBUFAuVUkudG9nZ2xlQ2hhdFxuICAgIH0sXG4gICAgNzA6IHtcbiAgICAgICAgY2hhcmFjdGVyOiBcIkZcIixcbiAgICAgICAgaWQ6IFwiZmlsbXN0cmlwUG9wb3ZlclwiLFxuICAgICAgICBmdW5jdGlvbjogQVBQLlVJLnRvZ2dsZUZpbG1TdHJpcFxuICAgIH0sXG4gICAgNzc6IHtcbiAgICAgICAgY2hhcmFjdGVyOiBcIk1cIixcbiAgICAgICAgaWQ6IFwibXV0ZVBvcG92ZXJcIixcbiAgICAgICAgZnVuY3Rpb246IEFQUC5VSS50b2dnbGVBdWRpb1xuICAgIH0sXG4gICAgODQ6IHtcbiAgICAgICAgY2hhcmFjdGVyOiBcIlRcIixcbiAgICAgICAgZnVuY3Rpb246IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYoIUFQUC5SVEMubG9jYWxBdWRpby5pc011dGVkKCkpIHtcbiAgICAgICAgICAgICAgICBBUFAuVUkudG9nZ2xlQXVkaW8oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG4gICAgODY6IHtcbiAgICAgICAgY2hhcmFjdGVyOiBcIlZcIixcbiAgICAgICAgaWQ6IFwidG9nZ2xlVmlkZW9Qb3BvdmVyXCIsXG4gICAgICAgIGZ1bmN0aW9uOiBBUFAuVUkudG9nZ2xlVmlkZW9cbiAgICB9XG59O1xuXG5cbnZhciBLZXlib2FyZFNob3J0Y3V0ID0ge1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgd2luZG93Lm9ua2V5dXAgPSBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICB2YXIga2V5Y29kZSA9IGUud2hpY2g7XG4gICAgICAgICAgICBpZighKCQoXCI6Zm9jdXNcIikuaXMoXCJpbnB1dFt0eXBlPXRleHRdXCIpIHx8XG4gICAgICAgICAgICAgICAgJChcIjpmb2N1c1wiKS5pcyhcImlucHV0W3R5cGU9cGFzc3dvcmRdXCIpIHx8XG4gICAgICAgICAgICAgICAgJChcIjpmb2N1c1wiKS5pcyhcInRleHRhcmVhXCIpKSkge1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2Ygc2hvcnRjdXRzW2tleWNvZGVdID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgICAgICAgIHNob3J0Y3V0c1trZXljb2RlXS5mdW5jdGlvbigpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmIChrZXljb2RlID49IFwiMFwiLmNoYXJDb2RlQXQoMCkgJiZcbiAgICAgICAgICAgICAgICAgICAga2V5Y29kZSA8PSBcIjlcIi5jaGFyQ29kZUF0KDApKSB7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5VSS5jbGlja09uVmlkZW8oa2V5Y29kZSAtIFwiMFwiLmNoYXJDb2RlQXQoMCkgKyAxKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy9lc2Mgd2hpbGUgdGhlIHNtaWxleXMgYXJlIHZpc2libGUgaGlkZXMgdGhlbVxuICAgICAgICAgICAgfSBlbHNlIGlmIChrZXljb2RlID09PSAyNyAmJiAkKCcjc21pbGV5c0NvbnRhaW5lcicpLmlzKCc6dmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgQVBQLlVJLnRvZ2dsZVNtaWxleXMoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICB3aW5kb3cub25rZXlkb3duID0gZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgaWYoISgkKFwiOmZvY3VzXCIpLmlzKFwiaW5wdXRbdHlwZT10ZXh0XVwiKSB8fFxuICAgICAgICAgICAgICAgICQoXCI6Zm9jdXNcIikuaXMoXCJpbnB1dFt0eXBlPXBhc3N3b3JkXVwiKSB8fFxuICAgICAgICAgICAgICAgICQoXCI6Zm9jdXNcIikuaXMoXCJ0ZXh0YXJlYVwiKSkpIHtcbiAgICAgICAgICAgICAgICBpZihlLndoaWNoID09PSBcIlRcIi5jaGFyQ29kZUF0KDApKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKEFQUC5SVEMubG9jYWxBdWRpby5pc011dGVkKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5VSS50b2dnbGVBdWRpbygpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICQoJ2JvZHknKS5wb3BvdmVyKHsgc2VsZWN0b3I6ICdbZGF0YS10b2dnbGU9cG9wb3Zlcl0nLFxuICAgICAgICAgICAgdHJpZ2dlcjogJ2NsaWNrIGhvdmVyJyxcbiAgICAgICAgICAgIGNvbnRlbnQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZShcImNvbnRlbnRcIikgK1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmdldFNob3J0Y3V0KHRoaXMuZ2V0QXR0cmlidXRlKFwic2hvcnRjdXRcIikpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIGluZGljYXRlcyB0aGUgcG9wb3ZlciBhc3NvY2lhdGVkIHdpdGggdGhlIHNob3J0Y3V0XG4gICAgICogQHJldHVybnMge3N0cmluZ30gdGhlIGtleWJvYXJkIHNob3J0Y3V0IHVzZWQgZm9yIHRoZSBpZCBnaXZlblxuICAgICAqL1xuICAgIGdldFNob3J0Y3V0OiBmdW5jdGlvbiAoaWQpIHtcbiAgICAgICAgZm9yICh2YXIga2V5Y29kZSBpbiBzaG9ydGN1dHMpIHtcbiAgICAgICAgICAgIGlmIChzaG9ydGN1dHMuaGFzT3duUHJvcGVydHkoa2V5Y29kZSkpIHtcbiAgICAgICAgICAgICAgICBpZiAoc2hvcnRjdXRzW2tleWNvZGVdLmlkID09PSBpZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCIgKFwiICsgc2hvcnRjdXRzW2tleWNvZGVdLmNoYXJhY3RlciArIFwiKVwiO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEtleWJvYXJkU2hvcnRjdXQ7XG4iLCIvKipcbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gU2ltdWxjYXN0TG9nZ2VyKG5hbWUsIGx2bCkge1xuICAgIHRoaXMubmFtZSA9IG5hbWU7XG4gICAgdGhpcy5sdmwgPSBsdmw7XG59XG5cblNpbXVsY2FzdExvZ2dlci5wcm90b3R5cGUubG9nID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICBpZiAodGhpcy5sdmwpIHtcbiAgICAgICAgY29uc29sZS5sb2codGV4dCk7XG4gICAgfVxufTtcblxuU2ltdWxjYXN0TG9nZ2VyLnByb3RvdHlwZS5pbmZvID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICBpZiAodGhpcy5sdmwgPiAxKSB7XG4gICAgICAgIGNvbnNvbGUuaW5mbyh0ZXh0KTtcbiAgICB9XG59O1xuXG5TaW11bGNhc3RMb2dnZXIucHJvdG90eXBlLmZpbmUgPSBmdW5jdGlvbiAodGV4dCkge1xuICAgIGlmICh0aGlzLmx2bCA+IDIpIHtcbiAgICAgICAgY29uc29sZS5sb2codGV4dCk7XG4gICAgfVxufTtcblxuU2ltdWxjYXN0TG9nZ2VyLnByb3RvdHlwZS5lcnJvciA9IGZ1bmN0aW9uICh0ZXh0KSB7XG4gICAgY29uc29sZS5lcnJvcih0ZXh0KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2ltdWxjYXN0TG9nZ2VyOyIsInZhciBTaW11bGNhc3RMb2dnZXIgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RMb2dnZXJcIik7XG52YXIgU2ltdWxjYXN0VXRpbHMgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RVdGlsc1wiKTtcbnZhciBNZWRpYVN0cmVhbVR5cGUgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvTWVkaWFTdHJlYW1UeXBlc1wiKTtcblxuZnVuY3Rpb24gU2ltdWxjYXN0UmVjZWl2ZXIoKSB7XG4gICAgdGhpcy5zaW11bGNhc3RVdGlscyA9IG5ldyBTaW11bGNhc3RVdGlscygpO1xuICAgIHRoaXMubG9nZ2VyID0gbmV3IFNpbXVsY2FzdExvZ2dlcignU2ltdWxjYXN0UmVjZWl2ZXInLCAxKTtcbn1cblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9yZW1vdGVWaWRlb1NvdXJjZUNhY2hlID0gJyc7XG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3JlbW90ZU1hcHMgPSB7XG4gICAgbXNpZDJRdWFsaXR5OiB7fSxcbiAgICBzc3JjMk1zaWQ6IHt9LFxuICAgIG1zaWQyc3NyYzoge30sXG4gICAgcmVjZWl2aW5nVmlkZW9TdHJlYW1zOiB7fVxufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9jYWNoZVJlbW90ZVZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHRoaXMuX3JlbW90ZVZpZGVvU291cmNlQ2FjaGUgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9nZXRWaWRlb1NvdXJjZXMobGluZXMpO1xufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9yZXN0b3JlUmVtb3RlVmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgdGhpcy5fcmVtb3RlVmlkZW9Tb3VyY2VDYWNoZSk7XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX2Vuc3VyZUdvb2dDb25mZXJlbmNlID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHNiO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnRW5zdXJpbmcgeC1nb29nbGUtY29uZmVyZW5jZSBmbGFnLi4uJylcblxuICAgIGlmICh0aGlzLnNpbXVsY2FzdFV0aWxzLl9pbmRleE9mQXJyYXkoJ2E9eC1nb29nbGUtZmxhZzpjb25mZXJlbmNlJywgbGluZXMpID09PSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9lbXB0eUNvbXBvdW5kSW5kZXgpIHtcbiAgICAgICAgLy8gVE9ETyhncCkgZG8gdGhhdCBmb3IgdGhlIGF1ZGlvIGFzIHdlbGwgYXMgc3VnZ2VzdGVkIGJ5IGZpcHBvLlxuICAgICAgICAvLyBBZGQgdGhlIGdvb2dsZSBjb25mZXJlbmNlIGZsYWdcbiAgICAgICAgc2IgPSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9nZXRWaWRlb1NvdXJjZXMobGluZXMpO1xuICAgICAgICBzYiA9IFsnYT14LWdvb2dsZS1mbGFnOmNvbmZlcmVuY2UnXS5jb25jYXQoc2IpO1xuICAgICAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCBzYik7XG4gICAgfVxufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl9yZXN0b3JlU2ltdWxjYXN0R3JvdXBzID0gZnVuY3Rpb24gKHNiKSB7XG4gICAgdGhpcy5fcmVzdG9yZVJlbW90ZVZpZGVvU291cmNlcyhzYik7XG59O1xuXG4vKipcbiAqIFJlc3RvcmVzIHRoZSBzaW11bGNhc3QgZ3JvdXBzIG9mIHRoZSByZW1vdGUgZGVzY3JpcHRpb24uIEluXG4gKiB0cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiB3ZSByZW1vdmUgdGhvc2UgaW4gb3JkZXIgZm9yIHRoZSBzZXQgcmVtb3RlXG4gKiBkZXNjcmlwdGlvbiB0byBzdWNjZWVkLiBUaGUgZm9jdXMgbmVlZHMgdGhlIHNpZ25hbCB0aGUgZ3JvdXBzIHRvIG5ld1xuICogcGFydGljaXBhbnRzLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgdmFyIHNiO1xuXG4gICAgaWYgKCF0aGlzLnNpbXVsY2FzdFV0aWxzLmlzVmFsaWREZXNjcmlwdGlvbihkZXNjKSkge1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICBpZiAoY29uZmlnLmVuYWJsZVNpbXVsY2FzdCkge1xuICAgICAgICBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgICAgICB0aGlzLl9yZXN0b3JlU2ltdWxjYXN0R3JvdXBzKHNiKTtcblxuICAgICAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiBkZXNjO1xufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9lbnN1cmVPcmRlciA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHZhciB2aWRlb1NvdXJjZXMsIHNiO1xuXG4gICAgdmlkZW9Tb3VyY2VzID0gdGhpcy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdO1xuICAgIHNiID0gdGhpcy5fY29tcGlsZVZpZGVvU291cmNlcyh2aWRlb1NvdXJjZXMpO1xuXG4gICAgdGhpcy5fcmVwbGFjZVZpZGVvU291cmNlcyhsaW5lcywgc2IpO1xufTtcblxuU2ltdWxjYXN0UmVjZWl2ZXIucHJvdG90eXBlLl91cGRhdGVSZW1vdGVNYXBzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIHJlbW90ZVZpZGVvU291cmNlcyA9IHRoaXMuc2ltdWxjYXN0VXRpbHMucGFyc2VNZWRpYShsaW5lcywgWyd2aWRlbyddKVswXSxcbiAgICAgICAgdmlkZW9Tb3VyY2UsIHF1YWxpdHk7XG5cbiAgICAvLyAocmUpIGluaXRpYWxpemUgdGhlIHJlbW90ZSBtYXBzLlxuICAgIHRoaXMuX3JlbW90ZU1hcHMubXNpZDJRdWFsaXR5ID0ge307XG4gICAgdGhpcy5fcmVtb3RlTWFwcy5zc3JjMk1zaWQgPSB7fTtcbiAgICB0aGlzLl9yZW1vdGVNYXBzLm1zaWQyc3NyYyA9IHt9O1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmIChyZW1vdGVWaWRlb1NvdXJjZXMuZ3JvdXBzICYmIHJlbW90ZVZpZGVvU291cmNlcy5ncm91cHMubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgIHJlbW90ZVZpZGVvU291cmNlcy5ncm91cHMuZm9yRWFjaChmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgIGlmIChncm91cC5zZW1hbnRpY3MgPT09ICdTSU0nICYmIGdyb3VwLnNzcmNzICYmIGdyb3VwLnNzcmNzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgICAgIHF1YWxpdHkgPSAwO1xuICAgICAgICAgICAgICAgIGdyb3VwLnNzcmNzLmZvckVhY2goZnVuY3Rpb24gKHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmlkZW9Tb3VyY2UgPSByZW1vdGVWaWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXTtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5fcmVtb3RlTWFwcy5tc2lkMlF1YWxpdHlbdmlkZW9Tb3VyY2UubXNpZF0gPSBxdWFsaXR5Kys7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuX3JlbW90ZU1hcHMuc3NyYzJNc2lkW3ZpZGVvU291cmNlLnNzcmNdID0gdmlkZW9Tb3VyY2UubXNpZDtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5fcmVtb3RlTWFwcy5tc2lkMnNzcmNbdmlkZW9Tb3VyY2UubXNpZF0gPSB2aWRlb1NvdXJjZS5zc3JjO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuX3NldFJlY2VpdmluZ1ZpZGVvU3RyZWFtID0gZnVuY3Rpb24gKHJlc291cmNlLCBzc3JjKSB7XG4gICAgdGhpcy5fcmVtb3RlTWFwcy5yZWNlaXZpbmdWaWRlb1N0cmVhbXNbcmVzb3VyY2VdID0gc3NyYztcbn07XG5cbi8qKlxuICogUmV0dXJucyBhIHN0cmVhbSB3aXRoIHNpbmdsZSB2aWRlbyB0cmFjaywgdGhlIG9uZSBjdXJyZW50bHkgYmVpbmdcbiAqIHJlY2VpdmVkIGJ5IHRoaXMgZW5kcG9pbnQuXG4gKlxuICogQHBhcmFtIHN0cmVhbSB0aGUgcmVtb3RlIHNpbXVsY2FzdCBzdHJlYW0uXG4gKiBAcmV0dXJucyB7d2Via2l0TWVkaWFTdHJlYW19XG4gKi9cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbSA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICB2YXIgdHJhY2tzLCBpLCBlbGVjdGVkVHJhY2ssIG1zaWQsIHF1YWxpdHkgPSAwLCByZWNlaXZpbmdUcmFja0lkO1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmIChjb25maWcuZW5hYmxlU2ltdWxjYXN0KSB7XG5cbiAgICAgICAgc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkuc29tZShmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgICAgIHJldHVybiBPYmplY3Qua2V5cyhzZWxmLl9yZW1vdGVNYXBzLnJlY2VpdmluZ1ZpZGVvU3RyZWFtcykuc29tZShmdW5jdGlvbiAocmVzb3VyY2UpIHtcbiAgICAgICAgICAgICAgICB2YXIgc3NyYyA9IHNlbGYuX3JlbW90ZU1hcHMucmVjZWl2aW5nVmlkZW9TdHJlYW1zW3Jlc291cmNlXTtcbiAgICAgICAgICAgICAgICB2YXIgbXNpZCA9IHNlbGYuX3JlbW90ZU1hcHMuc3NyYzJNc2lkW3NzcmNdO1xuICAgICAgICAgICAgICAgIGlmIChtc2lkID09IFtzdHJlYW0uaWQsIHRyYWNrLmlkXS5qb2luKCcgJykpIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlY3RlZFRyYWNrID0gdHJhY2s7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoIWVsZWN0ZWRUcmFjaykge1xuICAgICAgICAgICAgLy8gd2UgZG9uJ3QgaGF2ZSBhbiBlbGVjdGVkIHRyYWNrLCBjaG9vc2UgYnkgaW5pdGlhbCBxdWFsaXR5LlxuICAgICAgICAgICAgdHJhY2tzID0gc3RyZWFtLmdldFZpZGVvVHJhY2tzKCk7XG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgbXNpZCA9IFtzdHJlYW0uaWQsIHRyYWNrc1tpXS5pZF0uam9pbignICcpO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLl9yZW1vdGVNYXBzLm1zaWQyUXVhbGl0eVttc2lkXSA9PT0gcXVhbGl0eSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVjdGVkVHJhY2sgPSB0cmFja3NbaV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gVE9ETyhncCkgaWYgdGhlIGluaXRpYWxRdWFsaXR5IGNvdWxkIG5vdCBiZSBzYXRpc2ZpZWQsIGxvd2VyXG4gICAgICAgICAgICAvLyB0aGUgcmVxdWlyZW1lbnQgYW5kIHRyeSBhZ2Fpbi5cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiAoZWxlY3RlZFRyYWNrKVxuICAgICAgICA/IG5ldyB3ZWJraXRNZWRpYVN0cmVhbShbZWxlY3RlZFRyYWNrXSlcbiAgICAgICAgOiBzdHJlYW07XG59O1xuXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nU1NSQyA9IGZ1bmN0aW9uIChqaWQpIHtcbiAgICB2YXIgcmVzb3VyY2UgPSBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpO1xuICAgIHZhciBzc3JjID0gdGhpcy5fcmVtb3RlTWFwcy5yZWNlaXZpbmdWaWRlb1N0cmVhbXNbcmVzb3VyY2VdO1xuXG4gICAgLy8gSWYgd2UgaGF2ZW4ndCByZWNlaXZpbmcgYSBcImNoYW5nZWRcIiBldmVudCB5ZXQsIHRoZW4gd2UgbXVzdCBiZSByZWNlaXZpbmdcbiAgICAvLyBsb3cgcXVhbGl0eSAodGhhdCB0aGUgc2VuZGVyIGFsd2F5cyBzdHJlYW1zKS5cbiAgICBpZighc3NyYylcbiAgICB7XG4gICAgICAgIHZhciByZW1vdGVTdHJlYW1PYmplY3QgPSBBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgIHZhciByZW1vdGVTdHJlYW0gPSByZW1vdGVTdHJlYW1PYmplY3QuZ2V0T3JpZ2luYWxTdHJlYW0oKTtcbiAgICAgICAgdmFyIHRyYWNrcyA9IHJlbW90ZVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICBpZiAodHJhY2tzKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBrID0gMDsgayA8IHRyYWNrcy5sZW5ndGg7IGsrKykge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IHRyYWNrc1trXTtcbiAgICAgICAgICAgICAgICB2YXIgbXNpZCA9IFtyZW1vdGVTdHJlYW0uaWQsIHRyYWNrLmlkXS5qb2luKCcgJyk7XG4gICAgICAgICAgICAgICAgdmFyIF9zc3JjID0gdGhpcy5fcmVtb3RlTWFwcy5tc2lkMnNzcmNbbXNpZF07XG4gICAgICAgICAgICAgICAgdmFyIHF1YWxpdHkgPSB0aGlzLl9yZW1vdGVNYXBzLm1zaWQyUXVhbGl0eVttc2lkXTtcbiAgICAgICAgICAgICAgICBpZiAocXVhbGl0eSA9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNzcmMgPSBfc3NyYztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gc3NyYztcbn07XG5cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS5nZXRSZWNlaXZpbmdWaWRlb1N0cmVhbUJ5U1NSQyA9IGZ1bmN0aW9uIChzc3JjKVxue1xuICAgIHZhciBzaWQsIGVsZWN0ZWRTdHJlYW07XG4gICAgdmFyIGksIGosIGs7XG4gICAgdmFyIGppZCA9IEFQUC54bXBwLmdldEppZEZyb21TU1JDKHNzcmMpO1xuICAgIGlmKGppZCAmJiBBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXSlcbiAgICB7XG4gICAgICAgIHZhciByZW1vdGVTdHJlYW1PYmplY3QgPSBBUFAuUlRDLnJlbW90ZVN0cmVhbXNbamlkXVtNZWRpYVN0cmVhbVR5cGUuVklERU9fVFlQRV07XG4gICAgICAgIHZhciByZW1vdGVTdHJlYW0gPSByZW1vdGVTdHJlYW1PYmplY3QuZ2V0T3JpZ2luYWxTdHJlYW0oKTtcbiAgICAgICAgdmFyIHRyYWNrcyA9IHJlbW90ZVN0cmVhbS5nZXRWaWRlb1RyYWNrcygpO1xuICAgICAgICBpZiAodHJhY2tzKSB7XG4gICAgICAgICAgICBmb3IgKGsgPSAwOyBrIDwgdHJhY2tzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRyYWNrID0gdHJhY2tzW2tdO1xuICAgICAgICAgICAgICAgIHZhciBtc2lkID0gW3JlbW90ZVN0cmVhbS5pZCwgdHJhY2suaWRdLmpvaW4oJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIgdG1wID0gdGhpcy5fcmVtb3RlTWFwcy5tc2lkMnNzcmNbbXNpZF07XG4gICAgICAgICAgICAgICAgaWYgKHRtcCA9PSBzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZWN0ZWRTdHJlYW0gPSBuZXcgd2Via2l0TWVkaWFTdHJlYW0oW3RyYWNrXSk7XG4gICAgICAgICAgICAgICAgICAgIHNpZCA9IHJlbW90ZVN0cmVhbU9iamVjdC5zaWQ7XG4gICAgICAgICAgICAgICAgICAgIC8vIHN0cmVhbSBmb3VuZCwgc3RvcC5cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgY29uc29sZS5kZWJ1ZyhBUFAuUlRDLnJlbW90ZVN0cmVhbXMsIGppZCwgc3NyYyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgc2lkOiBzaWQsXG4gICAgICAgIHN0cmVhbTogZWxlY3RlZFN0cmVhbVxuICAgIH07XG59O1xuXG4vKipcbiAqIEdldHMgdGhlIGZ1bGx5IHF1YWxpZmllZCBtc2lkIChzdHJlYW0uaWQgKyB0cmFjay5pZCkgYXNzb2NpYXRlZCB0byB0aGVcbiAqIFNTUkMuXG4gKlxuICogQHBhcmFtIHNzcmNcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RSZWNlaXZlci5wcm90b3R5cGUuZ2V0UmVtb3RlVmlkZW9TdHJlYW1JZEJ5U1NSQyA9IGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgcmV0dXJuIHRoaXMuX3JlbW90ZU1hcHMuc3NyYzJNc2lkW3NzcmNdO1xufTtcblxuLyoqXG4gKiBSZW1vdmVzIHRoZSBzc3JjLWdyb3VwOlNJTSBmcm9tIHRoZSByZW1vdGUgZGVzY3JpcHRpb24gYmFjYXVzZSBDaHJvbWVcbiAqIGVpdGhlciBnZXRzIGNvbmZ1c2VkIGFuZCB0aGlua3MgdGhpcyBpcyBhbiBGSUQgZ3JvdXAgb3IsIGlmIGFuIEZJRCBncm91cFxuICogaXMgYWxyZWFkeSBwcmVzZW50LCBpdCBmYWlscyB0byBzZXQgdGhlIHJlbW90ZSBkZXNjcmlwdGlvbi5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdFJlY2VpdmVyLnByb3RvdHlwZS50cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG5cbiAgICBpZiAoZGVzYyAmJiBkZXNjLnNkcCkge1xuICAgICAgICB2YXIgc2IgPSBkZXNjLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG5cbiAgICAgICAgdGhpcy5fdXBkYXRlUmVtb3RlTWFwcyhzYik7XG4gICAgICAgIHRoaXMuX2NhY2hlUmVtb3RlVmlkZW9Tb3VyY2VzKHNiKTtcblxuICAgICAgICAvLyBOT1RFKGdwKSB0aGlzIG5lZWRzIHRvIGJlIGNhbGxlZCBhZnRlciB1cGRhdGVSZW1vdGVNYXBzIGJlY2F1c2Ugd2VcbiAgICAgICAgLy8gbmVlZCB0aGUgc2ltdWxjYXN0IGdyb3VwIGluIHRoZSBfdXBkYXRlUmVtb3RlTWFwcygpIG1ldGhvZC5cbiAgICAgICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVtb3ZlU2ltdWxjYXN0R3JvdXAoc2IpO1xuXG4gICAgICAgIGlmIChkZXNjLnNkcC5pbmRleE9mKCdhPXNzcmMtZ3JvdXA6U0lNJykgIT09IC0xKSB7XG4gICAgICAgICAgICAvLyBXZSBkb24ndCBuZWVkIHRoZSBnb29nIGNvbmZlcmVuY2UgZmxhZyBpZiB3ZSdyZSBub3QgZG9pbmdcbiAgICAgICAgICAgIC8vIHNpbXVsY2FzdC5cbiAgICAgICAgICAgIHRoaXMuX2Vuc3VyZUdvb2dDb25mZXJlbmNlKHNiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuZmluZShbJ1RyYW5zZm9ybWVkIHJlbW90ZSBkZXNjcmlwdGlvbicsIGRlc2Muc2RwXS5qb2luKCcgJykpO1xuICAgIH1cblxuICAgIHJldHVybiBkZXNjO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTaW11bGNhc3RSZWNlaXZlcjsiLCJ2YXIgU2ltdWxjYXN0TG9nZ2VyID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0TG9nZ2VyXCIpO1xudmFyIFNpbXVsY2FzdFV0aWxzID0gcmVxdWlyZShcIi4vU2ltdWxjYXN0VXRpbHNcIik7XG5cbmZ1bmN0aW9uIFNpbXVsY2FzdFNlbmRlcigpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzID0gbmV3IFNpbXVsY2FzdFV0aWxzKCk7XG4gICAgdGhpcy5sb2dnZXIgPSBuZXcgU2ltdWxjYXN0TG9nZ2VyKCdTaW11bGNhc3RTZW5kZXInLCAxKTtcbn1cblxuU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5kaXNwbGF5ZWRMb2NhbFZpZGVvU3RyZWFtID0gbnVsbDtcblxuU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fZ2VuZXJhdGVHdWlkID0gKGZ1bmN0aW9uICgpIHtcbiAgICBmdW5jdGlvbiBzNCgpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKDEgKyBNYXRoLnJhbmRvbSgpKSAqIDB4MTAwMDApXG4gICAgICAgICAgICAudG9TdHJpbmcoMTYpXG4gICAgICAgICAgICAuc3Vic3RyaW5nKDEpO1xuICAgIH1cblxuICAgIHJldHVybiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzNCgpICsgczQoKSArICctJyArIHM0KCkgKyAnLScgKyBzNCgpICsgJy0nICtcbiAgICAgICAgICAgIHM0KCkgKyAnLScgKyBzNCgpICsgczQoKSArIHM0KCk7XG4gICAgfTtcbn0oKSk7XG5cbi8vIFJldHVybnMgYSByYW5kb20gaW50ZWdlciBiZXR3ZWVuIG1pbiAoaW5jbHVkZWQpIGFuZCBtYXggKGV4Y2x1ZGVkKVxuLy8gVXNpbmcgTWF0aC5yb3VuZCgpIGdpdmVzIGEgbm9uLXVuaWZvcm0gZGlzdHJpYnV0aW9uIVxuU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fZ2VuZXJhdGVSYW5kb21TU1JDID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBtaW4gPSAwLCBtYXggPSAweGZmZmZmZmZmO1xuICAgIHJldHVybiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSkgKyBtaW47XG59O1xuXG5TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmdldExvY2FsVmlkZW9TdHJlYW0gPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuICh0aGlzLmRpc3BsYXllZExvY2FsVmlkZW9TdHJlYW0gIT0gbnVsbClcbiAgICAgICAgPyB0aGlzLmRpc3BsYXllZExvY2FsVmlkZW9TdHJlYW1cbiAgICAgICAgLy8gaW4gY2FzZSB3ZSBoYXZlIG5vIHNpbXVsY2FzdCBhdCBhbGwsIGkuZS4gd2UgZGlkbid0IHBlcmZvcm0gdGhlIEdVTVxuICAgICAgICA6IEFQUC5SVEMubG9jYWxWaWRlby5nZXRPcmlnaW5hbFN0cmVhbSgpO1xufTtcblxuZnVuY3Rpb24gTmF0aXZlU2ltdWxjYXN0U2VuZGVyKCkge1xuICAgIFNpbXVsY2FzdFNlbmRlci5jYWxsKHRoaXMpOyAvLyBjYWxsIHRoZSBzdXBlciBjb25zdHJ1Y3Rvci5cbn1cblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSk7XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2xvY2FsRXhwbG9zaW9uTWFwID0ge307XG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9pc1VzaW5nU2NyZWVuU3RyZWFtID0gZmFsc2U7XG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9sb2NhbFZpZGVvU291cmNlQ2FjaGUgPSAnJztcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5yZXNldCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLl9sb2NhbEV4cGxvc2lvbk1hcCA9IHt9O1xuICAgIHRoaXMuX2lzVXNpbmdTY3JlZW5TdHJlYW0gPSBBUFAuZGVza3RvcHNoYXJpbmcuaXNVc2luZ1NjcmVlblN0cmVhbSgpO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fY2FjaGVMb2NhbFZpZGVvU291cmNlcyA9IGZ1bmN0aW9uIChsaW5lcykge1xuICAgIHRoaXMuX2xvY2FsVmlkZW9Tb3VyY2VDYWNoZSA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2dldFZpZGVvU291cmNlcyhsaW5lcyk7XG59O1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9yZXN0b3JlTG9jYWxWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCB0aGlzLl9sb2NhbFZpZGVvU291cmNlQ2FjaGUpO1xufTtcblxuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fYXBwZW5kU2ltdWxjYXN0R3JvdXAgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgdmlkZW9Tb3VyY2VzLCBzc3JjR3JvdXAsIHNpbVNTUkMsIG51bU9mU3VicyA9IDIsIGksIHNiLCBtc2lkO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnQXBwZW5kaW5nIHNpbXVsY2FzdCBncm91cC4uLicpO1xuXG4gICAgLy8gR2V0IHRoZSBwcmltYXJ5IFNTUkMgaW5mb3JtYXRpb24uXG4gICAgdmlkZW9Tb3VyY2VzID0gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdO1xuXG4gICAgLy8gU3RhcnQgYnVpbGRpbmcgdGhlIFNJTSBTU1JDIGdyb3VwLlxuICAgIHNzcmNHcm91cCA9IFsnYT1zc3JjLWdyb3VwOlNJTSddO1xuXG4gICAgLy8gVGhlIHZpZGVvIHNvdXJjZSBidWZmZXIuXG4gICAgc2IgPSBbXTtcblxuICAgIC8vIENyZWF0ZSB0aGUgc2ltdWxjYXN0IHN1Yi1zdHJlYW1zLlxuICAgIGZvciAoaSA9IDA7IGkgPCBudW1PZlN1YnM7IGkrKykge1xuICAgICAgICAvLyBUT0RPKGdwKSBwcmV2ZW50IFNTUkMgY29sbGlzaW9uLlxuICAgICAgICBzaW1TU1JDID0gdGhpcy5fZ2VuZXJhdGVSYW5kb21TU1JDKCk7XG4gICAgICAgIHNzcmNHcm91cC5wdXNoKHNpbVNTUkMpO1xuXG4gICAgICAgIGlmICh2aWRlb1NvdXJjZXMuYmFzZSkge1xuICAgICAgICAgICAgc2Iuc3BsaWNlLmFwcGx5KHNiLCBbc2IubGVuZ3RoLCAwXS5jb25jYXQoXG4gICAgICAgICAgICAgICAgW1tcImE9c3NyYzpcIiwgc2ltU1NSQywgXCIgY25hbWU6XCIsIHZpZGVvU291cmNlcy5iYXNlLmNuYW1lXS5qb2luKCcnKSxcbiAgICAgICAgICAgICAgICAgICAgW1wiYT1zc3JjOlwiLCBzaW1TU1JDLCBcIiBtc2lkOlwiLCB2aWRlb1NvdXJjZXMuYmFzZS5tc2lkXS5qb2luKCcnKV1cbiAgICAgICAgICAgICkpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbyhbJ0dlbmVyYXRlZCBzdWJzdHJlYW0gJywgaSwgJyB3aXRoIFNTUkMgJywgc2ltU1NSQywgJy4nXS5qb2luKCcnKSk7XG5cbiAgICB9XG5cbiAgICAvLyBBZGQgdGhlIGdyb3VwIHNpbSBsYXllcnMuXG4gICAgc2Iuc3BsaWNlKDAsIDAsIHNzcmNHcm91cC5qb2luKCcgJykpXG5cbiAgICB0aGlzLnNpbXVsY2FzdFV0aWxzLl9yZXBsYWNlVmlkZW9Tb3VyY2VzKGxpbmVzLCBzYik7XG59O1xuXG4vLyBEb2VzIHRoZSBhY3R1YWwgcGF0Y2hpbmcuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9lbnN1cmVTaW11bGNhc3RHcm91cCA9IGZ1bmN0aW9uIChsaW5lcykge1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnRW5zdXJpbmcgc2ltdWxjYXN0IGdyb3VwLi4uJyk7XG5cbiAgICBpZiAodGhpcy5zaW11bGNhc3RVdGlscy5faW5kZXhPZkFycmF5KCdhPXNzcmMtZ3JvdXA6U0lNJywgbGluZXMpID09PSB0aGlzLnNpbXVsY2FzdFV0aWxzLl9lbXB0eUNvbXBvdW5kSW5kZXgpIHtcbiAgICAgICAgdGhpcy5fYXBwZW5kU2ltdWxjYXN0R3JvdXAobGluZXMpO1xuICAgICAgICB0aGlzLl9jYWNoZUxvY2FsVmlkZW9Tb3VyY2VzKGxpbmVzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvLyB2ZXJpZnkgdGhhdCB0aGUgc3NyY3MgcGFydGljaXBhdGluZyBpbiB0aGUgU0lNIGdyb3VwIGFyZSBwcmVzZW50XG4gICAgICAgIC8vIGluIHRoZSBTRFAgKG5lZWRlZCBmb3IgcHJlc2VuY2UpLlxuICAgICAgICB0aGlzLl9yZXN0b3JlTG9jYWxWaWRlb1NvdXJjZXMobGluZXMpO1xuICAgIH1cbn07XG5cbi8qKlxuICogUHJvZHVjZXMgYSBzaW5nbGUgc3RyZWFtIHdpdGggbXVsdGlwbGUgdHJhY2tzIGZvciBsb2NhbCB2aWRlbyBzb3VyY2VzLlxuICpcbiAqIEBwYXJhbSBsaW5lc1xuICogQHByaXZhdGVcbiAqL1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fZXhwbG9kZVNpbXVsY2FzdFNlbmRlclNvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgc2IsIG1zaWQsIHNpZCwgdGlkLCB2aWRlb1NvdXJjZXMsIHNlbGY7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdFeHBsb2RpbmcgbG9jYWwgdmlkZW8gc291cmNlcy4uLicpO1xuXG4gICAgdmlkZW9Tb3VyY2VzID0gdGhpcy5zaW11bGNhc3RVdGlscy5wYXJzZU1lZGlhKGxpbmVzLCBbJ3ZpZGVvJ10pWzBdO1xuXG4gICAgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHZpZGVvU291cmNlcy5ncm91cHMgJiYgdmlkZW9Tb3VyY2VzLmdyb3Vwcy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgdmlkZW9Tb3VyY2VzLmdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChncm91cCkge1xuICAgICAgICAgICAgaWYgKGdyb3VwLnNlbWFudGljcyA9PT0gJ1NJTScpIHtcbiAgICAgICAgICAgICAgICBncm91cC5zc3Jjcy5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBtc2lkIGZvciB0aGlzIHNzcmMuLlxuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5fbG9jYWxFeHBsb3Npb25NYXBbc3NyY10pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIC4uIGVpdGhlciBmcm9tIHRoZSBleHBsb3Npb24gbWFwLi5cbiAgICAgICAgICAgICAgICAgICAgICAgIG1zaWQgPSBzZWxmLl9sb2NhbEV4cGxvc2lvbk1hcFtzc3JjXTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIC4uIG9yIGdlbmVyYXRlIGEgbmV3IG9uZSAobXNpZCkuXG4gICAgICAgICAgICAgICAgICAgICAgICBzaWQgPSB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLnN1YnN0cmluZygwLCB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkLmluZGV4T2YoJyAnKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHRpZCA9IHNlbGYuX2dlbmVyYXRlR3VpZCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IFtzaWQsIHRpZF0uam9pbignICcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5fbG9jYWxFeHBsb3Npb25NYXBbc3NyY10gPSBtc2lkO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gQXNzaWduIGl0IHRvIHRoZSBzb3VyY2Ugb2JqZWN0LlxuICAgICAgICAgICAgICAgICAgICB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkID0gbXNpZDtcblxuICAgICAgICAgICAgICAgICAgICAvLyBUT0RPKGdwKSBDaGFuZ2UgdGhlIG1zaWQgb2YgYXNzb2NpYXRlZCBzb3VyY2VzLlxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBzYiA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2NvbXBpbGVWaWRlb1NvdXJjZXModmlkZW9Tb3VyY2VzKTtcblxuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHNiKTtcbn07XG5cbi8qKlxuICogR1VNIGZvciBzaW11bGNhc3QuXG4gKlxuICogQHBhcmFtIGNvbnN0cmFpbnRzXG4gKiBAcGFyYW0gc3VjY2Vzc1xuICogQHBhcmFtIGVyclxuICovXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgc3VjY2VzcywgZXJyKSB7XG5cbiAgICAvLyBUaGVyZSdzIG5vdGhpbmcgc3BlY2lhbCB0byBkbyBmb3IgbmF0aXZlIHNpbXVsY2FzdCwgc28ganVzdCBkbyBhIG5vcm1hbCBHVU0uXG4gICAgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYShjb25zdHJhaW50cywgZnVuY3Rpb24gKGhxU3RyZWFtKSB7XG4gICAgICAgIHN1Y2Nlc3MoaHFTdHJlYW0pO1xuICAgIH0sIGVycik7XG59O1xuXG4vKipcbiAqIFByZXBhcmVzIHRoZSBsb2NhbCBkZXNjcmlwdGlvbiBmb3IgcHVibGljIHVzYWdlIChpLmUuIHRvIGJlIHNpZ25hbGVkXG4gKiB0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7UlRDU2Vzc2lvbkRlc2NyaXB0aW9ufVxuICovXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICB2YXIgc2I7XG5cbiAgICBpZiAoIXRoaXMuc2ltdWxjYXN0VXRpbHMuaXNWYWxpZERlc2NyaXB0aW9uKGRlc2MpIHx8IHRoaXMuX2lzVXNpbmdTY3JlZW5TdHJlYW0pIHtcbiAgICAgICAgcmV0dXJuIGRlc2M7XG4gICAgfVxuXG5cbiAgICBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgIHRoaXMuX2V4cGxvZGVTaW11bGNhc3RTZW5kZXJTb3VyY2VzKHNiKTtcblxuICAgIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogZGVzYy50eXBlLFxuICAgICAgICBzZHA6IHNiLmpvaW4oJ1xcclxcbicpXG4gICAgfSk7XG5cbiAgICB0aGlzLmxvZ2dlci5maW5lKFsnRXhwbG9kZWQgbG9jYWwgdmlkZW8gc291cmNlcycsIGRlc2Muc2RwXS5qb2luKCcgJykpO1xuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG4vKipcbiAqIEVuc3VyZXMgdGhhdCB0aGUgc2ltdWxjYXN0IGdyb3VwIGlzIHByZXNlbnQgaW4gdGhlIGFuc3dlciwgX2lmXyBuYXRpdmVcbiAqIHNpbXVsY2FzdCBpcyBlbmFibGVkLFxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuTmF0aXZlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1BbnN3ZXIgPSBmdW5jdGlvbiAoZGVzYykge1xuXG4gICAgaWYgKCF0aGlzLnNpbXVsY2FzdFV0aWxzLmlzVmFsaWREZXNjcmlwdGlvbihkZXNjKSB8fCB0aGlzLl9pc1VzaW5nU2NyZWVuU3RyZWFtKSB7XG4gICAgICAgIHJldHVybiBkZXNjO1xuICAgIH1cblxuICAgIHZhciBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgIC8vIEV2ZW4gaWYgd2UgaGF2ZSBlbmFibGVkIG5hdGl2ZSBzaW11bGNhc3RpbmcgcHJldmlvdXNseVxuICAgIC8vICh3aXRoIGEgY2FsbCB0byBTTEQgd2l0aCBhbiBhcHByb3ByaWF0ZSBTRFAsIGZvciBleGFtcGxlKSxcbiAgICAvLyBjcmVhdGVBbnN3ZXIgc2VlbXMgdG8gY29uc2lzdGVudGx5IGdlbmVyYXRlIGluY29tcGxldGUgU0RQXG4gICAgLy8gd2l0aCBtaXNzaW5nIFNTUkNTLlxuICAgIC8vXG4gICAgLy8gU28sIHN1YnNlcXVlbnQgY2FsbHMgdG8gU0xEIHdpbGwgaGF2ZSBtaXNzaW5nIFNTUkNTIGFuZCBwcmVzZW5jZVxuICAgIC8vIHdvbid0IGhhdmUgdGhlIGNvbXBsZXRlIGxpc3Qgb2YgU1JDcy5cbiAgICB0aGlzLl9lbnN1cmVTaW11bGNhc3RHcm91cChzYik7XG5cbiAgICBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgc2RwOiBzYi5qb2luKCdcXHJcXG4nKVxuICAgIH0pO1xuXG4gICAgdGhpcy5sb2dnZXIuZmluZShbJ1RyYW5zZm9ybWVkIGFuc3dlcicsIGRlc2Muc2RwXS5qb2luKCcgJykpO1xuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5cbi8qKlxuICpcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5OYXRpdmVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLl9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZCA9IGZ1bmN0aW9uIChzc3JjLCBlbmFibGVkKSB7XG4gICAgLy8gTm90aGluZyB0byBkbyBoZXJlLCBuYXRpdmUgc2ltdWxjYXN0IGRvZXMgdGhhdCBhdXRvLW1hZ2ljYWxseS5cbn07XG5cbk5hdGl2ZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBOYXRpdmVTaW11bGNhc3RTZW5kZXI7XG5cbmZ1bmN0aW9uIFNpbXBsZVNpbXVsY2FzdFNlbmRlcigpIHtcbiAgICBTaW11bGNhc3RTZW5kZXIuY2FsbCh0aGlzKTtcbn1cblxuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSk7XG5cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUubG9jYWxTdHJlYW0gPSBudWxsO1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fbG9jYWxNYXBzID0ge1xuICAgIG1zaWRzOiBbXSxcbiAgICBtc2lkMnNzcmM6IHt9XG59O1xuXG4vKipcbiAqIEdyb3VwcyBsb2NhbCB2aWRlbyBzb3VyY2VzIHRvZ2V0aGVyIGluIHRoZSBzc3JjLWdyb3VwOlNJTSBncm91cC5cbiAqXG4gKiBAcGFyYW0gbGluZXNcbiAqIEBwcml2YXRlXG4gKi9cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX2dyb3VwTG9jYWxWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgc2IsIHZpZGVvU291cmNlcywgc3NyY3MgPSBbXSwgc3NyYztcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0dyb3VwaW5nIGxvY2FsIHZpZGVvIHNvdXJjZXMuLi4nKTtcblxuICAgIHZpZGVvU291cmNlcyA9IHRoaXMuc2ltdWxjYXN0VXRpbHMucGFyc2VNZWRpYShsaW5lcywgWyd2aWRlbyddKVswXTtcblxuICAgIGZvciAoc3NyYyBpbiB2aWRlb1NvdXJjZXMuc291cmNlcykge1xuICAgICAgICAvLyBqaXRzaS1tZWV0IGRlc3Ryb3lzL2NyZWF0ZXMgc3RyZWFtcyBhdCB2YXJpb3VzIHBsYWNlcyBjYXVzaW5nXG4gICAgICAgIC8vIHRoZSBvcmlnaW5hbCBsb2NhbCBzdHJlYW0gaWRzIHRvIGNoYW5nZS4gVGhlIG9ubHkgdGhpbmcgdGhhdFxuICAgICAgICAvLyByZW1haW5zIHVuY2hhbmdlZCBpcyB0aGUgdHJhY2tpZC5cbiAgICAgICAgdGhpcy5fbG9jYWxNYXBzLm1zaWQyc3NyY1t2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5tc2lkLnNwbGl0KCcgJylbMV1dID0gc3NyYztcbiAgICB9XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gVE9ETyhncCkgYWRkIG9ubHkgXCJmcmVlXCIgc291cmNlcy5cbiAgICB0aGlzLl9sb2NhbE1hcHMubXNpZHMuZm9yRWFjaChmdW5jdGlvbiAobXNpZCkge1xuICAgICAgICBzc3Jjcy5wdXNoKHNlbGYuX2xvY2FsTWFwcy5tc2lkMnNzcmNbbXNpZF0pO1xuICAgIH0pO1xuXG4gICAgaWYgKCF2aWRlb1NvdXJjZXMuZ3JvdXBzKSB7XG4gICAgICAgIHZpZGVvU291cmNlcy5ncm91cHMgPSBbXTtcbiAgICB9XG5cbiAgICB2aWRlb1NvdXJjZXMuZ3JvdXBzLnB1c2goe1xuICAgICAgICAnc2VtYW50aWNzJzogJ1NJTScsXG4gICAgICAgICdzc3Jjcyc6IHNzcmNzXG4gICAgfSk7XG5cbiAgICBzYiA9IHRoaXMuc2ltdWxjYXN0VXRpbHMuX2NvbXBpbGVWaWRlb1NvdXJjZXModmlkZW9Tb3VyY2VzKTtcblxuICAgIHRoaXMuc2ltdWxjYXN0VXRpbHMuX3JlcGxhY2VWaWRlb1NvdXJjZXMobGluZXMsIHNiKTtcbn07XG5cbi8qKlxuICogR1VNIGZvciBzaW11bGNhc3QuXG4gKlxuICogQHBhcmFtIGNvbnN0cmFpbnRzXG4gKiBAcGFyYW0gc3VjY2Vzc1xuICogQHBhcmFtIGVyclxuICovXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgc3VjY2VzcywgZXJyKSB7XG5cbiAgICAvLyBUT0RPKGdwKSB3aGF0IGlmIHdlIHJlcXVlc3QgYSByZXNvbHV0aW9uIG5vdCBzdXBwb3J0ZWQgYnkgdGhlIGhhcmR3YXJlP1xuICAgIC8vIFRPRE8oZ3ApIG1ha2UgdGhlIGxxIHN0cmVhbSBjb25maWd1cmFibGU7IGFsdGhvdWdoIHRoaXMgd291bGRuJ3Qgd29yayB3aXRoIG5hdGl2ZSBzaW11bGNhc3RcbiAgICB2YXIgbHFDb25zdHJhaW50cyA9IHtcbiAgICAgICAgYXVkaW86IGZhbHNlLFxuICAgICAgICB2aWRlbzoge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgICAgbWF4V2lkdGg6IDMyMCxcbiAgICAgICAgICAgICAgICBtYXhIZWlnaHQ6IDE4MCxcbiAgICAgICAgICAgICAgICBtYXhGcmFtZVJhdGU6IDE1XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnSFEgY29uc3RyYWludHM6ICcsIGNvbnN0cmFpbnRzKTtcbiAgICB0aGlzLmxvZ2dlci5pbmZvKCdMUSBjb25zdHJhaW50czogJywgbHFDb25zdHJhaW50cyk7XG5cblxuICAgIC8vIE5PVEUoZ3ApIGlmIHdlIHJlcXVlc3QgdGhlIGxxIHN0cmVhbSBmaXJzdCB3ZWJraXRHZXRVc2VyTWVkaWFcbiAgICAvLyBmYWlscyByYW5kb21seS4gVGVzdGVkIHdpdGggQ2hyb21lIDM3LiBBcyBmaXBwbyBzdWdnZXN0ZWQsIHRoZVxuICAgIC8vIHJlYXNvbiBhcHBlYXJzIHRvIGJlIHRoYXQgQ2hyb21lIG9ubHkgYWNxdWlyZXMgdGhlIGNhbSBvbmNlIGFuZFxuICAgIC8vIHRoZW4gZG93bnNjYWxlcyB0aGUgcGljdHVyZSAoaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTM0NjYxNiNjMTEpXG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYShjb25zdHJhaW50cywgZnVuY3Rpb24gKGhxU3RyZWFtKSB7XG5cbiAgICAgICAgc2VsZi5sb2NhbFN0cmVhbSA9IGhxU3RyZWFtO1xuXG4gICAgICAgIC8vIHJlc2V0IGxvY2FsIG1hcHMuXG4gICAgICAgIHNlbGYuX2xvY2FsTWFwcy5tc2lkcyA9IFtdO1xuICAgICAgICBzZWxmLl9sb2NhbE1hcHMubXNpZDJzc3JjID0ge307XG5cbiAgICAgICAgLy8gYWRkIGhxIHRyYWNraWQgdG8gbG9jYWwgbWFwXG4gICAgICAgIHNlbGYuX2xvY2FsTWFwcy5tc2lkcy5wdXNoKGhxU3RyZWFtLmdldFZpZGVvVHJhY2tzKClbMF0uaWQpO1xuXG4gICAgICAgIG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEobHFDb25zdHJhaW50cywgZnVuY3Rpb24gKGxxU3RyZWFtKSB7XG5cbiAgICAgICAgICAgIHNlbGYuZGlzcGxheWVkTG9jYWxWaWRlb1N0cmVhbSA9IGxxU3RyZWFtO1xuXG4gICAgICAgICAgICAvLyBOT1RFKGdwKSBUaGUgc3BlY2lmaWNhdGlvbiBzYXlzIEFycmF5LmZvckVhY2goKSB3aWxsIHZpc2l0XG4gICAgICAgICAgICAvLyB0aGUgYXJyYXkgZWxlbWVudHMgaW4gbnVtZXJpYyBvcmRlciwgYW5kIHRoYXQgaXQgZG9lc24ndFxuICAgICAgICAgICAgLy8gdmlzaXQgZWxlbWVudHMgdGhhdCBkb24ndCBleGlzdC5cblxuICAgICAgICAgICAgLy8gYWRkIGxxIHRyYWNraWQgdG8gbG9jYWwgbWFwXG4gICAgICAgICAgICBzZWxmLl9sb2NhbE1hcHMubXNpZHMuc3BsaWNlKDAsIDAsIGxxU3RyZWFtLmdldFZpZGVvVHJhY2tzKClbMF0uaWQpO1xuXG4gICAgICAgICAgICBzZWxmLmxvY2FsU3RyZWFtLmFkZFRyYWNrKGxxU3RyZWFtLmdldFZpZGVvVHJhY2tzKClbMF0pO1xuICAgICAgICAgICAgc3VjY2VzcyhzZWxmLmxvY2FsU3RyZWFtKTtcbiAgICAgICAgfSwgZXJyKTtcbiAgICB9LCBlcnIpO1xufTtcblxuLyoqXG4gKiBQcmVwYXJlcyB0aGUgbG9jYWwgZGVzY3JpcHRpb24gZm9yIHB1YmxpYyB1c2FnZSAoaS5lLiB0byBiZSBzaWduYWxlZFxuICogdGhyb3VnaCBKaW5nbGUgdG8gdGhlIGZvY3VzKS5cbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMge1JUQ1Nlc3Npb25EZXNjcmlwdGlvbn1cbiAqL1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgdmFyIHNiO1xuXG4gICAgaWYgKCF0aGlzLnNpbXVsY2FzdFV0aWxzLmlzVmFsaWREZXNjcmlwdGlvbihkZXNjKSkge1xuICAgICAgICByZXR1cm4gZGVzYztcbiAgICB9XG5cbiAgICBzYiA9IGRlc2Muc2RwLnNwbGl0KCdcXHJcXG4nKTtcblxuICAgIHRoaXMuX2dyb3VwTG9jYWxWaWRlb1NvdXJjZXMoc2IpO1xuXG4gICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICB9KTtcblxuICAgIHRoaXMubG9nZ2VyLmZpbmUoJ0dyb3VwZWQgbG9jYWwgdmlkZW8gc291cmNlcycpO1xuICAgIHRoaXMubG9nZ2VyLmZpbmUoZGVzYy5zZHApO1xuXG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG4vKipcbiAqIEVuc3VyZXMgdGhhdCB0aGUgc2ltdWxjYXN0IGdyb3VwIGlzIHByZXNlbnQgaW4gdGhlIGFuc3dlciwgX2lmXyBuYXRpdmVcbiAqIHNpbXVsY2FzdCBpcyBlbmFibGVkLFxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1BbnN3ZXIgPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiBkZXNjO1xufTtcblxuXG4vKipcbiAqXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW1wbGVTaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuXG4gICAgdmFyIHNiID0gZGVzYy5zZHAuc3BsaXQoJ1xcclxcbicpO1xuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscy5fcmVtb3ZlU2ltdWxjYXN0R3JvdXAoc2IpO1xuXG4gICAgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiBkZXNjLnR5cGUsXG4gICAgICAgIHNkcDogc2Iuam9pbignXFxyXFxuJylcbiAgICB9KTtcblxuICAgIHRoaXMubG9nZ2VyLmZpbmUoJ1RyYW5zZm9ybWVkIGxvY2FsIGRlc2NyaXB0aW9uJyk7XG4gICAgdGhpcy5sb2dnZXIuZmluZShkZXNjLnNkcCk7XG5cbiAgICByZXR1cm4gZGVzYztcbn07XG5cblNpbXBsZVNpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuX3NldExvY2FsVmlkZW9TdHJlYW1FbmFibGVkID0gZnVuY3Rpb24gKHNzcmMsIGVuYWJsZWQpIHtcbiAgICB2YXIgdHJhY2tpZDtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLmxvZ2dlci5sb2coWydSZXF1ZXN0ZWQgdG8nLCBlbmFibGVkID8gJ2VuYWJsZScgOiAnZGlzYWJsZScsIHNzcmNdLmpvaW4oJyAnKSk7XG4gICAgaWYgKE9iamVjdC5rZXlzKHRoaXMuX2xvY2FsTWFwcy5tc2lkMnNzcmMpLnNvbWUoZnVuY3Rpb24gKHRpZCkge1xuICAgICAgICAvLyBTZWFyY2ggZm9yIHRoZSB0cmFjayBpZCB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBzc3JjXG4gICAgICAgIGlmIChzZWxmLl9sb2NhbE1hcHMubXNpZDJzc3JjW3RpZF0gPT0gc3NyYykge1xuICAgICAgICAgICAgdHJhY2tpZCA9IHRpZDtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgfSkgJiYgc2VsZi5sb2NhbFN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLnNvbWUoZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICAgIC8vIFN0YXJ0L3N0b3AgdGhlIHRyYWNrIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIHRyYWNrIGlkXG4gICAgICAgIGlmICh0cmFjay5pZCA9PT0gdHJhY2tpZCkge1xuICAgICAgICAgICAgdHJhY2suZW5hYmxlZCA9IGVuYWJsZWQ7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH0pKSB7XG4gICAgICAgIHRoaXMubG9nZ2VyLmxvZyhbdHJhY2tpZCwgZW5hYmxlZCA/ICdlbmFibGVkJyA6ICdkaXNhYmxlZCddLmpvaW4oJyAnKSk7XG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoZW5hYmxlZFxuICAgICAgICAgICAgPyAnc2ltdWxjYXN0bGF5ZXJzdGFydGVkJ1xuICAgICAgICAgICAgOiAnc2ltdWxjYXN0bGF5ZXJzdG9wcGVkJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoXCJJIGRvbid0IGhhdmUgYSBsb2NhbCBzdHJlYW0gd2l0aCBTU1JDIFwiICsgc3NyYyk7XG4gICAgfVxufTtcblxuU2ltcGxlU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFNpbXBsZVNpbXVsY2FzdFNlbmRlcjtcblxuZnVuY3Rpb24gTm9TaW11bGNhc3RTZW5kZXIoKSB7XG4gICAgU2ltdWxjYXN0U2VuZGVyLmNhbGwodGhpcyk7XG59XG5cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZSk7XG5cbi8qKlxuICogR1VNIGZvciBzaW11bGNhc3QuXG4gKlxuICogQHBhcmFtIGNvbnN0cmFpbnRzXG4gKiBAcGFyYW0gc3VjY2Vzc1xuICogQHBhcmFtIGVyclxuICovXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhID0gZnVuY3Rpb24gKGNvbnN0cmFpbnRzLCBzdWNjZXNzLCBlcnIpIHtcbiAgICBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLCBmdW5jdGlvbiAoaHFTdHJlYW0pIHtcbiAgICAgICAgc3VjY2VzcyhocVN0cmVhbSk7XG4gICAgfSwgZXJyKTtcbn07XG5cbi8qKlxuICogUHJlcGFyZXMgdGhlIGxvY2FsIGRlc2NyaXB0aW9uIGZvciBwdWJsaWMgdXNhZ2UgKGkuZS4gdG8gYmUgc2lnbmFsZWRcbiAqIHRocm91Z2ggSmluZ2xlIHRvIHRoZSBmb2N1cykuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHtSVENTZXNzaW9uRGVzY3JpcHRpb259XG4gKi9cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG4vKipcbiAqIEVuc3VyZXMgdGhhdCB0aGUgc2ltdWxjYXN0IGdyb3VwIGlzIHByZXNlbnQgaW4gdGhlIGFuc3dlciwgX2lmXyBuYXRpdmVcbiAqIHNpbXVsY2FzdCBpcyBlbmFibGVkLFxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7Kn1cbiAqL1xuTm9TaW11bGNhc3RTZW5kZXIucHJvdG90eXBlLnRyYW5zZm9ybUFuc3dlciA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIGRlc2M7XG59O1xuXG5cbi8qKlxuICpcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2MpIHtcbiAgICByZXR1cm4gZGVzYztcbn07XG5cbk5vU2ltdWxjYXN0U2VuZGVyLnByb3RvdHlwZS5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQgPSBmdW5jdGlvbiAoc3NyYywgZW5hYmxlZCkge1xuXG59O1xuXG5Ob1NpbXVsY2FzdFNlbmRlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBOb1NpbXVsY2FzdFNlbmRlcjtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgXCJuYXRpdmVcIjogTmF0aXZlU2ltdWxjYXN0U2VuZGVyLFxuICAgIFwibm9cIjogTm9TaW11bGNhc3RTZW5kZXJcbn1cbiIsInZhciBTaW11bGNhc3RMb2dnZXIgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RMb2dnZXJcIik7XG5cbi8qKlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBTaW11bGNhc3RVdGlscygpIHtcbiAgICB0aGlzLmxvZ2dlciA9IG5ldyBTaW11bGNhc3RMb2dnZXIoXCJTaW11bGNhc3RVdGlsc1wiLCAxKTtcbn1cblxuLyoqXG4gKlxuICogQHR5cGUge3t9fVxuICogQHByaXZhdGVcbiAqL1xuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9lbXB0eUNvbXBvdW5kSW5kZXggPSB7fTtcblxuLyoqXG4gKlxuICogQHBhcmFtIGxpbmVzXG4gKiBAcGFyYW0gdmlkZW9Tb3VyY2VzXG4gKiBAcHJpdmF0ZVxuICovXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX3JlcGxhY2VWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAobGluZXMsIHZpZGVvU291cmNlcykge1xuICAgIHZhciBpLCBpblZpZGVvID0gZmFsc2UsIGluZGV4ID0gLTEsIGhvd01hbnkgPSAwO1xuXG4gICAgdGhpcy5sb2dnZXIuaW5mbygnUmVwbGFjaW5nIHZpZGVvIHNvdXJjZXMuLi4nKTtcblxuICAgIGZvciAoaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAoaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ209Jy5sZW5ndGgpID09PSAnbT0nKSB7XG4gICAgICAgICAgICAvLyBPdXQgb2YgdmlkZW8uXG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ209dmlkZW8gJy5sZW5ndGgpID09PSAnbT12aWRlbyAnKSB7XG4gICAgICAgICAgICAvLyBJbiB2aWRlby5cbiAgICAgICAgICAgIGluVmlkZW8gPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGluVmlkZW8gJiYgKGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1zc3JjOicubGVuZ3RoKSA9PT0gJ2E9c3NyYzonXG4gICAgICAgICAgICB8fCBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYy1ncm91cDonLmxlbmd0aCkgPT09ICdhPXNzcmMtZ3JvdXA6JykpIHtcblxuICAgICAgICAgICAgaWYgKGluZGV4ID09PSAtMSkge1xuICAgICAgICAgICAgICAgIGluZGV4ID0gaTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaG93TWFueSsrO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gIGVmZmljaWVuY3kgYmFieSA7KVxuICAgIGxpbmVzLnNwbGljZS5hcHBseShsaW5lcyxcbiAgICAgICAgW2luZGV4LCBob3dNYW55XS5jb25jYXQodmlkZW9Tb3VyY2VzKSk7XG5cbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5pc1ZhbGlkRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYylcbntcbiAgICByZXR1cm4gZGVzYyAmJiBkZXNjICE9IG51bGxcbiAgICAgICAgJiYgZGVzYy50eXBlICYmIGRlc2MudHlwZSAhPSAnJ1xuICAgICAgICAmJiBkZXNjLnNkcCAmJiBkZXNjLnNkcCAhPSAnJztcbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fZ2V0VmlkZW9Tb3VyY2VzID0gZnVuY3Rpb24gKGxpbmVzKSB7XG4gICAgdmFyIGksIGluVmlkZW8gPSBmYWxzZSwgc2IgPSBbXTtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0dldHRpbmcgdmlkZW8gc291cmNlcy4uLicpO1xuXG4gICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnbT0nLmxlbmd0aCkgPT09ICdtPScpIHtcbiAgICAgICAgICAgIC8vIE91dCBvZiB2aWRlby5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFpblZpZGVvICYmIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnbT12aWRlbyAnLmxlbmd0aCkgPT09ICdtPXZpZGVvICcpIHtcbiAgICAgICAgICAgIC8vIEluIHZpZGVvLlxuICAgICAgICAgICAgaW5WaWRlbyA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW5WaWRlbyAmJiBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c3NyYzonLmxlbmd0aCkgPT09ICdhPXNzcmM6Jykge1xuICAgICAgICAgICAgLy8gSW4gU1NSQy5cbiAgICAgICAgICAgIHNiLnB1c2gobGluZXNbaV0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGluVmlkZW8gJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmMtZ3JvdXA6Jy5sZW5ndGgpID09PSAnYT1zc3JjLWdyb3VwOicpIHtcbiAgICAgICAgICAgIHNiLnB1c2gobGluZXNbaV0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNiO1xufTtcblxuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLnBhcnNlTWVkaWEgPSBmdW5jdGlvbiAobGluZXMsIG1lZGlhdHlwZXMpIHtcbiAgICB2YXIgaSwgcmVzID0gW10sIHR5cGUsIGN1cl9tZWRpYSwgaWR4LCBzc3JjcywgY3VyX3NzcmMsIHNzcmMsXG4gICAgICAgIHNzcmNfYXR0cmlidXRlLCBncm91cCwgc2VtYW50aWNzLCBza2lwID0gdHJ1ZTtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ1BhcnNpbmcgbWVkaWEgc291cmNlcy4uLicpO1xuXG4gICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ209Jy5sZW5ndGgpID09PSAnbT0nKSB7XG5cbiAgICAgICAgICAgIHR5cGUgPSBsaW5lc1tpXVxuICAgICAgICAgICAgICAgIC5zdWJzdHIoJ209Jy5sZW5ndGgsIGxpbmVzW2ldLmluZGV4T2YoJyAnKSAtICdtPScubGVuZ3RoKTtcbiAgICAgICAgICAgIHNraXAgPSBtZWRpYXR5cGVzICE9PSB1bmRlZmluZWQgJiYgbWVkaWF0eXBlcy5pbmRleE9mKHR5cGUpID09PSAtMTtcblxuICAgICAgICAgICAgaWYgKCFza2lwKSB7XG4gICAgICAgICAgICAgICAgY3VyX21lZGlhID0ge1xuICAgICAgICAgICAgICAgICAgICAndHlwZSc6IHR5cGUsXG4gICAgICAgICAgICAgICAgICAgICdzb3VyY2VzJzoge30sXG4gICAgICAgICAgICAgICAgICAgICdncm91cHMnOiBbXVxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICByZXMucHVzaChjdXJfbWVkaWEpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgIH0gZWxzZSBpZiAoIXNraXAgJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmM6Jy5sZW5ndGgpID09PSAnYT1zc3JjOicpIHtcblxuICAgICAgICAgICAgaWR4ID0gbGluZXNbaV0uaW5kZXhPZignICcpO1xuICAgICAgICAgICAgc3NyYyA9IGxpbmVzW2ldLnN1YnN0cmluZygnYT1zc3JjOicubGVuZ3RoLCBpZHgpO1xuICAgICAgICAgICAgaWYgKGN1cl9tZWRpYS5zb3VyY2VzW3NzcmNdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBjdXJfc3NyYyA9IHsnc3NyYyc6IHNzcmN9O1xuICAgICAgICAgICAgICAgIGN1cl9tZWRpYS5zb3VyY2VzW3NzcmNdID0gY3VyX3NzcmM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNzcmNfYXR0cmlidXRlID0gbGluZXNbaV0uc3Vic3RyKGlkeCArIDEpLnNwbGl0KCc6JywgMilbMF07XG4gICAgICAgICAgICBjdXJfc3NyY1tzc3JjX2F0dHJpYnV0ZV0gPSBsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVsxXTtcblxuICAgICAgICAgICAgaWYgKGN1cl9tZWRpYS5iYXNlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBjdXJfbWVkaWEuYmFzZSA9IGN1cl9zc3JjO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgIH0gZWxzZSBpZiAoIXNraXAgJiYgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNzcmMtZ3JvdXA6Jy5sZW5ndGgpID09PSAnYT1zc3JjLWdyb3VwOicpIHtcbiAgICAgICAgICAgIGlkeCA9IGxpbmVzW2ldLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgIHNlbWFudGljcyA9IGxpbmVzW2ldLnN1YnN0cigwLCBpZHgpLnN1YnN0cignYT1zc3JjLWdyb3VwOicubGVuZ3RoKTtcbiAgICAgICAgICAgIHNzcmNzID0gbGluZXNbaV0uc3Vic3RyKGlkeCkudHJpbSgpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICBncm91cCA9IHtcbiAgICAgICAgICAgICAgICAnc2VtYW50aWNzJzogc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICdzc3Jjcyc6IHNzcmNzXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgY3VyX21lZGlhLmdyb3Vwcy5wdXNoKGdyb3VwKTtcbiAgICAgICAgfSBlbHNlIGlmICghc2tpcCAmJiAobGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPXNlbmRyZWN2Jy5sZW5ndGgpID09PSAnYT1zZW5kcmVjdicgfHxcbiAgICAgICAgICAgIGxpbmVzW2ldLnN1YnN0cmluZygwLCAnYT1yZWN2b25seScubGVuZ3RoKSA9PT0gJ2E9cmVjdm9ubHknIHx8XG4gICAgICAgICAgICBsaW5lc1tpXS5zdWJzdHJpbmcoMCwgJ2E9c2VuZG9ubHknLmxlbmd0aCkgPT09ICdhPXNlbmRvbmx5JyB8fFxuICAgICAgICAgICAgbGluZXNbaV0uc3Vic3RyaW5nKDAsICdhPWluYWN0aXZlJy5sZW5ndGgpID09PSAnYT1pbmFjdGl2ZScpKSB7XG5cbiAgICAgICAgICAgIGN1cl9tZWRpYS5kaXJlY3Rpb24gPSBsaW5lc1tpXS5zdWJzdHJpbmcoJ2E9Jy5sZW5ndGgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlcztcbn07XG5cbi8qKlxuICogVGhlIF9pbmRleE9mQXJyYXkoKSBtZXRob2QgcmV0dXJucyB0aGUgZmlyc3QgYSBDb21wb3VuZEluZGV4IGF0IHdoaWNoIGFcbiAqIGdpdmVuIGVsZW1lbnQgY2FuIGJlIGZvdW5kIGluIHRoZSBhcnJheSwgb3IgX2VtcHR5Q29tcG91bmRJbmRleCBpZiBpdCBpc1xuICogbm90IHByZXNlbnQuXG4gKlxuICogRXhhbXBsZTpcbiAqXG4gKiBfaW5kZXhPZkFycmF5KCczJywgWyAndGhpcyBpcyBsaW5lIDEnLCAndGhpcyBpcyBsaW5lIDInLCAndGhpcyBpcyBsaW5lIDMnIF0pXG4gKlxuICogcmV0dXJucyB7cm93OiAyLCBjb2x1bW46IDE0fVxuICpcbiAqIEBwYXJhbSBuZWVkbGVcbiAqIEBwYXJhbSBoYXlzdGFja1xuICogQHBhcmFtIHN0YXJ0XG4gKiBAcmV0dXJucyB7fVxuICogQHByaXZhdGVcbiAqL1xuU2ltdWxjYXN0VXRpbHMucHJvdG90eXBlLl9pbmRleE9mQXJyYXkgPSBmdW5jdGlvbiAobmVlZGxlLCBoYXlzdGFjaywgc3RhcnQpIHtcbiAgICB2YXIgbGVuZ3RoID0gaGF5c3RhY2subGVuZ3RoLCBpZHgsIGk7XG5cbiAgICBpZiAoIXN0YXJ0KSB7XG4gICAgICAgIHN0YXJ0ID0gMDtcbiAgICB9XG5cbiAgICBmb3IgKGkgPSBzdGFydDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlkeCA9IGhheXN0YWNrW2ldLmluZGV4T2YobmVlZGxlKTtcbiAgICAgICAgaWYgKGlkeCAhPT0gLTEpIHtcbiAgICAgICAgICAgIHJldHVybiB7cm93OiBpLCBjb2x1bW46IGlkeH07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2VtcHR5Q29tcG91bmRJbmRleDtcbn07XG5cblNpbXVsY2FzdFV0aWxzLnByb3RvdHlwZS5fcmVtb3ZlU2ltdWxjYXN0R3JvdXAgPSBmdW5jdGlvbiAobGluZXMpIHtcbiAgICB2YXIgaTtcblxuICAgIGZvciAoaSA9IGxpbmVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgIGlmIChsaW5lc1tpXS5pbmRleE9mKCdhPXNzcmMtZ3JvdXA6U0lNJykgIT09IC0xKSB7XG4gICAgICAgICAgICBsaW5lcy5zcGxpY2UoaSwgMSk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5TaW11bGNhc3RVdGlscy5wcm90b3R5cGUuX2NvbXBpbGVWaWRlb1NvdXJjZXMgPSBmdW5jdGlvbiAodmlkZW9Tb3VyY2VzKSB7XG4gICAgdmFyIHNiID0gW10sIHNzcmMsIGFkZGVkU1NSQ3MgPSBbXTtcblxuICAgIHRoaXMubG9nZ2VyLmluZm8oJ0NvbXBpbGluZyB2aWRlbyBzb3VyY2VzLi4uJyk7XG5cbiAgICAvLyBBZGQgdGhlIGdyb3Vwc1xuICAgIGlmICh2aWRlb1NvdXJjZXMuZ3JvdXBzICYmIHZpZGVvU291cmNlcy5ncm91cHMubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgIHZpZGVvU291cmNlcy5ncm91cHMuZm9yRWFjaChmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgIGlmIChncm91cC5zc3JjcyAmJiBncm91cC5zc3Jjcy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgICAgICAgICBzYi5wdXNoKFtbJ2E9c3NyYy1ncm91cDonLCBncm91cC5zZW1hbnRpY3NdLmpvaW4oJycpLCBncm91cC5zc3Jjcy5qb2luKCcgJyldLmpvaW4oJyAnKSk7XG5cbiAgICAgICAgICAgICAgICAvLyBpZiAoZ3JvdXAuc2VtYW50aWNzICE9PSAnU0lNJykge1xuICAgICAgICAgICAgICAgIGdyb3VwLnNzcmNzLmZvckVhY2goZnVuY3Rpb24gKHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgYWRkZWRTU1JDcy5wdXNoKHNzcmMpO1xuICAgICAgICAgICAgICAgICAgICBzYi5zcGxpY2UuYXBwbHkoc2IsIFtzYi5sZW5ndGgsIDBdLmNvbmNhdChbXG4gICAgICAgICAgICAgICAgICAgICAgICBbXCJhPXNzcmM6XCIsIHNzcmMsIFwiIGNuYW1lOlwiLCB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5jbmFtZV0uam9pbignJyksXG4gICAgICAgICAgICAgICAgICAgICAgICBbXCJhPXNzcmM6XCIsIHNzcmMsIFwiIG1zaWQ6XCIsIHZpZGVvU291cmNlcy5zb3VyY2VzW3NzcmNdLm1zaWRdLmpvaW4oJycpXSkpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIC8vfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBUaGVuIGFkZCBhbnkgZnJlZSBzb3VyY2VzLlxuICAgIGlmICh2aWRlb1NvdXJjZXMuc291cmNlcykge1xuICAgICAgICBmb3IgKHNzcmMgaW4gdmlkZW9Tb3VyY2VzLnNvdXJjZXMpIHtcbiAgICAgICAgICAgIGlmIChhZGRlZFNTUkNzLmluZGV4T2Yoc3NyYykgPT09IC0xKSB7XG4gICAgICAgICAgICAgICAgc2Iuc3BsaWNlLmFwcGx5KHNiLCBbc2IubGVuZ3RoLCAwXS5jb25jYXQoW1xuICAgICAgICAgICAgICAgICAgICBbXCJhPXNzcmM6XCIsIHNzcmMsIFwiIGNuYW1lOlwiLCB2aWRlb1NvdXJjZXMuc291cmNlc1tzc3JjXS5jbmFtZV0uam9pbignJyksXG4gICAgICAgICAgICAgICAgICAgIFtcImE9c3NyYzpcIiwgc3NyYywgXCIgbXNpZDpcIiwgdmlkZW9Tb3VyY2VzLnNvdXJjZXNbc3NyY10ubXNpZF0uam9pbignJyldKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gc2I7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNpbXVsY2FzdFV0aWxzOyIsIi8qanNsaW50IHBsdXNwbHVzOiB0cnVlICovXG4vKmpzbGludCBub21lbjogdHJ1ZSovXG5cbnZhciBTaW11bGNhc3RTZW5kZXIgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RTZW5kZXJcIik7XG52YXIgTm9TaW11bGNhc3RTZW5kZXIgPSBTaW11bGNhc3RTZW5kZXJbXCJub1wiXTtcbnZhciBOYXRpdmVTaW11bGNhc3RTZW5kZXIgPSBTaW11bGNhc3RTZW5kZXJbXCJuYXRpdmVcIl07XG52YXIgU2ltdWxjYXN0UmVjZWl2ZXIgPSByZXF1aXJlKFwiLi9TaW11bGNhc3RSZWNlaXZlclwiKTtcbnZhciBTaW11bGNhc3RVdGlscyA9IHJlcXVpcmUoXCIuL1NpbXVsY2FzdFV0aWxzXCIpO1xudmFyIFJUQ0V2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9SVENFdmVudHNcIik7XG5cblxuLyoqXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNpbXVsY2FzdE1hbmFnZXIoKSB7XG5cbiAgICAvLyBDcmVhdGUgdGhlIHNpbXVsY2FzdCB1dGlsaXRpZXMuXG4gICAgdGhpcy5zaW11bGNhc3RVdGlscyA9IG5ldyBTaW11bGNhc3RVdGlscygpO1xuXG4gICAgLy8gQ3JlYXRlIHJlbW90ZSBzaW11bGNhc3QuXG4gICAgdGhpcy5zaW11bGNhc3RSZWNlaXZlciA9IG5ldyBTaW11bGNhc3RSZWNlaXZlcigpO1xuXG4gICAgLy8gSW5pdGlhbGl6ZSBsb2NhbCBzaW11bGNhc3QuXG5cbiAgICAvLyBUT0RPKGdwKSBtb3ZlIGludG8gU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0VXNlck1lZGlhIGFuZCB0YWtlIGludG9cbiAgICAvLyBhY2NvdW50IGNvbnN0cmFpbnRzLlxuICAgIGlmICghY29uZmlnLmVuYWJsZVNpbXVsY2FzdCkge1xuICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlciA9IG5ldyBOb1NpbXVsY2FzdFNlbmRlcigpO1xuICAgIH0gZWxzZSB7XG5cbiAgICAgICAgdmFyIGlzQ2hyb21pdW0gPSB3aW5kb3cuY2hyb21lLFxuICAgICAgICAgICAgdmVuZG9yTmFtZSA9IHdpbmRvdy5uYXZpZ2F0b3IudmVuZG9yO1xuICAgICAgICBpZihpc0Nocm9taXVtICE9PSBudWxsICYmIGlzQ2hyb21pdW0gIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgLyogc2tpcCBvcGVyYSAqL1xuICAgICAgICAgICAgJiYgdmVuZG9yTmFtZSA9PT0gXCJHb29nbGUgSW5jLlwiXG4gICAgICAgICAgICAvKiBza2lwIENocm9taXVtIGFzIHN1Z2dlc3RlZCBieSBmaXBwbyAqL1xuICAgICAgICAgICAgJiYgIXdpbmRvdy5uYXZpZ2F0b3IuYXBwVmVyc2lvbi5tYXRjaCgvQ2hyb21pdW1cXC8vKSApIHtcbiAgICAgICAgICAgIHZhciB2ZXIgPSBwYXJzZUludCh3aW5kb3cubmF2aWdhdG9yLmFwcFZlcnNpb24ubWF0Y2goL0Nocm9tZVxcLyhcXGQrKVxcLi8pWzFdLCAxMCk7XG4gICAgICAgICAgICBpZiAodmVyID4gMzcpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlciA9IG5ldyBOYXRpdmVTaW11bGNhc3RTZW5kZXIoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaW11bGNhc3RTZW5kZXIgPSBuZXcgTm9TaW11bGNhc3RTZW5kZXIoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2ltdWxjYXN0U2VuZGVyID0gbmV3IE5vU2ltdWxjYXN0U2VuZGVyKCk7XG4gICAgICAgIH1cblxuICAgIH1cbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5TSU1VTENBU1RfTEFZRVJfQ0hBTkdFRCxcbiAgICAgICAgZnVuY3Rpb24gKGVuZHBvaW50U2ltdWxjYXN0TGF5ZXJzKSB7XG4gICAgICAgICAgICBlbmRwb2ludFNpbXVsY2FzdExheWVycy5mb3JFYWNoKGZ1bmN0aW9uIChlc2wpIHtcbiAgICAgICAgICAgICAgICB2YXIgc3NyYyA9IGVzbC5zaW11bGNhc3RMYXllci5wcmltYXJ5U1NSQztcbiAgICAgICAgICAgICAgICBzaW11bGNhc3QuX3NldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKGVzbC5lbmRwb2ludCwgc3NyYyk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgQVBQLlJUQy5hZGRMaXN0ZW5lcihSVENFdmVudHMuU0lNVUxDQVNUX1NUQVJULCBmdW5jdGlvbiAoc2ltdWxjYXN0TGF5ZXIpIHtcbiAgICAgICAgdmFyIHNzcmMgPSBzaW11bGNhc3RMYXllci5wcmltYXJ5U1NSQztcbiAgICAgICAgc2ltdWxjYXN0Ll9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZChzc3JjLCB0cnVlKTtcbiAgICB9KTtcbiAgICBBUFAuUlRDLmFkZExpc3RlbmVyKFJUQ0V2ZW50cy5TSU1VTENBU1RfU1RPUCwgZnVuY3Rpb24gKHNpbXVsY2FzdExheWVyKSB7XG4gICAgICAgIHZhciBzc3JjID0gc2ltdWxjYXN0TGF5ZXIucHJpbWFyeVNTUkM7XG4gICAgICAgIHNpbXVsY2FzdC5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQoc3NyYywgZmFsc2UpO1xuICAgIH0pO1xuXG59XG5cbi8qKlxuICogUmVzdG9yZXMgdGhlIHNpbXVsY2FzdCBncm91cHMgb2YgdGhlIHJlbW90ZSBkZXNjcmlwdGlvbi4gSW5cbiAqIHRyYW5zZm9ybVJlbW90ZURlc2NyaXB0aW9uIHdlIHJlbW92ZSB0aG9zZSBpbiBvcmRlciBmb3IgdGhlIHNldCByZW1vdGVcbiAqIGRlc2NyaXB0aW9uIHRvIHN1Y2NlZWQuIFRoZSBmb2N1cyBuZWVkcyB0aGUgc2lnbmFsIHRoZSBncm91cHMgdG8gbmV3XG4gKiBwYXJ0aWNpcGFudHMuXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLnJldmVyc2VUcmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbihkZXNjKTtcbn07XG5cbi8qKlxuICogUmVtb3ZlcyB0aGUgc3NyYy1ncm91cDpTSU0gZnJvbSB0aGUgcmVtb3RlIGRlc2NyaXB0aW9uIGJhY2F1c2UgQ2hyb21lXG4gKiBlaXRoZXIgZ2V0cyBjb25mdXNlZCBhbmQgdGhpbmtzIHRoaXMgaXMgYW4gRklEIGdyb3VwIG9yLCBpZiBhbiBGSUQgZ3JvdXBcbiAqIGlzIGFscmVhZHkgcHJlc2VudCwgaXQgZmFpbHMgdG8gc2V0IHRoZSByZW1vdGUgZGVzY3JpcHRpb24uXG4gKlxuICogQHBhcmFtIGRlc2NcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS50cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24oZGVzYyk7XG59O1xuXG4vKipcbiAqIEdldHMgdGhlIGZ1bGx5IHF1YWxpZmllZCBtc2lkIChzdHJlYW0uaWQgKyB0cmFjay5pZCkgYXNzb2NpYXRlZCB0byB0aGVcbiAqIFNTUkMuXG4gKlxuICogQHBhcmFtIHNzcmNcbiAqIEByZXR1cm5zIHsqfVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5nZXRSZW1vdGVWaWRlb1N0cmVhbUlkQnlTU1JDID0gZnVuY3Rpb24gKHNzcmMpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RSZWNlaXZlci5nZXRSZW1vdGVWaWRlb1N0cmVhbUlkQnlTU1JDKHNzcmMpO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIGEgc3RyZWFtIHdpdGggc2luZ2xlIHZpZGVvIHRyYWNrLCB0aGUgb25lIGN1cnJlbnRseSBiZWluZ1xuICogcmVjZWl2ZWQgYnkgdGhpcyBlbmRwb2ludC5cbiAqXG4gKiBAcGFyYW0gc3RyZWFtIHRoZSByZW1vdGUgc2ltdWxjYXN0IHN0cmVhbS5cbiAqIEByZXR1cm5zIHt3ZWJraXRNZWRpYVN0cmVhbX1cbiAqL1xuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0gPSBmdW5jdGlvbiAoc3RyZWFtKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0oc3RyZWFtKTtcbn07XG5cbi8qKlxuICpcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzYykge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFNlbmRlci50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKGRlc2MpO1xufTtcblxuLyoqXG4gKlxuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldExvY2FsVmlkZW9TdHJlYW0gPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5zaW11bGNhc3RTZW5kZXIuZ2V0TG9jYWxWaWRlb1N0cmVhbSgpO1xufTtcblxuLyoqXG4gKiBHVU0gZm9yIHNpbXVsY2FzdC5cbiAqXG4gKiBAcGFyYW0gY29uc3RyYWludHNcbiAqIEBwYXJhbSBzdWNjZXNzXG4gKiBAcGFyYW0gZXJyXG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgc3VjY2VzcywgZXJyKSB7XG5cbiAgICB0aGlzLnNpbXVsY2FzdFNlbmRlci5nZXRVc2VyTWVkaWEoY29uc3RyYWludHMsIHN1Y2Nlc3MsIGVycik7XG59O1xuXG4vKipcbiAqIFByZXBhcmVzIHRoZSBsb2NhbCBkZXNjcmlwdGlvbiBmb3IgcHVibGljIHVzYWdlIChpLmUuIHRvIGJlIHNpZ25hbGVkXG4gKiB0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpLlxuICpcbiAqIEBwYXJhbSBkZXNjXG4gKiBAcmV0dXJucyB7UlRDU2Vzc2lvbkRlc2NyaXB0aW9ufVxuICovXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0U2VuZGVyLnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKGRlc2MpO1xufTtcblxuLyoqXG4gKiBFbnN1cmVzIHRoYXQgdGhlIHNpbXVsY2FzdCBncm91cCBpcyBwcmVzZW50IGluIHRoZSBhbnN3ZXIsIF9pZl8gbmF0aXZlXG4gKiBzaW11bGNhc3QgaXMgZW5hYmxlZCxcbiAqXG4gKiBAcGFyYW0gZGVzY1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnRyYW5zZm9ybUFuc3dlciA9IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0U2VuZGVyLnRyYW5zZm9ybUFuc3dlcihkZXNjKTtcbn07XG5cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLmdldFJlY2VpdmluZ1NTUkMgPSBmdW5jdGlvbiAoamlkKSB7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIuZ2V0UmVjZWl2aW5nU1NSQyhqaWQpO1xufTtcblxuU2ltdWxjYXN0TWFuYWdlci5wcm90b3R5cGUuZ2V0UmVjZWl2aW5nVmlkZW9TdHJlYW1CeVNTUkMgPSBmdW5jdGlvbiAobXNpZCkge1xuICAgIHJldHVybiB0aGlzLnNpbXVsY2FzdFJlY2VpdmVyLmdldFJlY2VpdmluZ1ZpZGVvU3RyZWFtQnlTU1JDKG1zaWQpO1xufTtcblxuLyoqXG4gKlxuICogQHBhcmFtIGxpbmVzXG4gKiBAcGFyYW0gbWVkaWF0eXBlc1xuICogQHJldHVybnMgeyp9XG4gKi9cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLnBhcnNlTWVkaWEgPSBmdW5jdGlvbihsaW5lcywgbWVkaWF0eXBlcykge1xuICAgIHZhciBzYiA9IGxpbmVzLnNkcC5zcGxpdCgnXFxyXFxuJyk7XG4gICAgcmV0dXJuIHRoaXMuc2ltdWxjYXN0VXRpbHMucGFyc2VNZWRpYShzYiwgbWVkaWF0eXBlcyk7XG59O1xuXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5fc2V0UmVjZWl2aW5nVmlkZW9TdHJlYW0gPSBmdW5jdGlvbihyZXNvdXJjZSwgc3NyYykge1xuICAgIHRoaXMuc2ltdWxjYXN0UmVjZWl2ZXIuX3NldFJlY2VpdmluZ1ZpZGVvU3RyZWFtKHJlc291cmNlLCBzc3JjKTtcbn07XG5cblNpbXVsY2FzdE1hbmFnZXIucHJvdG90eXBlLl9zZXRMb2NhbFZpZGVvU3RyZWFtRW5hYmxlZCA9IGZ1bmN0aW9uKHNzcmMsIGVuYWJsZWQpIHtcbiAgICB0aGlzLnNpbXVsY2FzdFNlbmRlci5fc2V0TG9jYWxWaWRlb1N0cmVhbUVuYWJsZWQoc3NyYywgZW5hYmxlZCk7XG59O1xuXG5TaW11bGNhc3RNYW5hZ2VyLnByb3RvdHlwZS5yZXNldFNlbmRlciA9IGZ1bmN0aW9uKCkge1xuICAgIGlmICh0eXBlb2YgdGhpcy5zaW11bGNhc3RTZW5kZXIucmVzZXQgPT09ICdmdW5jdGlvbicpe1xuICAgICAgICB0aGlzLnNpbXVsY2FzdFNlbmRlci5yZXNldCgpO1xuICAgIH1cbn07XG5cbnZhciBzaW11bGNhc3QgPSBuZXcgU2ltdWxjYXN0TWFuYWdlcigpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHNpbXVsY2FzdDsiLCIvKipcbiAqIFByb3ZpZGVzIHN0YXRpc3RpY3MgZm9yIHRoZSBsb2NhbCBzdHJlYW0uXG4gKi9cblxuXG4vKipcbiAqIFNpemUgb2YgdGhlIHdlYmF1ZGlvIGFuYWxpemVyIGJ1ZmZlci5cbiAqIEB0eXBlIHtudW1iZXJ9XG4gKi9cbnZhciBXRUJBVURJT19BTkFMSVpFUl9GRlRfU0laRSA9IDIwNDg7XG5cbi8qKlxuICogVmFsdWUgb2YgdGhlIHdlYmF1ZGlvIGFuYWxpemVyIHNtb290aGluZyB0aW1lIHBhcmFtZXRlci5cbiAqIEB0eXBlIHtudW1iZXJ9XG4gKi9cbnZhciBXRUJBVURJT19BTkFMSVpFUl9TTU9PVElOR19USU1FID0gMC44O1xuXG4vKipcbiAqIENvbnZlcnRzIHRpbWUgZG9tYWluIGRhdGEgYXJyYXkgdG8gYXVkaW8gbGV2ZWwuXG4gKiBAcGFyYW0gYXJyYXkgdGhlIHRpbWUgZG9tYWluIGRhdGEgYXJyYXkuXG4gKiBAcmV0dXJucyB7bnVtYmVyfSB0aGUgYXVkaW8gbGV2ZWxcbiAqL1xuZnVuY3Rpb24gdGltZURvbWFpbkRhdGFUb0F1ZGlvTGV2ZWwoc2FtcGxlcykge1xuXG4gICAgdmFyIG1heFZvbHVtZSA9IDA7XG5cbiAgICB2YXIgbGVuZ3RoID0gc2FtcGxlcy5sZW5ndGg7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChtYXhWb2x1bWUgPCBzYW1wbGVzW2ldKVxuICAgICAgICAgICAgbWF4Vm9sdW1lID0gc2FtcGxlc1tpXTtcbiAgICB9XG5cbiAgICByZXR1cm4gcGFyc2VGbG9hdCgoKG1heFZvbHVtZSAtIDEyNykgLyAxMjgpLnRvRml4ZWQoMykpO1xufTtcblxuLyoqXG4gKiBBbmltYXRlcyBhdWRpbyBsZXZlbCBjaGFuZ2VcbiAqIEBwYXJhbSBuZXdMZXZlbCB0aGUgbmV3IGF1ZGlvIGxldmVsXG4gKiBAcGFyYW0gbGFzdExldmVsIHRoZSBsYXN0IGF1ZGlvIGxldmVsXG4gKiBAcmV0dXJucyB7TnVtYmVyfSB0aGUgYXVkaW8gbGV2ZWwgdG8gYmUgc2V0XG4gKi9cbmZ1bmN0aW9uIGFuaW1hdGVMZXZlbChuZXdMZXZlbCwgbGFzdExldmVsKVxue1xuICAgIHZhciB2YWx1ZSA9IDA7XG4gICAgdmFyIGRpZmYgPSBsYXN0TGV2ZWwgLSBuZXdMZXZlbDtcbiAgICBpZihkaWZmID4gMC4yKVxuICAgIHtcbiAgICAgICAgdmFsdWUgPSBsYXN0TGV2ZWwgLSAwLjI7XG4gICAgfVxuICAgIGVsc2UgaWYoZGlmZiA8IC0wLjQpXG4gICAge1xuICAgICAgICB2YWx1ZSA9IGxhc3RMZXZlbCArIDAuNDtcbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgdmFsdWUgPSBuZXdMZXZlbDtcbiAgICB9XG5cbiAgICByZXR1cm4gcGFyc2VGbG9hdCh2YWx1ZS50b0ZpeGVkKDMpKTtcbn1cblxuXG4vKipcbiAqIDx0dD5Mb2NhbFN0YXRzQ29sbGVjdG9yPC90dD4gY2FsY3VsYXRlcyBzdGF0aXN0aWNzIGZvciB0aGUgbG9jYWwgc3RyZWFtLlxuICpcbiAqIEBwYXJhbSBzdHJlYW0gdGhlIGxvY2FsIHN0cmVhbVxuICogQHBhcmFtIGludGVydmFsIHN0YXRzIHJlZnJlc2ggaW50ZXJ2YWwgZ2l2ZW4gaW4gbXMuXG4gKiBAcGFyYW0ge2Z1bmN0aW9uKExvY2FsU3RhdHNDb2xsZWN0b3IpfSB1cGRhdGVDYWxsYmFjayB0aGUgY2FsbGJhY2sgY2FsbGVkIG9uIHN0YXRzXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlLlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIExvY2FsU3RhdHNDb2xsZWN0b3Ioc3RyZWFtLCBpbnRlcnZhbCwgc3RhdGlzdGljc1NlcnZpY2UsIGV2ZW50RW1pdHRlcikge1xuICAgIHdpbmRvdy5BdWRpb0NvbnRleHQgPSB3aW5kb3cuQXVkaW9Db250ZXh0IHx8IHdpbmRvdy53ZWJraXRBdWRpb0NvbnRleHQ7XG4gICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5pbnRlcnZhbElkID0gbnVsbDtcbiAgICB0aGlzLmludGVydmFsTWlsaXMgPSBpbnRlcnZhbDtcbiAgICB0aGlzLmV2ZW50RW1pdHRlciA9IGV2ZW50RW1pdHRlcjtcbiAgICB0aGlzLmF1ZGlvTGV2ZWwgPSAwO1xuICAgIHRoaXMuc3RhdGlzdGljc1NlcnZpY2UgPSBzdGF0aXN0aWNzU2VydmljZTtcbn1cblxuLyoqXG4gKiBTdGFydHMgdGhlIGNvbGxlY3RpbmcgdGhlIHN0YXRpc3RpY3MuXG4gKi9cbkxvY2FsU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLnN0YXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgIGlmIChjb25maWcuZGlzYWJsZUF1ZGlvTGV2ZWxzIHx8ICF3aW5kb3cuQXVkaW9Db250ZXh0KVxuICAgICAgICByZXR1cm47XG5cbiAgICB2YXIgY29udGV4dCA9IG5ldyBBdWRpb0NvbnRleHQoKTtcbiAgICB2YXIgYW5hbHlzZXIgPSBjb250ZXh0LmNyZWF0ZUFuYWx5c2VyKCk7XG4gICAgYW5hbHlzZXIuc21vb3RoaW5nVGltZUNvbnN0YW50ID0gV0VCQVVESU9fQU5BTElaRVJfU01PT1RJTkdfVElNRTtcbiAgICBhbmFseXNlci5mZnRTaXplID0gV0VCQVVESU9fQU5BTElaRVJfRkZUX1NJWkU7XG5cblxuICAgIHZhciBzb3VyY2UgPSBjb250ZXh0LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHRoaXMuc3RyZWFtKTtcbiAgICBzb3VyY2UuY29ubmVjdChhbmFseXNlcik7XG5cblxuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIHRoaXMuaW50ZXJ2YWxJZCA9IHNldEludGVydmFsKFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgYXJyYXkgPSBuZXcgVWludDhBcnJheShhbmFseXNlci5mcmVxdWVuY3lCaW5Db3VudCk7XG4gICAgICAgICAgICBhbmFseXNlci5nZXRCeXRlVGltZURvbWFpbkRhdGEoYXJyYXkpO1xuICAgICAgICAgICAgdmFyIGF1ZGlvTGV2ZWwgPSB0aW1lRG9tYWluRGF0YVRvQXVkaW9MZXZlbChhcnJheSk7XG4gICAgICAgICAgICBpZihhdWRpb0xldmVsICE9IHNlbGYuYXVkaW9MZXZlbCkge1xuICAgICAgICAgICAgICAgIHNlbGYuYXVkaW9MZXZlbCA9IGFuaW1hdGVMZXZlbChhdWRpb0xldmVsLCBzZWxmLmF1ZGlvTGV2ZWwpO1xuICAgICAgICAgICAgICAgIHNlbGYuZXZlbnRFbWl0dGVyLmVtaXQoXG4gICAgICAgICAgICAgICAgICAgIFwic3RhdGlzdGljcy5hdWRpb0xldmVsXCIsXG4gICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdGlzdGljc1NlcnZpY2UuTE9DQUxfSklELFxuICAgICAgICAgICAgICAgICAgICBzZWxmLmF1ZGlvTGV2ZWwpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICB0aGlzLmludGVydmFsTWlsaXNcbiAgICApO1xuXG59O1xuXG4vKipcbiAqIFN0b3BzIGNvbGxlY3RpbmcgdGhlIHN0YXRpc3RpY3MuXG4gKi9cbkxvY2FsU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLnN0b3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgaWYgKHRoaXMuaW50ZXJ2YWxJZCkge1xuICAgICAgICBjbGVhckludGVydmFsKHRoaXMuaW50ZXJ2YWxJZCk7XG4gICAgICAgIHRoaXMuaW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBMb2NhbFN0YXRzQ29sbGVjdG9yOyIsIi8qIGdsb2JhbCBzc3JjMmppZCAqL1xuLyoganNoaW50IC1XMTE3ICovXG52YXIgUlRDQnJvd3NlclR5cGUgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvUlRDQnJvd3NlclR5cGVcIik7XG5cblxuLyoqXG4gKiBDYWxjdWxhdGVzIHBhY2tldCBsb3N0IHBlcmNlbnQgdXNpbmcgdGhlIG51bWJlciBvZiBsb3N0IHBhY2tldHMgYW5kIHRoZVxuICogbnVtYmVyIG9mIGFsbCBwYWNrZXQuXG4gKiBAcGFyYW0gbG9zdFBhY2tldHMgdGhlIG51bWJlciBvZiBsb3N0IHBhY2tldHNcbiAqIEBwYXJhbSB0b3RhbFBhY2tldHMgdGhlIG51bWJlciBvZiBhbGwgcGFja2V0cy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IHBhY2tldCBsb3NzIHBlcmNlbnRcbiAqL1xuZnVuY3Rpb24gY2FsY3VsYXRlUGFja2V0TG9zcyhsb3N0UGFja2V0cywgdG90YWxQYWNrZXRzKSB7XG4gICAgaWYoIXRvdGFsUGFja2V0cyB8fCB0b3RhbFBhY2tldHMgPD0gMCB8fCAhbG9zdFBhY2tldHMgfHwgbG9zdFBhY2tldHMgPD0gMClcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgcmV0dXJuIE1hdGgucm91bmQoKGxvc3RQYWNrZXRzL3RvdGFsUGFja2V0cykqMTAwKTtcbn1cblxuZnVuY3Rpb24gZ2V0U3RhdFZhbHVlKGl0ZW0sIG5hbWUpIHtcbiAgICBpZigha2V5TWFwW0FQUC5SVEMuZ2V0QnJvd3NlclR5cGUoKV1bbmFtZV0pXG4gICAgICAgIHRocm93IFwiVGhlIHByb3BlcnR5IGlzbid0IHN1cHBvcnRlZCFcIjtcbiAgICB2YXIga2V5ID0ga2V5TWFwW0FQUC5SVEMuZ2V0QnJvd3NlclR5cGUoKV1bbmFtZV07XG4gICAgcmV0dXJuIEFQUC5SVEMuZ2V0QnJvd3NlclR5cGUoKSA9PSBSVENCcm93c2VyVHlwZS5SVENfQlJPV1NFUl9DSFJPTUU/IGl0ZW0uc3RhdChrZXkpIDogaXRlbVtrZXldO1xufVxuXG4vKipcbiAqIFBlZXIgc3RhdGlzdGljcyBkYXRhIGhvbGRlci5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBQZWVyU3RhdHMoKVxue1xuICAgIHRoaXMuc3NyYzJMb3NzID0ge307XG4gICAgdGhpcy5zc3JjMkF1ZGlvTGV2ZWwgPSB7fTtcbiAgICB0aGlzLnNzcmMyYml0cmF0ZSA9IHt9O1xuICAgIHRoaXMuc3NyYzJyZXNvbHV0aW9uID0ge307XG59XG5cbi8qKlxuICogVGhlIGJhbmR3aWR0aFxuICogQHR5cGUge3t9fVxuICovXG5QZWVyU3RhdHMuYmFuZHdpZHRoID0ge307XG5cbi8qKlxuICogVGhlIGJpdCByYXRlXG4gKiBAdHlwZSB7e319XG4gKi9cblBlZXJTdGF0cy5iaXRyYXRlID0ge307XG5cblxuXG4vKipcbiAqIFRoZSBwYWNrZXQgbG9zcyByYXRlXG4gKiBAdHlwZSB7e319XG4gKi9cblBlZXJTdGF0cy5wYWNrZXRMb3NzID0gbnVsbDtcblxuLyoqXG4gKiBTZXRzIHBhY2tldHMgbG9zcyByYXRlIGZvciBnaXZlbiA8dHQ+c3NyYzwvdHQ+IHRoYXQgYmxvbmcgdG8gdGhlIHBlZXJcbiAqIHJlcHJlc2VudGVkIGJ5IHRoaXMgaW5zdGFuY2UuXG4gKiBAcGFyYW0gc3NyYyBhdWRpbyBvciB2aWRlbyBSVFAgc3RyZWFtIFNTUkMuXG4gKiBAcGFyYW0gbG9zc1JhdGUgbmV3IHBhY2tldCBsb3NzIHJhdGUgdmFsdWUgdG8gYmUgc2V0LlxuICovXG5QZWVyU3RhdHMucHJvdG90eXBlLnNldFNzcmNMb3NzID0gZnVuY3Rpb24gKHNzcmMsIGxvc3NSYXRlKVxue1xuICAgIHRoaXMuc3NyYzJMb3NzW3NzcmNdID0gbG9zc1JhdGU7XG59O1xuXG4vKipcbiAqIFNldHMgcmVzb2x1dGlvbiBmb3IgZ2l2ZW4gPHR0PnNzcmM8L3R0PiB0aGF0IGJlbG9uZyB0byB0aGUgcGVlclxuICogcmVwcmVzZW50ZWQgYnkgdGhpcyBpbnN0YW5jZS5cbiAqIEBwYXJhbSBzc3JjIGF1ZGlvIG9yIHZpZGVvIFJUUCBzdHJlYW0gU1NSQy5cbiAqIEBwYXJhbSByZXNvbHV0aW9uIG5ldyByZXNvbHV0aW9uIHZhbHVlIHRvIGJlIHNldC5cbiAqL1xuUGVlclN0YXRzLnByb3RvdHlwZS5zZXRTc3JjUmVzb2x1dGlvbiA9IGZ1bmN0aW9uIChzc3JjLCByZXNvbHV0aW9uKVxue1xuICAgIGlmKHJlc29sdXRpb24gPT09IG51bGwgJiYgdGhpcy5zc3JjMnJlc29sdXRpb25bc3NyY10pXG4gICAge1xuICAgICAgICBkZWxldGUgdGhpcy5zc3JjMnJlc29sdXRpb25bc3NyY107XG4gICAgfVxuICAgIGVsc2UgaWYocmVzb2x1dGlvbiAhPT0gbnVsbClcbiAgICAgICAgdGhpcy5zc3JjMnJlc29sdXRpb25bc3NyY10gPSByZXNvbHV0aW9uO1xufTtcblxuLyoqXG4gKiBTZXRzIHRoZSBiaXQgcmF0ZSBmb3IgZ2l2ZW4gPHR0PnNzcmM8L3R0PiB0aGF0IGJsb25nIHRvIHRoZSBwZWVyXG4gKiByZXByZXNlbnRlZCBieSB0aGlzIGluc3RhbmNlLlxuICogQHBhcmFtIHNzcmMgYXVkaW8gb3IgdmlkZW8gUlRQIHN0cmVhbSBTU1JDLlxuICogQHBhcmFtIGJpdHJhdGUgbmV3IGJpdHJhdGUgdmFsdWUgdG8gYmUgc2V0LlxuICovXG5QZWVyU3RhdHMucHJvdG90eXBlLnNldFNzcmNCaXRyYXRlID0gZnVuY3Rpb24gKHNzcmMsIGJpdHJhdGUpXG57XG4gICAgaWYodGhpcy5zc3JjMmJpdHJhdGVbc3NyY10pXG4gICAge1xuICAgICAgICB0aGlzLnNzcmMyYml0cmF0ZVtzc3JjXS5kb3dubG9hZCArPSBiaXRyYXRlLmRvd25sb2FkO1xuICAgICAgICB0aGlzLnNzcmMyYml0cmF0ZVtzc3JjXS51cGxvYWQgKz0gYml0cmF0ZS51cGxvYWQ7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICB0aGlzLnNzcmMyYml0cmF0ZVtzc3JjXSA9IGJpdHJhdGU7XG4gICAgfVxufTtcblxuLyoqXG4gKiBTZXRzIG5ldyBhdWRpbyBsZXZlbChpbnB1dCBvciBvdXRwdXQpIGZvciBnaXZlbiA8dHQ+c3NyYzwvdHQ+IHRoYXQgaWRlbnRpZmllc1xuICogdGhlIHN0cmVhbSB3aGljaCBiZWxvbmdzIHRvIHRoZSBwZWVyIHJlcHJlc2VudGVkIGJ5IHRoaXMgaW5zdGFuY2UuXG4gKiBAcGFyYW0gc3NyYyBSVFAgc3RyZWFtIFNTUkMgZm9yIHdoaWNoIGN1cnJlbnQgYXVkaW8gbGV2ZWwgdmFsdWUgd2lsbCBiZVxuICogICAgICAgIHVwZGF0ZWQuXG4gKiBAcGFyYW0gYXVkaW9MZXZlbCB0aGUgbmV3IGF1ZGlvIGxldmVsIHZhbHVlIHRvIGJlIHNldC4gVmFsdWUgaXMgdHJ1bmNhdGVkIHRvXG4gKiAgICAgICAgZml0IHRoZSByYW5nZSBmcm9tIDAgdG8gMS5cbiAqL1xuUGVlclN0YXRzLnByb3RvdHlwZS5zZXRTc3JjQXVkaW9MZXZlbCA9IGZ1bmN0aW9uIChzc3JjLCBhdWRpb0xldmVsKVxue1xuICAgIC8vIFJhbmdlIGxpbWl0IDAgLSAxXG4gICAgdGhpcy5zc3JjMkF1ZGlvTGV2ZWxbc3NyY10gPSBNYXRoLm1pbihNYXRoLm1heChhdWRpb0xldmVsLCAwKSwgMSk7XG59O1xuXG4vKipcbiAqIEFycmF5IHdpdGggdGhlIHRyYW5zcG9ydCBpbmZvcm1hdGlvbi5cbiAqIEB0eXBlIHtBcnJheX1cbiAqL1xuUGVlclN0YXRzLnRyYW5zcG9ydCA9IFtdO1xuXG5cbi8qKlxuICogPHR0PlN0YXRzQ29sbGVjdG9yPC90dD4gcmVnaXN0ZXJzIGZvciBzdGF0cyB1cGRhdGVzIG9mIGdpdmVuXG4gKiA8dHQ+cGVlcmNvbm5lY3Rpb248L3R0PiBpbiBnaXZlbiA8dHQ+aW50ZXJ2YWw8L3R0Pi4gT24gZWFjaCB1cGRhdGUgcGFydGljdWxhclxuICogc3RhdHMgYXJlIGV4dHJhY3RlZCBhbmQgcHV0IGluIHtAbGluayBQZWVyU3RhdHN9IG9iamVjdHMuIE9uY2UgdGhlIHByb2Nlc3NpbmdcbiAqIGlzIGRvbmUgPHR0PmF1ZGlvTGV2ZWxzVXBkYXRlQ2FsbGJhY2s8L3R0PiBpcyBjYWxsZWQgd2l0aCA8dHQ+dGhpczwvdHQ+XG4gKiBpbnN0YW5jZSBhcyBhbiBldmVudCBzb3VyY2UuXG4gKlxuICogQHBhcmFtIHBlZXJjb25uZWN0aW9uIHdlYlJUQyBwZWVyIGNvbm5lY3Rpb24gb2JqZWN0LlxuICogQHBhcmFtIGludGVydmFsIHN0YXRzIHJlZnJlc2ggaW50ZXJ2YWwgZ2l2ZW4gaW4gbXMuXG4gKiBAcGFyYW0ge2Z1bmN0aW9uKFN0YXRzQ29sbGVjdG9yKX0gYXVkaW9MZXZlbHNVcGRhdGVDYWxsYmFjayB0aGUgY2FsbGJhY2tcbiAqIGNhbGxlZCBvbiBzdGF0cyB1cGRhdGUuXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gU3RhdHNDb2xsZWN0b3IocGVlcmNvbm5lY3Rpb24sIGF1ZGlvTGV2ZWxzSW50ZXJ2YWwsIHN0YXRzSW50ZXJ2YWwsIGV2ZW50RW1pdHRlcilcbntcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uID0gcGVlcmNvbm5lY3Rpb247XG4gICAgdGhpcy5iYXNlbGluZUF1ZGlvTGV2ZWxzUmVwb3J0ID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRBdWRpb0xldmVsc1JlcG9ydCA9IG51bGw7XG4gICAgdGhpcy5jdXJyZW50U3RhdHNSZXBvcnQgPSBudWxsO1xuICAgIHRoaXMuYmFzZWxpbmVTdGF0c1JlcG9ydCA9IG51bGw7XG4gICAgdGhpcy5hdWRpb0xldmVsc0ludGVydmFsSWQgPSBudWxsO1xuICAgIHRoaXMuZXZlbnRFbWl0dGVyID0gZXZlbnRFbWl0dGVyO1xuXG4gICAgLyoqXG4gICAgICogR2F0aGVyIFBlZXJDb25uZWN0aW9uIHN0YXRzIG9uY2UgZXZlcnkgdGhpcyBtYW55IG1pbGxpc2Vjb25kcy5cbiAgICAgKi9cbiAgICB0aGlzLkdBVEhFUl9JTlRFUlZBTCA9IDEwMDAwO1xuXG4gICAgLyoqXG4gICAgICogTG9nIHN0YXRzIHZpYSB0aGUgZm9jdXMgb25jZSBldmVyeSB0aGlzIG1hbnkgbWlsbGlzZWNvbmRzLlxuICAgICAqL1xuICAgIHRoaXMuTE9HX0lOVEVSVkFMID0gNjAwMDA7XG5cbiAgICAvKipcbiAgICAgKiBHYXRoZXIgc3RhdHMgYW5kIHN0b3JlIHRoZW0gaW4gdGhpcy5zdGF0c1RvQmVMb2dnZWQuXG4gICAgICovXG4gICAgdGhpcy5nYXRoZXJTdGF0c0ludGVydmFsSWQgPSBudWxsO1xuXG4gICAgLyoqXG4gICAgICogU2VuZCB0aGUgc3RhdHMgYWxyZWFkeSBzYXZlZCBpbiB0aGlzLnN0YXRzVG9CZUxvZ2dlZCB0byBiZSBsb2dnZWQgdmlhXG4gICAgICogdGhlIGZvY3VzLlxuICAgICAqL1xuICAgIHRoaXMubG9nU3RhdHNJbnRlcnZhbElkID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIFN0b3JlcyB0aGUgc3RhdGlzdGljcyB3aGljaCB3aWxsIGJlIHNlbmQgdG8gdGhlIGZvY3VzIHRvIGJlIGxvZ2dlZC5cbiAgICAgKi9cbiAgICB0aGlzLnN0YXRzVG9CZUxvZ2dlZCA9XG4gICAge1xuICAgICAgICB0aW1lc3RhbXBzOiBbXSxcbiAgICAgICAgc3RhdHM6IHt9XG4gICAgfTtcblxuICAgIC8vIFVwZGF0ZXMgc3RhdHMgaW50ZXJ2YWxcbiAgICB0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxNaWxpcyA9IGF1ZGlvTGV2ZWxzSW50ZXJ2YWw7XG5cbiAgICB0aGlzLnN0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgdGhpcy5zdGF0c0ludGVydmFsTWlsaXMgPSBzdGF0c0ludGVydmFsO1xuICAgIC8vIE1hcCBvZiBqaWRzIHRvIFBlZXJTdGF0c1xuICAgIHRoaXMuamlkMnN0YXRzID0ge307XG59XG5cbm1vZHVsZS5leHBvcnRzID0gU3RhdHNDb2xsZWN0b3I7XG5cbi8qKlxuICogU3RvcHMgc3RhdHMgdXBkYXRlcy5cbiAqL1xuU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLnN0b3AgPSBmdW5jdGlvbiAoKVxue1xuICAgIGlmICh0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZClcbiAgICB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5hdWRpb0xldmVsc0ludGVydmFsSWQpO1xuICAgICAgICB0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5zdGF0c0ludGVydmFsSWQpO1xuICAgICAgICB0aGlzLnN0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5sb2dTdGF0c0ludGVydmFsSWQpO1xuICAgICAgICB0aGlzLmxvZ1N0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5nYXRoZXJTdGF0c0ludGVydmFsSWQpO1xuICAgICAgICB0aGlzLmdhdGhlclN0YXRzSW50ZXJ2YWxJZCA9IG51bGw7XG4gICAgfVxufTtcblxuLyoqXG4gKiBDYWxsYmFjayBwYXNzZWQgdG8gPHR0PmdldFN0YXRzPC90dD4gbWV0aG9kLlxuICogQHBhcmFtIGVycm9yIGFuIGVycm9yIHRoYXQgb2NjdXJyZWQgb24gPHR0PmdldFN0YXRzPC90dD4gY2FsbC5cbiAqL1xuU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLmVycm9yQ2FsbGJhY2sgPSBmdW5jdGlvbiAoZXJyb3IpXG57XG4gICAgY29uc29sZS5lcnJvcihcIkdldCBzdGF0cyBlcnJvclwiLCBlcnJvcik7XG4gICAgdGhpcy5zdG9wKCk7XG59O1xuXG4vKipcbiAqIFN0YXJ0cyBzdGF0cyB1cGRhdGVzLlxuICovXG5TdGF0c0NvbGxlY3Rvci5wcm90b3R5cGUuc3RhcnQgPSBmdW5jdGlvbiAoKVxue1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZighY29uZmlnLmRpc2FibGVBdWRpb0xldmVscykge1xuICAgICAgICB0aGlzLmF1ZGlvTGV2ZWxzSW50ZXJ2YWxJZCA9IHNldEludGVydmFsKFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIC8vIEludGVydmFsIHVwZGF0ZXNcbiAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVwb3J0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzdWx0cyA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXJlcG9ydCB8fCAhcmVwb3J0LnJlc3VsdCB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGVvZiByZXBvcnQucmVzdWx0ICE9ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gcmVwb3J0O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydC5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5lcnJvcihcIkdvdCBpbnRlcnZhbCByZXBvcnRcIiwgcmVzdWx0cyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmN1cnJlbnRBdWRpb0xldmVsc1JlcG9ydCA9IHJlc3VsdHM7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnByb2Nlc3NBdWRpb0xldmVsUmVwb3J0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmJhc2VsaW5lQXVkaW9MZXZlbHNSZXBvcnQgPVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY3VycmVudEF1ZGlvTGV2ZWxzUmVwb3J0O1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBzZWxmLmVycm9yQ2FsbGJhY2tcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHNlbGYuYXVkaW9MZXZlbHNJbnRlcnZhbE1pbGlzXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgaWYoIWNvbmZpZy5kaXNhYmxlU3RhdHMpIHtcbiAgICAgICAgdGhpcy5zdGF0c0ludGVydmFsSWQgPSBzZXRJbnRlcnZhbChcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAvLyBJbnRlcnZhbCB1cGRhdGVzXG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFyZXBvcnQgfHwgIXJlcG9ydC5yZXN1bHQgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgcmVwb3J0LnJlc3VsdCAhPSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9maXJlZm94XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vY2hyb21lXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IHJlcG9ydC5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5lcnJvcihcIkdvdCBpbnRlcnZhbCByZXBvcnRcIiwgcmVzdWx0cyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmN1cnJlbnRTdGF0c1JlcG9ydCA9IHJlc3VsdHM7XG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucHJvY2Vzc1N0YXRzUmVwb3J0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJVbnN1cHBvcnRlZCBrZXk6XCIgKyBlLCBlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5iYXNlbGluZVN0YXRzUmVwb3J0ID0gc2VsZi5jdXJyZW50U3RhdHNSZXBvcnQ7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHNlbGYuZXJyb3JDYWxsYmFja1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc2VsZi5zdGF0c0ludGVydmFsTWlsaXNcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZmlnLmxvZ1N0YXRzKSB7XG4gICAgICAgIHRoaXMuZ2F0aGVyU3RhdHNJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5hZGRTdGF0c1RvQmVMb2dnZWQocmVwb3J0LnJlc3VsdCgpKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB0aGlzLkdBVEhFUl9JTlRFUlZBTFxuICAgICAgICApO1xuXG4gICAgICAgIHRoaXMubG9nU3RhdHNJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoXG4gICAgICAgICAgICBmdW5jdGlvbigpIHsgc2VsZi5sb2dTdGF0cygpOyB9LFxuICAgICAgICAgICAgdGhpcy5MT0dfSU5URVJWQUwpO1xuICAgIH1cbn07XG5cbi8qKlxuICogQ29udmVydHMgdGhlIHN0YXRzIHRvIHRoZSBmb3JtYXQgdXNlZCBmb3IgbG9nZ2luZywgYW5kIHNhdmVzIHRoZSBkYXRhIGluXG4gKiB0aGlzLnN0YXRzVG9CZUxvZ2dlZC5cbiAqIEBwYXJhbSByZXBvcnRzIFJlcG9ydHMgYXMgZ2l2ZW4gYnkgd2Via2l0UlRDUGVyQ29ubmVjdGlvbi5nZXRTdGF0cy5cbiAqL1xuU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLmFkZFN0YXRzVG9CZUxvZ2dlZCA9IGZ1bmN0aW9uIChyZXBvcnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBudW1fcmVjb3JkcyA9IHRoaXMuc3RhdHNUb0JlTG9nZ2VkLnRpbWVzdGFtcHMubGVuZ3RoO1xuICAgIHRoaXMuc3RhdHNUb0JlTG9nZ2VkLnRpbWVzdGFtcHMucHVzaChuZXcgRGF0ZSgpLmdldFRpbWUoKSk7XG4gICAgcmVwb3J0cy5tYXAoZnVuY3Rpb24gKHJlcG9ydCkge1xuICAgICAgICB2YXIgc3RhdCA9IHNlbGYuc3RhdHNUb0JlTG9nZ2VkLnN0YXRzW3JlcG9ydC5pZF07XG4gICAgICAgIGlmICghc3RhdCkge1xuICAgICAgICAgICAgc3RhdCA9IHNlbGYuc3RhdHNUb0JlTG9nZ2VkLnN0YXRzW3JlcG9ydC5pZF0gPSB7fTtcbiAgICAgICAgfVxuICAgICAgICBzdGF0LnR5cGUgPSByZXBvcnQudHlwZTtcbiAgICAgICAgcmVwb3J0Lm5hbWVzKCkubWFwKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICB2YXIgdmFsdWVzID0gc3RhdFtuYW1lXTtcbiAgICAgICAgICAgIGlmICghdmFsdWVzKSB7XG4gICAgICAgICAgICAgICAgdmFsdWVzID0gc3RhdFtuYW1lXSA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgd2hpbGUgKHZhbHVlcy5sZW5ndGggPCBudW1fcmVjb3Jkcykge1xuICAgICAgICAgICAgICAgIHZhbHVlcy5wdXNoKG51bGwpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFsdWVzLnB1c2gocmVwb3J0LnN0YXQobmFtZSkpO1xuICAgICAgICB9KTtcbiAgICB9KTtcbn07XG5cblN0YXRzQ29sbGVjdG9yLnByb3RvdHlwZS5sb2dTdGF0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIGlmKCFBUFAueG1wcC5zZW5kTG9ncyh0aGlzLnN0YXRzVG9CZUxvZ2dlZCkpXG4gICAgICAgIHJldHVybjtcbiAgICAvLyBSZXNldCB0aGUgc3RhdHNcbiAgICB0aGlzLnN0YXRzVG9CZUxvZ2dlZC5zdGF0cyA9IHt9O1xuICAgIHRoaXMuc3RhdHNUb0JlTG9nZ2VkLnRpbWVzdGFtcHMgPSBbXTtcbn07XG52YXIga2V5TWFwID0ge307XG5rZXlNYXBbUlRDQnJvd3NlclR5cGUuUlRDX0JST1dTRVJfRklSRUZPWF0gPSB7XG4gICAgXCJzc3JjXCI6IFwic3NyY1wiLFxuICAgIFwicGFja2V0c1JlY2VpdmVkXCI6IFwicGFja2V0c1JlY2VpdmVkXCIsXG4gICAgXCJwYWNrZXRzTG9zdFwiOiBcInBhY2tldHNMb3N0XCIsXG4gICAgXCJwYWNrZXRzU2VudFwiOiBcInBhY2tldHNTZW50XCIsXG4gICAgXCJieXRlc1JlY2VpdmVkXCI6IFwiYnl0ZXNSZWNlaXZlZFwiLFxuICAgIFwiYnl0ZXNTZW50XCI6IFwiYnl0ZXNTZW50XCJcbn07XG5rZXlNYXBbUlRDQnJvd3NlclR5cGUuUlRDX0JST1dTRVJfQ0hST01FXSA9IHtcbiAgICBcInJlY2VpdmVCYW5kd2lkdGhcIjogXCJnb29nQXZhaWxhYmxlUmVjZWl2ZUJhbmR3aWR0aFwiLFxuICAgIFwic2VuZEJhbmR3aWR0aFwiOiBcImdvb2dBdmFpbGFibGVTZW5kQmFuZHdpZHRoXCIsXG4gICAgXCJyZW1vdGVBZGRyZXNzXCI6IFwiZ29vZ1JlbW90ZUFkZHJlc3NcIixcbiAgICBcInRyYW5zcG9ydFR5cGVcIjogXCJnb29nVHJhbnNwb3J0VHlwZVwiLFxuICAgIFwibG9jYWxBZGRyZXNzXCI6IFwiZ29vZ0xvY2FsQWRkcmVzc1wiLFxuICAgIFwiYWN0aXZlQ29ubmVjdGlvblwiOiBcImdvb2dBY3RpdmVDb25uZWN0aW9uXCIsXG4gICAgXCJzc3JjXCI6IFwic3NyY1wiLFxuICAgIFwicGFja2V0c1JlY2VpdmVkXCI6IFwicGFja2V0c1JlY2VpdmVkXCIsXG4gICAgXCJwYWNrZXRzU2VudFwiOiBcInBhY2tldHNTZW50XCIsXG4gICAgXCJwYWNrZXRzTG9zdFwiOiBcInBhY2tldHNMb3N0XCIsXG4gICAgXCJieXRlc1JlY2VpdmVkXCI6IFwiYnl0ZXNSZWNlaXZlZFwiLFxuICAgIFwiYnl0ZXNTZW50XCI6IFwiYnl0ZXNTZW50XCIsXG4gICAgXCJnb29nRnJhbWVIZWlnaHRSZWNlaXZlZFwiOiBcImdvb2dGcmFtZUhlaWdodFJlY2VpdmVkXCIsXG4gICAgXCJnb29nRnJhbWVXaWR0aFJlY2VpdmVkXCI6IFwiZ29vZ0ZyYW1lV2lkdGhSZWNlaXZlZFwiLFxuICAgIFwiZ29vZ0ZyYW1lSGVpZ2h0U2VudFwiOiBcImdvb2dGcmFtZUhlaWdodFNlbnRcIixcbiAgICBcImdvb2dGcmFtZVdpZHRoU2VudFwiOiBcImdvb2dGcmFtZVdpZHRoU2VudFwiLFxuICAgIFwiYXVkaW9JbnB1dExldmVsXCI6IFwiYXVkaW9JbnB1dExldmVsXCIsXG4gICAgXCJhdWRpb091dHB1dExldmVsXCI6IFwiYXVkaW9PdXRwdXRMZXZlbFwiXG59O1xuXG5cbi8qKlxuICogU3RhdHMgcHJvY2Vzc2luZyBsb2dpYy5cbiAqL1xuU3RhdHNDb2xsZWN0b3IucHJvdG90eXBlLnByb2Nlc3NTdGF0c1JlcG9ydCA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAoIXRoaXMuYmFzZWxpbmVTdGF0c1JlcG9ydCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZm9yICh2YXIgaWR4IGluIHRoaXMuY3VycmVudFN0YXRzUmVwb3J0KSB7XG4gICAgICAgIHZhciBub3cgPSB0aGlzLmN1cnJlbnRTdGF0c1JlcG9ydFtpZHhdO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKGdldFN0YXRWYWx1ZShub3csICdyZWNlaXZlQmFuZHdpZHRoJykgfHxcbiAgICAgICAgICAgICAgICBnZXRTdGF0VmFsdWUobm93LCAnc2VuZEJhbmR3aWR0aCcpKSB7XG4gICAgICAgICAgICAgICAgUGVlclN0YXRzLmJhbmR3aWR0aCA9IHtcbiAgICAgICAgICAgICAgICAgICAgXCJkb3dubG9hZFwiOiBNYXRoLnJvdW5kKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChnZXRTdGF0VmFsdWUobm93LCAncmVjZWl2ZUJhbmR3aWR0aCcpKSAvIDEwMDApLFxuICAgICAgICAgICAgICAgICAgICBcInVwbG9hZFwiOiBNYXRoLnJvdW5kKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChnZXRTdGF0VmFsdWUobm93LCAnc2VuZEJhbmR3aWR0aCcpKSAvIDEwMDApXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjYXRjaChlKXsvKm5vdCBzdXBwb3J0ZWQqL31cblxuICAgICAgICBpZihub3cudHlwZSA9PSAnZ29vZ0NhbmRpZGF0ZVBhaXInKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgaXAsIHR5cGUsIGxvY2FsSVAsIGFjdGl2ZTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaXAgPSBnZXRTdGF0VmFsdWUobm93LCAncmVtb3RlQWRkcmVzcycpO1xuICAgICAgICAgICAgICAgIHR5cGUgPSBnZXRTdGF0VmFsdWUobm93LCBcInRyYW5zcG9ydFR5cGVcIik7XG4gICAgICAgICAgICAgICAgbG9jYWxJUCA9IGdldFN0YXRWYWx1ZShub3csIFwibG9jYWxBZGRyZXNzXCIpO1xuICAgICAgICAgICAgICAgIGFjdGl2ZSA9IGdldFN0YXRWYWx1ZShub3csIFwiYWN0aXZlQ29ubmVjdGlvblwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoKGUpey8qbm90IHN1cHBvcnRlZCovfVxuICAgICAgICAgICAgaWYoIWlwIHx8ICF0eXBlIHx8ICFsb2NhbElQIHx8IGFjdGl2ZSAhPSBcInRydWVcIilcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIHZhciBhZGRyZXNzU2F2ZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBQZWVyU3RhdHMudHJhbnNwb3J0Lmxlbmd0aDsgaSsrKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGlmKFBlZXJTdGF0cy50cmFuc3BvcnRbaV0uaXAgPT0gaXAgJiZcbiAgICAgICAgICAgICAgICAgICAgUGVlclN0YXRzLnRyYW5zcG9ydFtpXS50eXBlID09IHR5cGUgJiZcbiAgICAgICAgICAgICAgICAgICAgUGVlclN0YXRzLnRyYW5zcG9ydFtpXS5sb2NhbGlwID09IGxvY2FsSVApXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBhZGRyZXNzU2F2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmKGFkZHJlc3NTYXZlZClcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIFBlZXJTdGF0cy50cmFuc3BvcnQucHVzaCh7bG9jYWxpcDogbG9jYWxJUCwgaXA6IGlwLCB0eXBlOiB0eXBlfSk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKG5vdy50eXBlID09IFwiY2FuZGlkYXRlcGFpclwiKVxuICAgICAgICB7XG4gICAgICAgICAgICBpZihub3cuc3RhdGUgPT0gXCJzdWNjZWVkZWRcIilcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcblxuICAgICAgICAgICAgdmFyIGxvY2FsID0gdGhpcy5jdXJyZW50U3RhdHNSZXBvcnRbbm93LmxvY2FsQ2FuZGlkYXRlSWRdO1xuICAgICAgICAgICAgdmFyIHJlbW90ZSA9IHRoaXMuY3VycmVudFN0YXRzUmVwb3J0W25vdy5yZW1vdGVDYW5kaWRhdGVJZF07XG4gICAgICAgICAgICBQZWVyU3RhdHMudHJhbnNwb3J0LnB1c2goe2xvY2FsaXA6IGxvY2FsLmlwQWRkcmVzcyArIFwiOlwiICsgbG9jYWwucG9ydE51bWJlcixcbiAgICAgICAgICAgICAgICBpcDogcmVtb3RlLmlwQWRkcmVzcyArIFwiOlwiICsgcmVtb3RlLnBvcnROdW1iZXIsIHR5cGU6IGxvY2FsLnRyYW5zcG9ydH0pO1xuXG4gICAgICAgIH1cblxuICAgICAgICBpZiAobm93LnR5cGUgIT0gJ3NzcmMnICYmIG5vdy50eXBlICE9IFwib3V0Ym91bmRydHBcIiAmJlxuICAgICAgICAgICAgbm93LnR5cGUgIT0gXCJpbmJvdW5kcnRwXCIpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGJlZm9yZSA9IHRoaXMuYmFzZWxpbmVTdGF0c1JlcG9ydFtpZHhdO1xuICAgICAgICBpZiAoIWJlZm9yZSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGdldFN0YXRWYWx1ZShub3csICdzc3JjJykgKyAnIG5vdCBlbm91Z2ggZGF0YScpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc3NyYyA9IGdldFN0YXRWYWx1ZShub3csICdzc3JjJyk7XG4gICAgICAgIGlmKCFzc3JjKVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIHZhciBqaWQgPSBBUFAueG1wcC5nZXRKaWRGcm9tU1NSQyhzc3JjKTtcbiAgICAgICAgaWYgKCFqaWQgJiYgKERhdGUubm93KCkgLSBub3cudGltZXN0YW1wKSA8IDMwMDApIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIk5vIGppZCBmb3Igc3NyYzogXCIgKyBzc3JjKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGppZFN0YXRzID0gdGhpcy5qaWQyc3RhdHNbamlkXTtcbiAgICAgICAgaWYgKCFqaWRTdGF0cykge1xuICAgICAgICAgICAgamlkU3RhdHMgPSBuZXcgUGVlclN0YXRzKCk7XG4gICAgICAgICAgICB0aGlzLmppZDJzdGF0c1tqaWRdID0gamlkU3RhdHM7XG4gICAgICAgIH1cblxuXG4gICAgICAgIHZhciBpc0Rvd25sb2FkU3RyZWFtID0gdHJ1ZTtcbiAgICAgICAgdmFyIGtleSA9ICdwYWNrZXRzUmVjZWl2ZWQnO1xuICAgICAgICBpZiAoIWdldFN0YXRWYWx1ZShub3csIGtleSkpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlzRG93bmxvYWRTdHJlYW0gPSBmYWxzZTtcbiAgICAgICAgICAgIGtleSA9ICdwYWNrZXRzU2VudCc7XG4gICAgICAgICAgICBpZiAoIWdldFN0YXRWYWx1ZShub3csIGtleSkpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTm8gcGFja2V0c1JlY2VpdmVkIG5vciBwYWNrZXRTZW50IHN0YXQgZm91bmRcIik7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHBhY2tldHNOb3cgPSBnZXRTdGF0VmFsdWUobm93LCBrZXkpO1xuICAgICAgICBpZighcGFja2V0c05vdyB8fCBwYWNrZXRzTm93IDwgMClcbiAgICAgICAgICAgIHBhY2tldHNOb3cgPSAwO1xuXG4gICAgICAgIHZhciBwYWNrZXRzQmVmb3JlID0gZ2V0U3RhdFZhbHVlKGJlZm9yZSwga2V5KTtcbiAgICAgICAgaWYoIXBhY2tldHNCZWZvcmUgfHwgcGFja2V0c0JlZm9yZSA8IDApXG4gICAgICAgICAgICBwYWNrZXRzQmVmb3JlID0gMDtcbiAgICAgICAgdmFyIHBhY2tldFJhdGUgPSBwYWNrZXRzTm93IC0gcGFja2V0c0JlZm9yZTtcbiAgICAgICAgaWYoIXBhY2tldFJhdGUgfHwgcGFja2V0UmF0ZSA8IDApXG4gICAgICAgICAgICBwYWNrZXRSYXRlID0gMDtcbiAgICAgICAgdmFyIGN1cnJlbnRMb3NzID0gZ2V0U3RhdFZhbHVlKG5vdywgJ3BhY2tldHNMb3N0Jyk7XG4gICAgICAgIGlmKCFjdXJyZW50TG9zcyB8fCBjdXJyZW50TG9zcyA8IDApXG4gICAgICAgICAgICBjdXJyZW50TG9zcyA9IDA7XG4gICAgICAgIHZhciBwcmV2aW91c0xvc3MgPSBnZXRTdGF0VmFsdWUoYmVmb3JlLCAncGFja2V0c0xvc3QnKTtcbiAgICAgICAgaWYoIXByZXZpb3VzTG9zcyB8fCBwcmV2aW91c0xvc3MgPCAwKVxuICAgICAgICAgICAgcHJldmlvdXNMb3NzID0gMDtcbiAgICAgICAgdmFyIGxvc3NSYXRlID0gY3VycmVudExvc3MgLSBwcmV2aW91c0xvc3M7XG4gICAgICAgIGlmKCFsb3NzUmF0ZSB8fCBsb3NzUmF0ZSA8IDApXG4gICAgICAgICAgICBsb3NzUmF0ZSA9IDA7XG4gICAgICAgIHZhciBwYWNrZXRzVG90YWwgPSAocGFja2V0UmF0ZSArIGxvc3NSYXRlKTtcblxuICAgICAgICBqaWRTdGF0cy5zZXRTc3JjTG9zcyhzc3JjLFxuICAgICAgICAgICAge1wicGFja2V0c1RvdGFsXCI6IHBhY2tldHNUb3RhbCxcbiAgICAgICAgICAgICAgICBcInBhY2tldHNMb3N0XCI6IGxvc3NSYXRlLFxuICAgICAgICAgICAgICAgIFwiaXNEb3dubG9hZFN0cmVhbVwiOiBpc0Rvd25sb2FkU3RyZWFtfSk7XG5cblxuICAgICAgICB2YXIgYnl0ZXNSZWNlaXZlZCA9IDAsIGJ5dGVzU2VudCA9IDA7XG4gICAgICAgIGlmKGdldFN0YXRWYWx1ZShub3csIFwiYnl0ZXNSZWNlaXZlZFwiKSlcbiAgICAgICAge1xuICAgICAgICAgICAgYnl0ZXNSZWNlaXZlZCA9IGdldFN0YXRWYWx1ZShub3csIFwiYnl0ZXNSZWNlaXZlZFwiKSAtXG4gICAgICAgICAgICAgICAgZ2V0U3RhdFZhbHVlKGJlZm9yZSwgXCJieXRlc1JlY2VpdmVkXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoZ2V0U3RhdFZhbHVlKG5vdywgXCJieXRlc1NlbnRcIikpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ5dGVzU2VudCA9IGdldFN0YXRWYWx1ZShub3csIFwiYnl0ZXNTZW50XCIpIC1cbiAgICAgICAgICAgICAgICBnZXRTdGF0VmFsdWUoYmVmb3JlLCBcImJ5dGVzU2VudFwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciB0aW1lID0gTWF0aC5yb3VuZCgobm93LnRpbWVzdGFtcCAtIGJlZm9yZS50aW1lc3RhbXApIC8gMTAwMCk7XG4gICAgICAgIGlmKGJ5dGVzUmVjZWl2ZWQgPD0gMCB8fCB0aW1lIDw9IDApXG4gICAgICAgIHtcbiAgICAgICAgICAgIGJ5dGVzUmVjZWl2ZWQgPSAwO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgYnl0ZXNSZWNlaXZlZCA9IE1hdGgucm91bmQoKChieXRlc1JlY2VpdmVkICogOCkgLyB0aW1lKSAvIDEwMDApO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoYnl0ZXNTZW50IDw9IDAgfHwgdGltZSA8PSAwKVxuICAgICAgICB7XG4gICAgICAgICAgICBieXRlc1NlbnQgPSAwO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgYnl0ZXNTZW50ID0gTWF0aC5yb3VuZCgoKGJ5dGVzU2VudCAqIDgpIC8gdGltZSkgLyAxMDAwKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGppZFN0YXRzLnNldFNzcmNCaXRyYXRlKHNzcmMsIHtcbiAgICAgICAgICAgIFwiZG93bmxvYWRcIjogYnl0ZXNSZWNlaXZlZCxcbiAgICAgICAgICAgIFwidXBsb2FkXCI6IGJ5dGVzU2VudH0pO1xuXG4gICAgICAgIHZhciByZXNvbHV0aW9uID0ge2hlaWdodDogbnVsbCwgd2lkdGg6IG51bGx9O1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lSGVpZ2h0UmVjZWl2ZWRcIikgJiZcbiAgICAgICAgICAgICAgICBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZVdpZHRoUmVjZWl2ZWRcIikpIHtcbiAgICAgICAgICAgICAgICByZXNvbHV0aW9uLmhlaWdodCA9IGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lSGVpZ2h0UmVjZWl2ZWRcIik7XG4gICAgICAgICAgICAgICAgcmVzb2x1dGlvbi53aWR0aCA9IGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lV2lkdGhSZWNlaXZlZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lSGVpZ2h0U2VudFwiKSAmJlxuICAgICAgICAgICAgICAgIGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lV2lkdGhTZW50XCIpKSB7XG4gICAgICAgICAgICAgICAgcmVzb2x1dGlvbi5oZWlnaHQgPSBnZXRTdGF0VmFsdWUobm93LCBcImdvb2dGcmFtZUhlaWdodFNlbnRcIik7XG4gICAgICAgICAgICAgICAgcmVzb2x1dGlvbi53aWR0aCA9IGdldFN0YXRWYWx1ZShub3csIFwiZ29vZ0ZyYW1lV2lkdGhTZW50XCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhdGNoKGUpey8qbm90IHN1cHBvcnRlZCovfVxuXG4gICAgICAgIGlmKHJlc29sdXRpb24uaGVpZ2h0ICYmIHJlc29sdXRpb24ud2lkdGgpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGppZFN0YXRzLnNldFNzcmNSZXNvbHV0aW9uKHNzcmMsIHJlc29sdXRpb24pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2VcbiAgICAgICAge1xuICAgICAgICAgICAgamlkU3RhdHMuc2V0U3NyY1Jlc29sdXRpb24oc3NyYywgbnVsbCk7XG4gICAgICAgIH1cblxuXG4gICAgfVxuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIEppZCBzdGF0c1xuICAgIHZhciB0b3RhbFBhY2tldHMgPSB7ZG93bmxvYWQ6IDAsIHVwbG9hZDogMH07XG4gICAgdmFyIGxvc3RQYWNrZXRzID0ge2Rvd25sb2FkOiAwLCB1cGxvYWQ6IDB9O1xuICAgIHZhciBiaXRyYXRlRG93bmxvYWQgPSAwO1xuICAgIHZhciBiaXRyYXRlVXBsb2FkID0gMDtcbiAgICB2YXIgcmVzb2x1dGlvbnMgPSB7fTtcbiAgICBPYmplY3Qua2V5cyh0aGlzLmppZDJzdGF0cykuZm9yRWFjaChcbiAgICAgICAgZnVuY3Rpb24gKGppZClcbiAgICAgICAge1xuICAgICAgICAgICAgT2JqZWN0LmtleXMoc2VsZi5qaWQyc3RhdHNbamlkXS5zc3JjMkxvc3MpLmZvckVhY2goXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHNzcmMpXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IFwidXBsb2FkXCI7XG4gICAgICAgICAgICAgICAgICAgIGlmKHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJMb3NzW3NzcmNdLmlzRG93bmxvYWRTdHJlYW0pXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gXCJkb3dubG9hZFwiO1xuICAgICAgICAgICAgICAgICAgICB0b3RhbFBhY2tldHNbdHlwZV0gKz1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJMb3NzW3NzcmNdLnBhY2tldHNUb3RhbDtcbiAgICAgICAgICAgICAgICAgICAgbG9zdFBhY2tldHNbdHlwZV0gKz1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJMb3NzW3NzcmNdLnBhY2tldHNMb3N0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBPYmplY3Qua2V5cyhzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyYml0cmF0ZSkuZm9yRWFjaChcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBiaXRyYXRlRG93bmxvYWQgKz1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJiaXRyYXRlW3NzcmNdLmRvd25sb2FkO1xuICAgICAgICAgICAgICAgICAgICBiaXRyYXRlVXBsb2FkICs9XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmppZDJzdGF0c1tqaWRdLnNzcmMyYml0cmF0ZVtzc3JjXS51cGxvYWQ7XG5cbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHNlbGYuamlkMnN0YXRzW2ppZF0uc3NyYzJiaXRyYXRlW3NzcmNdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXNvbHV0aW9uc1tqaWRdID0gc2VsZi5qaWQyc3RhdHNbamlkXS5zc3JjMnJlc29sdXRpb247XG4gICAgICAgIH1cbiAgICApO1xuXG4gICAgUGVlclN0YXRzLmJpdHJhdGUgPSB7XCJ1cGxvYWRcIjogYml0cmF0ZVVwbG9hZCwgXCJkb3dubG9hZFwiOiBiaXRyYXRlRG93bmxvYWR9O1xuXG4gICAgUGVlclN0YXRzLnBhY2tldExvc3MgPSB7XG4gICAgICAgIHRvdGFsOlxuICAgICAgICAgICAgY2FsY3VsYXRlUGFja2V0TG9zcyhsb3N0UGFja2V0cy5kb3dubG9hZCArIGxvc3RQYWNrZXRzLnVwbG9hZCxcbiAgICAgICAgICAgICAgICAgICAgdG90YWxQYWNrZXRzLmRvd25sb2FkICsgdG90YWxQYWNrZXRzLnVwbG9hZCksXG4gICAgICAgIGRvd25sb2FkOlxuICAgICAgICAgICAgY2FsY3VsYXRlUGFja2V0TG9zcyhsb3N0UGFja2V0cy5kb3dubG9hZCwgdG90YWxQYWNrZXRzLmRvd25sb2FkKSxcbiAgICAgICAgdXBsb2FkOlxuICAgICAgICAgICAgY2FsY3VsYXRlUGFja2V0TG9zcyhsb3N0UGFja2V0cy51cGxvYWQsIHRvdGFsUGFja2V0cy51cGxvYWQpXG4gICAgfTtcbiAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwic3RhdGlzdGljcy5jb25uZWN0aW9uc3RhdHNcIixcbiAgICAgICAge1xuICAgICAgICAgICAgXCJiaXRyYXRlXCI6IFBlZXJTdGF0cy5iaXRyYXRlLFxuICAgICAgICAgICAgXCJwYWNrZXRMb3NzXCI6IFBlZXJTdGF0cy5wYWNrZXRMb3NzLFxuICAgICAgICAgICAgXCJiYW5kd2lkdGhcIjogUGVlclN0YXRzLmJhbmR3aWR0aCxcbiAgICAgICAgICAgIFwicmVzb2x1dGlvblwiOiByZXNvbHV0aW9ucyxcbiAgICAgICAgICAgIFwidHJhbnNwb3J0XCI6IFBlZXJTdGF0cy50cmFuc3BvcnRcbiAgICAgICAgfSk7XG4gICAgUGVlclN0YXRzLnRyYW5zcG9ydCA9IFtdO1xuXG59O1xuXG4vKipcbiAqIFN0YXRzIHByb2Nlc3NpbmcgbG9naWMuXG4gKi9cblN0YXRzQ29sbGVjdG9yLnByb3RvdHlwZS5wcm9jZXNzQXVkaW9MZXZlbFJlcG9ydCA9IGZ1bmN0aW9uICgpXG57XG4gICAgaWYgKCF0aGlzLmJhc2VsaW5lQXVkaW9MZXZlbHNSZXBvcnQpXG4gICAge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZm9yICh2YXIgaWR4IGluIHRoaXMuY3VycmVudEF1ZGlvTGV2ZWxzUmVwb3J0KVxuICAgIHtcbiAgICAgICAgdmFyIG5vdyA9IHRoaXMuY3VycmVudEF1ZGlvTGV2ZWxzUmVwb3J0W2lkeF07XG5cbiAgICAgICAgaWYgKG5vdy50eXBlICE9ICdzc3JjJylcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgYmVmb3JlID0gdGhpcy5iYXNlbGluZUF1ZGlvTGV2ZWxzUmVwb3J0W2lkeF07XG4gICAgICAgIGlmICghYmVmb3JlKVxuICAgICAgICB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oZ2V0U3RhdFZhbHVlKG5vdywgJ3NzcmMnKSArICcgbm90IGVub3VnaCBkYXRhJyk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBzc3JjID0gZ2V0U3RhdFZhbHVlKG5vdywgJ3NzcmMnKTtcbiAgICAgICAgdmFyIGppZCA9IEFQUC54bXBwLmdldEppZEZyb21TU1JDKHNzcmMpO1xuICAgICAgICBpZiAoIWppZCAmJiAoRGF0ZS5ub3coKSAtIG5vdy50aW1lc3RhbXApIDwgMzAwMClcbiAgICAgICAge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTm8gamlkIGZvciBzc3JjOiBcIiArIHNzcmMpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgamlkU3RhdHMgPSB0aGlzLmppZDJzdGF0c1tqaWRdO1xuICAgICAgICBpZiAoIWppZFN0YXRzKVxuICAgICAgICB7XG4gICAgICAgICAgICBqaWRTdGF0cyA9IG5ldyBQZWVyU3RhdHMoKTtcbiAgICAgICAgICAgIHRoaXMuamlkMnN0YXRzW2ppZF0gPSBqaWRTdGF0cztcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEF1ZGlvIGxldmVsXG4gICAgICAgIHZhciBhdWRpb0xldmVsID0gbnVsbDtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXVkaW9MZXZlbCA9IGdldFN0YXRWYWx1ZShub3csICdhdWRpb0lucHV0TGV2ZWwnKTtcbiAgICAgICAgICAgIGlmICghYXVkaW9MZXZlbClcbiAgICAgICAgICAgICAgICBhdWRpb0xldmVsID0gZ2V0U3RhdFZhbHVlKG5vdywgJ2F1ZGlvT3V0cHV0TGV2ZWwnKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaChlKSB7Lypub3Qgc3VwcG9ydGVkKi9cbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkF1ZGlvIExldmVscyBhcmUgbm90IGF2YWlsYWJsZSBpbiB0aGUgc3RhdGlzdGljcy5cIik7XG4gICAgICAgICAgICBjbGVhckludGVydmFsKHRoaXMuYXVkaW9MZXZlbHNJbnRlcnZhbElkKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChhdWRpb0xldmVsKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBUT0RPOiBjYW4ndCBmaW5kIHNwZWNzIGFib3V0IHdoYXQgdGhpcyB2YWx1ZSByZWFsbHkgaXMsXG4gICAgICAgICAgICAvLyBidXQgaXQgc2VlbXMgdG8gdmFyeSBiZXR3ZWVuIDAgYW5kIGFyb3VuZCAzMmsuXG4gICAgICAgICAgICBhdWRpb0xldmVsID0gYXVkaW9MZXZlbCAvIDMyNzY3O1xuICAgICAgICAgICAgamlkU3RhdHMuc2V0U3NyY0F1ZGlvTGV2ZWwoc3NyYywgYXVkaW9MZXZlbCk7XG4gICAgICAgICAgICBpZihqaWQgIT0gQVBQLnhtcHAubXlKaWQoKSlcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwic3RhdGlzdGljcy5hdWRpb0xldmVsXCIsIGppZCwgYXVkaW9MZXZlbCk7XG4gICAgICAgIH1cblxuICAgIH1cblxuXG59OyIsIi8qKlxuICogQ3JlYXRlZCBieSBocmlzdG8gb24gOC80LzE0LlxuICovXG52YXIgTG9jYWxTdGF0cyA9IHJlcXVpcmUoXCIuL0xvY2FsU3RhdHNDb2xsZWN0b3IuanNcIik7XG52YXIgUlRQU3RhdHMgPSByZXF1aXJlKFwiLi9SVFBTdGF0c0NvbGxlY3Rvci5qc1wiKTtcbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFN0cmVhbUV2ZW50VHlwZXMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvU3RyZWFtRXZlbnRUeXBlcy5qc1wiKTtcbnZhciBYTVBQRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UveG1wcC9YTVBQRXZlbnRzXCIpO1xuXG52YXIgZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG52YXIgbG9jYWxTdGF0cyA9IG51bGw7XG5cbnZhciBydHBTdGF0cyA9IG51bGw7XG5cbmZ1bmN0aW9uIHN0b3BMb2NhbCgpXG57XG4gICAgaWYobG9jYWxTdGF0cylcbiAgICB7XG4gICAgICAgIGxvY2FsU3RhdHMuc3RvcCgpO1xuICAgICAgICBsb2NhbFN0YXRzID0gbnVsbDtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHN0b3BSZW1vdGUoKVxue1xuICAgIGlmKHJ0cFN0YXRzKVxuICAgIHtcbiAgICAgICAgcnRwU3RhdHMuc3RvcCgpO1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChcInN0YXRpc3RpY3Muc3RvcFwiKTtcbiAgICAgICAgcnRwU3RhdHMgPSBudWxsO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc3RhcnRSZW1vdGVTdGF0cyAocGVlcmNvbm5lY3Rpb24pIHtcbiAgICBpZihydHBTdGF0cylcbiAgICB7XG4gICAgICAgIHJ0cFN0YXRzLnN0b3AoKTtcbiAgICAgICAgcnRwU3RhdHMgPSBudWxsO1xuICAgIH1cblxuICAgIHJ0cFN0YXRzID0gbmV3IFJUUFN0YXRzKHBlZXJjb25uZWN0aW9uLCAyMDAsIDIwMDAsIGV2ZW50RW1pdHRlcik7XG4gICAgcnRwU3RhdHMuc3RhcnQoKTtcbn1cblxuZnVuY3Rpb24gb25TdHJlYW1DcmVhdGVkKHN0cmVhbSlcbntcbiAgICBpZihzdHJlYW0uZ2V0T3JpZ2luYWxTdHJlYW0oKS5nZXRBdWRpb1RyYWNrcygpLmxlbmd0aCA9PT0gMClcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgbG9jYWxTdGF0cyA9IG5ldyBMb2NhbFN0YXRzKHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpLCAxMDAsIHN0YXRpc3RpY3MsXG4gICAgICAgIGV2ZW50RW1pdHRlcik7XG4gICAgbG9jYWxTdGF0cy5zdGFydCgpO1xufVxuXG5mdW5jdGlvbiBvbkRpc3Bvc2VDb25mZXJlbmNlKG9uVW5sb2FkKSB7XG4gICAgc3RvcFJlbW90ZSgpO1xuICAgIGlmKG9uVW5sb2FkKSB7XG4gICAgICAgIHN0b3BMb2NhbCgpO1xuICAgICAgICBldmVudEVtaXR0ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgfVxufVxuXG5cbnZhciBzdGF0aXN0aWNzID1cbntcbiAgICAvKipcbiAgICAgKiBJbmRpY2F0ZXMgdGhhdCB0aGlzIGF1ZGlvIGxldmVsIGlzIGZvciBsb2NhbCBqaWQuXG4gICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgKi9cbiAgICBMT0NBTF9KSUQ6ICdsb2NhbCcsXG5cbiAgICBhZGRBdWRpb0xldmVsTGlzdGVuZXI6IGZ1bmN0aW9uKGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKFwic3RhdGlzdGljcy5hdWRpb0xldmVsXCIsIGxpc3RlbmVyKTtcbiAgICB9LFxuXG4gICAgcmVtb3ZlQXVkaW9MZXZlbExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcihcInN0YXRpc3RpY3MuYXVkaW9MZXZlbFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIGFkZENvbm5lY3Rpb25TdGF0c0xpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbihcInN0YXRpc3RpY3MuY29ubmVjdGlvbnN0YXRzXCIsIGxpc3RlbmVyKTtcbiAgICB9LFxuXG4gICAgcmVtb3ZlQ29ubmVjdGlvblN0YXRzTGlzdGVuZXI6IGZ1bmN0aW9uKGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKFwic3RhdGlzdGljcy5jb25uZWN0aW9uc3RhdHNcIiwgbGlzdGVuZXIpO1xuICAgIH0sXG5cblxuICAgIGFkZFJlbW90ZVN0YXRzU3RvcExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5vbihcInN0YXRpc3RpY3Muc3RvcFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIHJlbW92ZVJlbW90ZVN0YXRzU3RvcExpc3RlbmVyOiBmdW5jdGlvbihsaXN0ZW5lcilcbiAgICB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5yZW1vdmVMaXN0ZW5lcihcInN0YXRpc3RpY3Muc3RvcFwiLCBsaXN0ZW5lcik7XG4gICAgfSxcblxuICAgIHN0b3A6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgc3RvcExvY2FsKCk7XG4gICAgICAgIHN0b3BSZW1vdGUoKTtcbiAgICAgICAgaWYoZXZlbnRFbWl0dGVyKVxuICAgICAgICB7XG4gICAgICAgICAgICBldmVudEVtaXR0ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc3RvcFJlbW90ZVN0YXRpc3RpY3M6IGZ1bmN0aW9uKClcbiAgICB7XG4gICAgICAgIHN0b3BSZW1vdGUoKTtcbiAgICB9LFxuXG4gICAgc3RhcnQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgQVBQLlJUQy5hZGRTdHJlYW1MaXN0ZW5lcihvblN0cmVhbUNyZWF0ZWQsXG4gICAgICAgICAgICBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ1JFQVRFRCk7XG4gICAgICAgIEFQUC54bXBwLmFkZExpc3RlbmVyKFhNUFBFdmVudHMuRElTUE9TRV9DT05GRVJFTkNFLCBvbkRpc3Bvc2VDb25mZXJlbmNlKTtcbiAgICAgICAgQVBQLnhtcHAuYWRkTGlzdGVuZXIoWE1QUEV2ZW50cy5DQUxMX0lOQ09NSU5HLCBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgIHN0YXJ0UmVtb3RlU3RhdHMoZXZlbnQucGVlcmNvbm5lY3Rpb24pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbn07XG5cblxuXG5cbm1vZHVsZS5leHBvcnRzID0gc3RhdGlzdGljczsiLCJ2YXIgaTE4biA9IHJlcXVpcmUoXCJpMThuZXh0LWNsaWVudFwiKTtcbnZhciBsYW5ndWFnZXMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS90cmFuc2xhdGlvbi9sYW5ndWFnZXNcIik7XG52YXIgREVGQVVMVF9MQU5HID0gbGFuZ3VhZ2VzLkVOO1xudmFyIGluaXRpYWxpemVkID0gZmFsc2U7XG52YXIgd2FpdGluZ0ZvckluaXQgPSBbXTtcblxuaTE4bi5hZGRQb3N0UHJvY2Vzc29yKFwicmVzb2x2ZUFwcE5hbWVcIiwgZnVuY3Rpb24odmFsdWUsIGtleSwgb3B0aW9ucykge1xuICAgIHJldHVybiB2YWx1ZS5yZXBsYWNlKFwiX19hcHBfX1wiLCBpbnRlcmZhY2VDb25maWcuQVBQX05BTUUpO1xufSk7XG5cblxuXG52YXIgZGVmYXVsdE9wdGlvbnMgPSB7XG4gICAgZGV0ZWN0TG5nUVM6IFwibGFuZ1wiLFxuICAgIHVzZUNvb2tpZTogZmFsc2UsXG4gICAgZmFsbGJhY2tMbmc6IERFRkFVTFRfTEFORyxcbiAgICBsb2FkOiBcInVuc3BlY2lmaWNcIixcbiAgICByZXNHZXRQYXRoOiAnbGFuZy9fX25zX18tX19sbmdfXy5qc29uJyxcbiAgICBuczoge1xuICAgICAgICBuYW1lc3BhY2VzOiBbJ21haW4nLCAnbGFuZ3VhZ2VzJ10sXG4gICAgICAgIGRlZmF1bHROczogJ21haW4nXG4gICAgfSxcbiAgICBsbmdXaGl0ZWxpc3QgOiBsYW5ndWFnZXMuZ2V0TGFuZ3VhZ2VzKCksXG4gICAgZmFsbGJhY2tPbk51bGw6IHRydWUsXG4gICAgdXNlRGF0YUF0dHJPcHRpb25zOiB0cnVlLFxuICAgIGFwcDogaW50ZXJmYWNlQ29uZmlnLkFQUF9OQU1FLFxuICAgIGdldEFzeW5jOiB0cnVlLFxuICAgIGN1c3RvbUxvYWQ6IGZ1bmN0aW9uKGxuZywgbnMsIG9wdGlvbnMsIGRvbmUpIHtcbiAgICAgICAgdmFyIHJlc1BhdGggPSBcImxhbmcvX19uc19fLV9fbG5nX18uanNvblwiO1xuICAgICAgICBpZihsbmcgPT09IGxhbmd1YWdlcy5FTilcbiAgICAgICAgICAgIHJlc1BhdGggPSBcImxhbmcvX19uc19fLmpzb25cIjtcbiAgICAgICAgdmFyIHVybCA9IGkxOG4uZnVuY3Rpb25zLmFwcGx5UmVwbGFjZW1lbnQocmVzUGF0aCwgeyBsbmc6IGxuZywgbnM6IG5zIH0pO1xuICAgICAgICBpbml0aWFsaXplZCA9IGZhbHNlO1xuICAgICAgICBpMThuLmZ1bmN0aW9ucy5hamF4KHtcbiAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCB4aHIpIHtcbiAgICAgICAgICAgICAgICBpMThuLmZ1bmN0aW9ucy5sb2coJ2xvYWRlZDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgZG9uZShudWxsLCBkYXRhKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBlcnJvciA6IGZ1bmN0aW9uKHhociwgc3RhdHVzLCBlcnJvcikge1xuICAgICAgICAgICAgICAgIGlmICgoc3RhdHVzICYmIHN0YXR1cyA9PSAyMDApIHx8XG4gICAgICAgICAgICAgICAgICAgICh4aHIgJiYgeGhyLnN0YXR1cyAmJiB4aHIuc3RhdHVzID09IDIwMCkpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gZmlsZSBsb2FkZWQgYnV0IGludmFsaWQganNvbiwgc3RvcCB3YXN0ZSB0aW1lICFcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMuZXJyb3IoJ1RoZXJlIGlzIGEgdHlwbyBpbjogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICgoc3RhdHVzICYmIHN0YXR1cyA9PSA0MDQpIHx8XG4gICAgICAgICAgICAgICAgICAgICh4aHIgJiYgeGhyLnN0YXR1cyAmJiB4aHIuc3RhdHVzID09IDQwNCkpIHtcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMubG9nKCdEb2VzIG5vdCBleGlzdDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHRoZVN0YXR1cyA9IHN0YXR1cyA/IHN0YXR1cyA6XG4gICAgICAgICAgICAgICAgICAgICAgICAoKHhociAmJiB4aHIuc3RhdHVzKSA/IHhoci5zdGF0dXMgOiBudWxsKTtcbiAgICAgICAgICAgICAgICAgICAgaTE4bi5mdW5jdGlvbnMubG9nKHRoZVN0YXR1cyArICcgd2hlbiBsb2FkaW5nICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHt9KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkYXRhVHlwZTogXCJqc29uXCIsXG4gICAgICAgICAgICBhc3luYyA6IG9wdGlvbnMuZ2V0QXN5bmNcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8vICAgICAgICAgICAgICBvcHRpb25zIGZvciBjYWNoaW5nXG4vLyAgICAgICAgICAgICAgICB1c2VMb2NhbFN0b3JhZ2U6IHRydWUsXG4vLyAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2VFeHBpcmF0aW9uVGltZTogODY0MDAwMDAgLy8gaW4gbXMsIGRlZmF1bHQgMSB3ZWVrXG59O1xuXG5mdW5jdGlvbiBpbml0Q29tcGxldGVkKHQpXG57XG4gICAgaW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgICQoXCJbZGF0YS1pMThuXVwiKS5pMThuKCk7XG4gICAgZm9yKHZhciBpID0gMDsgaSA8IHdhaXRpbmdGb3JJbml0Lmxlbmd0aDsgaSsrKVxuICAgIHtcbiAgICAgICAgdmFyIG9iaiA9IHdhaXRpbmdGb3JJbml0W2ldO1xuICAgICAgICBvYmouY2FsbGJhY2soaTE4bi50KG9iai5rZXkpKTtcbiAgICB9XG4gICAgd2FpdGluZ0ZvckluaXQgPSBbXTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaW5pdDogZnVuY3Rpb24gKGxhbmcpIHtcbiAgICAgICAgaW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgICAgICAgdmFyIG9wdGlvbnMgPSBkZWZhdWx0T3B0aW9ucztcbiAgICAgICAgaWYobGFuZylcbiAgICAgICAgICAgIG9wdGlvbnMubG5nID0gbGFuZztcbiAgICAgICAgaTE4bi5pbml0KG9wdGlvbnMsIGluaXRDb21wbGV0ZWQpO1xuICAgIH0sXG4gICAgdHJhbnNsYXRlU3RyaW5nOiBmdW5jdGlvbiAoa2V5LCBjYikge1xuICAgICAgICBpZighY2IpXG4gICAgICAgICAgICByZXR1cm4gaTE4bi50KGtleSk7XG5cbiAgICAgICAgaWYoaW5pdGlhbGl6ZWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNiKGkxOG4udChrZXkpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlXG4gICAgICAgIHtcbiAgICAgICAgICAgIHdhaXRpbmdGb3JJbml0LnB1c2goe1wiY2FsbGJhY2tcIjogY2IsIFwia2V5XCI6IGtleX0pO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZXRMYW5ndWFnZTogZnVuY3Rpb24gKGxhbmcpIHtcbiAgICAgICAgaW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgICAgICAgaWYoIWxhbmcpXG4gICAgICAgICAgICBsYW5nID0gREVGQVVMVF9MQU5HO1xuICAgICAgICBpMThuLnNldExuZyhsYW5nLCBkZWZhdWx0T3B0aW9ucywgaW5pdENvbXBsZXRlZCk7XG4gICAgfSxcbiAgICBnZXRDdXJyZW50TGFuZ3VhZ2U6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGkxOG4ubG5nKCk7XG4gICAgfSxcbiAgICB0cmFuc2xhdGVFbGVtZW50OiBmdW5jdGlvbiAoc2VsZWN0b3IpIHtcbiAgICAgICAgc2VsZWN0b3IuaTE4bigpO1xuICAgIH1cbn07XG4iLCIvKiBqc2hpbnQgLVcxMTcgKi9cbnZhciBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbiA9IHJlcXVpcmUoXCIuL1RyYWNlYWJsZVBlZXJDb25uZWN0aW9uXCIpO1xudmFyIFNEUERpZmZlciA9IHJlcXVpcmUoXCIuL1NEUERpZmZlclwiKTtcbnZhciBTRFBVdGlsID0gcmVxdWlyZShcIi4vU0RQVXRpbFwiKTtcbnZhciBTRFAgPSByZXF1aXJlKFwiLi9TRFBcIik7XG52YXIgUlRDQnJvd3NlclR5cGUgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS9SVEMvUlRDQnJvd3NlclR5cGVcIik7XG5cbi8vIEppbmdsZSBzdHVmZlxuZnVuY3Rpb24gSmluZ2xlU2Vzc2lvbihtZSwgc2lkLCBjb25uZWN0aW9uLCBzZXJ2aWNlKSB7XG4gICAgdGhpcy5tZSA9IG1lO1xuICAgIHRoaXMuc2lkID0gc2lkO1xuICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm5lY3Rpb247XG4gICAgdGhpcy5pbml0aWF0b3IgPSBudWxsO1xuICAgIHRoaXMucmVzcG9uZGVyID0gbnVsbDtcbiAgICB0aGlzLmlzSW5pdGlhdG9yID0gbnVsbDtcbiAgICB0aGlzLnBlZXJqaWQgPSBudWxsO1xuICAgIHRoaXMuc3RhdGUgPSBudWxsO1xuICAgIHRoaXMubG9jYWxTRFAgPSBudWxsO1xuICAgIHRoaXMucmVtb3RlU0RQID0gbnVsbDtcbiAgICB0aGlzLnJlbGF5ZWRTdHJlYW1zID0gW107XG4gICAgdGhpcy5zdGFydFRpbWUgPSBudWxsO1xuICAgIHRoaXMuc3RvcFRpbWUgPSBudWxsO1xuICAgIHRoaXMubWVkaWFfY29uc3RyYWludHMgPSBudWxsO1xuICAgIHRoaXMucGNfY29uc3RyYWludHMgPSBudWxsO1xuICAgIHRoaXMuaWNlX2NvbmZpZyA9IHt9O1xuICAgIHRoaXMuZHJpcF9jb250YWluZXIgPSBbXTtcbiAgICB0aGlzLnNlcnZpY2UgPSBzZXJ2aWNlO1xuXG4gICAgdGhpcy51c2V0cmlja2xlID0gdHJ1ZTtcbiAgICB0aGlzLnVzZXByYW5zd2VyID0gZmFsc2U7IC8vIGVhcmx5IHRyYW5zcG9ydCB3YXJtdXAgLS0gbWluZCB5b3UsIHRoaXMgbWlnaHQgZmFpbC4gZGVwZW5kcyBvbiB3ZWJydGMgaXNzdWUgMTcxOFxuICAgIHRoaXMudXNlZHJpcCA9IGZhbHNlOyAvLyBkcmlwcGluZyBpcyBzZW5kaW5nIHRyaWNrbGUgY2FuZGlkYXRlcyBub3Qgb25lLWJ5LW9uZVxuXG4gICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0aWNlY2FuZGlkYXRlID0gZmFsc2U7XG5cbiAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSBudWxsO1xuXG4gICAgdGhpcy5yZWFzb24gPSBudWxsO1xuXG4gICAgdGhpcy5hZGRzc3JjID0gW107XG4gICAgdGhpcy5yZW1vdmVzc3JjID0gW107XG4gICAgdGhpcy5wZW5kaW5nb3AgPSBudWxsO1xuICAgIHRoaXMuc3dpdGNoc3RyZWFtcyA9IGZhbHNlO1xuXG4gICAgdGhpcy53YWl0ID0gdHJ1ZTtcbiAgICB0aGlzLmxvY2FsU3RyZWFtc1NTUkMgPSBudWxsO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGluZGljYXRvciB3aGljaCBkZXRlcm1pbmVzIHdoZXRoZXIgdGhlIChsb2NhbCkgdmlkZW8gaGFzIGJlZW4gbXV0ZWRcbiAgICAgKiBpbiByZXNwb25zZSB0byBhIHVzZXIgY29tbWFuZCBpbiBjb250cmFzdCB0byBhbiBhdXRvbWF0aWMgZGVjaXNpb24gbWFkZVxuICAgICAqIGJ5IHRoZSBhcHBsaWNhdGlvbiBsb2dpYy5cbiAgICAgKi9cbiAgICB0aGlzLnZpZGVvTXV0ZUJ5VXNlciA9IGZhbHNlO1xufVxuXG4vL1RPRE86IHRoaXMgYXJyYXkgbXVzdCBiZSByZW1vdmVkIHdoZW4gZmlyZWZveCBpbXBsZW1lbnQgbXVsdGlzdHJlYW0gc3VwcG9ydFxuSmluZ2xlU2Vzc2lvbi5ub3RSZWNlaXZlZFNTUkNzID0gW107XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmluaXRpYXRlID0gZnVuY3Rpb24gKHBlZXJqaWQsIGlzSW5pdGlhdG9yKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmICh0aGlzLnN0YXRlICE9PSBudWxsKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ2F0dGVtcHQgdG8gaW5pdGlhdGUgb24gc2Vzc2lvbiAnICsgdGhpcy5zaWQgK1xuICAgICAgICAgICAgJ2luIHN0YXRlICcgKyB0aGlzLnN0YXRlKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmlzSW5pdGlhdG9yID0gaXNJbml0aWF0b3I7XG4gICAgdGhpcy5zdGF0ZSA9ICdwZW5kaW5nJztcbiAgICB0aGlzLmluaXRpYXRvciA9IGlzSW5pdGlhdG9yID8gdGhpcy5tZSA6IHBlZXJqaWQ7XG4gICAgdGhpcy5yZXNwb25kZXIgPSAhaXNJbml0aWF0b3IgPyB0aGlzLm1lIDogcGVlcmppZDtcbiAgICB0aGlzLnBlZXJqaWQgPSBwZWVyamlkO1xuICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMubGFzdGljZWNhbmRpZGF0ZSA9IGZhbHNlO1xuXG4gICAgdGhpcy5wZWVyY29ubmVjdGlvblxuICAgICAgICA9IG5ldyBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbihcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5qaW5nbGUuaWNlX2NvbmZpZyxcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5qaW5nbGUucGNfY29uc3RyYWludHMgKTtcblxuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2VjYW5kaWRhdGUgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi5zZW5kSWNlQ2FuZGlkYXRlKGV2ZW50LmNhbmRpZGF0ZSk7XG4gICAgfTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uYWRkc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiUkVNT1RFIFNUUkVBTSBBRERFRDogXCIgKyBldmVudC5zdHJlYW0gKyBcIiAtIFwiICsgZXZlbnQuc3RyZWFtLmlkKTtcbiAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1BZGRlZChldmVudCk7XG4gICAgfTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9ucmVtb3Zlc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIC8vIFJlbW92ZSB0aGUgc3RyZWFtIGZyb20gcmVtb3RlU3RyZWFtc1xuICAgICAgICAvLyBGSVhNRTogcmVtb3Rlc3RyZWFtcmVtb3ZlZC5qaW5nbGUgbm90IGRlZmluZWQgYW55d2hlcmUodW51c2VkKVxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdyZW1vdGVzdHJlYW1yZW1vdmVkLmppbmdsZScsIFtldmVudCwgc2VsZi5zaWRdKTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBpZiAoIShzZWxmICYmIHNlbGYucGVlcmNvbm5lY3Rpb24pKSByZXR1cm47XG4gICAgfTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGlmICghKHNlbGYgJiYgc2VsZi5wZWVyY29ubmVjdGlvbikpIHJldHVybjtcbiAgICAgICAgc3dpdGNoIChzZWxmLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSkge1xuICAgICAgICAgICAgY2FzZSAnY29ubmVjdGVkJzpcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdkaXNjb25uZWN0ZWQnOlxuICAgICAgICAgICAgICAgIHRoaXMuc3RvcFRpbWUgPSBuZXcgRGF0ZSgpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIG9uSWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlKHNlbGYuc2lkLCBzZWxmKTtcbiAgICB9O1xuICAgIC8vIGFkZCBhbnkgbG9jYWwgYW5kIHJlbGF5ZWQgc3RyZWFtXG4gICAgQVBQLlJUQy5sb2NhbFN0cmVhbXMuZm9yRWFjaChmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRTdHJlYW0oc3RyZWFtLmdldE9yaWdpbmFsU3RyZWFtKCkpO1xuICAgIH0pO1xuICAgIHRoaXMucmVsYXllZFN0cmVhbXMuZm9yRWFjaChmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRTdHJlYW0oc3RyZWFtKTtcbiAgICB9KTtcbn07XG5cbmZ1bmN0aW9uIG9uSWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlKHNpZCwgc2Vzc2lvbikge1xuICAgIHN3aXRjaCAoc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpIHtcbiAgICAgICAgY2FzZSAnY2hlY2tpbmcnOlxuICAgICAgICAgICAgc2Vzc2lvbi50aW1lQ2hlY2tpbmcgPSAobmV3IERhdGUoKSkuZ2V0VGltZSgpO1xuICAgICAgICAgICAgc2Vzc2lvbi5maXJzdGNvbm5lY3QgPSB0cnVlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2NvbXBsZXRlZCc6IC8vIG9uIGNhbGxlciBzaWRlXG4gICAgICAgIGNhc2UgJ2Nvbm5lY3RlZCc6XG4gICAgICAgICAgICBpZiAoc2Vzc2lvbi5maXJzdGNvbm5lY3QpIHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmZpcnN0Y29ubmVjdCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHZhciBtZXRhZGF0YSA9IHt9O1xuICAgICAgICAgICAgICAgIG1ldGFkYXRhLnNldHVwVGltZVxuICAgICAgICAgICAgICAgICAgICA9IChuZXcgRGF0ZSgpKS5nZXRUaW1lKCkgLSBzZXNzaW9uLnRpbWVDaGVja2luZztcbiAgICAgICAgICAgICAgICBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYocmVzICYmIHJlcy5yZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcy5yZXN1bHQoKS5mb3JFYWNoKGZ1bmN0aW9uIChyZXBvcnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVwb3J0LnR5cGUgPT0gJ2dvb2dDYW5kaWRhdGVQYWlyJyAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBvcnQuc3RhdCgnZ29vZ0FjdGl2ZUNvbm5lY3Rpb24nKSA9PSAndHJ1ZScpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEubG9jYWxDYW5kaWRhdGVUeXBlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IHJlcG9ydC5zdGF0KCdnb29nTG9jYWxDYW5kaWRhdGVUeXBlJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLnJlbW90ZUNhbmRpZGF0ZVR5cGVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gcmVwb3J0LnN0YXQoJ2dvb2dSZW1vdGVDYW5kaWRhdGVUeXBlJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbG9nIHBhaXIgYXMgd2VsbCBzbyB3ZSBjYW4gZ2V0IG5pY2UgcGllXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNoYXJ0c1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YS5jYW5kaWRhdGVQYWlyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IHJlcG9ydC5zdGF0KCdnb29nTG9jYWxDYW5kaWRhdGVUeXBlJykgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc7JyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3J0LnN0YXQoJ2dvb2dSZW1vdGVDYW5kaWRhdGVUeXBlJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHJlcG9ydC5zdGF0KCdnb29nUmVtb3RlQWRkcmVzcycpLmluZGV4T2YoJ1snKSA9PT0gMClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEuaXB2NiA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgfVxufVxuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5hY2NlcHQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMuc3RhdGUgPSAnYWN0aXZlJztcblxuICAgIHZhciBwcmFuc3dlciA9IHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbjtcbiAgICBpZiAoIXByYW5zd2VyIHx8IHByYW5zd2VyLnR5cGUgIT0gJ3ByYW5zd2VyJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKCdnb2luZyBmcm9tIHByYW5zd2VyIHRvIGFuc3dlcicpO1xuICAgIGlmICh0aGlzLnVzZXRyaWNrbGUpIHtcbiAgICAgICAgLy8gcmVtb3ZlIGNhbmRpZGF0ZXMgYWxyZWFkeSBzZW50IGZyb20gc2Vzc2lvbi1hY2NlcHRcbiAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHByYW5zd2VyLnNkcCwgJ2E9Y2FuZGlkYXRlOicpO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBwcmFuc3dlci5zZHAgPSBwcmFuc3dlci5zZHAucmVwbGFjZShsaW5lc1tpXSArICdcXHJcXG4nLCAnJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgd2hpbGUgKFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLnNkcCwgJ2E9aW5hY3RpdmUnKSkge1xuICAgICAgICAvLyBGSVhNRTogY2hhbmdlIGFueSBpbmFjdGl2ZSB0byBzZW5kcmVjdiBvciB3aGF0ZXZlciB0aGV5IHdlcmUgb3JpZ2luYWxseVxuICAgICAgICBwcmFuc3dlci5zZHAgPSBwcmFuc3dlci5zZHAucmVwbGFjZSgnYT1pbmFjdGl2ZScsICdhPXNlbmRyZWN2Jyk7XG4gICAgfVxuICAgIHByYW5zd2VyID0gQVBQLnNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihwcmFuc3dlcik7XG4gICAgdmFyIHByc2RwID0gbmV3IFNEUChwcmFuc3dlci5zZHApO1xuICAgIHZhciBhY2NlcHQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3Nlc3Npb24tYWNjZXB0JyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICByZXNwb25kZXI6IHRoaXMucmVzcG9uZGVyLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZCB9KTtcbiAgICBwcnNkcC50b0ppbmdsZShhY2NlcHQsIHRoaXMuaW5pdGlhdG9yID09IHRoaXMubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLCB0aGlzLmxvY2FsU3RyZWFtc1NTUkMpO1xuICAgIHZhciBzZHAgPSB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwO1xuICAgIHdoaWxlIChTRFBVdGlsLmZpbmRfbGluZShzZHAsICdhPWluYWN0aXZlJykpIHtcbiAgICAgICAgLy8gRklYTUU6IGNoYW5nZSBhbnkgaW5hY3RpdmUgdG8gc2VuZHJlY3Ygb3Igd2hhdGV2ZXIgdGhleSB3ZXJlIG9yaWdpbmFsbHlcbiAgICAgICAgc2RwID0gc2RwLnJlcGxhY2UoJ2E9aW5hY3RpdmUnLCAnYT1zZW5kcmVjdicpO1xuICAgIH1cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRMb2NhbERlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdhbnN3ZXInLCBzZHA6IHNkcH0pLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuXG4gICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lXG4gICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUudGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbikge1xuICAgIHRoaXMuc3RhdGUgPSAnZW5kZWQnO1xuICAgIHRoaXMucmVhc29uID0gcmVhc29uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWN0aXZlID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLnN0YXRlID09ICdhY3RpdmUnO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChjYW5kaWRhdGUpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKGNhbmRpZGF0ZSAmJiAhdGhpcy5sYXN0aWNlY2FuZGlkYXRlKSB7XG4gICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW2NhbmRpZGF0ZS5zZHBNTGluZUluZGV4XSwgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKTtcbiAgICAgICAgdmFyIGpjYW5kID0gU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShjYW5kaWRhdGUuY2FuZGlkYXRlKTtcbiAgICAgICAgaWYgKCEoaWNlICYmIGpjYW5kKSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignZmFpbGVkIHRvIGdldCBpY2UgJiYgamNhbmQnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcblxuICAgICAgICBpZiAoamNhbmQudHlwZSA9PT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChqY2FuZC50eXBlID09PSAncmVsYXknKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMudXNldHJpY2tsZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMudXNlZHJpcCkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRyaXBfY29udGFpbmVyLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBzdGFydCAyMG1zIGNhbGxvdXRcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuZHJpcF9jb250YWluZXIubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGVzKHNlbGYuZHJpcF9jb250YWluZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5kcmlwX2NvbnRhaW5lciA9IFtdO1xuICAgICAgICAgICAgICAgICAgICB9LCAyMCk7XG5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5kcmlwX2NvbnRhaW5lci5wdXNoKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoW2NhbmRpZGF0ZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy9jb25zb2xlLmxvZygnc2VuZEljZUNhbmRpZGF0ZTogbGFzdCBjYW5kaWRhdGUuJyk7XG4gICAgICAgIGlmICghdGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzaG91bGQgc2VuZCBmdWxsIG9mZmVyIG5vdy4uLicpO1xuICAgICAgICAgICAgdmFyIGluaXQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbjogdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnR5cGUgPT0gJ29mZmVyJyA/ICdzZXNzaW9uLWluaXRpYXRlJyA6ICdzZXNzaW9uLWFjY2VwdCcsXG4gICAgICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgICAgIHNpZDogdGhpcy5zaWR9KTtcbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgdmFyIHNlbmRKaW5nbGUgPSBmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIGlmKCFzc3JjKVxuICAgICAgICAgICAgICAgICAgICBzc3JjID0ge307XG4gICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC50b0ppbmdsZShpbml0LCBzZWxmLmluaXRpYXRvciA9PSBzZWxmLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJywgc3NyYyk7XG4gICAgICAgICAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShpbml0LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXNzaW9uIGluaXRpYXRlIGFjaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAnb2ZmZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbmRKaW5nbGUoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxhc3RpY2VjYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICBjb25zb2xlLmxvZygnSGF2ZSB3ZSBlbmNvdW50ZXJlZCBhbnkgc3JmbHggY2FuZGlkYXRlcz8gJyArIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdIYXZlIHdlIGVuY291bnRlcmVkIGFueSByZWxheSBjYW5kaWRhdGVzPyAnICsgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKTtcblxuICAgICAgICBpZiAoISh0aGlzLmhhZHN0dW5jYW5kaWRhdGUgfHwgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKSAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlICE9ICdjbG9zZWQnKSB7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdub3N0dW5jYW5kaWRhdGVzLmppbmdsZScsIFt0aGlzLnNpZF0pO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZXMgPSBmdW5jdGlvbiAoY2FuZGlkYXRlcykge1xuICAgIGNvbnNvbGUubG9nKCdzZW5kSWNlQ2FuZGlkYXRlcycsIGNhbmRpZGF0ZXMpO1xuICAgIHZhciBjYW5kID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICd0cmFuc3BvcnQtaW5mbycsXG4gICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pO1xuICAgIGZvciAodmFyIG1pZCA9IDA7IG1pZCA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBtaWQrKykge1xuICAgICAgICB2YXIgY2FuZHMgPSBjYW5kaWRhdGVzLmZpbHRlcihmdW5jdGlvbiAoZWwpIHsgcmV0dXJuIGVsLnNkcE1MaW5lSW5kZXggPT0gbWlkOyB9KTtcbiAgICAgICAgdmFyIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0uc3BsaXQoJ1xcclxcbicpWzBdKTtcbiAgICAgICAgaWYgKGNhbmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sIHRoaXMubG9jYWxTRFAuc2Vzc2lvbik7XG4gICAgICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgICAgIGNhbmQuYygnY29udGVudCcsIHtjcmVhdG9yOiB0aGlzLmluaXRpYXRvciA9PSB0aGlzLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJyxcbiAgICAgICAgICAgICAgICBuYW1lOiAoY2FuZHNbMF0uc2RwTWlkPyBjYW5kc1swXS5zZHBNaWQgOiBtbGluZS5tZWRpYSlcbiAgICAgICAgICAgIH0pLmMoJ3RyYW5zcG9ydCcsIGljZSk7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhbmRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgY2FuZC5jKCdjYW5kaWRhdGUnLCBTRFBVdGlsLmNhbmRpZGF0ZVRvSmluZ2xlKGNhbmRzW2ldLmNhbmRpZGF0ZSkpLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBhZGQgZmluZ2VycHJpbnRcbiAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICB2YXIgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpO1xuICAgICAgICAgICAgICAgIHRtcC5yZXF1aXJlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgY2FuZC5jKFxuICAgICAgICAgICAgICAgICAgICAnZmluZ2VycHJpbnQnLFxuICAgICAgICAgICAgICAgICAgICB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpkdGxzOjAnfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodG1wLmZpbmdlcnByaW50KTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdG1wLmZpbmdlcnByaW50O1xuICAgICAgICAgICAgICAgIGNhbmQuYXR0cnModG1wKTtcbiAgICAgICAgICAgICAgICBjYW5kLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYW5kLnVwKCk7IC8vIHRyYW5zcG9ydFxuICAgICAgICAgICAgY2FuZC51cCgpOyAvLyBjb250ZW50XG4gICAgICAgIH1cbiAgICB9XG4gICAgLy8gbWlnaHQgbWVyZ2UgbGFzdC1jYW5kaWRhdGUgbm90aWZpY2F0aW9uIGludG8gdGhpcywgYnV0IGl0IGlzIGNhbGxlZCBhbG90IGxhdGVyLiBTZWUgd2VicnRjIGlzc3VlICMyMzQwXG4gICAgLy9jb25zb2xlLmxvZygnd2FzIHRoaXMgdGhlIGxhc3QgY2FuZGlkYXRlJywgdGhpcy5sYXN0aWNlY2FuZGlkYXRlKTtcbiAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKGNhbmQsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgIGFjay5zb3VyY2UgPSAndHJhbnNwb3J0aW5mbyc7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3RoaXMuc2lkLCBhY2tdKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgIHJlYXNvbjogJChzdGFuemEpLmZpbmQoJ2Vycm9yIDpmaXJzdCcpWzBdLnRhZ05hbWUsXG4gICAgICAgICAgICB9Ont9O1xuICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ3RyYW5zcG9ydGluZm8nO1xuICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHRoaXMuc2lkLCBlcnJvcik7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbn07XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZE9mZmVyID0gZnVuY3Rpb24gKCkge1xuICAgIC8vY29uc29sZS5sb2coJ3NlbmRPZmZlci4uLicpO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZU9mZmVyKGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZE9mZmVyKHNkcCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdjcmVhdGVPZmZlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkT2ZmZXIgPSBmdW5jdGlvbiAoc2RwKSB7XG4gICAgLy9jb25zb2xlLmxvZygnY3JlYXRlZE9mZmVyJywgc2RwKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHZhciBzZW5kSmluZ2xlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW5pdCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbml0aWF0ZScsXG4gICAgICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICBzaWQ6IHRoaXMuc2lkfSk7XG4gICAgICAgIHNlbGYubG9jYWxTRFAudG9KaW5nbGUoaW5pdCwgdGhpcy5pbml0aWF0b3IgPT0gdGhpcy5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHRoaXMubG9jYWxTdHJlYW1zU1NSQyk7XG4gICAgICAgIHNlbGYuY29ubmVjdGlvbi5zZW5kSVEoaW5pdCxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB2YXIgYWNrID0ge307XG4gICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ29mZmVyJztcbiAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAxMDAwMCk7XG4gICAgfVxuICAgIHNkcC5zZHAgPSB0aGlzLmxvY2FsU0RQLnJhdztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24oc2RwLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBpZihzZWxmLnVzZXRyaWNrbGUpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xuICAgIHZhciBjYW5kcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLmxvY2FsU0RQLnJhdywgJ2E9Y2FuZGlkYXRlOicpO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FuZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIGNhbmQgPSBTRFBVdGlsLnBhcnNlX2ljZWNhbmRpZGF0ZShjYW5kc1tpXSk7XG4gICAgICAgIGlmIChjYW5kLnR5cGUgPT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChjYW5kLnR5cGUgPT0gJ3JlbGF5Jykge1xuICAgICAgICAgICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGVsZW0sIGRlc2N0eXBlKSB7XG4gICAgLy9jb25zb2xlLmxvZygnc2V0dGluZyByZW1vdGUgZGVzY3JpcHRpb24uLi4gJywgZGVzY3R5cGUpO1xuICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUCgnJyk7XG4gICAgdGhpcy5yZW1vdGVTRFAuZnJvbUppbmdsZShlbGVtKTtcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAhPT0gbnVsbCkge1xuICAgICAgICBjb25zb2xlLmxvZygnc2V0UmVtb3RlRGVzY3JpcHRpb24gd2hlbiByZW1vdGUgZGVzY3JpcHRpb24gaXMgbm90IG51bGwsIHNob3VsZCBiZSBwcmFuc3dlcicsIHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24pO1xuICAgICAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi50eXBlID09ICdwcmFuc3dlcicpIHtcbiAgICAgICAgICAgIHZhciBwcmFuc3dlciA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwcmFuc3dlci5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSB3ZSBoYXZlIGljZSB1ZnJhZyBhbmQgcHdkXG4gICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZSh0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHRoaXMucmVtb3RlU0RQLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSArPSBTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSB1ZnJhZz8nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUocHJhbnN3ZXIubWVkaWFbaV0sICdhPWljZS1wd2Q6JywgcHJhbnN3ZXIuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLm1lZGlhW2ldLCAnYT1pY2UtcHdkOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSBwd2Q/Jyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gY29weSBvdmVyIGNhbmRpZGF0ZXNcbiAgICAgICAgICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXMocHJhbnN3ZXIubWVkaWFbaV0sICdhPWNhbmRpZGF0ZTonKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciByZW1vdGVkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogZGVzY3R5cGUsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3fSk7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKHJlbW90ZWRlc2MsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldFJlbW90ZURlc2NyaXB0aW9uIGVycm9yJywgZSk7XG4gICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvcihzZWxmLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoZWxlbSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnY2xvc2VkJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdoYXZlLWxvY2FsLW9mZmVyJykge1xuICAgICAgICBjb25zb2xlLmxvZygndHJpY2tsZSBpY2UgY2FuZGlkYXRlIGFycml2aW5nIGJlZm9yZSBzZXNzaW9uIGFjY2VwdC4uLicpO1xuICAgICAgICAvLyBjcmVhdGUgYSBQUkFOU1dFUiBmb3Igc2V0UmVtb3RlRGVzY3JpcHRpb25cbiAgICAgICAgaWYgKCF0aGlzLnJlbW90ZVNEUCkge1xuICAgICAgICAgICAgdmFyIGNvYmJsZWQgPSAndj0wXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgICAgICAgICAncz0tXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ3Q9MCAwXFxyXFxuJztcbiAgICAgICAgICAgIC8vIGZpcnN0LCB0YWtlIHNvbWUgdGhpbmdzIGZyb20gdGhlIGxvY2FsIGRlc2NyaXB0aW9uXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdtPScpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgY29iYmxlZCArPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9cnRwbWFwOicpLmpvaW4oJ1xcclxcbicpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSkge1xuICAgICAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb2JibGVkICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUChjb2JibGVkKTtcbiAgICAgICAgfVxuICAgICAgICAvLyB0aGVuIGFkZCB0aGluZ3MgbGlrZSBpY2UgYW5kIGR0bHMgZnJvbSByZW1vdGUgY2FuZGlkYXRlXG4gICAgICAgIGVsZW0uZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNlbGYucmVtb3RlU0RQLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS11ZnJhZzonICsgdG1wLmF0dHIoJ3VmcmFnJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS1wd2Q6JyArIHRtcC5hdHRyKCdwd2QnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+ZmluZ2VycHJpbnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRtcC5hdHRyKCdoYXNoJykgKyAnICcgKyB0bXAudGV4dCgpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdubyBkdGxzIGZpbmdlcnByaW50ICh3ZWJydGMgaXNzdWUgIzE3MTg/KScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWNyeXB0bzoxIEFFU19DTV8xMjhfSE1BQ19TSEExXzgwIGlubGluZTpCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuXG4gICAgICAgIC8vIHdlIG5lZWQgYSBjb21wbGV0ZSBTRFAgd2l0aCBpY2UtdWZyYWcvaWNlLXB3ZCBpbiBhbGwgcGFydHNcbiAgICAgICAgLy8gdGhpcyBtYWtlcyB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBQUkFOU1dFUiBpcyBjb25zdHJ1Y3RlZCBzdWNoIHRoYXQgdGhlIGljZS11ZnJhZyBpcyBpbiBhbGwgbWVkaWFwYXJ0c1xuICAgICAgICAvLyBidXQgaXQgY291bGQgYmUgaW4gdGhlIHNlc3Npb24gcGFydCBhcyB3ZWxsLiBzaW5jZSB0aGUgY29kZSBhYm92ZSBjb25zdHJ1Y3RzIHRoaXMgc2RwIHRoaXMgY2FuJ3QgaGFwcGVuIGhvd2V2ZXJcbiAgICAgICAgdmFyIGlzY29tcGxldGUgPSB0aGlzLnJlbW90ZVNEUC5tZWRpYS5maWx0ZXIoZnVuY3Rpb24gKG1lZGlhcGFydCkge1xuICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWwuZmluZF9saW5lKG1lZGlhcGFydCwgJ2E9aWNlLXVmcmFnOicpO1xuICAgICAgICB9KS5sZW5ndGggPT0gdGhpcy5yZW1vdGVTRFAubWVkaWEubGVuZ3RoO1xuXG4gICAgICAgIGlmIChpc2NvbXBsZXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0dGluZyBwcmFuc3dlcicpO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdwcmFuc3dlcicsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3IH0pLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHByYW5zd2VyIGZhaWxlZCcsIGUudG9TdHJpbmcoKSk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldHRpbmcgcHJhbnN3ZXIgZmFpbGVkJywgZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdub3QgeWV0IHNldHRpbmcgcHJhbnN3ZXInKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBvcGVyYXRlIG9uIGVhY2ggY29udGVudCBlbGVtZW50XG4gICAgZWxlbS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gd291bGQgbG92ZSB0byBkZWFjdGl2YXRlIHRoaXMsIGJ1dCBmaXJlZm94IHN0aWxsIHJlcXVpcmVzIGl0XG4gICAgICAgIHZhciBpZHggPSAtMTtcbiAgICAgICAgdmFyIGk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBzZWxmLnJlbW90ZVNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLmluZGV4T2YoJ209JyArICQodGhpcykuYXR0cignbmFtZScpKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGlkeCA9IGk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlkeCA9PSAtMSkgeyAvLyBmYWxsIGJhY2sgdG8gbG9jYWxkZXNjcmlwdGlvblxuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHNlbGYubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUoc2VsZi5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9bWlkOicgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgfHxcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWR4ID0gaTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHZhciBuYW1lID0gJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgIC8vIFRPRE86IGNoZWNrIGljZS1wd2QgYW5kIGljZS11ZnJhZz9cbiAgICAgICAgJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+Y2FuZGlkYXRlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbGluZSwgY2FuZGlkYXRlO1xuICAgICAgICAgICAgbGluZSA9IFNEUFV0aWwuY2FuZGlkYXRlRnJvbUppbmdsZSh0aGlzKTtcbiAgICAgICAgICAgIGNhbmRpZGF0ZSA9IG5ldyBSVENJY2VDYW5kaWRhdGUoe3NkcE1MaW5lSW5kZXg6IGlkeCxcbiAgICAgICAgICAgICAgICBzZHBNaWQ6IG5hbWUsXG4gICAgICAgICAgICAgICAgY2FuZGlkYXRlOiBsaW5lfSk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignYWRkSWNlQ2FuZGlkYXRlIGZhaWxlZCcsIGUudG9TdHJpbmcoKSwgbGluZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEFuc3dlciA9IGZ1bmN0aW9uIChwcm92aXNpb25hbCkge1xuICAgIC8vY29uc29sZS5sb2coJ2NyZWF0ZUFuc3dlcicsIHByb3Zpc2lvbmFsKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgIGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZEFuc3dlcihzZHAsIHByb3Zpc2lvbmFsKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2NyZWF0ZUFuc3dlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkQW5zd2VyID0gZnVuY3Rpb24gKHNkcCwgcHJvdmlzaW9uYWwpIHtcbiAgICAvL2NvbnNvbGUubG9nKCdjcmVhdGVBbnN3ZXIgY2FsbGJhY2snKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHRoaXMudXNlcHJhbnN3ZXIgPSBwcm92aXNpb25hbCA9PT0gdHJ1ZTtcbiAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgIGlmICh0aGlzLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICBzZHAudHlwZSA9ICdwcmFuc3dlcic7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvY2FsU0RQLm1lZGlhW2ldID0gdGhpcy5sb2NhbFNEUC5tZWRpYVtpXS5yZXBsYWNlKCdhPXNlbmRyZWN2XFxyXFxuJywgJ2E9aW5hY3RpdmVcXHJcXG4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAucmF3ID0gdGhpcy5sb2NhbFNEUC5zZXNzaW9uICsgJ1xcclxcbicgKyB0aGlzLmxvY2FsU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgc2VuZEppbmdsZSA9IGZ1bmN0aW9uIChzc3Jjcykge1xuXG4gICAgICAgICAgICAgICAgdmFyIGFjY2VwdCA9ICRpcSh7dG86IHNlbGYucGVlcmppZCxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1hY2NlcHQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5pdGlhdG9yOiBzZWxmLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbmRlcjogc2VsZi5yZXNwb25kZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBzaWQ6IHNlbGYuc2lkIH0pO1xuICAgICAgICAgICAgICAgIHZhciBwdWJsaWNMb2NhbERlc2MgPSBBUFAuc2ltdWxjYXN0LnJldmVyc2VUcmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKHNkcCk7XG4gICAgICAgICAgICAgICAgdmFyIHB1YmxpY0xvY2FsU0RQID0gbmV3IFNEUChwdWJsaWNMb2NhbERlc2Muc2RwKTtcbiAgICAgICAgICAgICAgICBwdWJsaWNMb2NhbFNEUC50b0ppbmdsZShhY2NlcHQsIHNlbGYuaW5pdGlhdG9yID09IHNlbGYubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLCBzc3Jjcyk7XG4gICAgICAgICAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShhY2NlcHQsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFjay5zb3VyY2UgPSAnYW5zd2VyJztcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGFja10pO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAnYW5zd2VyJztcbiAgICAgICAgICAgICAgICAgICAgICAgIEppbmdsZVNlc3Npb24ub25KaW5nbGVFcnJvcihzZWxmLnNpZCwgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAxMDAwMCk7XG4gICAgfVxuICAgIHNkcC5zZHAgPSB0aGlzLmxvY2FsU0RQLnJhdztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24oc2RwLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldExvY2FsRGVzY3JpcHRpb24gc3VjY2VzcycpO1xuICAgICAgICAgICAgaWYgKHNlbGYudXNldHJpY2tsZSAmJiAhc2VsZi51c2VwcmFuc3dlcikge1xuICAgICAgICAgICAgICAgIHNlbmRKaW5nbGUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignc2V0TG9jYWxEZXNjcmlwdGlvbiBmYWlsZWQnLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIGNhbmRzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubG9jYWxTRFAucmF3LCAnYT1jYW5kaWRhdGU6Jyk7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBjYW5kcy5sZW5ndGg7IGorKykge1xuICAgICAgICB2YXIgY2FuZCA9IFNEUFV0aWwucGFyc2VfaWNlY2FuZGlkYXRlKGNhbmRzW2pdKTtcbiAgICAgICAgaWYgKGNhbmQudHlwZSA9PSAnc3JmbHgnKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHN0dW5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9IGVsc2UgaWYgKGNhbmQudHlwZSA9PSAncmVsYXknKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZFRlcm1pbmF0ZSA9IGZ1bmN0aW9uIChyZWFzb24sIHRleHQpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXMsXG4gICAgICAgIHRlcm0gPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgICAgIGFjdGlvbjogJ3Nlc3Npb24tdGVybWluYXRlJyxcbiAgICAgICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgICAgIHNpZDogdGhpcy5zaWR9KVxuICAgICAgICAgICAgLmMoJ3JlYXNvbicpXG4gICAgICAgICAgICAuYyhyZWFzb24gfHwgJ3N1Y2Nlc3MnKTtcblxuICAgIGlmICh0ZXh0KSB7XG4gICAgICAgIHRlcm0udXAoKS5jKCd0ZXh0JykudCh0ZXh0KTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKHRlcm0sXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24gPSBudWxsO1xuICAgICAgICAgICAgc2VsZi50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgIGFjay5zb3VyY2UgPSAndGVybWluYXRlJztcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGFja10pO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBlcnJvcl0pO1xuICAgICAgICB9LFxuICAgICAgICAxMDAwMCk7XG4gICAgaWYgKHRoaXMuc3RhdHNpbnRlcnZhbCAhPT0gbnVsbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbCh0aGlzLnN0YXRzaW50ZXJ2YWwpO1xuICAgICAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSBudWxsO1xuICAgIH1cbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmFkZFNvdXJjZSA9IGZ1bmN0aW9uIChlbGVtLCBmcm9tSmlkKSB7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gRklYTUU6IGRpcnR5IHdhaXRpbmdcbiAgICBpZiAoIXRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbilcbiAgICB7XG4gICAgICAgIGNvbnNvbGUud2FybihcImFkZFNvdXJjZSAtIGxvY2FsRGVzY3JpcHRpb24gbm90IHJlYWR5IHlldFwiKVxuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzZWxmLmFkZFNvdXJjZShlbGVtLCBmcm9tSmlkKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAyMDBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdhZGRzc3JjJywgbmV3IERhdGUoKS5nZXRUaW1lKCkpO1xuICAgIGNvbnNvbGUubG9nKCdpY2UnLCB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgdmFyIHNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgIHZhciBteVNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG5cbiAgICAkKGVsZW0pLmVhY2goZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICB2YXIgbmFtZSA9ICQoY29udGVudCkuYXR0cignbmFtZScpO1xuICAgICAgICB2YXIgbGluZXMgPSAnJztcbiAgICAgICAgdG1wID0gJChjb250ZW50KS5maW5kKCdzc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NlbWFudGljcycpO1xuICAgICAgICAgICAgdmFyIHNzcmNzID0gJCh0aGlzKS5maW5kKCc+c291cmNlJykubWFwKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgIH0pLmdldCgpO1xuXG4gICAgICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjLWdyb3VwOicgKyBzZW1hbnRpY3MgKyAnICcgKyBzc3Jjcy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHRtcCA9ICQoY29udGVudCkuZmluZCgnc291cmNlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpOyAvLyBjYW4gaGFuZGxlIGJvdGggPnNvdXJjZSBhbmQgPmRlc2NyaXB0aW9uPnNvdXJjZVxuICAgICAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc3NyYyA9ICQodGhpcykuYXR0cignc3NyYycpO1xuICAgICAgICAgICAgaWYobXlTZHAuY29udGFpbnNTU1JDKHNzcmMpKXtcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGlzIGhhcHBlbnMgd2hlbiBtdWx0aXBsZSBwYXJ0aWNpcGFudHMgY2hhbmdlIHRoZWlyIHN0cmVhbXMgYXQgdGhlIHNhbWUgdGltZSBhbmRcbiAgICAgICAgICAgICAgICAgKiBDb2xpYnJpRm9jdXMubW9kaWZ5U291cmNlcyBoYXZlIHRvIHdhaXQgZm9yIHN0YWJsZSBzdGF0ZS4gSW4gdGhlIG1lYW50aW1lIG11bHRpcGxlXG4gICAgICAgICAgICAgICAgICogYWRkc3NyYyBhcmUgc2NoZWR1bGVkIGZvciB1cGRhdGUgSVEuIFNlZVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkdvdCBhZGQgc3RyZWFtIHJlcXVlc3QgZm9yIG15IG93biBzc3JjOiBcIitzc3JjKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCQodGhpcykuYXR0cigndmFsdWUnKSAmJiAkKHRoaXMpLmF0dHIoJ3ZhbHVlJykubGVuZ3RoKVxuICAgICAgICAgICAgICAgICAgICBsaW5lcyArPSAnOicgKyAkKHRoaXMpLmF0dHIoJ3ZhbHVlJyk7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ1xcclxcbic7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhLCBpZHgpIHtcbiAgICAgICAgICAgIGlmICghU0RQVXRpbC5maW5kX2xpbmUobWVkaWEsICdhPW1pZDonICsgbmFtZSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgc2RwLm1lZGlhW2lkeF0gKz0gbGluZXM7XG4gICAgICAgICAgICBpZiAoIXNlbGYuYWRkc3NyY1tpZHhdKSBzZWxmLmFkZHNzcmNbaWR4XSA9ICcnO1xuICAgICAgICAgICAgc2VsZi5hZGRzc3JjW2lkeF0gKz0gbGluZXM7XG4gICAgICAgIH0pO1xuICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgfSk7XG4gICAgdGhpcy5tb2RpZnlTb3VyY2VzKCk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5yZW1vdmVTb3VyY2UgPSBmdW5jdGlvbiAoZWxlbSwgZnJvbUppZCkge1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIC8vIEZJWE1FOiBkaXJ0eSB3YWl0aW5nXG4gICAgaWYgKCF0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pXG4gICAge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJyZW1vdmVTb3VyY2UgLSBsb2NhbERlc2NyaXB0aW9uIG5vdCByZWFkeSB5ZXRcIilcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc2VsZi5yZW1vdmVTb3VyY2UoZWxlbSwgZnJvbUppZCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgMjAwXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zb2xlLmxvZygncmVtb3Zlc3NyYycsIG5ldyBEYXRlKCkuZ2V0VGltZSgpKTtcbiAgICBjb25zb2xlLmxvZygnaWNlJywgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpO1xuICAgIHZhciBzZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICB2YXIgbXlTZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuXG4gICAgJChlbGVtKS5lYWNoKGZ1bmN0aW9uIChpZHgsIGNvbnRlbnQpIHtcbiAgICAgICAgdmFyIG5hbWUgPSAkKGNvbnRlbnQpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgdmFyIGxpbmVzID0gJyc7XG4gICAgICAgIHRtcCA9ICQoY29udGVudCkuZmluZCgnc3NyYy1ncm91cFt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IHRoaXMuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKTtcbiAgICAgICAgICAgIHZhciBzc3JjcyA9ICQodGhpcykuZmluZCgnPnNvdXJjZScpLm1hcChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdzc3JjJyk7XG4gICAgICAgICAgICB9KS5nZXQoKTtcblxuICAgICAgICAgICAgaWYgKHNzcmNzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ2E9c3NyYy1ncm91cDonICsgc2VtYW50aWNzICsgJyAnICsgc3NyY3Muam9pbignICcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0bXAgPSAkKGNvbnRlbnQpLmZpbmQoJ3NvdXJjZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKTsgLy8gY2FuIGhhbmRsZSBib3RoID5zb3VyY2UgYW5kID5kZXNjcmlwdGlvbj5zb3VyY2VcbiAgICAgICAgdG1wLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHNzcmMgPSAkKHRoaXMpLmF0dHIoJ3NzcmMnKTtcbiAgICAgICAgICAgIC8vIFRoaXMgc2hvdWxkIG5ldmVyIGhhcHBlbiwgYnV0IGNhbiBiZSB1c2VmdWwgZm9yIGJ1ZyBkZXRlY3Rpb25cbiAgICAgICAgICAgIGlmKG15U2RwLmNvbnRhaW5zU1NSQyhzc3JjKSl7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkdvdCByZW1vdmUgc3RyZWFtIHJlcXVlc3QgZm9yIG15IG93biBzc3JjOiBcIitzc3JjKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCQodGhpcykuYXR0cigndmFsdWUnKSAmJiAkKHRoaXMpLmF0dHIoJ3ZhbHVlJykubGVuZ3RoKVxuICAgICAgICAgICAgICAgICAgICBsaW5lcyArPSAnOicgKyAkKHRoaXMpLmF0dHIoJ3ZhbHVlJyk7XG4gICAgICAgICAgICAgICAgbGluZXMgKz0gJ1xcclxcbic7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5tZWRpYS5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhLCBpZHgpIHtcbiAgICAgICAgICAgIGlmICghU0RQVXRpbC5maW5kX2xpbmUobWVkaWEsICdhPW1pZDonICsgbmFtZSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgc2RwLm1lZGlhW2lkeF0gKz0gbGluZXM7XG4gICAgICAgICAgICBpZiAoIXNlbGYucmVtb3Zlc3NyY1tpZHhdKSBzZWxmLnJlbW92ZXNzcmNbaWR4XSA9ICcnO1xuICAgICAgICAgICAgc2VsZi5yZW1vdmVzc3JjW2lkeF0gKz0gbGluZXM7XG4gICAgICAgIH0pO1xuICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgfSk7XG4gICAgdGhpcy5tb2RpZnlTb3VyY2VzKCk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5tb2RpZnlTb3VyY2VzID0gZnVuY3Rpb24gKHN1Y2Nlc3NDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnY2xvc2VkJykgcmV0dXJuO1xuICAgIGlmICghKHRoaXMuYWRkc3NyYy5sZW5ndGggfHwgdGhpcy5yZW1vdmVzc3JjLmxlbmd0aCB8fCB0aGlzLnBlbmRpbmdvcCAhPT0gbnVsbCB8fCB0aGlzLnN3aXRjaHN0cmVhbXMpKXtcbiAgICAgICAgLy8gVGhlcmUgaXMgbm90aGluZyB0byBkbyBzaW5jZSBzY2hlZHVsZWQgam9iIG1pZ2h0IGhhdmUgYmVlbiBleGVjdXRlZCBieSBhbm90aGVyIHN1Y2NlZWRpbmcgY2FsbFxuICAgICAgICB0aGlzLnNldExvY2FsRGVzY3JpcHRpb24oKTtcbiAgICAgICAgaWYoc3VjY2Vzc0NhbGxiYWNrKXtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBGSVhNRTogdGhpcyBpcyBhIGJpZyBoYWNrXG4gICAgLy8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD0yNjg4XG4gICAgLy8gXiBoYXMgYmVlbiBmaXhlZC5cbiAgICBpZiAoISh0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdzdGFibGUnICYmIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlID09ICdjb25uZWN0ZWQnKSkge1xuICAgICAgICBjb25zb2xlLndhcm4oJ21vZGlmeVNvdXJjZXMgbm90IHlldCcsIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUsIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICAgICAgdGhpcy53YWl0ID0gdHJ1ZTtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IHNlbGYubW9kaWZ5U291cmNlcyhzdWNjZXNzQ2FsbGJhY2spOyB9LCAyNTApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0aGlzLndhaXQpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IHNlbGYubW9kaWZ5U291cmNlcyhzdWNjZXNzQ2FsbGJhY2spOyB9LCAyNTAwKTtcbiAgICAgICAgdGhpcy53YWl0ID0gZmFsc2U7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBSZXNldCBzd2l0Y2ggc3RyZWFtcyBmbGFnXG4gICAgdGhpcy5zd2l0Y2hzdHJlYW1zID0gZmFsc2U7XG5cbiAgICB2YXIgc2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG5cbiAgICAvLyBhZGQgc291cmNlc1xuICAgIHRoaXMuYWRkc3NyYy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmVzLCBpZHgpIHtcbiAgICAgICAgc2RwLm1lZGlhW2lkeF0gKz0gbGluZXM7XG4gICAgfSk7XG4gICAgdGhpcy5hZGRzc3JjID0gW107XG5cbiAgICAvLyByZW1vdmUgc291cmNlc1xuICAgIHRoaXMucmVtb3Zlc3NyYy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmVzLCBpZHgpIHtcbiAgICAgICAgbGluZXMgPSBsaW5lcy5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGxpbmVzLnBvcCgpOyAvLyByZW1vdmUgZW1wdHkgbGFzdCBlbGVtZW50O1xuICAgICAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgICAgIHNkcC5tZWRpYVtpZHhdID0gc2RwLm1lZGlhW2lkeF0ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG4gICAgdGhpcy5yZW1vdmVzc3JjID0gW107XG5cbiAgICAvLyBGSVhNRTpcbiAgICAvLyB0aGlzIHdhcyBhIGhhY2sgZm9yIHRoZSBzaXR1YXRpb24gd2hlbiBvbmx5IG9uZSBwZWVyIGV4aXN0c1xuICAgIC8vIGluIHRoZSBjb25mZXJlbmNlLlxuICAgIC8vIGNoZWNrIGlmIHN0aWxsIHJlcXVpcmVkIGFuZCByZW1vdmVcbiAgICBpZiAoc2RwLm1lZGlhWzBdKVxuICAgICAgICBzZHAubWVkaWFbMF0gPSBzZHAubWVkaWFbMF0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG4gICAgaWYgKHNkcC5tZWRpYVsxXSlcbiAgICAgICAgc2RwLm1lZGlhWzFdID0gc2RwLm1lZGlhWzFdLnJlcGxhY2UoJ2E9cmVjdm9ubHknLCAnYT1zZW5kcmVjdicpO1xuXG4gICAgc2RwLnJhdyA9IHNkcC5zZXNzaW9uICsgc2RwLm1lZGlhLmpvaW4oJycpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0UmVtb3RlRGVzY3JpcHRpb24obmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogJ29mZmVyJywgc2RwOiBzZHAucmF3fSksXG4gICAgICAgIGZ1bmN0aW9uKCkge1xuXG4gICAgICAgICAgICBpZihzZWxmLnNpZ25hbGluZ1N0YXRlID09ICdjbG9zZWQnKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcImNyZWF0ZUFuc3dlciBhdHRlbXB0IG9uIGNsb3NlZCBzdGF0ZVwiKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY3JlYXRlQW5zd2VyKFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKG1vZGlmaWVkQW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGNoYW5nZSB2aWRlbyBkaXJlY3Rpb24sIHNlZSBodHRwczovL2dpdGh1Yi5jb20vaml0c2kvaml0bWVldC9pc3N1ZXMvNDFcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYucGVuZGluZ29wICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgc2RwID0gbmV3IFNEUChtb2RpZmllZEFuc3dlci5zZHApO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNkcC5tZWRpYS5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoKHNlbGYucGVuZGluZ29wKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ211dGUnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2RwLm1lZGlhWzFdID0gc2RwLm1lZGlhWzFdLnJlcGxhY2UoJ2E9c2VuZHJlY3YnLCAnYT1yZWN2b25seScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3VubXV0ZSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2RwLnJhdyA9IHNkcC5zZXNzaW9uICsgc2RwLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGlmaWVkQW5zd2VyLnNkcCA9IHNkcC5yYXc7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnBlbmRpbmdvcCA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogcHVzaGluZyBkb3duIGFuIGFuc3dlciB3aGlsZSBpY2UgY29ubmVjdGlvbiBzdGF0ZVxuICAgICAgICAgICAgICAgICAgICAvLyBpcyBzdGlsbCBjaGVja2luZyBpcyBiYWQuLi5cbiAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhzZWxmLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gdHJ5aW5nIHRvIHdvcmsgYXJvdW5kIGFub3RoZXIgY2hyb21lIGJ1Z1xuICAgICAgICAgICAgICAgICAgICAvL21vZGlmaWVkQW5zd2VyLnNkcCA9IG1vZGlmaWVkQW5zd2VyLnNkcC5yZXBsYWNlKC9hPXNldHVwOmFjdGl2ZS9nLCAnYT1zZXR1cDphY3RwYXNzJyk7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihtb2RpZmllZEFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ21vZGlmaWVkIHNldExvY2FsRGVzY3JpcHRpb24gb2snKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnNldExvY2FsRGVzY3JpcHRpb24oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZihzdWNjZXNzQ2FsbGJhY2spe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdtb2RpZmllZCBzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ21vZGlmaWVkIGFuc3dlciBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24oZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ21vZGlmeSBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgIH1cbiAgICApO1xufTtcblxuLyoqXG4gKiBTd2l0Y2hlcyB2aWRlbyBzdHJlYW1zLlxuICogQHBhcmFtIG5ld19zdHJlYW0gbmV3IHN0cmVhbSB0aGF0IHdpbGwgYmUgdXNlZCBhcyB2aWRlbyBvZiB0aGlzIHNlc3Npb24uXG4gKiBAcGFyYW0gb2xkU3RyZWFtIG9sZCB2aWRlbyBzdHJlYW0gb2YgdGhpcyBzZXNzaW9uLlxuICogQHBhcmFtIHN1Y2Nlc3NfY2FsbGJhY2sgY2FsbGJhY2sgZXhlY3V0ZWQgYWZ0ZXIgc3VjY2Vzc2Z1bCBzdHJlYW0gc3dpdGNoLlxuICovXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zd2l0Y2hTdHJlYW1zID0gZnVuY3Rpb24gKG5ld19zdHJlYW0sIG9sZFN0cmVhbSwgc3VjY2Vzc19jYWxsYmFjaykge1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgLy8gUmVtZW1iZXIgU0RQIHRvIGZpZ3VyZSBvdXQgYWRkZWQvcmVtb3ZlZCBTU1JDc1xuICAgIHZhciBvbGRTZHAgPSBudWxsO1xuICAgIGlmKHNlbGYucGVlcmNvbm5lY3Rpb24pIHtcbiAgICAgICAgaWYoc2VsZi5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgICBvbGRTZHAgPSBuZXcgU0RQKHNlbGYucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICB9XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24ucmVtb3ZlU3RyZWFtKG9sZFN0cmVhbSwgdHJ1ZSk7XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKG5ld19zdHJlYW0pO1xuICAgIH1cblxuICAgIEFQUC5SVEMuc3dpdGNoVmlkZW9TdHJlYW1zKG5ld19zdHJlYW0sIG9sZFN0cmVhbSk7XG5cbiAgICAvLyBDb25mZXJlbmNlIGlzIG5vdCBhY3RpdmVcbiAgICBpZighb2xkU2RwIHx8ICFzZWxmLnBlZXJjb25uZWN0aW9uKSB7XG4gICAgICAgIHN1Y2Nlc3NfY2FsbGJhY2soKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHNlbGYuc3dpdGNoc3RyZWFtcyA9IHRydWU7XG4gICAgc2VsZi5tb2RpZnlTb3VyY2VzKGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zb2xlLmxvZygnbW9kaWZ5IHNvdXJjZXMgZG9uZScpO1xuXG4gICAgICAgIHN1Y2Nlc3NfY2FsbGJhY2soKTtcblxuICAgICAgICB2YXIgbmV3U2RwID0gbmV3IFNEUChzZWxmLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgY29uc29sZS5sb2coXCJTRFBzXCIsIG9sZFNkcCwgbmV3U2RwKTtcbiAgICAgICAgc2VsZi5ub3RpZnlNeVNTUkNVcGRhdGUob2xkU2RwLCBuZXdTZHApO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBGaWd1cmVzIG91dCBhZGRlZC9yZW1vdmVkIHNzcmNzIGFuZCBzZW5kIHVwZGF0ZSBJUXMuXG4gKiBAcGFyYW0gb2xkX3NkcCBTRFAgb2JqZWN0IGZvciBvbGQgZGVzY3JpcHRpb24uXG4gKiBAcGFyYW0gbmV3X3NkcCBTRFAgb2JqZWN0IGZvciBuZXcgZGVzY3JpcHRpb24uXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLm5vdGlmeU15U1NSQ1VwZGF0ZSA9IGZ1bmN0aW9uIChvbGRfc2RwLCBuZXdfc2RwKSB7XG5cbiAgICBpZiAoISh0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdzdGFibGUnICYmXG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlID09ICdjb25uZWN0ZWQnKSl7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiVG9vIGVhcmx5IHRvIHNlbmQgdXBkYXRlc1wiKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIHNlbmQgc291cmNlLXJlbW92ZSBJUS5cbiAgICBzZHBEaWZmZXIgPSBuZXcgU0RQRGlmZmVyKG5ld19zZHAsIG9sZF9zZHApO1xuICAgIHZhciByZW1vdmUgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHtcbiAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc291cmNlLXJlbW92ZScsXG4gICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZFxuICAgICAgICB9XG4gICAgKTtcbiAgICB2YXIgcmVtb3ZlZCA9IHNkcERpZmZlci50b0ppbmdsZShyZW1vdmUpO1xuICAgIGlmIChyZW1vdmVkKSB7XG4gICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEocmVtb3ZlLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnZ290IHJlbW92ZSByZXN1bHQnLCByZXMpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdnb3QgcmVtb3ZlIGVycm9yJywgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmxvZygncmVtb3ZhbCBub3QgbmVjZXNzYXJ5Jyk7XG4gICAgfVxuXG4gICAgLy8gc2VuZCBzb3VyY2UtYWRkIElRLlxuICAgIHZhciBzZHBEaWZmZXIgPSBuZXcgU0RQRGlmZmVyKG9sZF9zZHAsIG5ld19zZHApO1xuICAgIHZhciBhZGQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHtcbiAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc291cmNlLWFkZCcsXG4gICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZFxuICAgICAgICB9XG4gICAgKTtcbiAgICB2YXIgYWRkZWQgPSBzZHBEaWZmZXIudG9KaW5nbGUoYWRkKTtcbiAgICBpZiAoYWRkZWQpIHtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShhZGQsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdnb3QgYWRkIHJlc3VsdCcsIHJlcyk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2dvdCBhZGQgZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdhZGRpdGlvbiBub3QgbmVjZXNzYXJ5Jyk7XG4gICAgfVxufTtcblxuLyoqXG4gKiBEZXRlcm1pbmVzIHdoZXRoZXIgdGhlIChsb2NhbCkgdmlkZW8gaXMgbXV0ZSBpLmUuIGFsbCB2aWRlbyB0cmFja3MgYXJlXG4gKiBkaXNhYmxlZC5cbiAqXG4gKiBAcmV0dXJuIDx0dD50cnVlPC90dD4gaWYgdGhlIChsb2NhbCkgdmlkZW8gaXMgbXV0ZSBpLmUuIGFsbCB2aWRlbyB0cmFja3MgYXJlXG4gKiBkaXNhYmxlZDsgb3RoZXJ3aXNlLCA8dHQ+ZmFsc2U8L3R0PlxuICovXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5pc1ZpZGVvTXV0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgdHJhY2tzID0gQVBQLlJUQy5sb2NhbFZpZGVvLmdldFZpZGVvVHJhY2tzKCk7XG4gICAgdmFyIG11dGUgPSB0cnVlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgaWYgKHRyYWNrc1tpXS5lbmFibGVkKSB7XG4gICAgICAgICAgICBtdXRlID0gZmFsc2U7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbXV0ZTtcbn07XG5cbi8qKlxuICogTXV0ZXMvdW5tdXRlcyB0aGUgKGxvY2FsKSB2aWRlbyBpLmUuIGVuYWJsZXMvZGlzYWJsZXMgYWxsIHZpZGVvIHRyYWNrcy5cbiAqXG4gKiBAcGFyYW0gbXV0ZSA8dHQ+dHJ1ZTwvdHQ+IHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gaS5lLiB0byBkaXNhYmxlIGFsbCB2aWRlb1xuICogdHJhY2tzOyBvdGhlcndpc2UsIDx0dD5mYWxzZTwvdHQ+XG4gKiBAcGFyYW0gY2FsbGJhY2sgYSBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHdpdGggPHR0Pm11dGU8L3R0PiBhZnRlciBhbGwgdmlkZW9cbiAqIHRyYWNrcyBoYXZlIGJlZW4gZW5hYmxlZC9kaXNhYmxlZC4gVGhlIGZ1bmN0aW9uIG1heSwgb3B0aW9uYWxseSwgcmV0dXJuXG4gKiBhbm90aGVyIGZ1bmN0aW9uIHdoaWNoIGlzIHRvIGJlIGludm9rZWQgYWZ0ZXIgdGhlIHdob2xlIG11dGUvdW5tdXRlIG9wZXJhdGlvblxuICogaGFzIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHkuXG4gKiBAcGFyYW0gb3B0aW9ucyBhbiBvYmplY3Qgd2hpY2ggc3BlY2lmaWVzIG9wdGlvbmFsIGFyZ3VtZW50cyBzdWNoIGFzIHRoZVxuICogPHR0PmJvb2xlYW48L3R0PiBrZXkgPHR0PmJ5VXNlcjwvdHQ+IHdpdGggZGVmYXVsdCB2YWx1ZSA8dHQ+dHJ1ZTwvdHQ+IHdoaWNoXG4gKiBzcGVjaWZpZXMgd2hldGhlciB0aGUgbWV0aG9kIHdhcyBpbml0aWF0ZWQgaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgKGluXG4gKiBjb250cmFzdCB0byBhbiBhdXRvbWF0aWMgZGVjaXNpb24gbWFkZSBieSB0aGUgYXBwbGljYXRpb24gbG9naWMpXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFZpZGVvTXV0ZSA9IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgIHZhciBieVVzZXI7XG5cbiAgICBpZiAob3B0aW9ucykge1xuICAgICAgICBieVVzZXIgPSBvcHRpb25zLmJ5VXNlcjtcbiAgICAgICAgaWYgKHR5cGVvZiBieVVzZXIgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBieVVzZXIgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgYnlVc2VyID0gdHJ1ZTtcbiAgICB9XG4gICAgLy8gVGhlIHVzZXIncyBjb21tYW5kIHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gdGFrZXMgcHJlY2VkZW5jZSBvdmVyIGFueVxuICAgIC8vIGF1dG9tYXRpYyBkZWNpc2lvbiBtYWRlIGJ5IHRoZSBhcHBsaWNhdGlvbiBsb2dpYy5cbiAgICBpZiAoYnlVc2VyKSB7XG4gICAgICAgIHRoaXMudmlkZW9NdXRlQnlVc2VyID0gbXV0ZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMudmlkZW9NdXRlQnlVc2VyKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGxvY2FsQ2FsbGJhY2sgPSBmdW5jdGlvbiAobXV0ZSkge1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5hZGRWaWRlb0luZm9Ub1ByZXNlbmNlKG11dGUpO1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG11dGUpXG4gICAgfTtcblxuICAgIGlmIChtdXRlID09IEFQUC5SVEMubG9jYWxWaWRlby5pc011dGVkKCkpXG4gICAge1xuICAgICAgICAvLyBFdmVuIGlmIG5vIGNoYW5nZSBvY2N1cnMsIHRoZSBzcGVjaWZpZWQgY2FsbGJhY2sgaXMgdG8gYmUgZXhlY3V0ZWQuXG4gICAgICAgIC8vIFRoZSBzcGVjaWZpZWQgY2FsbGJhY2sgbWF5LCBvcHRpb25hbGx5LCByZXR1cm4gYSBzdWNjZXNzQ2FsbGJhY2tcbiAgICAgICAgLy8gd2hpY2ggaXMgdG8gYmUgZXhlY3V0ZWQgYXMgd2VsbC5cbiAgICAgICAgdmFyIHN1Y2Nlc3NDYWxsYmFjayA9IGxvY2FsQ2FsbGJhY2sobXV0ZSk7XG5cbiAgICAgICAgaWYgKHN1Y2Nlc3NDYWxsYmFjaykge1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICBBUFAuUlRDLmxvY2FsVmlkZW8uc2V0TXV0ZSghbXV0ZSk7XG5cbiAgICAgICAgdGhpcy5oYXJkTXV0ZVZpZGVvKG11dGUpO1xuXG4gICAgICAgIHRoaXMubW9kaWZ5U291cmNlcyhsb2NhbENhbGxiYWNrKG11dGUpKTtcbiAgICB9XG59O1xuXG4vLyBTRFAtYmFzZWQgbXV0ZSBieSBnb2luZyByZWN2b25seS9zZW5kcmVjdlxuLy8gRklYTUU6IHNob3VsZCBwcm9iYWJseSBibGFjayBvdXQgdGhlIHNjcmVlbiBhcyB3ZWxsXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS50b2dnbGVWaWRlb011dGUgPSBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICB0aGlzLnNlcnZpY2Uuc2V0VmlkZW9NdXRlKEFQUC5SVEMubG9jYWxWaWRlby5pc011dGVkKCksIGNhbGxiYWNrKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmhhcmRNdXRlVmlkZW8gPSBmdW5jdGlvbiAobXV0ZWQpIHtcbiAgICB0aGlzLnBlbmRpbmdvcCA9IG11dGVkID8gJ211dGUnIDogJ3VubXV0ZSc7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kTXV0ZSA9IGZ1bmN0aW9uIChtdXRlZCwgY29udGVudCkge1xuICAgIHZhciBpbmZvID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLFxuICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWluZm8nLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWQgfSk7XG4gICAgaW5mby5jKG11dGVkID8gJ211dGUnIDogJ3VubXV0ZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgaW5mby5hdHRycyh7J2NyZWF0b3InOiB0aGlzLm1lID09IHRoaXMuaW5pdGlhdG9yID8gJ2NyZWF0b3InIDogJ3Jlc3BvbmRlcid9KTtcbiAgICBpZiAoY29udGVudCkge1xuICAgICAgICBpbmZvLmF0dHJzKHsnbmFtZSc6IGNvbnRlbnR9KTtcbiAgICB9XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kUmluZ2luZyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5mbyA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbmZvJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkIH0pO1xuICAgIGluZm8uYygncmluZ2luZycsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uIChpbnRlcnZhbCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgcmVjdiA9IHthdWRpbzogMCwgdmlkZW86IDB9O1xuICAgIHZhciBsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3RyZWN2ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3Rsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxvc3MgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB2YXIgZGVsdGEgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uICYmIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24gKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAvLyBUT0RPOiB0aGVyZSBhcmUgc28gbXVjaCBzdGF0aXN0aWNzIHlvdSBjYW4gZ2V0IGZyb20gdGhpcy4uXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnR5cGUgPT0gJ3NzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFja2V0c3JlY3YgPSByZXN1bHRzW2ldLnN0YXQoJ3BhY2tldHNSZWNlaXZlZCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHBhY2tldHNsb3N0ID0gcmVzdWx0c1tpXS5zdGF0KCdwYWNrZXRzTG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhY2tldHNyZWN2ICYmIHBhY2tldHNsb3N0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0c3JlY3YgPSBwYXJzZUludChwYWNrZXRzcmVjdiwgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2tldHNsb3N0ID0gcGFyc2VJbnQocGFja2V0c2xvc3QsIDEwKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnN0YXQoJ2dvb2dGcmFtZVJhdGVSZWNlaXZlZCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3Rsb3N0LnZpZGVvID0gbG9zdC52aWRlbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdHJlY3YudmlkZW8gPSByZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWN2LnZpZGVvID0gcGFja2V0c3JlY3Y7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvc3QudmlkZW8gPSBwYWNrZXRzbG9zdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bG9zdC5hdWRpbyA9IGxvc3QuYXVkaW87XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RyZWN2LmF1ZGlvID0gcmVjdi5hdWRpbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdi5hdWRpbyA9IHBhY2tldHNyZWN2O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3N0LmF1ZGlvID0gcGFja2V0c2xvc3Q7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGRlbHRhLmF1ZGlvID0gcmVjdi5hdWRpbyAtIGxhc3RyZWN2LmF1ZGlvO1xuICAgICAgICAgICAgICAgIGRlbHRhLnZpZGVvID0gcmVjdi52aWRlbyAtIGxhc3RyZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgIGxvc3MuYXVkaW8gPSAoZGVsdGEuYXVkaW8gPiAwKSA/IE1hdGguY2VpbCgxMDAgKiAobG9zdC5hdWRpbyAtIGxhc3Rsb3N0LmF1ZGlvKSAvIGRlbHRhLmF1ZGlvKSA6IDA7XG4gICAgICAgICAgICAgICAgbG9zcy52aWRlbyA9IChkZWx0YS52aWRlbyA+IDApID8gTWF0aC5jZWlsKDEwMCAqIChsb3N0LnZpZGVvIC0gbGFzdGxvc3QudmlkZW8pIC8gZGVsdGEudmlkZW8pIDogMDtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwYWNrZXRsb3NzLmppbmdsZScsIFtzZWxmLnNpZCwgbG9zc10pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LCBpbnRlcnZhbCB8fCAzMDAwKTtcbiAgICByZXR1cm4gdGhpcy5zdGF0c2ludGVydmFsO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yID0gZnVuY3Rpb24gKHNlc3Npb24sIGVycm9yKVxue1xuICAgIGNvbnNvbGUuZXJyb3IoXCJKaW5nbGUgZXJyb3JcIiwgZXJyb3IpO1xufVxuXG5KaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvciA9IGZ1bmN0aW9uIChzZXNzaW9uLCBlcnJvcilcbntcbiAgICB0aGlzLnNlcnZpY2Uuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCAgXCJTb3JyeVwiLFxuICAgICAgICBcIkludGVybmFsIGFwcGxpY2F0aW9uIGVycm9yW3NldFJlbW90ZURlc2NyaXB0aW9uXVwiKTtcbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBwdXQgb3VyIHNzcmNzIGludG8gcHJlc2VuY2Ugc28gb3RoZXIgY2xpZW50cyBjYW4gaWRlbnRpZnkgb3VyIHN0cmVhbVxuICAgIHZhciBuZXdzc3JjcyA9IFtdO1xuICAgIHZhciBtZWRpYSA9IEFQUC5zaW11bGNhc3QucGFyc2VNZWRpYSh0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pO1xuICAgIG1lZGlhLmZvckVhY2goZnVuY3Rpb24gKG1lZGlhKSB7XG5cbiAgICAgICAgaWYoT2JqZWN0LmtleXMobWVkaWEuc291cmNlcykubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgLy8gVE9ETyhncCkgbWF5YmUgZXhjbHVkZSBGSUQgc3RyZWFtcz9cbiAgICAgICAgICAgIE9iamVjdC5rZXlzKG1lZGlhLnNvdXJjZXMpLmZvckVhY2goZnVuY3Rpb24gKHNzcmMpIHtcbiAgICAgICAgICAgICAgICBuZXdzc3Jjcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgJ3NzcmMnOiBzc3JjLFxuICAgICAgICAgICAgICAgICAgICAndHlwZSc6IG1lZGlhLnR5cGUsXG4gICAgICAgICAgICAgICAgICAgICdkaXJlY3Rpb24nOiBtZWRpYS5kaXJlY3Rpb25cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYodGhpcy5sb2NhbFN0cmVhbXNTU1JDICYmIHRoaXMubG9jYWxTdHJlYW1zU1NSQ1ttZWRpYS50eXBlXSlcbiAgICAgICAge1xuICAgICAgICAgICAgbmV3c3NyY3MucHVzaCh7XG4gICAgICAgICAgICAgICAgJ3NzcmMnOiB0aGlzLmxvY2FsU3RyZWFtc1NTUkNbbWVkaWEudHlwZV0sXG4gICAgICAgICAgICAgICAgJ3R5cGUnOiBtZWRpYS50eXBlLFxuICAgICAgICAgICAgICAgICdkaXJlY3Rpb24nOiBtZWRpYS5kaXJlY3Rpb25cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKCduZXcgc3NyY3MnLCBuZXdzc3Jjcyk7XG5cbiAgICAvLyBIYXZlIHRvIGNsZWFyIHByZXNlbmNlIG1hcCB0byBnZXQgcmlkIG9mIHJlbW92ZWQgc3RyZWFtc1xuICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmNsZWFyUHJlc2VuY2VNZWRpYSgpO1xuXG4gICAgaWYgKG5ld3NzcmNzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPD0gbmV3c3NyY3MubGVuZ3RoOyBpICsrKSB7XG4gICAgICAgICAgICAvLyBDaGFuZ2UgdmlkZW8gdHlwZSB0byBzY3JlZW5cbiAgICAgICAgICAgIGlmIChuZXdzc3Jjc1tpLTFdLnR5cGUgPT09ICd2aWRlbycgJiYgQVBQLmRlc2t0b3BzaGFyaW5nLmlzVXNpbmdTY3JlZW5TdHJlYW0oKSkge1xuICAgICAgICAgICAgICAgIG5ld3NzcmNzW2ktMV0udHlwZSA9ICdzY3JlZW4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmVtdWMuYWRkTWVkaWFUb1ByZXNlbmNlKGksXG4gICAgICAgICAgICAgICAgbmV3c3NyY3NbaS0xXS50eXBlLCBuZXdzc3Jjc1tpLTFdLnNzcmMsIG5ld3NzcmNzW2ktMV0uZGlyZWN0aW9uKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH1cbn1cblxuLy8gYW4gYXR0ZW1wdCB0byB3b3JrIGFyb3VuZCBodHRwczovL2dpdGh1Yi5jb20vaml0c2kvaml0bWVldC9pc3N1ZXMvMzJcbmZ1bmN0aW9uIHNlbmRLZXlmcmFtZShwYykge1xuICAgIGNvbnNvbGUubG9nKCdzZW5ka2V5ZnJhbWUnLCBwYy5pY2VDb25uZWN0aW9uU3RhdGUpO1xuICAgIGlmIChwYy5pY2VDb25uZWN0aW9uU3RhdGUgIT09ICdjb25uZWN0ZWQnKSByZXR1cm47IC8vIHNhZmUuLi5cbiAgICBwYy5zZXRSZW1vdGVEZXNjcmlwdGlvbihcbiAgICAgICAgcGMucmVtb3RlRGVzY3JpcHRpb24sXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHBjLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAobW9kaWZpZWRBbnN3ZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgcGMuc2V0TG9jYWxEZXNjcmlwdGlvbihcbiAgICAgICAgICAgICAgICAgICAgICAgIG1vZGlmaWVkQW5zd2VyLFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vb3BcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndHJpZ2dlcktleWZyYW1lIHNldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndHJpZ2dlcktleWZyYW1lIGNyZWF0ZUFuc3dlciBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0cmlnZ2VyS2V5ZnJhbWUgc2V0UmVtb3RlRGVzY3JpcHRpb24gZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcigpO1xuICAgICAgICB9XG4gICAgKTtcbn1cblxuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5yZW1vdGVTdHJlYW1BZGRlZCA9IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciB0aGVzc3JjO1xuICAgIHZhciBzc3JjMmppZCA9IHRoaXMuY29ubmVjdGlvbi5lbXVjLnNzcmMyamlkO1xuXG4gICAgLy8gbG9vayB1cCBhbiBhc3NvY2lhdGVkIEpJRCBmb3IgYSBzdHJlYW0gaWRcbiAgICBpZiAoZGF0YS5zdHJlYW0uaWQgJiYgZGF0YS5zdHJlYW0uaWQuaW5kZXhPZignbWl4ZWRtc2xhYmVsJykgPT09IC0xKSB7XG4gICAgICAgIC8vIGxvb2sgb25seSBhdCBhPXNzcmM6IGFuZCBfbm90XyBhdCBhPXNzcmMtZ3JvdXA6IGxpbmVzXG5cbiAgICAgICAgdmFyIHNzcmNsaW5lc1xuICAgICAgICAgICAgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHAsICdhPXNzcmM6Jyk7XG4gICAgICAgIHNzcmNsaW5lcyA9IHNzcmNsaW5lcy5maWx0ZXIoZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgIC8vIE5PVEUoZ3ApIHByZXZpb3VzbHkgd2UgZmlsdGVyZWQgb24gdGhlIG1zbGFiZWwsIGJ1dCB0aGF0IHByb3BlcnR5XG4gICAgICAgICAgICAvLyBpcyBub3QgYWx3YXlzIHByZXNlbnQuXG4gICAgICAgICAgICAvLyByZXR1cm4gbGluZS5pbmRleE9mKCdtc2xhYmVsOicgKyBkYXRhLnN0cmVhbS5sYWJlbCkgIT09IC0xO1xuXG4gICAgICAgICAgICByZXR1cm4gKChsaW5lLmluZGV4T2YoJ21zaWQ6JyArIGRhdGEuc3RyZWFtLmlkKSAhPT0gLTEpKTtcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChzc3JjbGluZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aGVzc3JjID0gc3NyY2xpbmVzWzBdLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdO1xuXG4gICAgICAgICAgICAvLyBXZSBzaWduYWwgb3VyIHN0cmVhbXMgKHRocm91Z2ggSmluZ2xlIHRvIHRoZSBmb2N1cykgYmVmb3JlIHdlIHNldFxuICAgICAgICAgICAgLy8gb3VyIHByZXNlbmNlICh0aHJvdWdoIHdoaWNoIHBlZXJzIGFzc29jaWF0ZSByZW1vdGUgc3RyZWFtcyB0b1xuICAgICAgICAgICAgLy8gamlkcykuIFNvLCBpdCBtaWdodCBhcnJpdmUgdGhhdCBhIHJlbW90ZSBzdHJlYW0gaXMgYWRkZWQgYnV0XG4gICAgICAgICAgICAvLyBzc3JjMmppZCBpcyBub3QgeWV0IHVwZGF0ZWQgYW5kIHRodXMgZGF0YS5wZWVyamlkIGNhbm5vdCBiZVxuICAgICAgICAgICAgLy8gc3VjY2Vzc2Z1bGx5IHNldC4gSGVyZSB3ZSB3YWl0IGZvciB1cCB0byBhIHNlY29uZCBmb3IgdGhlXG4gICAgICAgICAgICAvLyBwcmVzZW5jZSB0byBhcnJpdmUuXG5cbiAgICAgICAgICAgIGlmICghc3NyYzJqaWRbdGhlc3NyY10pIHtcbiAgICAgICAgICAgICAgICAvLyBUT0RPKGdwKSBsaW1pdCB3YWl0IGR1cmF0aW9uIHRvIDEgc2VjLlxuICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfShkYXRhKSwgMjUwKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG9rIHRvIG92ZXJ3cml0ZSB0aGUgb25lIGZyb20gZm9jdXM/IG1pZ2h0IHNhdmUgd29yayBpbiBjb2xpYnJpLmpzXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnYXNzb2NpYXRlZCBqaWQnLCBzc3JjMmppZFt0aGVzc3JjXSwgZGF0YS5wZWVyamlkKTtcbiAgICAgICAgICAgIGlmIChzc3JjMmppZFt0aGVzc3JjXSkge1xuICAgICAgICAgICAgICAgIGRhdGEucGVlcmppZCA9IHNzcmMyamlkW3RoZXNzcmNdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy9UT0RPOiB0aGlzIGNvZGUgc2hvdWxkIGJlIHJlbW92ZWQgd2hlbiBmaXJlZm94IGltcGxlbWVudCBtdWx0aXN0cmVhbSBzdXBwb3J0XG4gICAgaWYoQVBQLlJUQy5nZXRCcm93c2VyVHlwZSgpID09IFJUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0ZJUkVGT1gpXG4gICAge1xuICAgICAgICBpZigoSmluZ2xlU2Vzc2lvbi5ub3RSZWNlaXZlZFNTUkNzLmxlbmd0aCA9PSAwKSB8fFxuICAgICAgICAgICAgIXNzcmMyamlkW0ppbmdsZVNlc3Npb24ubm90UmVjZWl2ZWRTU1JDc1tKaW5nbGVTZXNzaW9uLm5vdFJlY2VpdmVkU1NSQ3MubGVuZ3RoIC0gMV1dKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBsaW1pdCB3YWl0IGR1cmF0aW9uIHRvIDEgc2VjLlxuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbihkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0oZGF0YSksIDI1MCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGVzc3JjID0gSmluZ2xlU2Vzc2lvbi5ub3RSZWNlaXZlZFNTUkNzLnBvcCgpO1xuICAgICAgICBpZiAoc3NyYzJqaWRbdGhlc3NyY10pIHtcbiAgICAgICAgICAgIGRhdGEucGVlcmppZCA9IHNzcmMyamlkW3RoZXNzcmNdO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgQVBQLlJUQy5jcmVhdGVSZW1vdGVTdHJlYW0oZGF0YSwgdGhpcy5zaWQsIHRoZXNzcmMpO1xuXG4gICAgdmFyIGlzVmlkZW8gPSBkYXRhLnN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG4gICAgLy8gYW4gYXR0ZW1wdCB0byB3b3JrIGFyb3VuZCBodHRwczovL2dpdGh1Yi5jb20vaml0c2kvaml0bWVldC9pc3N1ZXMvMzJcbiAgICBpZiAoaXNWaWRlbyAmJlxuICAgICAgICBkYXRhLnBlZXJqaWQgJiYgdGhpcy5wZWVyamlkID09PSBkYXRhLnBlZXJqaWQgJiZcbiAgICAgICAgZGF0YS5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPT09IDAgJiZcbiAgICAgICAgQVBQLlJUQy5sb2NhbFZpZGVvLmdldFRyYWNrcygpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VuZEtleWZyYW1lKHNlbGYucGVlcmNvbm5lY3Rpb24pO1xuICAgICAgICB9LCAzMDAwKTtcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gSmluZ2xlU2Vzc2lvbjtcbiIsIi8qIGpzaGludCAtVzExNyAqL1xudmFyIFNEUFV0aWwgPSByZXF1aXJlKFwiLi9TRFBVdGlsXCIpO1xuXG4vLyBTRFAgU1RVRkZcbmZ1bmN0aW9uIFNEUChzZHApIHtcbiAgICB0aGlzLm1lZGlhID0gc2RwLnNwbGl0KCdcXHJcXG5tPScpO1xuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgdGhpcy5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICB0aGlzLm1lZGlhW2ldID0gJ209JyArIHRoaXMubWVkaWFbaV07XG4gICAgICAgIGlmIChpICE9IHRoaXMubWVkaWEubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgdGhpcy5tZWRpYVtpXSArPSAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnNlc3Npb24gPSB0aGlzLm1lZGlhLnNoaWZ0KCkgKyAnXFxyXFxuJztcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59XG4vKipcbiAqIFJldHVybnMgbWFwIG9mIE1lZGlhQ2hhbm5lbCBtYXBwZWQgcGVyIGNoYW5uZWwgaWR4LlxuICovXG5TRFAucHJvdG90eXBlLmdldE1lZGlhU3NyY01hcCA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbWVkaWFfc3NyY3MgPSB7fTtcbiAgICB2YXIgdG1wO1xuICAgIGZvciAodmFyIG1lZGlhaW5kZXggPSAwOyBtZWRpYWluZGV4IDwgc2VsZi5tZWRpYS5sZW5ndGg7IG1lZGlhaW5kZXgrKykge1xuICAgICAgICB0bXAgPSBTRFBVdGlsLmZpbmRfbGluZXMoc2VsZi5tZWRpYVttZWRpYWluZGV4XSwgJ2E9c3NyYzonKTtcbiAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPW1pZDonKSk7XG4gICAgICAgIHZhciBtZWRpYSA9IHtcbiAgICAgICAgICAgIG1lZGlhaW5kZXg6IG1lZGlhaW5kZXgsXG4gICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgIH07XG4gICAgICAgIG1lZGlhX3NzcmNzW21lZGlhaW5kZXhdID0gbWVkaWE7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICB2YXIgbGluZXNzcmMgPSBsaW5lLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdO1xuICAgICAgICAgICAgLy8gYWxsb2NhdGUgbmV3IENoYW5uZWxTc3JjXG4gICAgICAgICAgICBpZighbWVkaWEuc3NyY3NbbGluZXNzcmNdKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEuc3NyY3NbbGluZXNzcmNdID0ge1xuICAgICAgICAgICAgICAgICAgICBzc3JjOiBsaW5lc3NyYyxcbiAgICAgICAgICAgICAgICAgICAgbGluZXM6IFtdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG1lZGlhLnNzcmNzW2xpbmVzc3JjXS5saW5lcy5wdXNoKGxpbmUpO1xuICAgICAgICB9KTtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5maW5kX2xpbmVzKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPXNzcmMtZ3JvdXA6Jyk7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpe1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDEzKTtcbiAgICAgICAgICAgIHZhciBzc3JjcyA9IGxpbmUuc3Vic3RyKDE0ICsgc2VtYW50aWNzLmxlbmd0aCkuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIG1lZGlhLnNzcmNHcm91cHMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHNlbWFudGljczogc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICBzc3Jjczogc3NyY3NcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBtZWRpYV9zc3Jjcztcbn07XG4vKipcbiAqIFJldHVybnMgPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICogQHBhcmFtIHNzcmMgdGhlIHNzcmMgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICovXG5TRFAucHJvdG90eXBlLmNvbnRhaW5zU1NSQyA9IGZ1bmN0aW9uKHNzcmMpIHtcbiAgICB2YXIgbWVkaWFzID0gdGhpcy5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgY29udGFpbnMgPSBmYWxzZTtcbiAgICBPYmplY3Qua2V5cyhtZWRpYXMpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIHZhciBtZWRpYSA9IG1lZGlhc1ttZWRpYWluZGV4XTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIkNoZWNrXCIsIGNoYW5uZWwsIHNzcmMpO1xuICAgICAgICBpZihPYmplY3Qua2V5cyhtZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSAhPSAtMSl7XG4gICAgICAgICAgICBjb250YWlucyA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gY29udGFpbnM7XG59O1xuXG5cbi8vIHJlbW92ZSBpU0FDIGFuZCBDTiBmcm9tIFNEUFxuU0RQLnByb3RvdHlwZS5tYW5nbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGksIGosIG1saW5lLCBsaW5lcywgcnRwbWFwLCBuZXdkZXNjO1xuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGxpbmVzID0gdGhpcy5tZWRpYVtpXS5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGxpbmVzLnBvcCgpOyAvLyByZW1vdmUgZW1wdHkgbGFzdCBlbGVtZW50XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZShsaW5lcy5zaGlmdCgpKTtcbiAgICAgICAgaWYgKG1saW5lLm1lZGlhICE9ICdhdWRpbycpXG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgbmV3ZGVzYyA9ICcnO1xuICAgICAgICBtbGluZS5mbXQubGVuZ3RoID0gMDtcbiAgICAgICAgZm9yIChqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyKDAsIDkpID09ICdhPXJ0cG1hcDonKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5wYXJzZV9ydHBtYXAobGluZXNbal0pO1xuICAgICAgICAgICAgICAgIGlmIChydHBtYXAubmFtZSA9PSAnQ04nIHx8IHJ0cG1hcC5uYW1lID09ICdJU0FDJylcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgbWxpbmUuZm10LnB1c2gocnRwbWFwLmlkKTtcbiAgICAgICAgICAgICAgICBuZXdkZXNjICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG5ld2Rlc2MgKz0gbGluZXNbal0gKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLm1lZGlhW2ldID0gU0RQVXRpbC5idWlsZF9tbGluZShtbGluZSkgKyAnXFxyXFxuJztcbiAgICAgICAgdGhpcy5tZWRpYVtpXSArPSBuZXdkZXNjO1xuICAgIH1cbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59O1xuXG4vLyByZW1vdmUgbGluZXMgbWF0Y2hpbmcgcHJlZml4IGZyb20gc2Vzc2lvbiBzZWN0aW9uXG5TRFAucHJvdG90eXBlLnJlbW92ZVNlc3Npb25MaW5lcyA9IGZ1bmN0aW9uKHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCBwcmVmaXgpO1xuICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICBzZWxmLnNlc3Npb24gPSBzZWxmLnNlc3Npb24ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuLy8gcmVtb3ZlIGxpbmVzIG1hdGNoaW5nIHByZWZpeCBmcm9tIGEgbWVkaWEgc2VjdGlvbiBzcGVjaWZpZWQgYnkgbWVkaWFpbmRleFxuLy8gVE9ETzogbm9uLW51bWVyaWMgbWVkaWFpbmRleCBjb3VsZCBtYXRjaCBtaWRcblNEUC5wcm90b3R5cGUucmVtb3ZlTWVkaWFMaW5lcyA9IGZ1bmN0aW9uKG1lZGlhaW5kZXgsIHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgcHJlZml4KTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgc2VsZi5tZWRpYVttZWRpYWluZGV4XSA9IHNlbGYubWVkaWFbbWVkaWFpbmRleF0ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuXG4vLyBhZGQgY29udGVudCdzIHRvIGEgamluZ2xlIGVsZW1lbnRcblNEUC5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgdGhlY3JlYXRvciwgc3NyY3MpIHtcbi8vICAgIGNvbnNvbGUubG9nKFwiU1NSQ1wiICsgc3NyY3NbXCJhdWRpb1wiXSArIFwiIC0gXCIgKyBzc3Jjc1tcInZpZGVvXCJdKTtcbiAgICB2YXIgaSwgaiwgaywgbWxpbmUsIHNzcmMsIHJ0cG1hcCwgdG1wLCBsaW5lLCBsaW5lcztcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gbmV3IGJ1bmRsZSBwbGFuXG4gICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMuc2Vzc2lvbiwgJ2E9Z3JvdXA6JykpIHtcbiAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCAnYT1ncm91cDonKTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB0bXAgPSBsaW5lc1tpXS5zcGxpdCgnICcpO1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IHRtcC5zaGlmdCgpLnN1YnN0cig4KTtcbiAgICAgICAgICAgIGVsZW0uYygnZ3JvdXAnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowJywgc2VtYW50aWNzOnNlbWFudGljc30pO1xuICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IHRtcC5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY29udGVudCcsIHtuYW1lOiB0bXBbal19KS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLm1lZGlhW2ldLnNwbGl0KCdcXHJcXG4nKVswXSk7XG4gICAgICAgIGlmICghKG1saW5lLm1lZGlhID09PSAnYXVkaW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAndmlkZW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAnYXBwbGljYXRpb24nKSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNzcmM6JykpIHtcbiAgICAgICAgICAgIHNzcmMgPSBTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdOyAvLyB0YWtlIHRoZSBmaXJzdFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYoc3NyY3MgJiYgc3NyY3NbbWxpbmUubWVkaWFdKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNzcmMgPSBzc3Jjc1ttbGluZS5tZWRpYV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgc3NyYyA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgZWxlbS5jKCdjb250ZW50Jywge2NyZWF0b3I6IHRoZWNyZWF0b3IsIG5hbWU6IG1saW5lLm1lZGlhfSk7XG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1taWQ6JykpIHtcbiAgICAgICAgICAgIC8vIHByZWZlciBpZGVudGlmaWVyIGZyb20gYT1taWQgaWYgcHJlc2VudFxuICAgICAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPW1pZDonKSk7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHsgbmFtZTogbWlkIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0cG1hcDonKS5sZW5ndGgpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGVsZW0uYygnZGVzY3JpcHRpb24nLFxuICAgICAgICAgICAgICAgIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDoxJyxcbiAgICAgICAgICAgICAgICAgICAgbWVkaWE6IG1saW5lLm1lZGlhIH0pO1xuICAgICAgICAgICAgaWYgKHNzcmMpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzc3JjOiBzc3JjfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGogPSAwOyBqIDwgbWxpbmUuZm10Lmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9cnRwbWFwOicgKyBtbGluZS5mbXRbal0pO1xuICAgICAgICAgICAgICAgIGVsZW0uYygncGF5bG9hZC10eXBlJywgU0RQVXRpbC5wYXJzZV9ydHBtYXAocnRwbWFwKSk7XG4gICAgICAgICAgICAgICAgLy8gcHV0IGFueSAnYT1mbXRwOicgKyBtbGluZS5mbXRbal0gbGluZXMgaW50byA8cGFyYW0gbmFtZT1mb28gdmFsdWU9YmFyLz5cbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSkge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2ZtdHAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSk7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCB0bXAubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJywgdG1wW2tdKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuUnRjcEZiVG9KaW5nbGUoaSwgZWxlbSwgbWxpbmUuZm10W2pdKTsgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYlxuXG4gICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWNyeXB0bzonLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5jKCdlbmNyeXB0aW9uJywge3JlcXVpcmVkOiAxfSk7XG4gICAgICAgICAgICAgICAgdmFyIGNyeXB0byA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1jcnlwdG86JywgdGhpcy5zZXNzaW9uKTtcbiAgICAgICAgICAgICAgICBjcnlwdG8uZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnY3J5cHRvJywgU0RQVXRpbC5wYXJzZV9jcnlwdG8obGluZSkpLnVwKCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZW5jcnlwdGlvblxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoc3NyYykge1xuICAgICAgICAgICAgICAgIC8vIG5ldyBzdHlsZSBtYXBwaW5nXG4gICAgICAgICAgICAgICAgZWxlbS5jKCdzb3VyY2UnLCB7IHNzcmM6IHNzcmMsIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGdyb3VwIGJ5IHNzcmMgYW5kIHN1cHBvcnQgbXVsdGlwbGUgZGlmZmVyZW50IHNzcmNzXG4gICAgICAgICAgICAgICAgdmFyIHNzcmNsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpO1xuICAgICAgICAgICAgICAgIGlmKHNzcmNsaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNzcmNsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZHggPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsaW5lc3NyYyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGxpbmVzc3JjICE9IHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYyA9IGxpbmVzc3JjO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGt2LmluZGV4T2YoJzonKSA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeyBuYW1lOiBrdiB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IG5hbWU6IGt2LnNwbGl0KCc6JywgMilbMF0gfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHZhbHVlOiBrdi5zcGxpdCgnOicsIDIpWzFdIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcImNuYW1lXCIsIHZhbHVlOk1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZyg3KX0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBtc2lkID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgaWYobWxpbmUubWVkaWEgPT0gXCJhdWRpb1wiKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gQVBQLlJUQy5sb2NhbEF1ZGlvLmdldElkKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gQVBQLlJUQy5sb2NhbFZpZGVvLmdldElkKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgaWYobXNpZCAhPSBudWxsKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gbXNpZC5yZXBsYWNlKC9bXFx7LFxcfV0vZyxcIlwiKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcIm1zaWRcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe25hbWU6IFwibXNsYWJlbFwiLCB2YWx1ZTptc2lkfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7bmFtZTogXCJsYWJlbFwiLCB2YWx1ZTptc2lkfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gWEVQLTAzMzkgaGFuZGxlIHNzcmMtZ3JvdXAgYXR0cmlidXRlc1xuICAgICAgICAgICAgICAgIHZhciBzc3JjX2dyb3VwX2xpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbaV0sICdhPXNzcmMtZ3JvdXA6Jyk7XG4gICAgICAgICAgICAgICAgc3NyY19ncm91cF9saW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgICAgICAgICAgICAgaWR4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSBsaW5lLnN1YnN0cigwLCBpZHgpLnN1YnN0cigxMyk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBzc3JjcyA9IGxpbmUuc3Vic3RyKDE0ICsgc2VtYW50aWNzLmxlbmd0aCkuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNzcmNzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3NzcmMtZ3JvdXAnLCB7IHNlbWFudGljczogc2VtYW50aWNzLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgc3NyY3MuZm9yRWFjaChmdW5jdGlvbihzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdzb3VyY2UnLCB7IHNzcmM6IHNzcmMgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9cnRjcC1tdXgnKSkge1xuICAgICAgICAgICAgICAgIGVsZW0uYygncnRjcC1tdXgnKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBYRVAtMDI5MyAtLSBtYXAgYT1ydGNwLWZiOipcbiAgICAgICAgICAgIHRoaXMuUnRjcEZiVG9KaW5nbGUoaSwgZWxlbSwgJyonKTtcblxuICAgICAgICAgICAgLy8gWEVQLTAyOTRcbiAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1leHRtYXA6JykpIHtcbiAgICAgICAgICAgICAgICBsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1leHRtYXA6Jyk7XG4gICAgICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRtcCA9IFNEUFV0aWwucGFyc2VfZXh0bWFwKGxpbmVzW2pdKTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdydHAtaGRyZXh0JywgeyB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydHAtaGRyZXh0OjAnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdXJpOiB0bXAudXJpLFxuICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IHRtcC52YWx1ZSB9KTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRtcC5oYXNPd25Qcm9wZXJ0eSgnZGlyZWN0aW9uJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaCAodG1wLmRpcmVjdGlvbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3NlbmRvbmx5JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ3Jlc3BvbmRlcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAncmVjdm9ubHknOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnaW5pdGlhdG9yJ30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzZW5kcmVjdic6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdib3RoJ30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdpbmFjdGl2ZSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdub25lJ30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAvLyBUT0RPOiBoYW5kbGUgcGFyYW1zXG4gICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbGVtLnVwKCk7IC8vIGVuZCBvZiBkZXNjcmlwdGlvblxuICAgICAgICB9XG5cbiAgICAgICAgLy8gbWFwIGljZS11ZnJhZy9wd2QsIGR0bHMgZmluZ2VycHJpbnQsIGNhbmRpZGF0ZXNcbiAgICAgICAgdGhpcy5UcmFuc3BvcnRUb0ppbmdsZShpLCBlbGVtKTtcblxuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9c2VuZHJlY3YnLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnYm90aCd9KTtcbiAgICAgICAgfSBlbHNlIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1zZW5kb25seScsIHRoaXMuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdpbml0aWF0b3InfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9cmVjdm9ubHknLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAncmVzcG9uZGVyJ30pO1xuICAgICAgICB9IGVsc2UgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWluYWN0aXZlJywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ25vbmUnfSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1saW5lLnBvcnQgPT0gJzAnKSB7XG4gICAgICAgICAgICAvLyBlc3RvcyBoYWNrIHRvIHJlamVjdCBhbiBtLWxpbmVcbiAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdyZWplY3RlZCd9KTtcbiAgICAgICAgfVxuICAgICAgICBlbGVtLnVwKCk7IC8vIGVuZCBvZiBjb250ZW50XG4gICAgfVxuICAgIGVsZW0udXAoKTtcbiAgICByZXR1cm4gZWxlbTtcbn07XG5cblNEUC5wcm90b3R5cGUuVHJhbnNwb3J0VG9KaW5nbGUgPSBmdW5jdGlvbiAobWVkaWFpbmRleCwgZWxlbSkge1xuICAgIHZhciBpID0gbWVkaWFpbmRleDtcbiAgICB2YXIgdG1wO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBlbGVtLmMoJ3RyYW5zcG9ydCcpO1xuXG4gICAgLy8gWEVQLTAzNDMgRFRMUy9TQ1RQXG4gICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPXNjdHBtYXA6JykubGVuZ3RoKVxuICAgIHtcbiAgICAgICAgdmFyIHNjdHBtYXAgPSBTRFBVdGlsLmZpbmRfbGluZShcbiAgICAgICAgICAgIHRoaXMubWVkaWFbaV0sICdhPXNjdHBtYXA6Jywgc2VsZi5zZXNzaW9uKTtcbiAgICAgICAgaWYgKHNjdHBtYXApXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhciBzY3RwQXR0cnMgPSBTRFBVdGlsLnBhcnNlX3NjdHBtYXAoc2N0cG1hcCk7XG4gICAgICAgICAgICBlbGVtLmMoJ3NjdHBtYXAnLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czpkdGxzLXNjdHA6MScsXG4gICAgICAgICAgICAgICAgICAgIG51bWJlcjogc2N0cEF0dHJzWzBdLCAvKiBTQ1RQIHBvcnQgKi9cbiAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2w6IHNjdHBBdHRyc1sxXSwgLyogcHJvdG9jb2wgKi9cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIE9wdGlvbmFsIHN0cmVhbSBjb3VudCBhdHRyaWJ1dGVcbiAgICAgICAgICAgIGlmIChzY3RwQXR0cnMubGVuZ3RoID4gMilcbiAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHsgc3RyZWFtczogc2N0cEF0dHJzWzJdfSk7XG4gICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLy8gWEVQLTAzMjBcbiAgICB2YXIgZmluZ2VycHJpbnRzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMuc2Vzc2lvbik7XG4gICAgZmluZ2VycHJpbnRzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2ZpbmdlcnByaW50KGxpbmUpO1xuICAgICAgICB0bXAueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOmFwcHM6ZHRsczowJztcbiAgICAgICAgZWxlbS5jKCdmaW5nZXJwcmludCcpLnQodG1wLmZpbmdlcnByaW50KTtcbiAgICAgICAgZGVsZXRlIHRtcC5maW5nZXJwcmludDtcbiAgICAgICAgbGluZSA9IFNEUFV0aWwuZmluZF9saW5lKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPXNldHVwOicsIHNlbGYuc2Vzc2lvbik7XG4gICAgICAgIGlmIChsaW5lKSB7XG4gICAgICAgICAgICB0bXAuc2V0dXAgPSBsaW5lLnN1YnN0cig4KTtcbiAgICAgICAgfVxuICAgICAgICBlbGVtLmF0dHJzKHRtcCk7XG4gICAgICAgIGVsZW0udXAoKTsgLy8gZW5kIG9mIGZpbmdlcnByaW50XG4gICAgfSk7XG4gICAgdG1wID0gU0RQVXRpbC5pY2VwYXJhbXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgdGhpcy5zZXNzaW9uKTtcbiAgICBpZiAodG1wKSB7XG4gICAgICAgIHRtcC54bWxucyA9ICd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjEnO1xuICAgICAgICBlbGVtLmF0dHJzKHRtcCk7XG4gICAgICAgIC8vIFhFUC0wMTc2XG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1jYW5kaWRhdGU6JywgdGhpcy5zZXNzaW9uKSkgeyAvLyBhZGQgYW55IGE9Y2FuZGlkYXRlIGxpbmVzXG4gICAgICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgJ2E9Y2FuZGlkYXRlOicsIHRoaXMuc2Vzc2lvbik7XG4gICAgICAgICAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5jKCdjYW5kaWRhdGUnLCBTRFBVdGlsLmNhbmRpZGF0ZVRvSmluZ2xlKGxpbmUpKS51cCgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgdHJhbnNwb3J0XG59XG5cblNEUC5wcm90b3R5cGUuUnRjcEZiVG9KaW5nbGUgPSBmdW5jdGlvbiAobWVkaWFpbmRleCwgZWxlbSwgcGF5bG9hZHR5cGUpIHsgLy8gWEVQLTAyOTNcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgJ2E9cnRjcC1mYjonICsgcGF5bG9hZHR5cGUpO1xuICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHRtcCA9IFNEUFV0aWwucGFyc2VfcnRjcGZiKGxpbmUpO1xuICAgICAgICBpZiAodG1wLnR5cGUgPT0gJ3Ryci1pbnQnKSB7XG4gICAgICAgICAgICBlbGVtLmMoJ3J0Y3AtZmItdHJyLWludCcsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnLCB2YWx1ZTogdG1wLnBhcmFtc1swXX0pO1xuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZWxlbS5jKCdydGNwLWZiJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MCcsIHR5cGU6IHRtcC50eXBlfSk7XG4gICAgICAgICAgICBpZiAodG1wLnBhcmFtcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7J3N1YnR5cGUnOiB0bXAucGFyYW1zWzBdfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgIH1cbiAgICB9KTtcbn07XG5cblNEUC5wcm90b3R5cGUuUnRjcEZiRnJvbUppbmdsZSA9IGZ1bmN0aW9uIChlbGVtLCBwYXlsb2FkdHlwZSkgeyAvLyBYRVAtMDI5M1xuICAgIHZhciBtZWRpYSA9ICcnO1xuICAgIHZhciB0bXAgPSBlbGVtLmZpbmQoJz5ydGNwLWZiLXRyci1pbnRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRjcC1mYjowXCJdJyk7XG4gICAgaWYgKHRtcC5sZW5ndGgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcC1mYjonICsgJyonICsgJyAnICsgJ3Ryci1pbnQnICsgJyAnO1xuICAgICAgICBpZiAodG1wLmF0dHIoJ3ZhbHVlJykpIHtcbiAgICAgICAgICAgIG1lZGlhICs9IHRtcC5hdHRyKCd2YWx1ZScpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWVkaWEgKz0gJzAnO1xuICAgICAgICB9XG4gICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgIH1cbiAgICB0bXAgPSBlbGVtLmZpbmQoJz5ydGNwLWZiW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MFwiXScpO1xuICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcC1mYjonICsgcGF5bG9hZHR5cGUgKyAnICcgKyAkKHRoaXMpLmF0dHIoJ3R5cGUnKTtcbiAgICAgICAgaWYgKCQodGhpcykuYXR0cignc3VidHlwZScpKSB7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyAkKHRoaXMpLmF0dHIoJ3N1YnR5cGUnKTtcbiAgICAgICAgfVxuICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9KTtcbiAgICByZXR1cm4gbWVkaWE7XG59O1xuXG4vLyBjb25zdHJ1Y3QgYW4gU0RQIGZyb20gYSBqaW5nbGUgc3RhbnphXG5TRFAucHJvdG90eXBlLmZyb21KaW5nbGUgPSBmdW5jdGlvbiAoamluZ2xlKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMucmF3ID0gJ3Y9MFxcclxcbicgK1xuICAgICAgICAnbz0tICcgKyAnMTkyMzUxODUxNicgKyAnIDIgSU4gSVA0IDAuMC4wLjBcXHJcXG4nICsvLyBGSVhNRVxuICAgICAgICAncz0tXFxyXFxuJyArXG4gICAgICAgICd0PTAgMFxcclxcbic7XG4gICAgLy8gaHR0cDovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtaWV0Zi1tbXVzaWMtc2RwLWJ1bmRsZS1uZWdvdGlhdGlvbi0wNCNzZWN0aW9uLThcbiAgICBpZiAoJChqaW5nbGUpLmZpbmQoJz5ncm91cFt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOmdyb3VwaW5nOjBcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgJChqaW5nbGUpLmZpbmQoJz5ncm91cFt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOmdyb3VwaW5nOjBcIl0nKS5lYWNoKGZ1bmN0aW9uIChpZHgsIGdyb3VwKSB7XG4gICAgICAgICAgICB2YXIgY29udGVudHMgPSAkKGdyb3VwKS5maW5kKCc+Y29udGVudCcpLm1hcChmdW5jdGlvbiAoaWR4LCBjb250ZW50KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbnRlbnQuZ2V0QXR0cmlidXRlKCduYW1lJyk7XG4gICAgICAgICAgICB9KS5nZXQoKTtcbiAgICAgICAgICAgIGlmIChjb250ZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5yYXcgKz0gJ2E9Z3JvdXA6JyArIChncm91cC5nZXRBdHRyaWJ1dGUoJ3NlbWFudGljcycpIHx8IGdyb3VwLmdldEF0dHJpYnV0ZSgndHlwZScpKSArICcgJyArIGNvbnRlbnRzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLnNlc3Npb24gPSB0aGlzLnJhdztcbiAgICBqaW5nbGUuZmluZCgnPmNvbnRlbnQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIG0gPSBzZWxmLmppbmdsZTJtZWRpYSgkKHRoaXMpKTtcbiAgICAgICAgc2VsZi5tZWRpYS5wdXNoKG0pO1xuICAgIH0pO1xuXG4gICAgLy8gcmVjb25zdHJ1Y3QgbXNpZC1zZW1hbnRpYyAtLSBhcHBhcmVudGx5IG5vdCBuZWNlc3NhcnlcbiAgICAvKlxuICAgICB2YXIgbXNpZCA9IFNEUFV0aWwucGFyc2Vfc3NyYyh0aGlzLnJhdyk7XG4gICAgIGlmIChtc2lkLmhhc093blByb3BlcnR5KCdtc2xhYmVsJykpIHtcbiAgICAgdGhpcy5zZXNzaW9uICs9IFwiYT1tc2lkLXNlbWFudGljOiBXTVMgXCIgKyBtc2lkLm1zbGFiZWwgKyBcIlxcclxcblwiO1xuICAgICB9XG4gICAgICovXG5cbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59O1xuXG4vLyB0cmFuc2xhdGUgYSBqaW5nbGUgY29udGVudCBlbGVtZW50IGludG8gYW4gYW4gU0RQIG1lZGlhIHBhcnRcblNEUC5wcm90b3R5cGUuamluZ2xlMm1lZGlhID0gZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICB2YXIgbWVkaWEgPSAnJyxcbiAgICAgICAgZGVzYyA9IGNvbnRlbnQuZmluZCgnZGVzY3JpcHRpb24nKSxcbiAgICAgICAgc3NyYyA9IGRlc2MuYXR0cignc3NyYycpLFxuICAgICAgICBzZWxmID0gdGhpcyxcbiAgICAgICAgdG1wO1xuICAgIHZhciBzY3RwID0gY29udGVudC5maW5kKFxuICAgICAgICAnPnRyYW5zcG9ydD5zY3RwbWFwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6ZHRscy1zY3RwOjFcIl0nKTtcblxuICAgIHRtcCA9IHsgbWVkaWE6IGRlc2MuYXR0cignbWVkaWEnKSB9O1xuICAgIHRtcC5wb3J0ID0gJzEnO1xuICAgIGlmIChjb250ZW50LmF0dHIoJ3NlbmRlcnMnKSA9PSAncmVqZWN0ZWQnKSB7XG4gICAgICAgIC8vIGVzdG9zIGhhY2sgdG8gcmVqZWN0IGFuIG0tbGluZS5cbiAgICAgICAgdG1wLnBvcnQgPSAnMCc7XG4gICAgfVxuICAgIGlmIChjb250ZW50LmZpbmQoJz50cmFuc3BvcnQ+ZmluZ2VycHJpbnQnKS5sZW5ndGggfHwgZGVzYy5maW5kKCdlbmNyeXB0aW9uJykubGVuZ3RoKSB7XG4gICAgICAgIGlmIChzY3RwLmxlbmd0aClcbiAgICAgICAgICAgIHRtcC5wcm90byA9ICdEVExTL1NDVFAnO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICB0bXAucHJvdG8gPSAnUlRQL1NBVlBGJztcbiAgICB9IGVsc2Uge1xuICAgICAgICB0bXAucHJvdG8gPSAnUlRQL0FWUEYnO1xuICAgIH1cbiAgICBpZiAoIXNjdHAubGVuZ3RoKVxuICAgIHtcbiAgICAgICAgdG1wLmZtdCA9IGRlc2MuZmluZCgncGF5bG9hZC10eXBlJykubWFwKFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoJ2lkJyk7IH0pLmdldCgpO1xuICAgICAgICBtZWRpYSArPSBTRFBVdGlsLmJ1aWxkX21saW5lKHRtcCkgKyAnXFxyXFxuJztcbiAgICB9XG4gICAgZWxzZVxuICAgIHtcbiAgICAgICAgbWVkaWEgKz0gJ209YXBwbGljYXRpb24gMSBEVExTL1NDVFAgJyArIHNjdHAuYXR0cignbnVtYmVyJykgKyAnXFxyXFxuJztcbiAgICAgICAgbWVkaWEgKz0gJ2E9c2N0cG1hcDonICsgc2N0cC5hdHRyKCdudW1iZXInKSArXG4gICAgICAgICAgICAnICcgKyBzY3RwLmF0dHIoJ3Byb3RvY29sJyk7XG5cbiAgICAgICAgdmFyIHN0cmVhbUNvdW50ID0gc2N0cC5hdHRyKCdzdHJlYW1zJyk7XG4gICAgICAgIGlmIChzdHJlYW1Db3VudClcbiAgICAgICAgICAgIG1lZGlhICs9ICcgJyArIHN0cmVhbUNvdW50ICsgJ1xcclxcbic7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgIH1cblxuICAgIG1lZGlhICs9ICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgICBpZiAoIXNjdHAubGVuZ3RoKVxuICAgICAgICBtZWRpYSArPSAnYT1ydGNwOjEgSU4gSVA0IDAuMC4wLjBcXHJcXG4nO1xuICAgIHRtcCA9IGNvbnRlbnQuZmluZCgnPnRyYW5zcG9ydFt4bWxucz1cInVybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmljZS11ZHA6MVwiXScpO1xuICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgIGlmICh0bXAuYXR0cigndWZyYWcnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9pY2V1ZnJhZyh0bXAuYXR0cigndWZyYWcnKSkgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBpZiAodG1wLmF0dHIoJ3B3ZCcpKSB7XG4gICAgICAgICAgICBtZWRpYSArPSBTRFBVdGlsLmJ1aWxkX2ljZXB3ZCh0bXAuYXR0cigncHdkJykpICsgJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICAgICAgdG1wLmZpbmQoJz5maW5nZXJwcmludCcpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gRklYTUU6IGNoZWNrIG5hbWVzcGFjZSBhdCBzb21lIHBvaW50XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1maW5nZXJwcmludDonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ2hhc2gnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICcgJyArICQodGhpcykudGV4dCgpO1xuICAgICAgICAgICAgbWVkaWEgKz0gJ1xcclxcbic7XG4gICAgICAgICAgICBpZiAodGhpcy5nZXRBdHRyaWJ1dGUoJ3NldHVwJykpIHtcbiAgICAgICAgICAgICAgICBtZWRpYSArPSAnYT1zZXR1cDonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3NldHVwJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHN3aXRjaCAoY29udGVudC5hdHRyKCdzZW5kZXJzJykpIHtcbiAgICAgICAgY2FzZSAnaW5pdGlhdG9yJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNlbmRvbmx5XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdyZXNwb25kZXInOlxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9cmVjdm9ubHlcXHJcXG4nO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ25vbmUnOlxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9aW5hY3RpdmVcXHJcXG4nO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2JvdGgnOlxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9c2VuZHJlY3ZcXHJcXG4nO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIG1lZGlhICs9ICdhPW1pZDonICsgY29udGVudC5hdHRyKCduYW1lJykgKyAnXFxyXFxuJztcblxuICAgIC8vIDxkZXNjcmlwdGlvbj48cnRjcC1tdXgvPjwvZGVzY3JpcHRpb24+XG4gICAgLy8gc2VlIGh0dHA6Ly9jb2RlLmdvb2dsZS5jb20vcC9saWJqaW5nbGUvaXNzdWVzL2RldGFpbD9pZD0zMDkgLS0gbm8gc3BlYyB0aG91Z2hcbiAgICAvLyBhbmQgaHR0cDovL21haWwuamFiYmVyLm9yZy9waXBlcm1haWwvamluZ2xlLzIwMTEtRGVjZW1iZXIvMDAxNzYxLmh0bWxcbiAgICBpZiAoZGVzYy5maW5kKCdydGNwLW11eCcpLmxlbmd0aCkge1xuICAgICAgICBtZWRpYSArPSAnYT1ydGNwLW11eFxcclxcbic7XG4gICAgfVxuXG4gICAgaWYgKGRlc2MuZmluZCgnZW5jcnlwdGlvbicpLmxlbmd0aCkge1xuICAgICAgICBkZXNjLmZpbmQoJ2VuY3J5cHRpb24+Y3J5cHRvJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1jcnlwdG86JyArIHRoaXMuZ2V0QXR0cmlidXRlKCd0YWcnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICcgJyArIHRoaXMuZ2V0QXR0cmlidXRlKCdjcnlwdG8tc3VpdGUnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICcgJyArIHRoaXMuZ2V0QXR0cmlidXRlKCdrZXktcGFyYW1zJyk7XG4gICAgICAgICAgICBpZiAodGhpcy5nZXRBdHRyaWJ1dGUoJ3Nlc3Npb24tcGFyYW1zJykpIHtcbiAgICAgICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgnc2Vzc2lvbi1wYXJhbXMnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgZGVzYy5maW5kKCdwYXlsb2FkLXR5cGUnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9ydHBtYXAodGhpcykgKyAnXFxyXFxuJztcbiAgICAgICAgaWYgKCQodGhpcykuZmluZCgnPnBhcmFtZXRlcicpLmxlbmd0aCkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9Zm10cDonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ2lkJykgKyAnICc7XG4gICAgICAgICAgICBtZWRpYSArPSAkKHRoaXMpLmZpbmQoJ3BhcmFtZXRlcicpLm1hcChmdW5jdGlvbiAoKSB7IHJldHVybiAodGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKSA/ICh0aGlzLmdldEF0dHJpYnV0ZSgnbmFtZScpICsgJz0nKSA6ICcnKSArIHRoaXMuZ2V0QXR0cmlidXRlKCd2YWx1ZScpOyB9KS5nZXQoKS5qb2luKCc7ICcpO1xuICAgICAgICAgICAgbWVkaWEgKz0gJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICAgICAgLy8geGVwLTAyOTNcbiAgICAgICAgbWVkaWEgKz0gc2VsZi5SdGNwRmJGcm9tSmluZ2xlKCQodGhpcyksIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpKTtcbiAgICB9KTtcblxuICAgIC8vIHhlcC0wMjkzXG4gICAgbWVkaWEgKz0gc2VsZi5SdGNwRmJGcm9tSmluZ2xlKGRlc2MsICcqJyk7XG5cbiAgICAvLyB4ZXAtMDI5NFxuICAgIHRtcCA9IGRlc2MuZmluZCgnPnJ0cC1oZHJleHRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowXCJdJyk7XG4gICAgdG1wLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICBtZWRpYSArPSAnYT1leHRtYXA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpICsgJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3VyaScpICsgJ1xcclxcbic7XG4gICAgfSk7XG5cbiAgICBjb250ZW50LmZpbmQoJz50cmFuc3BvcnRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjFcIl0+Y2FuZGlkYXRlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9IFNEUFV0aWwuY2FuZGlkYXRlRnJvbUppbmdsZSh0aGlzKTtcbiAgICB9KTtcblxuICAgIC8vIFhFUC0wMzM5IGhhbmRsZSBzc3JjLWdyb3VwIGF0dHJpYnV0ZXNcbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uPnNzcmMtZ3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJykuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHNlbWFudGljcyA9IHRoaXMuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKTtcbiAgICAgICAgdmFyIHNzcmNzID0gJCh0aGlzKS5maW5kKCc+c291cmNlJykubWFwKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdzc3JjJyk7XG4gICAgICAgIH0pLmdldCgpO1xuXG4gICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9c3NyYy1ncm91cDonICsgc2VtYW50aWNzICsgJyAnICsgc3NyY3Muam9pbignICcpICsgJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIHRtcCA9IGNvbnRlbnQuZmluZCgnZGVzY3JpcHRpb24+c291cmNlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpO1xuICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNzcmMgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICAkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNzcmM6JyArIHNzcmMgKyAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgnbmFtZScpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCd2YWx1ZScpICYmIHRoaXMuZ2V0QXR0cmlidXRlKCd2YWx1ZScpLmxlbmd0aClcbiAgICAgICAgICAgICAgICBtZWRpYSArPSAnOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICB9KTtcbiAgICB9KTtcblxuICAgIHJldHVybiBtZWRpYTtcbn07XG5cblxubW9kdWxlLmV4cG9ydHMgPSBTRFA7XG5cbiIsImZ1bmN0aW9uIFNEUERpZmZlcihteVNEUCwgb3RoZXJTRFApIHtcbiAgICB0aGlzLm15U0RQID0gbXlTRFA7XG4gICAgdGhpcy5vdGhlclNEUCA9IG90aGVyU0RQO1xufVxuXG4vKipcbiAqIFJldHVybnMgbWFwIG9mIE1lZGlhQ2hhbm5lbCB0aGF0IGNvbnRhaW5zIG9ubHkgbWVkaWEgbm90IGNvbnRhaW5lZCBpbiA8dHQ+b3RoZXJTZHA8L3R0Pi4gTWFwcGVkIGJ5IGNoYW5uZWwgaWR4LlxuICogQHBhcmFtIG90aGVyU2RwIHRoZSBvdGhlciBTRFAgdG8gY2hlY2sgc3NyYyB3aXRoLlxuICovXG5TRFBEaWZmZXIucHJvdG90eXBlLmdldE5ld01lZGlhID0gZnVuY3Rpb24oKSB7XG5cbiAgICAvLyB0aGlzIGNvdWxkIGJlIHVzZWZ1bCBpbiBBcnJheS5wcm90b3R5cGUuXG4gICAgZnVuY3Rpb24gYXJyYXlFcXVhbHMoYXJyYXkpIHtcbiAgICAgICAgLy8gaWYgdGhlIG90aGVyIGFycmF5IGlzIGEgZmFsc3kgdmFsdWUsIHJldHVyblxuICAgICAgICBpZiAoIWFycmF5KVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuXG4gICAgICAgIC8vIGNvbXBhcmUgbGVuZ3RocyAtIGNhbiBzYXZlIGEgbG90IG9mIHRpbWVcbiAgICAgICAgaWYgKHRoaXMubGVuZ3RoICE9IGFycmF5Lmxlbmd0aClcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICBmb3IgKHZhciBpID0gMCwgbD10aGlzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgd2UgaGF2ZSBuZXN0ZWQgYXJyYXlzXG4gICAgICAgICAgICBpZiAodGhpc1tpXSBpbnN0YW5jZW9mIEFycmF5ICYmIGFycmF5W2ldIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICAgICAgICAgICAgICAvLyByZWN1cnNlIGludG8gdGhlIG5lc3RlZCBhcnJheXNcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXNbaV0uZXF1YWxzKGFycmF5W2ldKSlcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAodGhpc1tpXSAhPSBhcnJheVtpXSkge1xuICAgICAgICAgICAgICAgIC8vIFdhcm5pbmcgLSB0d28gZGlmZmVyZW50IG9iamVjdCBpbnN0YW5jZXMgd2lsbCBuZXZlciBiZSBlcXVhbDoge3g6MjB9ICE9IHt4OjIwfVxuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICB2YXIgbXlNZWRpYXMgPSB0aGlzLm15U0RQLmdldE1lZGlhU3NyY01hcCgpO1xuICAgIHZhciBvdGhlcnNNZWRpYXMgPSB0aGlzLm90aGVyU0RQLmdldE1lZGlhU3NyY01hcCgpO1xuICAgIHZhciBuZXdNZWRpYSA9IHt9O1xuICAgIE9iamVjdC5rZXlzKG90aGVyc01lZGlhcykuZm9yRWFjaChmdW5jdGlvbihvdGhlcnNNZWRpYUlkeCkge1xuICAgICAgICB2YXIgbXlNZWRpYSA9IG15TWVkaWFzW290aGVyc01lZGlhSWR4XTtcbiAgICAgICAgdmFyIG90aGVyc01lZGlhID0gb3RoZXJzTWVkaWFzW290aGVyc01lZGlhSWR4XTtcbiAgICAgICAgaWYoIW15TWVkaWEgJiYgb3RoZXJzTWVkaWEpIHtcbiAgICAgICAgICAgIC8vIEFkZCB3aG9sZSBjaGFubmVsXG4gICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0gPSBvdGhlcnNNZWRpYTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAvLyBMb29rIGZvciBuZXcgc3NyY3MgYWNjcm9zcyB0aGUgY2hhbm5lbFxuICAgICAgICBPYmplY3Qua2V5cyhvdGhlcnNNZWRpYS5zc3JjcykuZm9yRWFjaChmdW5jdGlvbihzc3JjKSB7XG4gICAgICAgICAgICBpZihPYmplY3Qua2V5cyhteU1lZGlhLnNzcmNzKS5pbmRleE9mKHNzcmMpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIC8vIEFsbG9jYXRlIGNoYW5uZWwgaWYgd2UndmUgZm91bmQgc3NyYyB0aGF0IGRvZXNuJ3QgZXhpc3QgaW4gb3VyIGNoYW5uZWxcbiAgICAgICAgICAgICAgICBpZighbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdKXtcbiAgICAgICAgICAgICAgICAgICAgbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgbWVkaWFpbmRleDogb3RoZXJzTWVkaWEubWVkaWFpbmRleCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1pZDogb3RoZXJzTWVkaWEubWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgc3NyY3M6IHt9LFxuICAgICAgICAgICAgICAgICAgICAgICAgc3NyY0dyb3VwczogW11cbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdLnNzcmNzW3NzcmNdID0gb3RoZXJzTWVkaWEuc3NyY3Nbc3NyY107XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIExvb2sgZm9yIG5ldyBzc3JjIGdyb3VwcyBhY3Jvc3MgdGhlIGNoYW5uZWxzXG4gICAgICAgIG90aGVyc01lZGlhLnNzcmNHcm91cHMuZm9yRWFjaChmdW5jdGlvbihvdGhlclNzcmNHcm91cCl7XG5cbiAgICAgICAgICAgIC8vIHRyeSB0byBtYXRjaCB0aGUgb3RoZXIgc3NyYy1ncm91cCB3aXRoIGFuIHNzcmMtZ3JvdXAgb2Ygb3Vyc1xuICAgICAgICAgICAgdmFyIG1hdGNoZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbXlNZWRpYS5zc3JjR3JvdXBzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdmFyIG15U3NyY0dyb3VwID0gbXlNZWRpYS5zc3JjR3JvdXBzW2ldO1xuICAgICAgICAgICAgICAgIGlmIChvdGhlclNzcmNHcm91cC5zZW1hbnRpY3MgPT0gbXlTc3JjR3JvdXAuc2VtYW50aWNzXG4gICAgICAgICAgICAgICAgICAgICYmIGFycmF5RXF1YWxzLmFwcGx5KG90aGVyU3NyY0dyb3VwLnNzcmNzLCBbbXlTc3JjR3JvdXAuc3NyY3NdKSkge1xuXG4gICAgICAgICAgICAgICAgICAgIG1hdGNoZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghbWF0Y2hlZCkge1xuICAgICAgICAgICAgICAgIC8vIEFsbG9jYXRlIGNoYW5uZWwgaWYgd2UndmUgZm91bmQgYW4gc3NyYy1ncm91cCB0aGF0IGRvZXNuJ3RcbiAgICAgICAgICAgICAgICAvLyBleGlzdCBpbiBvdXIgY2hhbm5lbFxuXG4gICAgICAgICAgICAgICAgaWYoIW5ld01lZGlhW290aGVyc01lZGlhSWR4XSl7XG4gICAgICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhaW5kZXg6IG90aGVyc01lZGlhLm1lZGlhaW5kZXgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtaWQ6IG90aGVyc01lZGlhLm1pZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XS5zc3JjR3JvdXBzLnB1c2gob3RoZXJTc3JjR3JvdXApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9KTtcbiAgICByZXR1cm4gbmV3TWVkaWE7XG59O1xuXG4vKipcbiAqIFNlbmRzIFNTUkMgdXBkYXRlIElRLlxuICogQHBhcmFtIHNkcE1lZGlhU3NyY3MgU1NSQ3MgbWFwIG9idGFpbmVkIGZyb20gU0RQLmdldE5ld01lZGlhLiBDbnRhaW5zIFNTUkNzIHRvIGFkZC9yZW1vdmUuXG4gKiBAcGFyYW0gc2lkIHNlc3Npb24gaWRlbnRpZmllciB0aGF0IHdpbGwgYmUgcHV0IGludG8gdGhlIElRLlxuICogQHBhcmFtIGluaXRpYXRvciBpbml0aWF0b3IgaWRlbnRpZmllci5cbiAqIEBwYXJhbSB0b0ppZCBkZXN0aW5hdGlvbiBKaWRcbiAqIEBwYXJhbSBpc0FkZCBpbmRpY2F0ZXMgaWYgdGhpcyBpcyByZW1vdmUgb3IgYWRkIG9wZXJhdGlvbi5cbiAqL1xuU0RQRGlmZmVyLnByb3RvdHlwZS50b0ppbmdsZSA9IGZ1bmN0aW9uKG1vZGlmeSkge1xuICAgIHZhciBzZHBNZWRpYVNzcmNzID0gdGhpcy5nZXROZXdNZWRpYSgpO1xuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIC8vIEZJWE1FOiBvbmx5IGFubm91bmNlIHZpZGVvIHNzcmNzIHNpbmNlIHdlIG1peCBhdWRpbyBhbmQgZG9udCBuZWVkXG4gICAgLy8gICAgICB0aGUgYXVkaW8gc3NyY3MgdGhlcmVmb3JlXG4gICAgdmFyIG1vZGlmaWVkID0gZmFsc2U7XG4gICAgT2JqZWN0LmtleXMoc2RwTWVkaWFTc3JjcykuZm9yRWFjaChmdW5jdGlvbihtZWRpYWluZGV4KXtcbiAgICAgICAgbW9kaWZpZWQgPSB0cnVlO1xuICAgICAgICB2YXIgbWVkaWEgPSBzZHBNZWRpYVNzcmNzW21lZGlhaW5kZXhdO1xuICAgICAgICBtb2RpZnkuYygnY29udGVudCcsIHtuYW1lOiBtZWRpYS5taWR9KTtcblxuICAgICAgICBtb2RpZnkuYygnZGVzY3JpcHRpb24nLCB7eG1sbnM6J3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDoxJywgbWVkaWE6IG1lZGlhLm1pZH0pO1xuICAgICAgICAvLyBGSVhNRTogbm90IGNvbXBsZXRseSBzdXJlIHRoaXMgb3BlcmF0ZXMgb24gYmxvY2tzIGFuZCAvIG9yIGhhbmRsZXMgZGlmZmVyZW50IHNzcmNzIGNvcnJlY3RseVxuICAgICAgICAvLyBnZW5lcmF0ZSBzb3VyY2VzIGZyb20gbGluZXNcbiAgICAgICAgT2JqZWN0LmtleXMobWVkaWEuc3NyY3MpLmZvckVhY2goZnVuY3Rpb24oc3NyY051bSkge1xuICAgICAgICAgICAgdmFyIG1lZGlhU3NyYyA9IG1lZGlhLnNzcmNzW3NzcmNOdW1dO1xuICAgICAgICAgICAgbW9kaWZ5LmMoJ3NvdXJjZScsIHsgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgIG1vZGlmeS5hdHRycyh7c3NyYzogbWVkaWFTc3JjLnNzcmN9KTtcbiAgICAgICAgICAgIC8vIGl0ZXJhdGUgb3ZlciBzc3JjIGxpbmVzXG4gICAgICAgICAgICBtZWRpYVNzcmMubGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgICAgIHZhciBpZHggPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgICAgICB2YXIga3YgPSBsaW5lLnN1YnN0cihpZHggKyAxKTtcbiAgICAgICAgICAgICAgICBtb2RpZnkuYygncGFyYW1ldGVyJyk7XG4gICAgICAgICAgICAgICAgaWYgKGt2LmluZGV4T2YoJzonKSA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYXR0cnMoeyBuYW1lOiBrdiB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYXR0cnMoeyBuYW1lOiBrdi5zcGxpdCgnOicsIDIpWzBdIH0pO1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYXR0cnMoeyB2YWx1ZToga3Yuc3BsaXQoJzonLCAyKVsxXSB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgbW9kaWZ5LnVwKCk7IC8vIGVuZCBvZiBwYXJhbWV0ZXJcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgbW9kaWZ5LnVwKCk7IC8vIGVuZCBvZiBzb3VyY2VcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gZ2VuZXJhdGUgc291cmNlIGdyb3VwcyBmcm9tIGxpbmVzXG4gICAgICAgIG1lZGlhLnNzcmNHcm91cHMuZm9yRWFjaChmdW5jdGlvbihzc3JjR3JvdXApIHtcbiAgICAgICAgICAgIGlmIChzc3JjR3JvdXAuc3NyY3MubGVuZ3RoICE9IDApIHtcblxuICAgICAgICAgICAgICAgIG1vZGlmeS5jKCdzc3JjLWdyb3VwJywge1xuICAgICAgICAgICAgICAgICAgICBzZW1hbnRpY3M6IHNzcmNHcm91cC5zZW1hbnRpY3MsXG4gICAgICAgICAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCdcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHNzcmNHcm91cC5zc3Jjcy5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgICAgIG1vZGlmeS5jKCdzb3VyY2UnLCB7IHNzcmM6IHNzcmMgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC51cCgpOyAvLyBlbmQgb2Ygc291cmNlXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgbW9kaWZ5LnVwKCk7IC8vIGVuZCBvZiBzc3JjLWdyb3VwXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgZGVzY3JpcHRpb25cbiAgICAgICAgbW9kaWZ5LnVwKCk7IC8vIGVuZCBvZiBjb250ZW50XG4gICAgfSk7XG5cbiAgICByZXR1cm4gbW9kaWZpZWQ7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNEUERpZmZlcjsiLCJTRFBVdGlsID0ge1xuICAgIGljZXBhcmFtczogZnVuY3Rpb24gKG1lZGlhZGVzYywgc2Vzc2lvbmRlc2MpIHtcbiAgICAgICAgdmFyIGRhdGEgPSBudWxsO1xuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtdWZyYWc6Jywgc2Vzc2lvbmRlc2MpICYmXG4gICAgICAgICAgICBTRFBVdGlsLmZpbmRfbGluZShtZWRpYWRlc2MsICdhPWljZS1wd2Q6Jywgc2Vzc2lvbmRlc2MpKSB7XG4gICAgICAgICAgICBkYXRhID0ge1xuICAgICAgICAgICAgICAgIHVmcmFnOiBTRFBVdGlsLnBhcnNlX2ljZXVmcmFnKFNEUFV0aWwuZmluZF9saW5lKG1lZGlhZGVzYywgJ2E9aWNlLXVmcmFnOicsIHNlc3Npb25kZXNjKSksXG4gICAgICAgICAgICAgICAgcHdkOiBTRFBVdGlsLnBhcnNlX2ljZXB3ZChTRFBVdGlsLmZpbmRfbGluZShtZWRpYWRlc2MsICdhPWljZS1wd2Q6Jywgc2Vzc2lvbmRlc2MpKVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2ljZXVmcmFnOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICByZXR1cm4gbGluZS5zdWJzdHJpbmcoMTIpO1xuICAgIH0sXG4gICAgYnVpbGRfaWNldWZyYWc6IGZ1bmN0aW9uIChmcmFnKSB7XG4gICAgICAgIHJldHVybiAnYT1pY2UtdWZyYWc6JyArIGZyYWc7XG4gICAgfSxcbiAgICBwYXJzZV9pY2Vwd2Q6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHJldHVybiBsaW5lLnN1YnN0cmluZygxMCk7XG4gICAgfSxcbiAgICBidWlsZF9pY2Vwd2Q6IGZ1bmN0aW9uIChwd2QpIHtcbiAgICAgICAgcmV0dXJuICdhPWljZS1wd2Q6JyArIHB3ZDtcbiAgICB9LFxuICAgIHBhcnNlX21pZDogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgcmV0dXJuIGxpbmUuc3Vic3RyaW5nKDYpO1xuICAgIH0sXG4gICAgcGFyc2VfbWxpbmU6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDIpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEubWVkaWEgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnBvcnQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnByb3RvID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgaWYgKHBhcnRzW3BhcnRzLmxlbmd0aCAtIDFdID09PSAnJykgeyAvLyB0cmFpbGluZyB3aGl0ZXNwYWNlXG4gICAgICAgICAgICBwYXJ0cy5wb3AoKTtcbiAgICAgICAgfVxuICAgICAgICBkYXRhLmZtdCA9IHBhcnRzO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIGJ1aWxkX21saW5lOiBmdW5jdGlvbiAobWxpbmUpIHtcbiAgICAgICAgcmV0dXJuICdtPScgKyBtbGluZS5tZWRpYSArICcgJyArIG1saW5lLnBvcnQgKyAnICcgKyBtbGluZS5wcm90byArICcgJyArIG1saW5lLmZtdC5qb2luKCcgJyk7XG4gICAgfSxcbiAgICBwYXJzZV9ydHBtYXA6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEuaWQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBwYXJ0cyA9IHBhcnRzWzBdLnNwbGl0KCcvJyk7XG4gICAgICAgIGRhdGEubmFtZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEuY2xvY2tyYXRlID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5jaGFubmVscyA9IHBhcnRzLmxlbmd0aCA/IHBhcnRzLnNoaWZ0KCkgOiAnMSc7XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogUGFyc2VzIFNEUCBsaW5lIFwiYT1zY3RwbWFwOi4uLlwiIGFuZCBleHRyYWN0cyBTQ1RQIHBvcnQgZnJvbSBpdC5cbiAgICAgKiBAcGFyYW0gbGluZSBlZy4gXCJhPXNjdHBtYXA6NTAwMCB3ZWJydGMtZGF0YWNoYW5uZWxcIlxuICAgICAqIEByZXR1cm5zIFtTQ1RQIHBvcnQgbnVtYmVyLCBwcm90b2NvbCwgc3RyZWFtc11cbiAgICAgKi9cbiAgICBwYXJzZV9zY3RwbWFwOiBmdW5jdGlvbiAobGluZSlcbiAgICB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEwKS5zcGxpdCgnICcpO1xuICAgICAgICB2YXIgc2N0cFBvcnQgPSBwYXJ0c1swXTtcbiAgICAgICAgdmFyIHByb3RvY29sID0gcGFydHNbMV07XG4gICAgICAgIC8vIFN0cmVhbSBjb3VudCBpcyBvcHRpb25hbFxuICAgICAgICB2YXIgc3RyZWFtQ291bnQgPSBwYXJ0cy5sZW5ndGggPiAyID8gcGFydHNbMl0gOiBudWxsO1xuICAgICAgICByZXR1cm4gW3NjdHBQb3J0LCBwcm90b2NvbCwgc3RyZWFtQ291bnRdOy8vIFNDVFAgcG9ydFxuICAgIH0sXG4gICAgYnVpbGRfcnRwbWFwOiBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgdmFyIGxpbmUgPSAnYT1ydHBtYXA6JyArIGVsLmdldEF0dHJpYnV0ZSgnaWQnKSArICcgJyArIGVsLmdldEF0dHJpYnV0ZSgnbmFtZScpICsgJy8nICsgZWwuZ2V0QXR0cmlidXRlKCdjbG9ja3JhdGUnKTtcbiAgICAgICAgaWYgKGVsLmdldEF0dHJpYnV0ZSgnY2hhbm5lbHMnKSAmJiBlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJykgIT0gJzEnKSB7XG4gICAgICAgICAgICBsaW5lICs9ICcvJyArIGVsLmdldEF0dHJpYnV0ZSgnY2hhbm5lbHMnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbGluZTtcbiAgICB9LFxuICAgIHBhcnNlX2NyeXB0bzogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoOSkuc3BsaXQoJyAnKSxcbiAgICAgICAgICAgIGRhdGEgPSB7fTtcbiAgICAgICAgZGF0YS50YWcgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhWydjcnlwdG8tc3VpdGUnXSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGFbJ2tleS1wYXJhbXMnXSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChwYXJ0cy5sZW5ndGgpIHtcbiAgICAgICAgICAgIGRhdGFbJ3Nlc3Npb24tcGFyYW1zJ10gPSBwYXJ0cy5qb2luKCcgJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBwYXJzZV9maW5nZXJwcmludDogZnVuY3Rpb24gKGxpbmUpIHsgLy8gUkZDIDQ1NzJcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTQpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEuaGFzaCA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEuZmluZ2VycHJpbnQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICAvLyBUT0RPIGFzc2VydCB0aGF0IGZpbmdlcnByaW50IHNhdGlzZmllcyAyVUhFWCAqKFwiOlwiIDJVSEVYKSA/XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfZm10cDogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgaSwga2V5LCB2YWx1ZSxcbiAgICAgICAgICAgIGRhdGEgPSBbXTtcbiAgICAgICAgcGFydHMuc2hpZnQoKTtcbiAgICAgICAgcGFydHMgPSBwYXJ0cy5qb2luKCcgJykuc3BsaXQoJzsnKTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBrZXkgPSBwYXJ0c1tpXS5zcGxpdCgnPScpWzBdO1xuICAgICAgICAgICAgd2hpbGUgKGtleS5sZW5ndGggJiYga2V5WzBdID09ICcgJykge1xuICAgICAgICAgICAgICAgIGtleSA9IGtleS5zdWJzdHJpbmcoMSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YWx1ZSA9IHBhcnRzW2ldLnNwbGl0KCc9JylbMV07XG4gICAgICAgICAgICBpZiAoa2V5ICYmIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgZGF0YS5wdXNoKHtuYW1lOiBrZXksIHZhbHVlOiB2YWx1ZX0pO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChrZXkpIHtcbiAgICAgICAgICAgICAgICAvLyByZmMgNDczMyAoRFRNRikgc3R5bGUgc3R1ZmZcbiAgICAgICAgICAgICAgICBkYXRhLnB1c2goe25hbWU6ICcnLCB2YWx1ZToga2V5fSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBwYXJzZV9pY2VjYW5kaWRhdGU6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBjYW5kaWRhdGUgPSB7fSxcbiAgICAgICAgICAgIGVsZW1zID0gbGluZS5zcGxpdCgnICcpO1xuICAgICAgICBjYW5kaWRhdGUuZm91bmRhdGlvbiA9IGVsZW1zWzBdLnN1YnN0cmluZygxMik7XG4gICAgICAgIGNhbmRpZGF0ZS5jb21wb25lbnQgPSBlbGVtc1sxXTtcbiAgICAgICAgY2FuZGlkYXRlLnByb3RvY29sID0gZWxlbXNbMl0udG9Mb3dlckNhc2UoKTtcbiAgICAgICAgY2FuZGlkYXRlLnByaW9yaXR5ID0gZWxlbXNbM107XG4gICAgICAgIGNhbmRpZGF0ZS5pcCA9IGVsZW1zWzRdO1xuICAgICAgICBjYW5kaWRhdGUucG9ydCA9IGVsZW1zWzVdO1xuICAgICAgICAvLyBlbGVtc1s2XSA9PiBcInR5cFwiXG4gICAgICAgIGNhbmRpZGF0ZS50eXBlID0gZWxlbXNbN107XG4gICAgICAgIGNhbmRpZGF0ZS5nZW5lcmF0aW9uID0gMDsgLy8gZGVmYXVsdCB2YWx1ZSwgbWF5IGJlIG92ZXJ3cml0dGVuIGJlbG93XG4gICAgICAgIGZvciAodmFyIGkgPSA4OyBpIDwgZWxlbXMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgICAgICAgIHN3aXRjaCAoZWxlbXNbaV0pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdyYWRkcic6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZVsncmVsLWFkZHInXSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncnBvcnQnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1wb3J0J10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2dlbmVyYXRpb24nOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZS50Y3B0eXBlID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBUT0RPXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdwYXJzZV9pY2VjYW5kaWRhdGUgbm90IHRyYW5zbGF0aW5nIFwiJyArIGVsZW1zW2ldICsgJ1wiID0gXCInICsgZWxlbXNbaSArIDFdICsgJ1wiJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY2FuZGlkYXRlLm5ldHdvcmsgPSAnMSc7XG4gICAgICAgIGNhbmRpZGF0ZS5pZCA9IE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCAxMCk7IC8vIG5vdCBhcHBsaWNhYmxlIHRvIFNEUCAtLSBGSVhNRTogc2hvdWxkIGJlIHVuaXF1ZSwgbm90IGp1c3QgcmFuZG9tXG4gICAgICAgIHJldHVybiBjYW5kaWRhdGU7XG4gICAgfSxcbiAgICBidWlsZF9pY2VjYW5kaWRhdGU6IGZ1bmN0aW9uIChjYW5kKSB7XG4gICAgICAgIHZhciBsaW5lID0gWydhPWNhbmRpZGF0ZTonICsgY2FuZC5mb3VuZGF0aW9uLCBjYW5kLmNvbXBvbmVudCwgY2FuZC5wcm90b2NvbCwgY2FuZC5wcmlvcml0eSwgY2FuZC5pcCwgY2FuZC5wb3J0LCAndHlwJywgY2FuZC50eXBlXS5qb2luKCcgJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBzd2l0Y2ggKGNhbmQudHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnc3JmbHgnOlxuICAgICAgICAgICAgY2FzZSAncHJmbHgnOlxuICAgICAgICAgICAgY2FzZSAncmVsYXknOlxuICAgICAgICAgICAgICAgIGlmIChjYW5kLmhhc093bkF0dHJpYnV0ZSgncmVsLWFkZHInKSAmJiBjYW5kLmhhc093bkF0dHJpYnV0ZSgncmVsLXBvcnQnKSkge1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICdyYWRkcic7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9IGNhbmRbJ3JlbC1hZGRyJ107XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICdycG9ydCc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9IGNhbmRbJ3JlbC1wb3J0J107XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3RjcHR5cGUnKSkge1xuICAgICAgICAgICAgbGluZSArPSAndGNwdHlwZSc7XG4gICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgIGxpbmUgKz0gY2FuZC50Y3B0eXBlO1xuICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIH1cbiAgICAgICAgbGluZSArPSAnZ2VuZXJhdGlvbic7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuaGFzT3duQXR0cmlidXRlKCdnZW5lcmF0aW9uJykgPyBjYW5kLmdlbmVyYXRpb24gOiAnMCc7XG4gICAgICAgIHJldHVybiBsaW5lO1xuICAgIH0sXG4gICAgcGFyc2Vfc3NyYzogZnVuY3Rpb24gKGRlc2MpIHtcbiAgICAgICAgLy8gcHJvcHJpZXRhcnkgbWFwcGluZyBvZiBhPXNzcmMgbGluZXNcbiAgICAgICAgLy8gVE9ETzogc2VlIFwiSmluZ2xlIFJUUCBTb3VyY2UgRGVzY3JpcHRpb25cIiBieSBKdWJlcnRpIGFuZCBQLiBUaGF0Y2hlciBvbiBnb29nbGUgZG9jc1xuICAgICAgICAvLyBhbmQgcGFyc2UgYWNjb3JkaW5nIHRvIHRoYXRcbiAgICAgICAgdmFyIGxpbmVzID0gZGVzYy5zcGxpdCgnXFxyXFxuJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgNykgPT0gJ2E9c3NyYzonKSB7XG4gICAgICAgICAgICAgICAgdmFyIGlkeCA9IGxpbmVzW2ldLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgICAgICBkYXRhW2xpbmVzW2ldLnN1YnN0cihpZHggKyAxKS5zcGxpdCgnOicsIDIpWzBdXSA9IGxpbmVzW2ldLnN1YnN0cihpZHggKyAxKS5zcGxpdCgnOicsIDIpWzFdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfcnRjcGZiOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cigxMCkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIGRhdGEgPSB7fTtcbiAgICAgICAgZGF0YS5wdCA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEudHlwZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEucGFyYW1zID0gcGFydHM7XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfZXh0bWFwOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cig5KS5zcGxpdCgnICcpO1xuICAgICAgICB2YXIgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLnZhbHVlID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgaWYgKGRhdGEudmFsdWUuaW5kZXhPZignLycpICE9IC0xKSB7XG4gICAgICAgICAgICBkYXRhLmRpcmVjdGlvbiA9IGRhdGEudmFsdWUuc3Vic3RyKGRhdGEudmFsdWUuaW5kZXhPZignLycpICsgMSk7XG4gICAgICAgICAgICBkYXRhLnZhbHVlID0gZGF0YS52YWx1ZS5zdWJzdHIoMCwgZGF0YS52YWx1ZS5pbmRleE9mKCcvJykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGF0YS5kaXJlY3Rpb24gPSAnYm90aCc7XG4gICAgICAgIH1cbiAgICAgICAgZGF0YS51cmkgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnBhcmFtcyA9IHBhcnRzO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIGZpbmRfbGluZTogZnVuY3Rpb24gKGhheXN0YWNrLCBuZWVkbGUsIHNlc3Npb25wYXJ0KSB7XG4gICAgICAgIHZhciBsaW5lcyA9IGhheXN0YWNrLnNwbGl0KCdcXHJcXG4nKTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKGxpbmVzW2ldLnN1YnN0cmluZygwLCBuZWVkbGUubGVuZ3RoKSA9PSBuZWVkbGUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbGluZXNbaV07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFzZXNzaW9ucGFydCkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIC8vIHNlYXJjaCBzZXNzaW9uIHBhcnRcbiAgICAgICAgbGluZXMgPSBzZXNzaW9ucGFydC5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tqXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGxpbmVzW2pdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9LFxuICAgIGZpbmRfbGluZXM6IGZ1bmN0aW9uIChoYXlzdGFjaywgbmVlZGxlLCBzZXNzaW9ucGFydCkge1xuICAgICAgICB2YXIgbGluZXMgPSBoYXlzdGFjay5zcGxpdCgnXFxyXFxuJyksXG4gICAgICAgICAgICBuZWVkbGVzID0gW107XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKVxuICAgICAgICAgICAgICAgIG5lZWRsZXMucHVzaChsaW5lc1tpXSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG5lZWRsZXMubGVuZ3RoIHx8ICFzZXNzaW9ucGFydCkge1xuICAgICAgICAgICAgcmV0dXJuIG5lZWRsZXM7XG4gICAgICAgIH1cbiAgICAgICAgLy8gc2VhcmNoIHNlc3Npb24gcGFydFxuICAgICAgICBsaW5lcyA9IHNlc3Npb25wYXJ0LnNwbGl0KCdcXHJcXG4nKTtcbiAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBsaW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgaWYgKGxpbmVzW2pdLnN1YnN0cmluZygwLCBuZWVkbGUubGVuZ3RoKSA9PSBuZWVkbGUpIHtcbiAgICAgICAgICAgICAgICBuZWVkbGVzLnB1c2gobGluZXNbal0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZWVkbGVzO1xuICAgIH0sXG4gICAgY2FuZGlkYXRlVG9KaW5nbGU6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIC8vIGE9Y2FuZGlkYXRlOjI5NzkxNjY2NjIgMSB1ZHAgMjExMzkzNzE1MSAxOTIuMTY4LjIuMTAwIDU3Njk4IHR5cCBob3N0IGdlbmVyYXRpb24gMFxuICAgICAgICAvLyAgICAgIDxjYW5kaWRhdGUgY29tcG9uZW50PS4uLiBmb3VuZGF0aW9uPS4uLiBnZW5lcmF0aW9uPS4uLiBpZD0uLi4gaXA9Li4uIG5ldHdvcms9Li4uIHBvcnQ9Li4uIHByaW9yaXR5PS4uLiBwcm90b2NvbD0uLi4gdHlwZT0uLi4vPlxuICAgICAgICBpZiAobGluZS5pbmRleE9mKCdjYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICAgICAgICAgIGxpbmUgPSAnYT0nICsgbGluZTtcbiAgICAgICAgfSBlbHNlIGlmIChsaW5lLnN1YnN0cmluZygwLCAxMikgIT0gJ2E9Y2FuZGlkYXRlOicpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdwYXJzZUNhbmRpZGF0ZSBjYWxsZWQgd2l0aCBhIGxpbmUgdGhhdCBpcyBub3QgYSBjYW5kaWRhdGUgbGluZScpO1xuICAgICAgICAgICAgY29uc29sZS5sb2cobGluZSk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBpZiAobGluZS5zdWJzdHJpbmcobGluZS5sZW5ndGggLSAyKSA9PSAnXFxyXFxuJykgLy8gY2hvbXAgaXRcbiAgICAgICAgICAgIGxpbmUgPSBsaW5lLnN1YnN0cmluZygwLCBsaW5lLmxlbmd0aCAtIDIpO1xuICAgICAgICB2YXIgY2FuZGlkYXRlID0ge30sXG4gICAgICAgICAgICBlbGVtcyA9IGxpbmUuc3BsaXQoJyAnKSxcbiAgICAgICAgICAgIGk7XG4gICAgICAgIGlmIChlbGVtc1s2XSAhPSAndHlwJykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ2RpZCBub3QgZmluZCB0eXAgaW4gdGhlIHJpZ2h0IHBsYWNlJyk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhsaW5lKTtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZS5mb3VuZGF0aW9uID0gZWxlbXNbMF0uc3Vic3RyaW5nKDEyKTtcbiAgICAgICAgY2FuZGlkYXRlLmNvbXBvbmVudCA9IGVsZW1zWzFdO1xuICAgICAgICBjYW5kaWRhdGUucHJvdG9jb2wgPSBlbGVtc1syXS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICBjYW5kaWRhdGUucHJpb3JpdHkgPSBlbGVtc1szXTtcbiAgICAgICAgY2FuZGlkYXRlLmlwID0gZWxlbXNbNF07XG4gICAgICAgIGNhbmRpZGF0ZS5wb3J0ID0gZWxlbXNbNV07XG4gICAgICAgIC8vIGVsZW1zWzZdID0+IFwidHlwXCJcbiAgICAgICAgY2FuZGlkYXRlLnR5cGUgPSBlbGVtc1s3XTtcblxuICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9ICcwJzsgLy8gZGVmYXVsdCwgbWF5IGJlIG92ZXJ3cml0dGVuIGJlbG93XG4gICAgICAgIGZvciAoaSA9IDg7IGkgPCBlbGVtcy5sZW5ndGg7IGkgKz0gMikge1xuICAgICAgICAgICAgc3dpdGNoIChlbGVtc1tpXSkge1xuICAgICAgICAgICAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlWydyZWwtYWRkciddID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdycG9ydCc6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZVsncmVsLXBvcnQnXSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnZ2VuZXJhdGlvbic6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZS5nZW5lcmF0aW9uID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICd0Y3B0eXBlJzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlLnRjcHR5cGUgPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6IC8vIFRPRE9cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ25vdCB0cmFuc2xhdGluZyBcIicgKyBlbGVtc1tpXSArICdcIiA9IFwiJyArIGVsZW1zW2kgKyAxXSArICdcIicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZS5uZXR3b3JrID0gJzEnO1xuICAgICAgICBjYW5kaWRhdGUuaWQgPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApOyAvLyBub3QgYXBwbGljYWJsZSB0byBTRFAgLS0gRklYTUU6IHNob3VsZCBiZSB1bmlxdWUsIG5vdCBqdXN0IHJhbmRvbVxuICAgICAgICByZXR1cm4gY2FuZGlkYXRlO1xuICAgIH0sXG4gICAgY2FuZGlkYXRlRnJvbUppbmdsZTogZnVuY3Rpb24gKGNhbmQpIHtcbiAgICAgICAgdmFyIGxpbmUgPSAnYT1jYW5kaWRhdGU6JztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnZm91bmRhdGlvbicpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnY29tcG9uZW50Jyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdwcm90b2NvbCcpOyAvLy50b1VwcGVyQ2FzZSgpOyAvLyBjaHJvbWUgTTIzIGRvZXNuJ3QgbGlrZSB0aGlzXG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdwcmlvcml0eScpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnaXAnKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3BvcnQnKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gJ3R5cCc7XG4gICAgICAgIGxpbmUgKz0gJyAnICsgY2FuZC5nZXRBdHRyaWJ1dGUoJ3R5cGUnKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIHN3aXRjaCAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSkge1xuICAgICAgICAgICAgY2FzZSAnc3JmbHgnOlxuICAgICAgICAgICAgY2FzZSAncHJmbHgnOlxuICAgICAgICAgICAgY2FzZSAncmVsYXknOlxuICAgICAgICAgICAgICAgIGlmIChjYW5kLmdldEF0dHJpYnV0ZSgncmVsLWFkZHInKSAmJiBjYW5kLmdldEF0dHJpYnV0ZSgncmVsLXBvcnQnKSkge1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICdyYWRkcic7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdyZWwtYWRkcicpO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncnBvcnQnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncmVsLXBvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjYW5kLmdldEF0dHJpYnV0ZSgncHJvdG9jb2wnKS50b0xvd2VyQ2FzZSgpID09ICd0Y3AnKSB7XG4gICAgICAgICAgICBsaW5lICs9ICd0Y3B0eXBlJztcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgndGNwdHlwZScpO1xuICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIH1cbiAgICAgICAgbGluZSArPSAnZ2VuZXJhdGlvbic7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdnZW5lcmF0aW9uJykgfHwgJzAnO1xuICAgICAgICByZXR1cm4gbGluZSArICdcXHJcXG4nO1xuICAgIH1cbn07XG5tb2R1bGUuZXhwb3J0cyA9IFNEUFV0aWw7IiwiZnVuY3Rpb24gVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24oaWNlX2NvbmZpZywgY29uc3RyYWludHMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIFJUQ1BlZXJjb25uZWN0aW9uID0gbmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSA/IG1velJUQ1BlZXJDb25uZWN0aW9uIDogd2Via2l0UlRDUGVlckNvbm5lY3Rpb247XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbiA9IG5ldyBSVENQZWVyY29ubmVjdGlvbihpY2VfY29uZmlnLCBjb25zdHJhaW50cyk7XG4gICAgdGhpcy51cGRhdGVMb2cgPSBbXTtcbiAgICB0aGlzLnN0YXRzID0ge307XG4gICAgdGhpcy5zdGF0c2ludGVydmFsID0gbnVsbDtcbiAgICB0aGlzLm1heHN0YXRzID0gMDsgLy8gbGltaXQgdG8gMzAwIHZhbHVlcywgaS5lLiA1IG1pbnV0ZXM7IHNldCB0byAwIHRvIGRpc2FibGVcblxuICAgIC8vIG92ZXJyaWRlIGFzIGRlc2lyZWRcbiAgICB0aGlzLnRyYWNlID0gZnVuY3Rpb24gKHdoYXQsIGluZm8pIHtcbiAgICAgICAgLy9jb25zb2xlLndhcm4oJ1dUUkFDRScsIHdoYXQsIGluZm8pO1xuICAgICAgICBzZWxmLnVwZGF0ZUxvZy5wdXNoKHtcbiAgICAgICAgICAgIHRpbWU6IG5ldyBEYXRlKCksXG4gICAgICAgICAgICB0eXBlOiB3aGF0LFxuICAgICAgICAgICAgdmFsdWU6IGluZm8gfHwgXCJcIlxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2VjYW5kaWRhdGUgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25pY2VjYW5kaWRhdGUnLCBKU09OLnN0cmluZ2lmeShldmVudC5jYW5kaWRhdGUsIG51bGwsICcgJykpO1xuICAgICAgICBpZiAoc2VsZi5vbmljZWNhbmRpZGF0ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25hZGRzdHJlYW0gPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25hZGRzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25hZGRzdHJlYW0nLCBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBpZiAoc2VsZi5vbmFkZHN0cmVhbSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbmFkZHN0cmVhbShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25yZW1vdmVzdHJlYW0gPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25yZW1vdmVzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25yZW1vdmVzdHJlYW0nLCBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBpZiAoc2VsZi5vbnJlbW92ZXN0cmVhbSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbnJlbW92ZXN0cmVhbShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uc2lnbmFsaW5nc3RhdGVjaGFuZ2UnLCBzZWxmLnNpZ25hbGluZ1N0YXRlKTtcbiAgICAgICAgaWYgKHNlbGYub25zaWduYWxpbmdzdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgc2VsZi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZScsIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICAgICAgaWYgKHNlbGYub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25uZWdvdGlhdGlvbm5lZWRlZCA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbm5lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgIGlmIChzZWxmLm9ubmVnb3RpYXRpb25uZWVkZWQgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25uZWdvdGlhdGlvbm5lZWRlZChldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHNlbGYub25kYXRhY2hhbm5lbCA9IG51bGw7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmRhdGFjaGFubmVsID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uZGF0YWNoYW5uZWwnLCBldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uZGF0YWNoYW5uZWwgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25kYXRhY2hhbm5lbChldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIGlmICghbmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSAmJiB0aGlzLm1heHN0YXRzKSB7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IHdpbmRvdy5zZXRJbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24oc3RhdHMpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVzdWx0cyA9IHN0YXRzLnJlc3VsdCgpO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzdWx0cy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKHJlc3VsdHNbaV0udHlwZSwgcmVzdWx0c1tpXS5pZCwgcmVzdWx0c1tpXS5uYW1lcygpKVxuICAgICAgICAgICAgICAgICAgICB2YXIgbm93ID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0c1tpXS5uYW1lcygpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpZCA9IHJlc3VsdHNbaV0uaWQgKyAnLScgKyBuYW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFzZWxmLnN0YXRzW2lkXSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydFRpbWU6IG5vdyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kVGltZTogbm93LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM6IFtdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lczogW11cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udmFsdWVzLnB1c2gocmVzdWx0c1tpXS5zdGF0KG5hbWUpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLnRpbWVzLnB1c2gobm93LmdldFRpbWUoKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5zdGF0c1tpZF0udmFsdWVzLmxlbmd0aCA+IHNlbGYubWF4c3RhdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS52YWx1ZXMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS50aW1lcy5zaGlmdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0uZW5kVGltZSA9IG5vdztcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgfSwgMTAwMCk7XG4gICAgfVxufTtcblxuZHVtcFNEUCA9IGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgcmV0dXJuICd0eXBlOiAnICsgZGVzY3JpcHRpb24udHlwZSArICdcXHJcXG4nICsgZGVzY3JpcHRpb24uc2RwO1xufVxuXG5pZiAoVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9fZGVmaW5lR2V0dGVyX18gIT09IHVuZGVmaW5lZCkge1xuICAgIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKCdzaWduYWxpbmdTdGF0ZScsIGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZTsgfSk7XG4gICAgVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9fZGVmaW5lR2V0dGVyX18oJ2ljZUNvbm5lY3Rpb25TdGF0ZScsIGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGU7IH0pO1xuICAgIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKCdsb2NhbERlc2NyaXB0aW9uJywgZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBwdWJsaWNMb2NhbERlc2NyaXB0aW9uID0gQVBQLnNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbih0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pO1xuICAgICAgICByZXR1cm4gcHVibGljTG9jYWxEZXNjcmlwdGlvbjtcbiAgICB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygncmVtb3RlRGVzY3JpcHRpb24nLCBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHB1YmxpY1JlbW90ZURlc2NyaXB0aW9uID0gQVBQLnNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24odGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbik7XG4gICAgICAgIHJldHVybiBwdWJsaWNSZW1vdGVEZXNjcmlwdGlvbjtcbiAgICB9KTtcbn1cblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFN0cmVhbSA9IGZ1bmN0aW9uIChzdHJlYW0pIHtcbiAgICB0aGlzLnRyYWNlKCdhZGRTdHJlYW0nLCBzdHJlYW0uaWQpO1xuICAgIEFQUC5zaW11bGNhc3QucmVzZXRTZW5kZXIoKTtcbiAgICB0cnlcbiAgICB7XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKHN0cmVhbSk7XG4gICAgfVxuICAgIGNhdGNoIChlKVxuICAgIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5yZW1vdmVTdHJlYW0gPSBmdW5jdGlvbiAoc3RyZWFtLCBzdG9wU3RyZWFtcykge1xuICAgIHRoaXMudHJhY2UoJ3JlbW92ZVN0cmVhbScsIHN0cmVhbS5pZCk7XG4gICAgQVBQLnNpbXVsY2FzdC5yZXNldFNlbmRlcigpO1xuICAgIGlmKHN0b3BTdHJlYW1zKSB7XG4gICAgICAgIHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmZvckVhY2goZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICAgICAgICB0cmFjay5zdG9wKCk7XG4gICAgICAgIH0pO1xuICAgICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAgICAgdHJhY2suc3RvcCgpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oc3RyZWFtKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVEYXRhQ2hhbm5lbCA9IGZ1bmN0aW9uIChsYWJlbCwgb3B0cykge1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZURhdGFDaGFubmVsJywgbGFiZWwsIG9wdHMpO1xuICAgIHJldHVybiB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZURhdGFDaGFubmVsKGxhYmVsLCBvcHRzKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2NyaXB0aW9uLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBkZXNjcmlwdGlvbiA9IEFQUC5zaW11bGNhc3QudHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihkZXNjcmlwdGlvbik7XG4gICAgdGhpcy50cmFjZSgnc2V0TG9jYWxEZXNjcmlwdGlvbicsIGR1bXBTRFAoZGVzY3JpcHRpb24pKTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24oZGVzY3JpcHRpb24sXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldExvY2FsRGVzY3JpcHRpb25PblN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdzZXRMb2NhbERlc2NyaXB0aW9uT25GYWlsdXJlJywgZXJyKTtcbiAgICAgICAgICAgIGZhaWx1cmVDYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG4gICAgKTtcbiAgICAvKlxuICAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsID09PSBudWxsICYmIHRoaXMubWF4c3RhdHMgPiAwKSB7XG4gICAgIC8vIHN0YXJ0IGdhdGhlcmluZyBzdGF0c1xuICAgICB9XG4gICAgICovXG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0UmVtb3RlRGVzY3JpcHRpb24gPSBmdW5jdGlvbiAoZGVzY3JpcHRpb24sIHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGRlc2NyaXB0aW9uID0gQVBQLnNpbXVsY2FzdC50cmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbihkZXNjcmlwdGlvbik7XG4gICAgdGhpcy50cmFjZSgnc2V0UmVtb3RlRGVzY3JpcHRpb24nLCBkdW1wU0RQKGRlc2NyaXB0aW9uKSk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRSZW1vdGVEZXNjcmlwdGlvbihkZXNjcmlwdGlvbixcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0UmVtb3RlRGVzY3JpcHRpb25PblN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdzZXRSZW1vdGVEZXNjcmlwdGlvbk9uRmFpbHVyZScsIGVycik7XG4gICAgICAgICAgICBmYWlsdXJlQ2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICk7XG4gICAgLypcbiAgICAgaWYgKHRoaXMuc3RhdHNpbnRlcnZhbCA9PT0gbnVsbCAmJiB0aGlzLm1heHN0YXRzID4gMCkge1xuICAgICAvLyBzdGFydCBnYXRoZXJpbmcgc3RhdHNcbiAgICAgfVxuICAgICAqL1xufTtcblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNsb3NlID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMudHJhY2UoJ3N0b3AnKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uIChzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaywgY29uc3RyYWludHMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy50cmFjZSgnY3JlYXRlT2ZmZXInLCBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cywgbnVsbCwgJyAnKSk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVPZmZlcihcbiAgICAgICAgZnVuY3Rpb24gKG9mZmVyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVPZmZlck9uU3VjY2VzcycsIGR1bXBTRFAob2ZmZXIpKTtcbiAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjayhvZmZlcik7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlT2ZmZXJPbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVBbnN3ZXIgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2ssIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZUFuc3dlcicsIEpTT04uc3RyaW5naWZ5KGNvbnN0cmFpbnRzLCBudWxsLCAnICcpKTtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgZnVuY3Rpb24gKGFuc3dlcikge1xuICAgICAgICAgICAgYW5zd2VyID0gQVBQLnNpbXVsY2FzdC50cmFuc2Zvcm1BbnN3ZXIoYW5zd2VyKTtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ2NyZWF0ZUFuc3dlck9uU3VjY2VzcycsIGR1bXBTRFAoYW5zd2VyKSk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soYW5zd2VyKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24oZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVBbnN3ZXJPbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoY2FuZGlkYXRlLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGUnLCBKU09OLnN0cmluZ2lmeShjYW5kaWRhdGUsIG51bGwsICcgJykpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgLyogbWF5YmUgbGF0ZXJcbiAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5hZGRJY2VDYW5kaWRhdGUoY2FuZGlkYXRlLFxuICAgICBmdW5jdGlvbiAoKSB7XG4gICAgIHNlbGYudHJhY2UoJ2FkZEljZUNhbmRpZGF0ZU9uU3VjY2VzcycpO1xuICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgfSxcbiAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICBzZWxmLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGVPbkZhaWx1cmUnLCBlcnIpO1xuICAgICBmYWlsdXJlQ2FsbGJhY2soZXJyKTtcbiAgICAgfVxuICAgICApO1xuICAgICAqL1xufTtcblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24oY2FsbGJhY2ssIGVycmJhY2spIHtcbiAgICBpZiAobmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSkge1xuICAgICAgICAvLyBpZ25vcmUgZm9yIG5vdy4uLlxuICAgICAgICBpZighZXJyYmFjaylcbiAgICAgICAgICAgIGVycmJhY2sgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhudWxsLGNhbGxiYWNrLGVycmJhY2spO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoY2FsbGJhY2spO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gVHJhY2VhYmxlUGVlckNvbm5lY3Rpb247XG5cbiIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgVUksIG1lc3NhZ2VIYW5kbGVyLFxuIHJvb21OYW1lLCBzZXNzaW9uVGVybWluYXRlZCwgU3Ryb3BoZSwgVXRpbCAqL1xudmFyIFhNUFBFdmVudHMgPSByZXF1aXJlKFwiLi4vLi4vc2VydmljZS94bXBwL1hNUFBFdmVudHNcIik7XG5cbi8qKlxuICogQ29udGFpbnMgbG9naWMgcmVzcG9uc2libGUgZm9yIGVuYWJsaW5nL2Rpc2FibGluZyBmdW5jdGlvbmFsaXR5IGF2YWlsYWJsZVxuICogb25seSB0byBtb2RlcmF0b3IgdXNlcnMuXG4gKi9cbnZhciBjb25uZWN0aW9uID0gbnVsbDtcbnZhciBmb2N1c1VzZXJKaWQ7XG5cbmZ1bmN0aW9uIGNyZWF0ZUV4cEJhY2tvZmZUaW1lcihzdGVwKSB7XG4gICAgdmFyIGNvdW50ID0gMTtcbiAgICByZXR1cm4gZnVuY3Rpb24gKHJlc2V0KSB7XG4gICAgICAgIC8vIFJlc2V0IGNhbGxcbiAgICAgICAgaWYgKHJlc2V0KSB7XG4gICAgICAgICAgICBjb3VudCA9IDE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2FsY3VsYXRlIG5leHQgdGltZW91dFxuICAgICAgICB2YXIgdGltZW91dCA9IE1hdGgucG93KDIsIGNvdW50IC0gMSk7XG4gICAgICAgIGNvdW50ICs9IDE7XG4gICAgICAgIHJldHVybiB0aW1lb3V0ICogc3RlcDtcbiAgICB9O1xufVxuXG52YXIgZ2V0TmV4dFRpbWVvdXQgPSBjcmVhdGVFeHBCYWNrb2ZmVGltZXIoMTAwMCk7XG52YXIgZ2V0TmV4dEVycm9yVGltZW91dCA9IGNyZWF0ZUV4cEJhY2tvZmZUaW1lcigxMDAwKTtcbi8vIEV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIHN0dWZmXG52YXIgZXh0ZXJuYWxBdXRoRW5hYmxlZCA9IGZhbHNlO1xuLy8gU2lwIGdhdGV3YXkgY2FuIGJlIGVuYWJsZWQgYnkgY29uZmlndXJpbmcgSmlnYXNpIGhvc3QgaW4gY29uZmlnLmpzIG9yXG4vLyBpdCB3aWxsIGJlIGVuYWJsZWQgYXV0b21hdGljYWxseSBpZiBmb2N1cyBkZXRlY3RzIHRoZSBjb21wb25lbnQgdGhyb3VnaFxuLy8gc2VydmljZSBkaXNjb3ZlcnkuXG52YXIgc2lwR2F0ZXdheUVuYWJsZWQgPSBjb25maWcuaG9zdHMuY2FsbF9jb250cm9sICE9PSB1bmRlZmluZWQ7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBudWxsO1xuXG52YXIgTW9kZXJhdG9yID0ge1xuICAgIGlzTW9kZXJhdG9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uICYmIGNvbm5lY3Rpb24uZW11Yy5pc01vZGVyYXRvcigpO1xuICAgIH0sXG5cbiAgICBpc1BlZXJNb2RlcmF0b3I6IGZ1bmN0aW9uIChwZWVySmlkKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uICYmXG4gICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuZ2V0TWVtYmVyUm9sZShwZWVySmlkKSA9PT0gJ21vZGVyYXRvcic7XG4gICAgfSxcblxuICAgIGlzRXh0ZXJuYWxBdXRoRW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gZXh0ZXJuYWxBdXRoRW5hYmxlZDtcbiAgICB9LFxuXG4gICAgaXNTaXBHYXRld2F5RW5hYmxlZDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gc2lwR2F0ZXdheUVuYWJsZWQ7XG4gICAgfSxcblxuICAgIHNldENvbm5lY3Rpb246IGZ1bmN0aW9uIChjb24pIHtcbiAgICAgICAgY29ubmVjdGlvbiA9IGNvbjtcbiAgICB9LFxuXG4gICAgaW5pdDogZnVuY3Rpb24gKHhtcHAsIGVtaXR0ZXIpIHtcbiAgICAgICAgdGhpcy54bXBwU2VydmljZSA9IHhtcHA7XG4gICAgICAgIGV2ZW50RW1pdHRlciA9IGVtaXR0ZXI7XG4gICAgfSxcblxuICAgIG9uTXVjTGVmdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25zb2xlLmluZm8oXCJTb21lb25lIGxlZnQgaXMgaXQgZm9jdXMgPyBcIiArIGppZCk7XG4gICAgICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIGlmIChyZXNvdXJjZSA9PT0gJ2ZvY3VzJyAmJiAhdGhpcy54bXBwU2VydmljZS5zZXNzaW9uVGVybWluYXRlZCkge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgIFwiRm9jdXMgaGFzIGxlZnQgdGhlIHJvb20gLSBsZWF2aW5nIGNvbmZlcmVuY2VcIik7XG4gICAgICAgICAgICAvL2hhbmdVcCgpO1xuICAgICAgICAgICAgLy8gV2UnZCByYXRoZXIgcmVsb2FkIHRvIGhhdmUgZXZlcnl0aGluZyByZS1pbml0aWFsaXplZFxuICAgICAgICAgICAgLy8gRklYTUU6IHNob3cgc29tZSBtZXNzYWdlIGJlZm9yZSByZWxvYWRcbiAgICAgICAgICAgIGxvY2F0aW9uLnJlbG9hZCgpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBcbiAgICBzZXRGb2N1c1VzZXJKaWQ6IGZ1bmN0aW9uIChmb2N1c0ppZCkge1xuICAgICAgICBpZiAoIWZvY3VzVXNlckppZCkge1xuICAgICAgICAgICAgZm9jdXNVc2VySmlkID0gZm9jdXNKaWQ7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJGb2N1cyBqaWQgc2V0IHRvOiBcIiArIGZvY3VzVXNlckppZCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgZ2V0Rm9jdXNVc2VySmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBmb2N1c1VzZXJKaWQ7XG4gICAgfSxcblxuICAgIGdldEZvY3VzQ29tcG9uZW50OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIEdldCBmb2N1cyBjb21wb25lbnQgYWRkcmVzc1xuICAgICAgICB2YXIgZm9jdXNDb21wb25lbnQgPSBjb25maWcuaG9zdHMuZm9jdXM7XG4gICAgICAgIC8vIElmIG5vdCBzcGVjaWZpZWQgdXNlIGRlZmF1bHQ6ICdmb2N1cy5kb21haW4nXG4gICAgICAgIGlmICghZm9jdXNDb21wb25lbnQpIHtcbiAgICAgICAgICAgIGZvY3VzQ29tcG9uZW50ID0gJ2ZvY3VzLicgKyBjb25maWcuaG9zdHMuZG9tYWluO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmb2N1c0NvbXBvbmVudDtcbiAgICB9LFxuXG4gICAgY3JlYXRlQ29uZmVyZW5jZUlxOiBmdW5jdGlvbiAocm9vbU5hbWUpIHtcbiAgICAgICAgLy8gR2VuZXJhdGUgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ3NldCd9KTtcbiAgICAgICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmJyaWRnZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdicmlkZ2UnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmJyaWRnZX0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGVsbCB0aGUgZm9jdXMgd2UgaGF2ZSBKaWdhc2kgY29uZmlndXJlZFxuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjYWxsX2NvbnRyb2wnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5jaGFubmVsTGFzdE4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnY2hhbm5lbExhc3ROJywgdmFsdWU6IGNvbmZpZy5jaGFubmVsTGFzdE59KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuYWRhcHRpdmVMYXN0TiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdhZGFwdGl2ZUxhc3ROJywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZUxhc3ROfSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLmFkYXB0aXZlU2ltdWxjYXN0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2FkYXB0aXZlU2ltdWxjYXN0JywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZVNpbXVsY2FzdH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5vcGVuU2N0cCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdvcGVuU2N0cCcsIHZhbHVlOiBjb25maWcub3BlblNjdHB9KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlRmlyZWZveFN1cHBvcnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnZW5hYmxlRmlyZWZveEhhY2tzJyxcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGNvbmZpZy5lbmFibGVGaXJlZm94U3VwcG9ydH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICByZXR1cm4gZWxlbTtcbiAgICB9LFxuXG4gICAgcGFyc2VDb25maWdPcHRpb25zOiBmdW5jdGlvbiAocmVzdWx0SXEpIHtcbiAgICBcbiAgICAgICAgTW9kZXJhdG9yLnNldEZvY3VzVXNlckppZChcbiAgICAgICAgICAgICQocmVzdWx0SXEpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdmb2N1c2ppZCcpKTtcbiAgICBcbiAgICAgICAgdmFyIGV4dEF1dGhQYXJhbVxuICAgICAgICAgICAgPSAkKHJlc3VsdElxKS5maW5kKCc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ2V4dGVybmFsQXV0aFxcJ10nKTtcbiAgICAgICAgaWYgKGV4dEF1dGhQYXJhbS5sZW5ndGgpIHtcbiAgICAgICAgICAgIGV4dGVybmFsQXV0aEVuYWJsZWQgPSBleHRBdXRoUGFyYW0uYXR0cigndmFsdWUnKSA9PT0gJ3RydWUnO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIkV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIGVuYWJsZWQ6IFwiICsgZXh0ZXJuYWxBdXRoRW5hYmxlZCk7XG4gICAgXG4gICAgICAgIC8vIENoZWNrIGlmIGZvY3VzIGhhcyBhdXRvLWRldGVjdGVkIEppZ2FzaSBjb21wb25lbnQodGhpcyB3aWxsIGJlIGFsc29cbiAgICAgICAgLy8gaW5jbHVkZWQgaWYgd2UgaGF2ZSBwYXNzZWQgb3VyIGhvc3QgZnJvbSB0aGUgY29uZmlnKVxuICAgICAgICBpZiAoJChyZXN1bHRJcSkuZmluZChcbiAgICAgICAgICAgICc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ3NpcEdhdGV3YXlFbmFibGVkXFwnXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2lwR2F0ZXdheUVuYWJsZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlNpcCBnYXRld2F5IGVuYWJsZWQ6IFwiICsgc2lwR2F0ZXdheUVuYWJsZWQpO1xuICAgIH0sXG5cbiAgICAvLyBGSVhNRTogd2UgbmVlZCB0byBzaG93IHRoZSBmYWN0IHRoYXQgd2UncmUgd2FpdGluZyBmb3IgdGhlIGZvY3VzXG4gICAgLy8gdG8gdGhlIHVzZXIob3IgdGhhdCBmb2N1cyBpcyBub3QgYXZhaWxhYmxlKVxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbiAocm9vbU5hbWUsIGNhbGxiYWNrKSB7XG4gICAgICAgIC8vIFRyeSB0byB1c2UgZm9jdXMgdXNlciBKSUQgZnJvbSB0aGUgY29uZmlnXG4gICAgICAgIE1vZGVyYXRvci5zZXRGb2N1c1VzZXJKaWQoY29uZmlnLmZvY3VzVXNlckppZCk7XG4gICAgICAgIC8vIFNlbmQgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGlxID0gTW9kZXJhdG9yLmNyZWF0ZUNvbmZlcmVuY2VJcShyb29tTmFtZSk7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICBpcSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICBpZiAoJ3RydWUnID09PSAkKHJlc3VsdCkuZmluZCgnY29uZmVyZW5jZScpLmF0dHIoJ3JlYWR5JykpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gUmVzZXQgYm90aCB0aW1lcnNcbiAgICAgICAgICAgICAgICAgICAgZ2V0TmV4dFRpbWVvdXQodHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgIGdldE5leHRFcnJvclRpbWVvdXQodHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIFNldHVwIGNvbmZpZyBvcHRpb25zXG4gICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5wYXJzZUNvbmZpZ09wdGlvbnMocmVzdWx0KTtcbiAgICAgICAgICAgICAgICAgICAgLy8gRXhlYyBjYWxsYmFja1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB3YWl0TXMgPSBnZXROZXh0VGltZW91dCgpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJXYWl0aW5nIGZvciB0aGUgZm9jdXMuLi4gXCIgKyB3YWl0TXMpO1xuICAgICAgICAgICAgICAgICAgICAvLyBSZXNldCBlcnJvciB0aW1lb3V0XG4gICAgICAgICAgICAgICAgICAgIGdldE5leHRFcnJvclRpbWVvdXQodHJ1ZSk7XG4gICAgICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5hbGxvY2F0ZUNvbmZlcmVuY2VGb2N1cyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHdhaXRNcyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIC8vIE5vdCBhdXRob3JpemVkIHRvIGNyZWF0ZSBuZXcgcm9vbVxuICAgICAgICAgICAgICAgIGlmICgkKGVycm9yKS5maW5kKCc+ZXJyb3I+bm90LWF1dGhvcml6ZWQnKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiVW5hdXRob3JpemVkIHRvIHN0YXJ0IHRoZSBjb25mZXJlbmNlXCIpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgdG9Eb21haW5cbiAgICAgICAgICAgICAgICAgICAgICAgID0gU3Ryb3BoZS5nZXREb21haW5Gcm9tSmlkKGVycm9yLmdldEF0dHJpYnV0ZSgndG8nKSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0b0RvbWFpbiA9PT0gY29uZmlnLmhvc3RzLmFub255bW91c2RvbWFpbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gd2UgYXJlIGNvbm5lY3RlZCB3aXRoIGFub255bW91cyBkb21haW4gYW5kXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBvbmx5IG5vbiBhbm9ueW1vdXMgdXNlcnMgY2FuIGNyZWF0ZSByb29tc1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gd2UgbXVzdCBhdXRob3JpemUgdGhlIHVzZXJcblxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi54bXBwU2VydmljZS5wcm9tcHRMb2dpbigpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkFVVEhFTlRJQ0FUSU9OX1JFUVVJUkVELCAvLyBFeHRlcm5hbCBhdXRoZW50aWNhdGlvbiBtb2RlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb29tTmFtZSwgY2FsbGJhY2spO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dEVycm9yVGltZW91dCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGb2N1cyBlcnJvciwgcmV0cnkgYWZ0ZXIgXCIgKyB3YWl0TXMsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAvLyBTaG93IG1lc3NhZ2VcbiAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIubm90aWZ5KFxuICAgICAgICAgICAgICAgICAgICAnQ29uZmVyZW5jZSBmb2N1cycsICdkaXNjb25uZWN0ZWQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgTW9kZXJhdG9yLmdldEZvY3VzQ29tcG9uZW50KCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyBub3QgYXZhaWxhYmxlIC0gcmV0cnkgaW4gJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAod2FpdE1zIC8gMTAwMCkgKyAnIHNlYycpO1xuICAgICAgICAgICAgICAgIC8vIFJlc2V0IHJlc3BvbnNlIHRpbWVvdXRcbiAgICAgICAgICAgICAgICBnZXROZXh0VGltZW91dCh0cnVlKTtcbiAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgICAgICAgICAgICAgICAgIH0sIHdhaXRNcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSxcblxuICAgIGdldEF1dGhVcmw6IGZ1bmN0aW9uIChyb29tTmFtZSwgdXJsQ2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGlxID0gJGlxKHt0bzogTW9kZXJhdG9yLmdldEZvY3VzQ29tcG9uZW50KCksIHR5cGU6ICdnZXQnfSk7XG4gICAgICAgIGlxLmMoJ2F1dGgtdXJsJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lXG4gICAgICAgIH0pO1xuICAgICAgICBjb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgIGlxLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSAkKHJlc3VsdCkuZmluZCgnYXV0aC11cmwnKS5hdHRyKCd1cmwnKTtcbiAgICAgICAgICAgICAgICBpZiAodXJsKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkdvdCBhdXRoIHVybDogXCIgKyB1cmwpO1xuICAgICAgICAgICAgICAgICAgICB1cmxDYWxsYmFjayh1cmwpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICAgICAgICAgICAgICBcIkZhaWxlZCB0byBnZXQgYXV0aCB1cmwgZnJvIG10aGUgZm9jdXNcIiwgcmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkdldCBhdXRoIHVybCBlcnJvclwiLCBlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBNb2RlcmF0b3I7XG5cblxuXG4iLCIvKiBnbG9iYWwgJCwgJGlxLCBjb25maWcsIGNvbm5lY3Rpb24sIGZvY3VzTXVjSmlkLCBtZXNzYWdlSGFuZGxlciwgTW9kZXJhdG9yLFxuICAgVG9vbGJhciwgVXRpbCAqL1xudmFyIE1vZGVyYXRvciA9IHJlcXVpcmUoXCIuL21vZGVyYXRvclwiKTtcblxuXG52YXIgcmVjb3JkaW5nVG9rZW4gPSBudWxsO1xudmFyIHJlY29yZGluZ0VuYWJsZWQ7XG5cbi8qKlxuICogV2hldGhlciB0byB1c2UgYSBqaXJlY29uIGNvbXBvbmVudCBmb3IgcmVjb3JkaW5nLCBvciB1c2UgdGhlIHZpZGVvYnJpZGdlXG4gKiB0aHJvdWdoIENPTElCUkkuXG4gKi9cbnZhciB1c2VKaXJlY29uID0gKHR5cGVvZiBjb25maWcuaG9zdHMuamlyZWNvbiAhPSBcInVuZGVmaW5lZFwiKTtcblxuLyoqXG4gKiBUaGUgSUQgb2YgdGhlIGppcmVjb24gcmVjb3JkaW5nIHNlc3Npb24uIEppcmVjb24gZ2VuZXJhdGVzIGl0IHdoZW4gd2VcbiAqIGluaXRpYWxseSBzdGFydCByZWNvcmRpbmcsIGFuZCBpdCBuZWVkcyB0byBiZSB1c2VkIGluIHN1YnNlcXVlbnQgcmVxdWVzdHNcbiAqIHRvIGppcmVjb24uXG4gKi9cbnZhciBqaXJlY29uUmlkID0gbnVsbDtcblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nVG9rZW4odG9rZW4pIHtcbiAgICByZWNvcmRpbmdUb2tlbiA9IHRva2VuO1xufVxuXG5mdW5jdGlvbiBzZXRSZWNvcmRpbmcoc3RhdGUsIHRva2VuLCBjYWxsYmFjaywgY29ubmVjdGlvbikge1xuICAgIGlmICh1c2VKaXJlY29uKXtcbiAgICAgICAgc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBzZXRSZWNvcmRpbmdDb2xpYnJpKHN0YXRlLCB0b2tlbiwgY2FsbGJhY2ssIGNvbm5lY3Rpb24pO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKSB7XG4gICAgaWYgKHN0YXRlID09IHJlY29yZGluZ0VuYWJsZWQpe1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGlxID0gJGlxKHt0bzogY29uZmlnLmhvc3RzLmppcmVjb24sIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ3JlY29yZGluZycsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvamlyZWNvbicsXG4gICAgICAgICAgICBhY3Rpb246IHN0YXRlID8gJ3N0YXJ0JyA6ICdzdG9wJyxcbiAgICAgICAgICAgIG11Y2ppZDogY29ubmVjdGlvbi5lbXVjLnJvb21qaWR9KTtcbiAgICBpZiAoIXN0YXRlKXtcbiAgICAgICAgaXEuYXR0cnMoe3JpZDogamlyZWNvblJpZH0pO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdTdGFydCByZWNvcmRpbmcnKTtcblxuICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICBpcSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgLy8gVE9ETyB3YWl0IGZvciBhbiBJUSB3aXRoIHRoZSByZWFsIHN0YXR1cywgc2luY2UgdGhpcyBpc1xuICAgICAgICAgICAgLy8gcHJvdmlzaW9uYWw/XG4gICAgICAgICAgICBqaXJlY29uUmlkID0gJChyZXN1bHQpLmZpbmQoJ3JlY29yZGluZycpLmF0dHIoJ3JpZCcpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1JlY29yZGluZyAnICsgKHN0YXRlID8gJ3N0YXJ0ZWQnIDogJ3N0b3BwZWQnKSArXG4gICAgICAgICAgICAgICAgJyhqaXJlY29uKScgKyByZXN1bHQpO1xuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IHN0YXRlO1xuICAgICAgICAgICAgaWYgKCFzdGF0ZSl7XG4gICAgICAgICAgICAgICAgamlyZWNvblJpZCA9IG51bGw7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKHN0YXRlKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRmFpbGVkIHRvIHN0YXJ0IHJlY29yZGluZywgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9KTtcbn1cblxuLy8gU2VuZHMgYSBDT0xJQlJJIG1lc3NhZ2Ugd2hpY2ggZW5hYmxlcyBvciBkaXNhYmxlcyAoYWNjb3JkaW5nIHRvICdzdGF0ZScpXG4vLyB0aGUgcmVjb3JkaW5nIG9uIHRoZSBicmlkZ2UuIFdhaXRzIGZvciB0aGUgcmVzdWx0IElRIGFuZCBjYWxscyAnY2FsbGJhY2snXG4vLyB3aXRoIHRoZSBuZXcgcmVjb3JkaW5nIHN0YXRlLCBhY2NvcmRpbmcgdG8gdGhlIElRLlxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nQ29saWJyaShzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrLCBjb25uZWN0aW9uKSB7XG4gICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBjb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQsIHR5cGU6ICdzZXQnfSk7XG4gICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvY29saWJyaSdcbiAgICB9KTtcbiAgICBlbGVtLmMoJ3JlY29yZGluZycsIHtzdGF0ZTogc3RhdGUsIHRva2VuOiB0b2tlbn0pO1xuXG4gICAgY29ubmVjdGlvbi5zZW5kSVEoZWxlbSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1NldCByZWNvcmRpbmcgXCInLCBzdGF0ZSwgJ1wiLiBSZXN1bHQ6JywgcmVzdWx0KTtcbiAgICAgICAgICAgIHZhciByZWNvcmRpbmdFbGVtID0gJChyZXN1bHQpLmZpbmQoJz5jb25mZXJlbmNlPnJlY29yZGluZycpO1xuICAgICAgICAgICAgdmFyIG5ld1N0YXRlID0gKCd0cnVlJyA9PT0gcmVjb3JkaW5nRWxlbS5hdHRyKCdzdGF0ZScpKTtcblxuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IG5ld1N0YXRlO1xuICAgICAgICAgICAgY2FsbGJhY2sobmV3U3RhdGUpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihlcnJvcik7XG4gICAgICAgICAgICBjYWxsYmFjayhyZWNvcmRpbmdFbmFibGVkKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cbnZhciBSZWNvcmRpbmcgPSB7XG4gICAgdG9nZ2xlUmVjb3JkaW5nOiBmdW5jdGlvbiAodG9rZW5FbXB0eUNhbGxiYWNrLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjaywgY29ubmVjdGlvbikge1xuICAgICAgICBpZiAoIU1vZGVyYXRvci5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgJ25vbi1mb2N1cywgb3IgY29uZmVyZW5jZSBub3QgeWV0IG9yZ2FuaXplZDonICtcbiAgICAgICAgICAgICAgICAgICAgJyBub3QgZW5hYmxpbmcgcmVjb3JkaW5nJyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgIC8vIEppcmVjb24gZG9lcyBub3QgKGN1cnJlbnRseSkgc3VwcG9ydCBhIHRva2VuLlxuICAgICAgICBpZiAoIXJlY29yZGluZ1Rva2VuICYmICF1c2VKaXJlY29uKSB7XG4gICAgICAgICAgICB0b2tlbkVtcHR5Q2FsbGJhY2soZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgc2V0UmVjb3JkaW5nVG9rZW4odmFsdWUpO1xuICAgICAgICAgICAgICAgIHNlbGYudG9nZ2xlUmVjb3JkaW5nKCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIG9sZFN0YXRlID0gcmVjb3JkaW5nRW5hYmxlZDtcbiAgICAgICAgc3RhcnRpbmdDYWxsYmFjayghb2xkU3RhdGUpO1xuICAgICAgICBzZXRSZWNvcmRpbmcoIW9sZFN0YXRlLFxuICAgICAgICAgICAgcmVjb3JkaW5nVG9rZW4sXG4gICAgICAgICAgICBmdW5jdGlvbiAoc3RhdGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIk5ldyByZWNvcmRpbmcgc3RhdGU6IFwiLCBzdGF0ZSk7XG4gICAgICAgICAgICAgICAgaWYgKHN0YXRlID09PSBvbGRTdGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogbmV3IGZvY3VzOlxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzIHdpbGwgbm90IHdvcmsgd2hlbiBtb2RlcmF0b3IgY2hhbmdlc1xuICAgICAgICAgICAgICAgICAgICAvLyBkdXJpbmcgYWN0aXZlIHNlc3Npb24uIFRoZW4gaXQgd2lsbCBhc3N1bWUgdGhhdFxuICAgICAgICAgICAgICAgICAgICAvLyByZWNvcmRpbmcgc3RhdHVzIGhhcyBjaGFuZ2VkIHRvIHRydWUsIGJ1dCBpdCBtaWdodCBoYXZlXG4gICAgICAgICAgICAgICAgICAgIC8vIGJlZW4gYWxyZWFkeSB0cnVlKGFuZCB3ZSBvbmx5IHJlY2VpdmVkIGFjdHVhbCBzdGF0dXMgZnJvbVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgZm9jdXMpLlxuICAgICAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgICAgICAvLyBTTyB3ZSBzdGFydCB3aXRoIHN0YXR1cyBudWxsLCBzbyB0aGF0IGl0IGlzIGluaXRpYWxpemVkXG4gICAgICAgICAgICAgICAgICAgIC8vIGhlcmUgYW5kIHdpbGwgZmFpbCBvbmx5IGFmdGVyIHNlY29uZCBjbGljaywgc28gaWYgaW52YWxpZFxuICAgICAgICAgICAgICAgICAgICAvLyB0b2tlbiB3YXMgdXNlZCB3ZSBoYXZlIHRvIHByZXNzIHRoZSBidXR0b24gdHdpY2UgYmVmb3JlXG4gICAgICAgICAgICAgICAgICAgIC8vIGN1cnJlbnQgc3RhdHVzIHdpbGwgYmUgZmV0Y2hlZCBhbmQgdG9rZW4gd2lsbCBiZSByZXNldC5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVsaWFibGUgd2F5IHdvdWxkIGJlIHRvIHJldHVybiBhdXRoZW50aWNhdGlvbiBlcnJvci5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igc3RhdHVzIHVwZGF0ZSB3aGVuIG1vZGVyYXRvciBjb25uZWN0cy5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igd2UgaGF2ZSB0byBzdG9wIHJlY29yZGluZyBzZXNzaW9uIHdoZW4gY3VycmVudFxuICAgICAgICAgICAgICAgICAgICAvLyBtb2RlcmF0b3IgbGVhdmVzIHRoZSByb29tLlxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZhaWxlZCB0byBjaGFuZ2UsIHJlc2V0IHRoZSB0b2tlbiBiZWNhdXNlIGl0IG1pZ2h0XG4gICAgICAgICAgICAgICAgICAgIC8vIGhhdmUgYmVlbiB3cm9uZ1xuICAgICAgICAgICAgICAgICAgICBzZXRSZWNvcmRpbmdUb2tlbihudWxsKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc3RhcnRlZENhbGxiYWNrKHN0YXRlKTtcblxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGNvbm5lY3Rpb25cbiAgICAgICAgKTtcbiAgICB9XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBSZWNvcmRpbmc7IiwiLyoganNoaW50IC1XMTE3ICovXG4vKiBhIHNpbXBsZSBNVUMgY29ubmVjdGlvbiBwbHVnaW5cbiAqIGNhbiBvbmx5IGhhbmRsZSBhIHNpbmdsZSBNVUMgcm9vbVxuICovXG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcbnZhciBNb2RlcmF0b3IgPSByZXF1aXJlKFwiLi9tb2RlcmF0b3JcIik7XG52YXIgSmluZ2xlU2Vzc2lvbiA9IHJlcXVpcmUoXCIuL0ppbmdsZVNlc3Npb25cIik7XG5cbnZhciBicmlkZ2VJc0Rvd24gPSBmYWxzZTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihYTVBQLCBldmVudEVtaXR0ZXIpIHtcbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ2VtdWMnLCB7XG4gICAgICAgIGNvbm5lY3Rpb246IG51bGwsXG4gICAgICAgIHJvb21qaWQ6IG51bGwsXG4gICAgICAgIG15cm9vbWppZDogbnVsbCxcbiAgICAgICAgbWVtYmVyczoge30sXG4gICAgICAgIGxpc3RfbWVtYmVyczogW10sIC8vIHNvIHdlIGNhbiBlbGVjdCBhIG5ldyBmb2N1c1xuICAgICAgICBwcmVzTWFwOiB7fSxcbiAgICAgICAgcHJlemlNYXA6IHt9LFxuICAgICAgICBqb2luZWQ6IGZhbHNlLFxuICAgICAgICBpc093bmVyOiBmYWxzZSxcbiAgICAgICAgcm9sZTogbnVsbCxcbiAgICAgICAgZm9jdXNNdWNKaWQ6IG51bGwsXG4gICAgICAgIHNzcmMyamlkOiB7fSxcbiAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG4gICAgICAgIH0sXG4gICAgICAgIGluaXRQcmVzZW5jZU1hcDogZnVuY3Rpb24gKG15cm9vbWppZCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWyd0byddID0gbXlyb29tamlkO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWyd4bnMnXSA9ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMnO1xuICAgICAgICB9LFxuICAgICAgICBkb0pvaW46IGZ1bmN0aW9uIChqaWQsIHBhc3N3b3JkKSB7XG4gICAgICAgICAgICB0aGlzLm15cm9vbWppZCA9IGppZDtcblxuICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiSm9pbmVkIE1VQyBhcyBcIiArIHRoaXMubXlyb29tamlkKTtcblxuICAgICAgICAgICAgdGhpcy5pbml0UHJlc2VuY2VNYXAodGhpcy5teXJvb21qaWQpO1xuXG4gICAgICAgICAgICBpZiAoIXRoaXMucm9vbWppZCkge1xuICAgICAgICAgICAgICAgIHRoaXMucm9vbWppZCA9IFN0cm9waGUuZ2V0QmFyZUppZEZyb21KaWQoamlkKTtcbiAgICAgICAgICAgICAgICAvLyBhZGQgaGFuZGxlcnMgKGp1c3Qgb25jZSlcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2UuYmluZCh0aGlzKSwgbnVsbCwgJ3ByZXNlbmNlJywgbnVsbCwgbnVsbCwgdGhpcy5yb29tamlkLCB7bWF0Y2hCYXJlOiB0cnVlfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmFkZEhhbmRsZXIodGhpcy5vblByZXNlbmNlVW5hdmFpbGFibGUuYmluZCh0aGlzKSwgbnVsbCwgJ3ByZXNlbmNlJywgJ3VuYXZhaWxhYmxlJywgbnVsbCwgdGhpcy5yb29tamlkLCB7bWF0Y2hCYXJlOiB0cnVlfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmFkZEhhbmRsZXIodGhpcy5vblByZXNlbmNlRXJyb3IuYmluZCh0aGlzKSwgbnVsbCwgJ3ByZXNlbmNlJywgJ2Vycm9yJywgbnVsbCwgdGhpcy5yb29tamlkLCB7bWF0Y2hCYXJlOiB0cnVlfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmFkZEhhbmRsZXIodGhpcy5vbk1lc3NhZ2UuYmluZCh0aGlzKSwgbnVsbCwgJ21lc3NhZ2UnLCBudWxsLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwYXNzd29yZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwYXNzd29yZCddID0gcGFzc3dvcmQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnNlbmRQcmVzZW5jZSgpO1xuICAgICAgICB9LFxuICAgICAgICBkb0xlYXZlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcImRvIGxlYXZlXCIsIHRoaXMubXlyb29tamlkKTtcbiAgICAgICAgICAgIHZhciBwcmVzID0gJHByZXMoe3RvOiB0aGlzLm15cm9vbWppZCwgdHlwZTogJ3VuYXZhaWxhYmxlJyB9KTtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcC5sZW5ndGggPSAwO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQocHJlcyk7XG4gICAgICAgIH0sXG4gICAgICAgIGNyZWF0ZU5vbkFub255bW91c1Jvb206IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMDQ1Lmh0bWwjY3JlYXRlcm9vbS1yZXNlcnZlZFxuXG4gICAgICAgICAgICB2YXIgZ2V0Rm9ybSA9ICRpcSh7dHlwZTogJ2dldCcsIHRvOiB0aGlzLnJvb21qaWR9KVxuICAgICAgICAgICAgICAgIC5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KVxuICAgICAgICAgICAgICAgIC5jKCd4Jywge3htbG5zOiAnamFiYmVyOng6ZGF0YScsIHR5cGU6ICdzdWJtaXQnfSk7XG5cbiAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShnZXRGb3JtLCBmdW5jdGlvbiAoZm9ybSkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCEkKGZvcm0pLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgICAgICAnPnF1ZXJ5PnhbeG1sbnM9XCJqYWJiZXI6eDpkYXRhXCJdJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnPmZpZWxkW3Zhcj1cIm11YyNyb29tY29uZmlnX3dob2lzXCJdJykubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbm9uLWFub255bW91cyByb29tcyBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB2YXIgZm9ybVN1Ym1pdCA9ICRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSk7XG5cbiAgICAgICAgICAgICAgICBmb3JtU3VibWl0LmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgICAgIGZvcm1TdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdGT1JNX1RZUEUnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3ZhbHVlJylcbiAgICAgICAgICAgICAgICAgICAgLnQoJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNyb29tY29uZmlnJykudXAoKS51cCgpO1xuXG4gICAgICAgICAgICAgICAgZm9ybVN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3dob2lzJ30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCd2YWx1ZScpLnQoJ2FueW9uZScpLnVwKCkudXAoKTtcblxuICAgICAgICAgICAgICAgIHNlbGYuY29ubmVjdGlvbi5zZW5kSVEoZm9ybVN1Ym1pdCk7XG5cbiAgICAgICAgICAgIH0sIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBnZXR0aW5nIHJvb20gY29uZmlndXJhdGlvbiBmb3JtXCIpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUHJlc2VuY2U6IGZ1bmN0aW9uIChwcmVzKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IHByZXMuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG5cbiAgICAgICAgICAgIC8vIFdoYXQgaXMgdGhpcyBmb3I/IEEgd29ya2Fyb3VuZCBmb3Igc29tZXRoaW5nP1xuICAgICAgICAgICAgaWYgKHByZXMuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgZXRoZXJwYWQgdGFnLlxuICAgICAgICAgICAgdmFyIGV0aGVycGFkID0gJChwcmVzKS5maW5kKCc+ZXRoZXJwYWQnKTtcbiAgICAgICAgICAgIGlmIChldGhlcnBhZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLmV0aGVycGFkX2Jhc2UgJiYgIU1vZGVyYXRvci5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuRVRIRVJQQUQsIGV0aGVycGFkLnRleHQoKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBwcmV6aSB0YWcuXG4gICAgICAgICAgICB2YXIgcHJlc2VudGF0aW9uID0gJChwcmVzKS5maW5kKCc+cHJlemknKTtcbiAgICAgICAgICAgIGlmIChwcmVzZW50YXRpb24ubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHVybCA9IHByZXNlbnRhdGlvbi5hdHRyKCd1cmwnKTtcbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudCA9IHByZXNlbnRhdGlvbi5maW5kKCc+Y3VycmVudCcpLnRleHQoKTtcblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdwcmVzZW50YXRpb24gaW5mbyByZWNlaXZlZCBmcm9tJywgZnJvbSwgdXJsKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnByZXppTWFwW2Zyb21dID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wcmV6aU1hcFtmcm9tXSA9IHVybDtcblxuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwcmVzZW50YXRpb25hZGRlZC5tdWMnLCBbZnJvbSwgdXJsLCBjdXJyZW50XSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdnb3Rvc2xpZGUubXVjJywgW2Zyb20sIHVybCwgY3VycmVudF0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKHRoaXMucHJlemlNYXBbZnJvbV0gIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSB0aGlzLnByZXppTWFwW2Zyb21dO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXppTWFwW2Zyb21dO1xuICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ByZXNlbnRhdGlvbnJlbW92ZWQubXVjJywgW2Zyb20sIHVybF0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBhdWRpbyBpbmZvIHRhZy5cbiAgICAgICAgICAgIHZhciBhdWRpb011dGVkID0gJChwcmVzKS5maW5kKCc+YXVkaW9tdXRlZCcpO1xuICAgICAgICAgICAgaWYgKGF1ZGlvTXV0ZWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYXVkaW9tdXRlZC5tdWMnLCBbZnJvbSwgYXVkaW9NdXRlZC50ZXh0KCldKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgdmlkZW8gaW5mbyB0YWcuXG4gICAgICAgICAgICB2YXIgdmlkZW9NdXRlZCA9ICQocHJlcykuZmluZCgnPnZpZGVvbXV0ZWQnKTtcbiAgICAgICAgICAgIGlmICh2aWRlb011dGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ZpZGVvbXV0ZWQubXVjJywgW2Zyb20sIHZpZGVvTXV0ZWQudGV4dCgpXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBzdGF0cyA9ICQocHJlcykuZmluZCgnPnN0YXRzJyk7XG4gICAgICAgICAgICBpZiAoc3RhdHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN0YXRzT2JqID0ge307XG4gICAgICAgICAgICAgICAgU3Ryb3BoZS5mb3JFYWNoQ2hpbGQoc3RhdHNbMF0sIFwic3RhdFwiLCBmdW5jdGlvbiAoZWwpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RhdHNPYmpbZWwuZ2V0QXR0cmlidXRlKFwibmFtZVwiKV0gPSBlbC5nZXRBdHRyaWJ1dGUoXCJ2YWx1ZVwiKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLlJFTU9URV9TVEFUUywgZnJvbSwgc3RhdHNPYmopO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBzdGF0dXMuXG4gICAgICAgICAgICBpZiAoJChwcmVzKS5maW5kKCc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdPnN0YXR1c1tjb2RlPVwiMjAxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmNyZWF0ZU5vbkFub255bW91c1Jvb20oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2Ugcm9sZXMuXG4gICAgICAgICAgICB2YXIgbWVtYmVyID0ge307XG4gICAgICAgICAgICBtZW1iZXIuc2hvdyA9ICQocHJlcykuZmluZCgnPnNob3cnKS50ZXh0KCk7XG4gICAgICAgICAgICBtZW1iZXIuc3RhdHVzID0gJChwcmVzKS5maW5kKCc+c3RhdHVzJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIHRtcCA9ICQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5pdGVtJyk7XG4gICAgICAgICAgICBtZW1iZXIuYWZmaWxpYXRpb24gPSB0bXAuYXR0cignYWZmaWxpYXRpb24nKTtcbiAgICAgICAgICAgIG1lbWJlci5yb2xlID0gdG1wLmF0dHIoJ3JvbGUnKTtcblxuICAgICAgICAgICAgLy8gRm9jdXMgcmVjb2duaXRpb25cbiAgICAgICAgICAgIG1lbWJlci5qaWQgPSB0bXAuYXR0cignamlkJyk7XG4gICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IGZhbHNlO1xuICAgICAgICAgICAgaWYgKG1lbWJlci5qaWRcbiAgICAgICAgICAgICAgICAmJiBtZW1iZXIuamlkLmluZGV4T2YoTW9kZXJhdG9yLmdldEZvY3VzVXNlckppZCgpICsgXCIvXCIpID09IDApIHtcbiAgICAgICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBuaWNrdGFnID0gJChwcmVzKS5maW5kKCc+bmlja1t4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2tcIl0nKTtcbiAgICAgICAgICAgIG1lbWJlci5kaXNwbGF5TmFtZSA9IChuaWNrdGFnLmxlbmd0aCA+IDAgPyBuaWNrdGFnLmh0bWwoKSA6IG51bGwpO1xuXG4gICAgICAgICAgICBpZiAoZnJvbSA9PSB0aGlzLm15cm9vbWppZCkge1xuICAgICAgICAgICAgICAgIGlmIChtZW1iZXIuYWZmaWxpYXRpb24gPT0gJ293bmVyJykgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5yb2xlICE9PSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJvbGUgPSBtZW1iZXIucm9sZTtcblxuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkxPQ0FMUk9MRV9DSEFOR0VELFxuICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSwgbWVtYmVyLCBwcmVzLCBNb2RlcmF0b3IuaXNNb2RlcmF0b3IoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5pc0V4dGVybmFsQXV0aEVuYWJsZWQoKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5qb2luZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5qb2luZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19KT0lORUQsIGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnB1c2goZnJvbSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLm1lbWJlcnNbZnJvbV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIC8vIG5ldyBwYXJ0aWNpcGFudFxuICAgICAgICAgICAgICAgIHRoaXMubWVtYmVyc1tmcm9tXSA9IG1lbWJlcjtcbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RfbWVtYmVycy5wdXNoKGZyb20pO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdlbnRlcmVkJywgZnJvbSwgbWVtYmVyKTtcbiAgICAgICAgICAgICAgICBpZiAobWVtYmVyLmlzRm9jdXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5mb2N1c011Y0ppZCA9IGZyb207XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIklnbm9yZSBmb2N1czogXCIgKyBmcm9tICsgXCIsIHJlYWwgSklEOiBcIiArIG1lbWJlci5qaWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGlkID0gJChwcmVzKS5maW5kKCc+dXNlcklEJykudGV4dCgpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgZW1haWwgPSAkKHByZXMpLmZpbmQoJz5lbWFpbCcpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZW1haWwubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSBlbWFpbC50ZXh0KCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5NVUNfRU5URVIsIGZyb20sIGlkLCBtZW1iZXIuZGlzcGxheU5hbWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gUHJlc2VuY2UgdXBkYXRlIGZvciBleGlzdGluZyBwYXJ0aWNpcGFudFxuICAgICAgICAgICAgICAgIC8vIFdhdGNoIHJvbGUgY2hhbmdlOlxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm1lbWJlcnNbZnJvbV0ucm9sZSAhPSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm1lbWJlcnNbZnJvbV0ucm9sZSA9IG1lbWJlci5yb2xlO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLk1VQ19ST0xFX0NIQU5HRUQsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZW1iZXIucm9sZSwgbWVtYmVyLmRpc3BsYXlOYW1lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIEFsd2F5cyB0cmlnZ2VyIHByZXNlbmNlIHRvIHVwZGF0ZSBiaW5kaW5nc1xuICAgICAgICAgICAgdGhpcy5wYXJzZVByZXNlbmNlKGZyb20sIG1lbWJlciwgcHJlcyk7XG5cbiAgICAgICAgICAgIC8vIFRyaWdnZXIgc3RhdHVzIG1lc3NhZ2UgdXBkYXRlXG4gICAgICAgICAgICBpZiAobWVtYmVyLnN0YXR1cykge1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuUFJFU0VOQ0VfU1RBVFVTLCBmcm9tLCBtZW1iZXIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZVVuYXZhaWxhYmxlOiBmdW5jdGlvbiAocHJlcykge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBwcmVzLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgLy8gU3RhdHVzIGNvZGUgMTEwIGluZGljYXRlcyB0aGF0IHRoaXMgbm90aWZpY2F0aW9uIGlzIFwic2VsZi1wcmVzZW5jZVwiLlxuICAgICAgICAgICAgaWYgKCEkKHByZXMpLmZpbmQoJz54W3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI3VzZXJcIl0+c3RhdHVzW2NvZGU9XCIxMTBcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5tZW1iZXJzW2Zyb21dO1xuICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnNwbGljZSh0aGlzLmxpc3RfbWVtYmVycy5pbmRleE9mKGZyb20pLCAxKTtcbiAgICAgICAgICAgICAgICB0aGlzLm9uUGFydGljaXBhbnRMZWZ0KGZyb20pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gSWYgdGhlIHN0YXR1cyBjb2RlIGlzIDExMCB0aGlzIG1lYW5zIHdlJ3JlIGxlYXZpbmcgYW5kIHdlIHdvdWxkIGxpa2VcbiAgICAgICAgICAgIC8vIHRvIHJlbW92ZSBldmVyeW9uZSBlbHNlIGZyb20gb3VyIHZpZXcsIHNvIHdlIHRyaWdnZXIgdGhlIGV2ZW50LlxuICAgICAgICAgICAgZWxzZSBpZiAodGhpcy5saXN0X21lbWJlcnMubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5saXN0X21lbWJlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG1lbWJlciA9IHRoaXMubGlzdF9tZW1iZXJzW2ldO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5tZW1iZXJzW2ldO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxpc3RfbWVtYmVycy5zcGxpY2UoaSwgMSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25QYXJ0aWNpcGFudExlZnQobWVtYmVyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoJChwcmVzKS5maW5kKCc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdPnN0YXR1c1tjb2RlPVwiMzA3XCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigna2lja2VkLm11YycsIFtmcm9tXSk7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubXlyb29tamlkID09PSBmcm9tKSB7XG4gICAgICAgICAgICAgICAgICAgIFhNUFAuZGlzcG9zZUNvbmZlcmVuY2UoZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLktJQ0tFRCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUHJlc2VuY2VFcnJvcjogZnVuY3Rpb24gKHByZXMpIHtcbiAgICAgICAgICAgIHZhciBmcm9tID0gcHJlcy5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIGlmICgkKHByZXMpLmZpbmQoJz5lcnJvclt0eXBlPVwiYXV0aFwiXT5ub3QtYXV0aG9yaXplZFt4bWxucz1cInVybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ29uIHBhc3N3b3JkIHJlcXVpcmVkJywgZnJvbSk7XG4gICAgICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuUEFTU1dPUkRfUkVRVUlSRUQsIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmRvSm9pbihmcm9tLCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCQocHJlcykuZmluZChcbiAgICAgICAgICAgICAgICAnPmVycm9yW3R5cGU9XCJjYW5jZWxcIl0+bm90LWFsbG93ZWRbeG1sbnM9XCJ1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphc1wiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciB0b0RvbWFpbiA9IFN0cm9waGUuZ2V0RG9tYWluRnJvbUppZChwcmVzLmdldEF0dHJpYnV0ZSgndG8nKSk7XG4gICAgICAgICAgICAgICAgaWYgKHRvRG9tYWluID09PSBjb25maWcuaG9zdHMuYW5vbnltb3VzZG9tYWluKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGVudGVyIHRoZSByb29tIGJ5IHJlcGx5aW5nIHdpdGggJ25vdC1hdXRob3JpemVkJy4gVGhpcyB3b3VsZFxuICAgICAgICAgICAgICAgICAgICAvLyByZXN1bHQgaW4gcmVjb25uZWN0aW9uIGZyb20gYXV0aG9yaXplZCBkb21haW4uXG4gICAgICAgICAgICAgICAgICAgIC8vIFdlJ3JlIGVpdGhlciBtaXNzaW5nIEppY29mby9Qcm9zb2R5IGNvbmZpZyBmb3IgYW5vbnltb3VzXG4gICAgICAgICAgICAgICAgICAgIC8vIGRvbWFpbnMgb3Igc29tZXRoaW5nIGlzIHdyb25nLlxuLy8gICAgICAgICAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcbiAgICAgICAgICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLm9wZW5SZXBvcnREaWFsb2cobnVsbCxcbiAgICAgICAgICAgICAgICAgICAgICAgICdPb3BzICEgV2UgY291bGRuYHQgam9pbiB0aGUgY29uZmVyZW5jZS4nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICcgVGhlcmUgbWlnaHQgYmUgc29tZSBwcm9ibGVtIHdpdGggc2VjdXJpdHknICtcbiAgICAgICAgICAgICAgICAgICAgICAgICcgY29uZmlndXJhdGlvbi4gUGxlYXNlIGNvbnRhY3Qgc2VydmljZScgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyBhZG1pbmlzdHJhdG9yLicsIHByZXMpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignb25QcmVzRXJyb3IgJywgcHJlcyk7XG4gICAgICAgICAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5vcGVuUmVwb3J0RGlhbG9nKG51bGwsXG4gICAgICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGNvdWxkbmB0IGNvbm5lY3QgdG8gdGhlIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdvblByZXNFcnJvciAnLCBwcmVzKTtcbiAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGNvdWxkbmB0IGNvbm5lY3QgdG8gdGhlIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgICAgICAgICAgcHJlcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZE1lc3NhZ2U6IGZ1bmN0aW9uIChib2R5LCBuaWNrbmFtZSkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnYm9keScsIGJvZHkpLnVwKCk7XG4gICAgICAgICAgICBpZiAobmlja25hbWUpIHtcbiAgICAgICAgICAgICAgICBtc2cuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSkudChuaWNrbmFtZSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuU0VORElOR19DSEFUX01FU1NBR0UsIGJvZHkpO1xuICAgICAgICB9LFxuICAgICAgICBzZXRTdWJqZWN0OiBmdW5jdGlvbiAoc3ViamVjdCkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnc3ViamVjdCcsIHN1YmplY3QpO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwidG9waWMgY2hhbmdlZCB0byBcIiArIHN1YmplY3QpO1xuICAgICAgICB9LFxuICAgICAgICBvbk1lc3NhZ2U6IGZ1bmN0aW9uIChtc2cpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiB0aGlzIGlzIGEgaGFjay4gYnV0IGppbmdsZSBvbiBtdWMgbWFrZXMgbmlja2NoYW5nZXMgaGFyZFxuICAgICAgICAgICAgdmFyIGZyb20gPSBtc2cuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICB2YXIgbmljayA9XG4gICAgICAgICAgICAgICAgJChtc2cpLmZpbmQoJz5uaWNrW3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmlja1wiXScpXG4gICAgICAgICAgICAgICAgICAgIC50ZXh0KCkgfHxcbiAgICAgICAgICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKTtcblxuICAgICAgICAgICAgdmFyIHR4dCA9ICQobXNnKS5maW5kKCc+Ym9keScpLnRleHQoKTtcbiAgICAgICAgICAgIHZhciB0eXBlID0gbXNnLmdldEF0dHJpYnV0ZShcInR5cGVcIik7XG4gICAgICAgICAgICBpZiAodHlwZSA9PSBcImVycm9yXCIpIHtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkNIQVRfRVJST1JfUkVDRUlWRUQsXG4gICAgICAgICAgICAgICAgICAgICQobXNnKS5maW5kKCc+dGV4dCcpLnRleHQoKSwgdHh0KTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHN1YmplY3QgPSAkKG1zZykuZmluZCgnPnN1YmplY3QnKTtcbiAgICAgICAgICAgIGlmIChzdWJqZWN0Lmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBzdWJqZWN0VGV4dCA9IHN1YmplY3QudGV4dCgpO1xuICAgICAgICAgICAgICAgIGlmIChzdWJqZWN0VGV4dCB8fCBzdWJqZWN0VGV4dCA9PSBcIlwiKSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuU1VCSkVDVF9DSEFOR0VELCBzdWJqZWN0VGV4dCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiU3ViamVjdCBpcyBjaGFuZ2VkIHRvIFwiICsgc3ViamVjdFRleHQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICBpZiAodHh0KSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ2NoYXQnLCBuaWNrLCB0eHQpO1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuTUVTU0FHRV9SRUNFSVZFRCxcbiAgICAgICAgICAgICAgICAgICAgZnJvbSwgbmljaywgdHh0LCB0aGlzLm15cm9vbWppZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgbG9ja1Jvb206IGZ1bmN0aW9uIChrZXksIG9uU3VjY2Vzcywgb25FcnJvciwgb25Ob3RTdXBwb3J0ZWQpIHtcbiAgICAgICAgICAgIC8vaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAwNDUuaHRtbCNyb29tY29uZmlnXG4gICAgICAgICAgICB2YXIgb2IgPSB0aGlzO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUSgkaXEoe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdnZXQnfSkuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSksXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChyZXMpLmZpbmQoJz5xdWVyeT54W3htbG5zPVwiamFiYmVyOng6ZGF0YVwiXT5maWVsZFt2YXI9XCJtdWMjcm9vbWNvbmZpZ19yb29tc2VjcmV0XCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZm9ybXN1Ym1pdCA9ICRpcSh7dG86IG9iLnJvb21qaWQsIHR5cGU6ICdzZXQnfSkuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3Jtc3VibWl0LmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdGT1JNX1RZUEUnfSkuYygndmFsdWUnKS50KCdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjcm9vbWNvbmZpZycpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdtdWMjcm9vbWNvbmZpZ19yb29tc2VjcmV0J30pLmMoJ3ZhbHVlJykudChrZXkpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZpeGVzIGEgYnVnIGluIHByb3NvZHkgMC45LisgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9seG1wcGQvaXNzdWVzL2RldGFpbD9pZD0zNzNcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdtdWMjcm9vbWNvbmZpZ193aG9pcyd9KS5jKCd2YWx1ZScpLnQoJ2FueW9uZScpLnVwKCkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBpcyBtdWMjcm9vbWNvbmZpZ19wYXNzd29yZHByb3RlY3RlZHJvb20gcmVxdWlyZWQ/XG4gICAgICAgICAgICAgICAgICAgICAgICBvYi5jb25uZWN0aW9uLnNlbmRJUShmb3Jtc3VibWl0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uU3VjY2VzcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbkVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9uTm90U3VwcG9ydGVkKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LCBvbkVycm9yKTtcbiAgICAgICAgfSxcbiAgICAgICAga2ljazogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAgICAgdmFyIGtpY2tJUSA9ICRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNhZG1pbid9KVxuICAgICAgICAgICAgICAgIC5jKCdpdGVtJywge25pY2s6IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCksIHJvbGU6ICdub25lJ30pXG4gICAgICAgICAgICAgICAgLmMoJ3JlYXNvbicpLnQoJ1lvdSBoYXZlIGJlZW4ga2lja2VkLicpLnVwKCkudXAoKS51cCgpO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgIGtpY2tJUSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdLaWNrIHBhcnRpY2lwYW50IHdpdGggamlkOiAnLCBqaWQsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ0tpY2sgcGFydGljaXBhbnQgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZFByZXNlbmNlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcHJlcyA9ICRwcmVzKHt0bzogdGhpcy5wcmVzTWFwWyd0byddIH0pO1xuICAgICAgICAgICAgcHJlcy5jKCd4Jywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3hucyddfSk7XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3Bhc3N3b3JkJykudCh0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMudXAoKTtcblxuICAgICAgICAgICAgLy8gU2VuZCBYRVAtMDExNSAnYycgc3RhbnphIHRoYXQgY29udGFpbnMgb3VyIGNhcGFiaWxpdGllcyBpbmZvXG4gICAgICAgICAgICBpZiAodGhpcy5jb25uZWN0aW9uLmNhcHMpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY2Fwcy5ub2RlID0gY29uZmlnLmNsaWVudE5vZGU7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdjJywgdGhpcy5jb25uZWN0aW9uLmNhcHMuZ2VuZXJhdGVDYXBzQXR0cnMoKSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcHJlcy5jKCd1c2VyLWFnZW50Jywge3htbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L3VzZXItYWdlbnQnfSlcbiAgICAgICAgICAgICAgICAudChuYXZpZ2F0b3IudXNlckFnZW50KS51cCgpO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydicmlkZ2VJc0Rvd24nXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnYnJpZGdlSXNEb3duJykudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZW1haWwnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnZW1haWwnKS50KHRoaXMucHJlc01hcFsnZW1haWwnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsndXNlcklkJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3VzZXJJZCcpLnQodGhpcy5wcmVzTWFwWyd1c2VySWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZGlzcGxheU5hbWUnXSkge1xuICAgICAgICAgICAgICAgIC8vIFhFUC0wMTcyXG4gICAgICAgICAgICAgICAgcHJlcy5jKCduaWNrJywge3htbG5zOiAnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmljayd9KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2F1ZGlvbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnYXVkaW9tdXRlZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWydhdWRpb25zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2F1ZGlvbXV0ZWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsndmlkZW9ucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCd2aWRlb211dGVkJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3ZpZGVvbnMnXX0pXG4gICAgICAgICAgICAgICAgICAgIC50KHRoaXMucHJlc01hcFsndmlkZW9tdXRlZCddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydzdGF0c25zJ10pIHtcbiAgICAgICAgICAgICAgICB2YXIgc3RhdHMgPSBwcmVzLmMoJ3N0YXRzJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ3N0YXRzbnMnXX0pO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIHN0YXQgaW4gdGhpcy5wcmVzTWFwW1wic3RhdHNcIl0pXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbXCJzdGF0c1wiXVtzdGF0XSAhPSBudWxsKVxuICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHMuYyhcInN0YXRcIiwge25hbWU6IHN0YXQsIHZhbHVlOiB0aGlzLnByZXNNYXBbXCJzdGF0c1wiXVtzdGF0XX0pLnVwKCk7XG4gICAgICAgICAgICAgICAgcHJlcy51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydwcmV6aW5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ3ByZXppJyxcbiAgICAgICAgICAgICAgICAgICAge3htbG5zOiB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICd1cmwnOiB0aGlzLnByZXNNYXBbJ3ByZXppdXJsJ119KVxuICAgICAgICAgICAgICAgICAgICAuYygnY3VycmVudCcpLnQodGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydldGhlcnBhZG5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2V0aGVycGFkJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ2V0aGVycGFkbnMnXX0pXG4gICAgICAgICAgICAgICAgICAgIC50KHRoaXMucHJlc01hcFsnZXRoZXJwYWRuYW1lJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygnbWVkaWEnLCB7eG1sbnM6IHRoaXMucHJlc01hcFsnbWVkaWFucyddfSk7XG4gICAgICAgICAgICAgICAgdmFyIHNvdXJjZU51bWJlciA9IDA7XG4gICAgICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5wcmVzTWFwKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCdzb3VyY2UnKSA+PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VOdW1iZXIrKztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGlmIChzb3VyY2VOdW1iZXIgPiAwKVxuICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8PSBzb3VyY2VOdW1iZXIgLyAzOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXMuYygnc291cmNlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7dHlwZTogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfdHlwZSddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzc3JjOiB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBpICsgJ19zc3JjJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfZGlyZWN0aW9uJ11cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx8ICdzZW5kcmVjdicgfVxuICAgICAgICAgICAgICAgICAgICAgICAgKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMudXAoKTtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKHByZXMpO1xuICAgICAgICB9LFxuICAgICAgICBhZGREaXNwbGF5TmFtZVRvUHJlc2VuY2U6IGZ1bmN0aW9uIChkaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydkaXNwbGF5TmFtZSddID0gZGlzcGxheU5hbWU7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZE1lZGlhVG9QcmVzZW5jZTogZnVuY3Rpb24gKHNvdXJjZU51bWJlciwgbXR5cGUsIHNzcmNzLCBkaXJlY3Rpb24pIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5wcmVzTWFwWydtZWRpYW5zJ10pXG4gICAgICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydtZWRpYW5zJ10gPSAnaHR0cDovL2VzdG9zLmRlL25zL21qcyc7XG5cbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc291cmNlJyArIHNvdXJjZU51bWJlciArICdfdHlwZSddID0gbXR5cGU7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBzb3VyY2VOdW1iZXIgKyAnX3NzcmMnXSA9IHNzcmNzO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgc291cmNlTnVtYmVyICsgJ19kaXJlY3Rpb24nXSA9IGRpcmVjdGlvbjtcbiAgICAgICAgfSxcbiAgICAgICAgY2xlYXJQcmVzZW5jZU1lZGlhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnByZXNNYXApLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignc291cmNlJykgIT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHNlbGYucHJlc01hcFtrZXldO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9LFxuICAgICAgICBhZGRQcmV6aVRvUHJlc2VuY2U6IGZ1bmN0aW9uICh1cmwsIGN1cnJlbnRTbGlkZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aW5zJ10gPSAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L3ByZXppJztcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsncHJleml1cmwnXSA9IHVybDtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsncHJlemljdXJyZW50J10gPSBjdXJyZW50U2xpZGU7XG4gICAgICAgIH0sXG4gICAgICAgIHJlbW92ZVByZXppRnJvbVByZXNlbmNlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBkZWxldGUgdGhpcy5wcmVzTWFwWydwcmV6aW5zJ107XG4gICAgICAgICAgICBkZWxldGUgdGhpcy5wcmVzTWFwWydwcmV6aXVybCddO1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMucHJlc01hcFsncHJlemljdXJyZW50J107XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEN1cnJlbnRTbGlkZVRvUHJlc2VuY2U6IGZ1bmN0aW9uIChjdXJyZW50U2xpZGUpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsncHJlemljdXJyZW50J10gPSBjdXJyZW50U2xpZGU7XG4gICAgICAgIH0sXG4gICAgICAgIGdldFByZXppOiBmdW5jdGlvbiAocm9vbWppZCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJlemlNYXBbcm9vbWppZF07XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEV0aGVycGFkVG9QcmVzZW5jZTogZnVuY3Rpb24gKGV0aGVycGFkTmFtZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydldGhlcnBhZG5zJ10gPSAnaHR0cDovL2ppdHNpLm9yZy9qaXRtZWV0L2V0aGVycGFkJztcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnZXRoZXJwYWRuYW1lJ10gPSBldGhlcnBhZE5hbWU7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEF1ZGlvSW5mb1RvUHJlc2VuY2U6IGZ1bmN0aW9uIChpc011dGVkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2F1ZGlvbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvYXVkaW8nO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydhdWRpb211dGVkJ10gPSBpc011dGVkLnRvU3RyaW5nKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZFZpZGVvSW5mb1RvUHJlc2VuY2U6IGZ1bmN0aW9uIChpc011dGVkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ZpZGVvbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvdmlkZW8nO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWyd2aWRlb211dGVkJ10gPSBpc011dGVkLnRvU3RyaW5nKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZENvbm5lY3Rpb25JbmZvVG9QcmVzZW5jZTogZnVuY3Rpb24gKHN0YXRzKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3N0YXRzbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvc3RhdHMnO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzdGF0cyddID0gc3RhdHM7XG4gICAgICAgIH0sXG4gICAgICAgIGZpbmRKaWRGcm9tUmVzb3VyY2U6IGZ1bmN0aW9uIChyZXNvdXJjZUppZCkge1xuICAgICAgICAgICAgaWYgKHJlc291cmNlSmlkICYmXG4gICAgICAgICAgICAgICAgcmVzb3VyY2VKaWQgPT09IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKHRoaXMubXlyb29tamlkKSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLm15cm9vbWppZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBwZWVySmlkID0gbnVsbDtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMubWVtYmVycykuc29tZShmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgICAgICAgICAgcGVlckppZCA9IGppZDtcbiAgICAgICAgICAgICAgICByZXR1cm4gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoamlkKSA9PT0gcmVzb3VyY2VKaWQ7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiBwZWVySmlkO1xuICAgICAgICB9LFxuICAgICAgICBhZGRCcmlkZ2VJc0Rvd25Ub1ByZXNlbmNlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2JyaWRnZUlzRG93biddID0gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkRW1haWxUb1ByZXNlbmNlOiBmdW5jdGlvbiAoZW1haWwpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnZW1haWwnXSA9IGVtYWlsO1xuICAgICAgICB9LFxuICAgICAgICBhZGRVc2VySWRUb1ByZXNlbmNlOiBmdW5jdGlvbiAodXNlcklkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3VzZXJJZCddID0gdXNlcklkO1xuICAgICAgICB9LFxuICAgICAgICBpc01vZGVyYXRvcjogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucm9sZSA9PT0gJ21vZGVyYXRvcic7XG4gICAgICAgIH0sXG4gICAgICAgIGdldE1lbWJlclJvbGU6IGZ1bmN0aW9uIChwZWVySmlkKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5tZW1iZXJzW3BlZXJKaWRdKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubWVtYmVyc1twZWVySmlkXS5yb2xlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUGFydGljaXBhbnRMZWZ0OiBmdW5jdGlvbiAoamlkKSB7XG5cbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuTVVDX0xFRlQsIGppZCk7XG5cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5qaW5nbGUudGVybWluYXRlQnlKaWQoamlkKTtcblxuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0UHJlemkoamlkKSkge1xuICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ByZXNlbnRhdGlvbnJlbW92ZWQubXVjJyxcbiAgICAgICAgICAgICAgICAgICAgW2ppZCwgdGhpcy5nZXRQcmV6aShqaWQpXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIE1vZGVyYXRvci5vbk11Y0xlZnQoamlkKTtcbiAgICAgICAgfSxcbiAgICAgICAgcGFyc2VQcmVzZW5jZTogZnVuY3Rpb24gKGZyb20sIG1lbWViZXIsIHByZXMpIHtcbiAgICAgICAgICAgIGlmKCQocHJlcykuZmluZChcIj5icmlkZ2VJc0Rvd25cIikubGVuZ3RoID4gMCAmJiAhYnJpZGdlSXNEb3duKSB7XG4gICAgICAgICAgICAgICAgYnJpZGdlSXNEb3duID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkJSSURHRV9ET1dOKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYobWVtZWJlci5pc0ZvY3VzKVxuICAgICAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgLy8gUmVtb3ZlIG9sZCBzc3JjcyBjb21pbmcgZnJvbSB0aGUgamlkXG4gICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnNzcmMyamlkKS5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYuc3NyYzJqaWRbc3NyY10gPT0gZnJvbSkge1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgc2VsZi5zc3JjMmppZFtzc3JjXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmFyIGNoYW5nZWRTdHJlYW1zID0gW107XG4gICAgICAgICAgICAkKHByZXMpLmZpbmQoJz5tZWRpYVt4bWxucz1cImh0dHA6Ly9lc3Rvcy5kZS9ucy9tanNcIl0+c291cmNlJykuZWFjaChmdW5jdGlvbiAoaWR4LCBzc3JjKSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhqaWQsICdhc3NvYyBzc3JjJywgc3NyYy5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSwgc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKSk7XG4gICAgICAgICAgICAgICAgdmFyIHNzcmNWID0gc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgICAgICBzZWxmLnNzcmMyamlkW3NzcmNWXSA9IGZyb207XG4gICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5ub3RSZWNlaXZlZFNTUkNzLnB1c2goc3NyY1YpO1xuXG5cbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IHNzcmMuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG5cbiAgICAgICAgICAgICAgICB2YXIgZGlyZWN0aW9uID0gc3NyYy5nZXRBdHRyaWJ1dGUoJ2RpcmVjdGlvbicpO1xuXG4gICAgICAgICAgICAgICAgY2hhbmdlZFN0cmVhbXMucHVzaCh7dHlwZTogdHlwZSwgZGlyZWN0aW9uOiBkaXJlY3Rpb259KTtcblxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuQ0hBTkdFRF9TVFJFQU1TLCBmcm9tLCBjaGFuZ2VkU3RyZWFtcyk7XG5cbiAgICAgICAgICAgIHZhciBkaXNwbGF5TmFtZSA9ICFjb25maWcuZGlzcGxheUppZHNcbiAgICAgICAgICAgICAgICA/IG1lbWViZXIuZGlzcGxheU5hbWUgOiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKTtcblxuICAgICAgICAgICAgaWYgKGRpc3BsYXlOYW1lICYmIGRpc3BsYXlOYW1lLmxlbmd0aCA+IDApXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5ESVNQTEFZX05BTUVfQ0hBTkdFRCwgZnJvbSwgZGlzcGxheU5hbWUpO1xuICAgICAgICAgICAgfVxuXG5cbiAgICAgICAgICAgIHZhciBpZCA9ICQocHJlcykuZmluZCgnPnVzZXJJRCcpLnRleHQoKTtcbiAgICAgICAgICAgIHZhciBlbWFpbCA9ICQocHJlcykuZmluZCgnPmVtYWlsJyk7XG4gICAgICAgICAgICBpZihlbWFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgaWQgPSBlbWFpbC50ZXh0KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuVVNFUl9JRF9DSEFOR0VELCBmcm9tLCBpZCk7XG4gICAgICAgIH1cbiAgICB9KTtcbn07XG5cbiIsIi8qIGpzaGludCAtVzExNyAqL1xuXG52YXIgSmluZ2xlU2Vzc2lvbiA9IHJlcXVpcmUoXCIuL0ppbmdsZVNlc3Npb25cIik7XG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcblxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKFhNUFAsIGV2ZW50RW1pdHRlcilcbntcbiAgICBmdW5jdGlvbiBDYWxsSW5jb21pbmdKaW5nbGUoc2lkLCBjb25uZWN0aW9uKSB7XG4gICAgICAgIHZhciBzZXNzID0gY29ubmVjdGlvbi5qaW5nbGUuc2Vzc2lvbnNbc2lkXTtcblxuICAgICAgICAvLyBUT0RPOiBkbyB3ZSBjaGVjayBhY3RpdmVjYWxsID09IG51bGw/XG4gICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwgPSBzZXNzO1xuXG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuQ0FMTF9JTkNPTUlORywgc2Vzcyk7XG5cbiAgICAgICAgLy8gVE9ETzogY2hlY2sgYWZmaWxpYXRpb24gYW5kL29yIHJvbGVcbiAgICAgICAgY29uc29sZS5sb2coJ2VtdWMgZGF0YSBmb3InLCBzZXNzLnBlZXJqaWQsIGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzW3Nlc3MucGVlcmppZF0pO1xuICAgICAgICBzZXNzLnVzZWRyaXAgPSB0cnVlOyAvLyBub3Qtc28tbmFpdmUgdHJpY2tsZSBpY2VcbiAgICAgICAgc2Vzcy5zZW5kQW5zd2VyKCk7XG4gICAgICAgIHNlc3MuYWNjZXB0KCk7XG5cbiAgICB9O1xuXG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdqaW5nbGUnLCB7XG4gICAgICAgIGNvbm5lY3Rpb246IG51bGwsXG4gICAgICAgIHNlc3Npb25zOiB7fSxcbiAgICAgICAgamlkMnNlc3Npb246IHt9LFxuICAgICAgICBpY2VfY29uZmlnOiB7aWNlU2VydmVyczogW119LFxuICAgICAgICBwY19jb25zdHJhaW50czoge30sXG4gICAgICAgIGFjdGl2ZWNhbGw6IG51bGwsXG4gICAgICAgIG1lZGlhX2NvbnN0cmFpbnRzOiB7XG4gICAgICAgICAgICBtYW5kYXRvcnk6IHtcbiAgICAgICAgICAgICAgICAnT2ZmZXJUb1JlY2VpdmVBdWRpbyc6IHRydWUsXG4gICAgICAgICAgICAgICAgJ09mZmVyVG9SZWNlaXZlVmlkZW8nOiB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBNb3pEb250T2ZmZXJEYXRhQ2hhbm5lbDogdHJ1ZSB3aGVuIHRoaXMgaXMgZmlyZWZveFxuICAgICAgICB9LFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24uZGlzY28pIHtcbiAgICAgICAgICAgICAgICAvLyBodHRwOi8veG1wcC5vcmcvZXh0ZW5zaW9ucy94ZXAtMDE2Ny5odG1sI3N1cHBvcnRcbiAgICAgICAgICAgICAgICAvLyBodHRwOi8veG1wcC5vcmcvZXh0ZW5zaW9ucy94ZXAtMDE3Ni5odG1sI3N1cHBvcnRcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOjEnKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOjEnKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmR0bHMtc2N0cDoxJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDphdWRpbycpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6dmlkZW8nKTtcblxuXG4gICAgICAgICAgICAgICAgLy8gdGhpcyBpcyBkZWFsdCB3aXRoIGJ5IFNEUCBPL0Egc28gd2UgZG9uJ3QgbmVlZCB0byBhbm5vdWNlIHRoaXNcbiAgICAgICAgICAgICAgICAvL3RoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRjcC1mYjowJyk7IC8vIFhFUC0wMjkzXG4gICAgICAgICAgICAgICAgLy90aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0cC1oZHJleHQ6MCcpOyAvLyBYRVAtMDI5NFxuICAgICAgICAgICAgICAgIGlmIChjb25maWcudXNlUnRjcE11eCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOmlldGY6cmZjOjU3NjEnKTsgLy8gcnRjcC1tdXhcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGNvbmZpZy51c2VCdW5kbGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3VybjppZXRmOnJmYzo1ODg4Jyk7IC8vIGE9Z3JvdXAsIGUuZy4gYnVuZGxlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3VybjppZXRmOnJmYzo1NTc2Jyk7IC8vIGE9c3NyY1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmFkZEhhbmRsZXIodGhpcy5vbkppbmdsZS5iaW5kKHRoaXMpLCAndXJuOnhtcHA6amluZ2xlOjEnLCAnaXEnLCAnc2V0JywgbnVsbCwgbnVsbCk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uSmluZ2xlOiBmdW5jdGlvbiAoaXEpIHtcbiAgICAgICAgICAgIHZhciBzaWQgPSAkKGlxKS5maW5kKCdqaW5nbGUnKS5hdHRyKCdzaWQnKTtcbiAgICAgICAgICAgIHZhciBhY3Rpb24gPSAkKGlxKS5maW5kKCdqaW5nbGUnKS5hdHRyKCdhY3Rpb24nKTtcbiAgICAgICAgICAgIHZhciBmcm9tSmlkID0gaXEuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICAvLyBzZW5kIGFjayBmaXJzdFxuICAgICAgICAgICAgdmFyIGFjayA9ICRpcSh7dHlwZTogJ3Jlc3VsdCcsXG4gICAgICAgICAgICAgICAgdG86IGZyb21KaWQsXG4gICAgICAgICAgICAgICAgaWQ6IGlxLmdldEF0dHJpYnV0ZSgnaWQnKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnb24gamluZ2xlICcgKyBhY3Rpb24gKyAnIGZyb20gJyArIGZyb21KaWQsIGlxKTtcbiAgICAgICAgICAgIHZhciBzZXNzID0gdGhpcy5zZXNzaW9uc1tzaWRdO1xuICAgICAgICAgICAgaWYgKCdzZXNzaW9uLWluaXRpYXRlJyAhPSBhY3Rpb24pIHtcbiAgICAgICAgICAgICAgICBpZiAoc2VzcyA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBhY2sudHlwZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgIGFjay5jKCdlcnJvcicsIHt0eXBlOiAnY2FuY2VsJ30pXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygnaXRlbS1ub3QtZm91bmQnLCB7eG1sbnM6ICd1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphcyd9KS51cCgpXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygndW5rbm93bi1zZXNzaW9uJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOmVycm9yczoxJ30pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChhY2spO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gY29tcGFyZSBmcm9tIHRvIHNlc3MucGVlcmppZCAoYmFyZSBqaWQgY29tcGFyaXNvbiBmb3IgbGF0ZXIgY29tcGF0IHdpdGggbWVzc2FnZS1tb2RlKVxuICAgICAgICAgICAgICAgIC8vIGxvY2FsIGppZCBpcyBub3QgY2hlY2tlZFxuICAgICAgICAgICAgICAgIGlmIChTdHJvcGhlLmdldEJhcmVKaWRGcm9tSmlkKGZyb21KaWQpICE9IFN0cm9waGUuZ2V0QmFyZUppZEZyb21KaWQoc2Vzcy5wZWVyamlkKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2ppZCBtaXNtYXRjaCBmb3Igc2Vzc2lvbiBpZCcsIHNpZCwgZnJvbUppZCwgc2Vzcy5wZWVyamlkKTtcbiAgICAgICAgICAgICAgICAgICAgYWNrLnR5cGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgICAgICBhY2suYygnZXJyb3InLCB7dHlwZTogJ2NhbmNlbCd9KVxuICAgICAgICAgICAgICAgICAgICAgICAgLmMoJ2l0ZW0tbm90LWZvdW5kJywge3htbG5zOiAndXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXMnfSkudXAoKVxuICAgICAgICAgICAgICAgICAgICAgICAgLmMoJ3Vua25vd24tc2Vzc2lvbicsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTplcnJvcnM6MSd9KTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChzZXNzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBleGlzdGluZyBzZXNzaW9uIHdpdGggc2FtZSBzZXNzaW9uIGlkXG4gICAgICAgICAgICAgICAgLy8gdGhpcyBtaWdodCBiZSBvdXQtb2Ytb3JkZXIgaWYgdGhlIHNlc3MucGVlcmppZCBpcyB0aGUgc2FtZSBhcyBmcm9tXG4gICAgICAgICAgICAgICAgYWNrLnR5cGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgIGFjay5jKCdlcnJvcicsIHt0eXBlOiAnY2FuY2VsJ30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCdzZXJ2aWNlLXVuYXZhaWxhYmxlJywge3htbG5zOiAndXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXMnfSkudXAoKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2R1cGxpY2F0ZSBzZXNzaW9uIGlkJywgc2lkKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChhY2spO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gRklYTUU6IGNoZWNrIGZvciBhIGRlZmluZWQgYWN0aW9uXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChhY2spO1xuICAgICAgICAgICAgLy8gc2VlIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMTY2Lmh0bWwjY29uY2VwdHMtc2Vzc2lvblxuICAgICAgICAgICAgc3dpdGNoIChhY3Rpb24pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdzZXNzaW9uLWluaXRpYXRlJzpcbiAgICAgICAgICAgICAgICAgICAgc2VzcyA9IG5ldyBKaW5nbGVTZXNzaW9uKFxuICAgICAgICAgICAgICAgICAgICAgICAgJChpcSkuYXR0cigndG8nKSwgJChpcSkuZmluZCgnamluZ2xlJykuYXR0cignc2lkJyksXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24sIFhNUFApO1xuICAgICAgICAgICAgICAgICAgICAvLyBjb25maWd1cmUgc2Vzc2lvblxuXG4gICAgICAgICAgICAgICAgICAgIHNlc3MubWVkaWFfY29uc3RyYWludHMgPSB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnBjX2NvbnN0cmFpbnRzID0gdGhpcy5wY19jb25zdHJhaW50cztcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5pY2VfY29uZmlnID0gdGhpcy5pY2VfY29uZmlnO1xuXG4gICAgICAgICAgICAgICAgICAgIHNlc3MuaW5pdGlhdGUoZnJvbUppZCwgZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogc2V0UmVtb3RlRGVzY3JpcHRpb24gc2hvdWxkIG9ubHkgYmUgZG9uZSB3aGVuIHRoaXMgY2FsbCBpcyB0byBiZSBhY2NlcHRlZFxuICAgICAgICAgICAgICAgICAgICBzZXNzLnNldFJlbW90ZURlc2NyaXB0aW9uKCQoaXEpLmZpbmQoJz5qaW5nbGUnKSwgJ29mZmVyJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzZXNzLnNpZF0gPSBzZXNzO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmppZDJzZXNzaW9uW3Nlc3MucGVlcmppZF0gPSBzZXNzO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIHRoZSBjYWxsYmFjayBzaG91bGQgZWl0aGVyXG4gICAgICAgICAgICAgICAgICAgIC8vIC5zZW5kQW5zd2VyIGFuZCAuYWNjZXB0XG4gICAgICAgICAgICAgICAgICAgIC8vIG9yIC5zZW5kVGVybWluYXRlIC0tIG5vdCBuZWNlc3NhcmlseSBzeW5jaHJvbnVzXG4gICAgICAgICAgICAgICAgICAgIENhbGxJbmNvbWluZ0ppbmdsZShzZXNzLnNpZCwgdGhpcy5jb25uZWN0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi1hY2NlcHQnOlxuICAgICAgICAgICAgICAgICAgICBzZXNzLnNldFJlbW90ZURlc2NyaXB0aW9uKCQoaXEpLmZpbmQoJz5qaW5nbGUnKSwgJ2Fuc3dlcicpO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLmFjY2VwdCgpO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsYWNjZXB0ZWQuamluZ2xlJywgW3Nlc3Muc2lkXSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Nlc3Npb24tdGVybWluYXRlJzpcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhpcyBpcyBub3QgdGhlIGZvY3VzIHNlbmRpbmcgdGhlIHRlcm1pbmF0ZSwgd2UgaGF2ZVxuICAgICAgICAgICAgICAgICAgICAvLyBub3RoaW5nIG1vcmUgdG8gZG8gaGVyZS5cbiAgICAgICAgICAgICAgICAgICAgaWYgKE9iamVjdC5rZXlzKHRoaXMuc2Vzc2lvbnMpLmxlbmd0aCA8IDFcbiAgICAgICAgICAgICAgICAgICAgICAgIHx8ICEodGhpcy5zZXNzaW9uc1tPYmplY3Qua2V5cyh0aGlzLnNlc3Npb25zKVswXV1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnN0YW5jZW9mIEppbmdsZVNlc3Npb24pKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndGVybWluYXRpbmcuLi4nLCBzZXNzLnNpZCk7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MudGVybWluYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudGVybWluYXRlKHNlc3Muc2lkKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCQoaXEpLmZpbmQoJz5qaW5nbGU+cmVhc29uJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLCBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vzcy5zaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vzcy5wZWVyamlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICQoaXEpLmZpbmQoJz5qaW5nbGU+cmVhc29uPjpmaXJzdCcpWzBdLnRhZ05hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJChpcSkuZmluZCgnPmppbmdsZT5yZWFzb24+dGV4dCcpLnRleHQoKVxuICAgICAgICAgICAgICAgICAgICAgICAgXSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzZXNzLnNpZCwgc2Vzcy5wZWVyamlkXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndHJhbnNwb3J0LWluZm8nOlxuICAgICAgICAgICAgICAgICAgICBzZXNzLmFkZEljZUNhbmRpZGF0ZSgkKGlxKS5maW5kKCc+amluZ2xlPmNvbnRlbnQnKSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Nlc3Npb24taW5mbyc6XG4gICAgICAgICAgICAgICAgICAgIHZhciBhZmZlY3RlZDtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCQoaXEpLmZpbmQoJz5qaW5nbGU+cmluZ2luZ1t4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3JpbmdpbmcuamluZ2xlJywgW3Nlc3Muc2lkXSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoJChpcSkuZmluZCgnPmppbmdsZT5tdXRlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYWZmZWN0ZWQgPSAkKGlxKS5maW5kKCc+amluZ2xlPm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykuYXR0cignbmFtZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignbXV0ZS5qaW5nbGUnLCBbc2Vzcy5zaWQsIGFmZmVjdGVkXSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoJChpcSkuZmluZCgnPmppbmdsZT51bm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhZmZlY3RlZCA9ICQoaXEpLmZpbmQoJz5qaW5nbGU+dW5tdXRlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3VubXV0ZS5qaW5nbGUnLCBbc2Vzcy5zaWQsIGFmZmVjdGVkXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnYWRkc291cmNlJzogLy8gRklYTUU6IHByb3ByaWV0YXJ5LCB1bi1qaW5nbGVpc2hcbiAgICAgICAgICAgICAgICBjYXNlICdzb3VyY2UtYWRkJzogLy8gRklYTUU6IHByb3ByaWV0YXJ5XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuYWRkU291cmNlKCQoaXEpLmZpbmQoJz5qaW5nbGU+Y29udGVudCcpLCBmcm9tSmlkKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncmVtb3Zlc291cmNlJzogLy8gRklYTUU6IHByb3ByaWV0YXJ5LCB1bi1qaW5nbGVpc2hcbiAgICAgICAgICAgICAgICBjYXNlICdzb3VyY2UtcmVtb3ZlJzogLy8gRklYTUU6IHByb3ByaWV0YXJ5XG4gICAgICAgICAgICAgICAgICAgIHNlc3MucmVtb3ZlU291cmNlKCQoaXEpLmZpbmQoJz5qaW5nbGU+Y29udGVudCcpLCBmcm9tSmlkKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdqaW5nbGUgYWN0aW9uIG5vdCBpbXBsZW1lbnRlZCcsIGFjdGlvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH0sXG4gICAgICAgIGluaXRpYXRlOiBmdW5jdGlvbiAocGVlcmppZCwgbXlqaWQpIHsgLy8gaW5pdGlhdGUgYSBuZXcgamluZ2xlc2Vzc2lvbiB0byBwZWVyamlkXG4gICAgICAgICAgICB2YXIgc2VzcyA9IG5ldyBKaW5nbGVTZXNzaW9uKG15amlkIHx8IHRoaXMuY29ubmVjdGlvbi5qaWQsXG4gICAgICAgICAgICAgICAgTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDEyKSwgLy8gcmFuZG9tIHN0cmluZ1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiwgWE1QUCk7XG4gICAgICAgICAgICAvLyBjb25maWd1cmUgc2Vzc2lvblxuXG4gICAgICAgICAgICBzZXNzLm1lZGlhX2NvbnN0cmFpbnRzID0gdGhpcy5tZWRpYV9jb25zdHJhaW50cztcbiAgICAgICAgICAgIHNlc3MucGNfY29uc3RyYWludHMgPSB0aGlzLnBjX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgc2Vzcy5pY2VfY29uZmlnID0gdGhpcy5pY2VfY29uZmlnO1xuXG4gICAgICAgICAgICBzZXNzLmluaXRpYXRlKHBlZXJqaWQsIHRydWUpO1xuICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzZXNzLnNpZF0gPSBzZXNzO1xuICAgICAgICAgICAgdGhpcy5qaWQyc2Vzc2lvbltzZXNzLnBlZXJqaWRdID0gc2VzcztcbiAgICAgICAgICAgIHNlc3Muc2VuZE9mZmVyKCk7XG4gICAgICAgICAgICByZXR1cm4gc2VzcztcbiAgICAgICAgfSxcbiAgICAgICAgdGVybWluYXRlOiBmdW5jdGlvbiAoc2lkLCByZWFzb24sIHRleHQpIHsgLy8gdGVybWluYXRlIGJ5IHNlc3Npb25pZCAob3IgYWxsIHNlc3Npb25zKVxuICAgICAgICAgICAgaWYgKHNpZCA9PT0gbnVsbCB8fCBzaWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGZvciAoc2lkIGluIHRoaXMuc2Vzc2lvbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuc2Vzc2lvbnNbc2lkXS5zdGF0ZSAhPSAnZW5kZWQnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3NpZF0uc2VuZFRlcm1pbmF0ZShyZWFzb24gfHwgKCF0aGlzLnNlc3Npb25zW3NpZF0uYWN0aXZlKCkpID8gJ2NhbmNlbCcgOiBudWxsLCB0ZXh0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2lkXS50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvblt0aGlzLnNlc3Npb25zW3NpZF0ucGVlcmppZF07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW3NpZF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLnNlc3Npb25zLmhhc093blByb3BlcnR5KHNpZCkpIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zZXNzaW9uc1tzaWRdLnN0YXRlICE9ICdlbmRlZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnNlbmRUZXJtaW5hdGUocmVhc29uIHx8ICghdGhpcy5zZXNzaW9uc1tzaWRdLmFjdGl2ZSgpKSA/ICdjYW5jZWwnIDogbnVsbCwgdGV4dCk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2lkXS50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuamlkMnNlc3Npb25bdGhpcy5zZXNzaW9uc1tzaWRdLnBlZXJqaWRdO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW3NpZF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIC8vIFVzZWQgdG8gdGVybWluYXRlIGEgc2Vzc2lvbiB3aGVuIGFuIHVuYXZhaWxhYmxlIHByZXNlbmNlIGlzIHJlY2VpdmVkLlxuICAgICAgICB0ZXJtaW5hdGVCeUppZDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAgICAgaWYgKHRoaXMuamlkMnNlc3Npb24uaGFzT3duUHJvcGVydHkoamlkKSkge1xuICAgICAgICAgICAgICAgIHZhciBzZXNzID0gdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgIGlmIChzZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MudGVybWluYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdwZWVyIHdlbnQgYXdheSBzaWxlbnRseScsIGppZCk7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuamlkMnNlc3Npb25bamlkXTtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignY2FsbHRlcm1pbmF0ZWQuamluZ2xlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIFtzZXNzLnNpZCwgamlkXSwgJ2dvbmUnKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIHRlcm1pbmF0ZVJlbW90ZUJ5SmlkOiBmdW5jdGlvbiAoamlkLCByZWFzb24pIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmppZDJzZXNzaW9uLmhhc093blByb3BlcnR5KGppZCkpIHtcbiAgICAgICAgICAgICAgICB2YXIgc2VzcyA9IHRoaXMuamlkMnNlc3Npb25bamlkXTtcbiAgICAgICAgICAgICAgICBpZiAoc2Vzcykge1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnNlbmRUZXJtaW5hdGUocmVhc29uIHx8ICghc2Vzcy5hY3RpdmUoKSkgPyAna2ljaycgOiBudWxsKTtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3Rlcm1pbmF0ZSBwZWVyIHdpdGggamlkJywgc2Vzcy5zaWQsIGppZCk7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuamlkMnNlc3Npb25bamlkXTtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignY2FsbHRlcm1pbmF0ZWQuamluZ2xlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIFtzZXNzLnNpZCwgamlkLCAna2lja2VkJ10pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZ2V0U3R1bkFuZFR1cm5DcmVkZW50aWFsczogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gZ2V0IHN0dW4gYW5kIHR1cm4gY29uZmlndXJhdGlvbiBmcm9tIHNlcnZlciB2aWEgeGVwLTAyMTVcbiAgICAgICAgICAgIC8vIHVzZXMgdGltZS1saW1pdGVkIGNyZWRlbnRpYWxzIGFzIGRlc2NyaWJlZCBpblxuICAgICAgICAgICAgLy8gaHR0cDovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtdWJlcnRpLWJlaGF2ZS10dXJuLXJlc3QtMDBcbiAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAvLyBzZWUgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9wcm9zb2R5LW1vZHVsZXMvc291cmNlL2Jyb3dzZS9tb2RfdHVybmNyZWRlbnRpYWxzL21vZF90dXJuY3JlZGVudGlhbHMubHVhXG4gICAgICAgICAgICAvLyBmb3IgYSBwcm9zb2R5IG1vZHVsZSB3aGljaCBpbXBsZW1lbnRzIHRoaXNcbiAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAvLyBjdXJyZW50bHksIHRoaXMgZG9lc24ndCB3b3JrIHdpdGggdXBkYXRlSWNlIGFuZCB0aGVyZWZvcmUgY3JlZGVudGlhbHMgd2l0aCBhIGxvbmdcbiAgICAgICAgICAgIC8vIHZhbGlkaXR5IGhhdmUgdG8gYmUgZmV0Y2hlZCBiZWZvcmUgY3JlYXRpbmcgdGhlIHBlZXJjb25uZWN0aW9uXG4gICAgICAgICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgcmVmcmVzaCB2aWEgdXBkYXRlSWNlIGFzIGRlc2NyaWJlZCBpblxuICAgICAgICAgICAgLy8gICAgICBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTE2NTBcbiAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgJGlxKHt0eXBlOiAnZ2V0JywgdG86IHRoaXMuY29ubmVjdGlvbi5kb21haW59KVxuICAgICAgICAgICAgICAgICAgICAuYygnc2VydmljZXMnLCB7eG1sbnM6ICd1cm46eG1wcDpleHRkaXNjbzoxJ30pLmMoJ3NlcnZpY2UnLCB7aG9zdDogJ3R1cm4uJyArIHRoaXMuY29ubmVjdGlvbi5kb21haW59KSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBpY2VzZXJ2ZXJzID0gW107XG4gICAgICAgICAgICAgICAgICAgICQocmVzKS5maW5kKCc+c2VydmljZXM+c2VydmljZScpLmVhY2goZnVuY3Rpb24gKGlkeCwgZWwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsID0gJChlbCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZGljdCA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHR5cGUgPSBlbC5hdHRyKCd0eXBlJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzdHVuJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgPSAnc3R1bjonICsgZWwuYXR0cignaG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigncG9ydCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCArPSAnOicgKyBlbC5hdHRyKCdwb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWNlc2VydmVycy5wdXNoKGRpY3QpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICd0dXJuJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICd0dXJucyc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsID0gdHlwZSArICc6JztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVsLmF0dHIoJ3VzZXJuYW1lJykpIHsgLy8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD0xNTA4XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvQ2hyb20oZXxpdW0pXFwvKFswLTldKylcXC4vKSAmJiBwYXJzZUludChuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKC9DaHJvbShlfGl1bSlcXC8oWzAtOV0rKVxcLi8pWzJdLCAxMCkgPCAyOCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9IGVsLmF0dHIoJ3VzZXJuYW1lJykgKyAnQCc7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXNlcm5hbWUgPSBlbC5hdHRyKCd1c2VybmFtZScpOyAvLyBvbmx5IHdvcmtzIGluIE0yOFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9IGVsLmF0dHIoJ2hvc3QnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVsLmF0dHIoJ3BvcnQnKSAmJiBlbC5hdHRyKCdwb3J0JykgIT0gJzM0NzgnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCArPSAnOicgKyBlbC5hdHRyKCdwb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVsLmF0dHIoJ3RyYW5zcG9ydCcpICYmIGVsLmF0dHIoJ3RyYW5zcG9ydCcpICE9ICd1ZHAnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCArPSAnP3RyYW5zcG9ydD0nICsgZWwuYXR0cigndHJhbnNwb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVsLmF0dHIoJ3Bhc3N3b3JkJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QuY3JlZGVudGlhbCA9IGVsLmF0dHIoJ3Bhc3N3b3JkJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWNlc2VydmVycy5wdXNoKGRpY3QpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuaWNlX2NvbmZpZy5pY2VTZXJ2ZXJzID0gaWNlc2VydmVycztcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdnZXR0aW5nIHR1cm4gY3JlZGVudGlhbHMgZmFpbGVkJywgZXJyKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdpcyBtb2RfdHVybmNyZWRlbnRpYWxzIG9yIHNpbWlsYXIgaW5zdGFsbGVkPycpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICAvLyBpbXBsZW1lbnQgcHVzaD9cbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUG9wdWxhdGVzIHRoZSBsb2cgZGF0YVxuICAgICAgICAgKi9cbiAgICAgICAgcG9wdWxhdGVEYXRhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgZGF0YSA9IHt9O1xuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5zZXNzaW9ucykuZm9yRWFjaChmdW5jdGlvbiAoc2lkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLnNlc3Npb25zW3NpZF07XG4gICAgICAgICAgICAgICAgaWYgKHNlc3Npb24ucGVlcmNvbm5lY3Rpb24gJiYgc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi51cGRhdGVMb2cpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IHNob3VsZCBwcm9iYWJseSBiZSBhIC5kdW1wIGNhbGxcbiAgICAgICAgICAgICAgICAgICAgZGF0YVtcImppbmdsZV9cIiArIHNlc3Npb24uc2lkXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVwZGF0ZUxvZzogc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi51cGRhdGVMb2csXG4gICAgICAgICAgICAgICAgICAgICAgICBzdGF0czogc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi5zdGF0cyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybDogd2luZG93LmxvY2F0aW9uLmhyZWZcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICB9XG4gICAgfSk7XG59O1xuXG4iLCIvKiBnbG9iYWwgU3Ryb3BoZSAqL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ2xvZ2dlcicsIHtcbiAgICAgICAgLy8gbG9ncyByYXcgc3RhbnphcyBhbmQgbWFrZXMgdGhlbSBhdmFpbGFibGUgZm9yIGRvd25sb2FkIGFzIEpTT05cbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgbG9nOiBbXSxcbiAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24ucmF3SW5wdXQgPSB0aGlzLmxvZ19pbmNvbWluZy5iaW5kKHRoaXMpO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnJhd091dHB1dCA9IHRoaXMubG9nX291dGdvaW5nLmJpbmQodGhpcyk7XG4gICAgICAgIH0sXG4gICAgICAgIGxvZ19pbmNvbWluZzogZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdGhpcy5sb2cucHVzaChbbmV3IERhdGUoKS5nZXRUaW1lKCksICdpbmNvbWluZycsIHN0YW56YV0pO1xuICAgICAgICB9LFxuICAgICAgICBsb2dfb3V0Z29pbmc6IGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLnB1c2goW25ldyBEYXRlKCkuZ2V0VGltZSgpLCAnb3V0Z29pbmcnLCBzdGFuemFdKTtcbiAgICAgICAgfVxuICAgIH0pO1xufTsiLCIvKiBnbG9iYWwgJCwgJGlxLCBjb25maWcsIGNvbm5lY3Rpb24sIGZvY3VzTXVjSmlkLCBmb3JjZU11dGVkLFxuICAgc2V0QXVkaW9NdXRlZCwgU3Ryb3BoZSAqL1xuLyoqXG4gKiBNb2RlcmF0ZSBjb25uZWN0aW9uIHBsdWdpbi5cbiAqL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoWE1QUCkge1xuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignbW9kZXJhdGUnLCB7XG4gICAgICAgIGNvbm5lY3Rpb246IG51bGwsXG4gICAgICAgIGluaXQ6IGZ1bmN0aW9uIChjb25uKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uTXV0ZS5iaW5kKHRoaXMpLFxuICAgICAgICAgICAgICAgICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvYXVkaW8nLFxuICAgICAgICAgICAgICAgICdpcScsXG4gICAgICAgICAgICAgICAgJ3NldCcsXG4gICAgICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICAgICAgICBudWxsKTtcbiAgICAgICAgfSxcbiAgICAgICAgc2V0TXV0ZTogZnVuY3Rpb24gKGppZCwgbXV0ZSkge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFwic2V0IG11dGVcIiwgbXV0ZSk7XG4gICAgICAgICAgICB2YXIgaXFUb0ZvY3VzID0gJGlxKHt0bzogdGhpcy5jb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgICAgICAuYygnbXV0ZScsIHtcbiAgICAgICAgICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvYXVkaW8nLFxuICAgICAgICAgICAgICAgICAgICBqaWQ6IGppZFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLnQobXV0ZS50b1N0cmluZygpKVxuICAgICAgICAgICAgICAgIC51cCgpO1xuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgIGlxVG9Gb2N1cyxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzZXQgbXV0ZScsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldCBtdXRlIGVycm9yJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICB9LFxuICAgICAgICBvbk11dGU6IGZ1bmN0aW9uIChpcSkge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBpcS5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIGlmIChmcm9tICE9PSB0aGlzLmNvbm5lY3Rpb24uZW11Yy5mb2N1c011Y0ppZCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIklnbm9yZWQgbXV0ZSBmcm9tIG5vbiBmb2N1cyBwZWVyXCIpO1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBtdXRlID0gJChpcSkuZmluZCgnbXV0ZScpO1xuICAgICAgICAgICAgaWYgKG11dGUubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRvTXV0ZUF1ZGlvID0gbXV0ZS50ZXh0KCkgPT09IFwidHJ1ZVwiO1xuICAgICAgICAgICAgICAgIEFQUC5VSS5zZXRBdWRpb011dGVkKGRvTXV0ZUF1ZGlvKTtcbiAgICAgICAgICAgICAgICBYTVBQLmZvcmNlTXV0ZWQgPSBkb011dGVBdWRpbztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBlamVjdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICAgICAgLy8gV2UncmUgbm90IHRoZSBmb2N1cywgc28gY2FuJ3QgdGVybWluYXRlXG4gICAgICAgICAgICAvL2Nvbm5lY3Rpb24uamluZ2xlLnRlcm1pbmF0ZVJlbW90ZUJ5SmlkKGppZCwgJ2tpY2snKTtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmtpY2soamlkKTtcbiAgICAgICAgfVxuICAgIH0pO1xufSIsIi8qIGpzaGludCAtVzExNyAqL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbigpIHtcbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ3JheW8nLFxuICAgICAgICB7XG4gICAgICAgICAgICBSQVlPX1hNTE5TOiAndXJuOnhtcHA6cmF5bzoxJyxcbiAgICAgICAgICAgIGNvbm5lY3Rpb246IG51bGwsXG4gICAgICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6cmF5bzpjbGllbnQ6MScpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKFxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uUmF5by5iaW5kKHRoaXMpLCB0aGlzLlJBWU9fWE1MTlMsICdpcScsICdzZXQnLCBudWxsLCBudWxsKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvblJheW86IGZ1bmN0aW9uIChpcSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIlJheW8gSVFcIiwgaXEpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGRpYWw6IGZ1bmN0aW9uICh0bywgZnJvbSwgcm9vbU5hbWUsIHJvb21QYXNzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgICAgIHZhciByZXEgPSAkaXEoXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzZXQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdG86IHRoaXMuY29ubmVjdGlvbi5lbXVjLmZvY3VzTXVjSmlkXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHJlcS5jKCdkaWFsJyxcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgeG1sbnM6IHRoaXMuUkFZT19YTUxOUyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiB0byxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZyb206IGZyb21cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgcmVxLmMoJ2hlYWRlcicsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6ICdKdmJSb29tTmFtZScsXG4gICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZTogcm9vbU5hbWVcbiAgICAgICAgICAgICAgICAgICAgfSkudXAoKTtcblxuICAgICAgICAgICAgICAgIGlmIChyb29tUGFzcyAhPT0gbnVsbCAmJiByb29tUGFzcy5sZW5ndGgpIHtcblxuICAgICAgICAgICAgICAgICAgICByZXEuYygnaGVhZGVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiAnSnZiUm9vbVBhc3N3b3JkJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZTogcm9vbVBhc3NcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLnVwKCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICAgICAgcmVxLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0RpYWwgcmVzdWx0ICcsIHJlc3VsdCk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciByZXNvdXJjZSA9ICQocmVzdWx0KS5maW5kKCdyZWYnKS5hdHRyKCd1cmknKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2FsbF9yZXNvdXJjZSA9IHJlc291cmNlLnN1YnN0cigneG1wcDonLmxlbmd0aCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiUmVjZWl2ZWQgY2FsbCByZXNvdXJjZTogXCIgKyB0aGlzLmNhbGxfcmVzb3VyY2UpO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnRGlhbCBlcnJvciAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGhhbmdfdXA6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY2FsbF9yZXNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJObyBjYWxsIGluIHByb2dyZXNzXCIpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgICAgIHZhciByZXEgPSAkaXEoXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzZXQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdG86IHRoaXMuY2FsbF9yZXNvdXJjZVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXEuYygnaGFuZ3VwJyxcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgeG1sbnM6IHRoaXMuUkFZT19YTUxOU1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgICAgIHJlcSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdIYW5ndXAgcmVzdWx0ICcsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNhbGxfcmVzb3VyY2UgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnSGFuZ3VwIGVycm9yICcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY2FsbF9yZXNvdXJjZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgKTtcbn07XG4iLCIvKipcbiAqIFN0cm9waGUgbG9nZ2VyIGltcGxlbWVudGF0aW9uLiBMb2dzIGZyb20gbGV2ZWwgV0FSTiBhbmQgYWJvdmUuXG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkge1xuXG4gICAgU3Ryb3BoZS5sb2cgPSBmdW5jdGlvbiAobGV2ZWwsIG1zZykge1xuICAgICAgICBzd2l0Y2ggKGxldmVsKSB7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuTG9nTGV2ZWwuV0FSTjpcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJTdHJvcGhlOiBcIiArIG1zZyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuTG9nTGV2ZWwuRVJST1I6XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuTG9nTGV2ZWwuRkFUQUw6XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIlN0cm9waGU6IFwiICsgbXNnKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBTdHJvcGhlLmdldFN0YXR1c1N0cmluZyA9IGZ1bmN0aW9uIChzdGF0dXMpIHtcbiAgICAgICAgc3dpdGNoIChzdGF0dXMpIHtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuRVJST1I6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiRVJST1JcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuQ09OTkVDVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJDT05ORUNUSU5HXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkNPTk5GQUlMOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkNPTk5GQUlMXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkFVVEhFTlRJQ0FUSU5HOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkFVVEhFTlRJQ0FUSU5HXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkFVVEhGQUlMOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkFVVEhGQUlMXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkNPTk5FQ1RFRDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJDT05ORUNURURcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVEVEOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkRJU0NPTk5FQ1RFRFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5ESVNDT05ORUNUSU5HOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkRJU0NPTk5FQ1RJTkdcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuQVRUQUNIRUQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQVRUQUNIRURcIjtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwidW5rbm93blwiO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCJ2YXIgTW9kZXJhdG9yID0gcmVxdWlyZShcIi4vbW9kZXJhdG9yXCIpO1xudmFyIEV2ZW50RW1pdHRlciA9IHJlcXVpcmUoXCJldmVudHNcIik7XG52YXIgUmVjb3JkaW5nID0gcmVxdWlyZShcIi4vcmVjb3JkaW5nXCIpO1xudmFyIFNEUCA9IHJlcXVpcmUoXCIuL1NEUFwiKTtcbnZhciBQYWtvID0gcmVxdWlyZShcInBha29cIik7XG52YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL1JUQy9TdHJlYW1FdmVudFR5cGVzXCIpO1xudmFyIFVJRXZlbnRzID0gcmVxdWlyZShcIi4uLy4uL3NlcnZpY2UvVUkvVUlFdmVudHNcIik7XG52YXIgWE1QUEV2ZW50cyA9IHJlcXVpcmUoXCIuLi8uLi9zZXJ2aWNlL3htcHAvWE1QUEV2ZW50c1wiKTtcblxudmFyIGV2ZW50RW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbnZhciBjb25uZWN0aW9uID0gbnVsbDtcbnZhciBhdXRoZW50aWNhdGVkVXNlciA9IGZhbHNlO1xuXG5mdW5jdGlvbiBjb25uZWN0KGppZCwgcGFzc3dvcmQsIHVpQ3JlZGVudGlhbHMpIHtcbiAgICB2YXIgYm9zaFxuICAgICAgICA9ICh1aUNyZWRlbnRpYWxzICYmIHVpQ3JlZGVudGlhbHMuYm9zaD8gdWlDcmVkZW50aWFscy5ib3NoIDogbnVsbClcbiAgICAgICAgfHwgY29uZmlnLmJvc2ggfHwgJy9odHRwLWJpbmQnO1xuICAgIGNvbm5lY3Rpb24gPSBuZXcgU3Ryb3BoZS5Db25uZWN0aW9uKGJvc2gpO1xuICAgIE1vZGVyYXRvci5zZXRDb25uZWN0aW9uKGNvbm5lY3Rpb24pO1xuXG4gICAgaWYodWlDcmVkZW50aWFscykge1xuICAgICAgICB2YXIgZW1haWwgPSB1aUNyZWRlbnRpYWxzLmVtYWlsO1xuICAgICAgICB2YXIgZGlzcGxheU5hbWUgPSB1aUNyZWRlbnRpYWxzLmRpc3BsYXlOYW1lO1xuICAgICAgICBpZiAoZW1haWwpIHtcbiAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRFbWFpbFRvUHJlc2VuY2UoZW1haWwpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZFVzZXJJZFRvUHJlc2VuY2UodWlDcmVkZW50aWFscy51aWQpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChkaXNwbGF5TmFtZSkge1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZERpc3BsYXlOYW1lVG9QcmVzZW5jZShkaXNwbGF5TmFtZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAvLyBmb3IgY2hyb21lLCBhZGQgbXVsdGlzdHJlYW0gY2FwXG4gICAgfVxuICAgIGNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzID0gQVBQLlJUQy5nZXRQQ0NvbnN0cmFpbnRzKCk7XG4gICAgaWYgKGNvbmZpZy51c2VJUHY2KSB7XG4gICAgICAgIC8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9MjgyOFxuICAgICAgICBpZiAoIWNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzLm9wdGlvbmFsKVxuICAgICAgICAgICAgY29ubmVjdGlvbi5qaW5nbGUucGNfY29uc3RyYWludHMub3B0aW9uYWwgPSBbXTtcbiAgICAgICAgY29ubmVjdGlvbi5qaW5nbGUucGNfY29uc3RyYWludHMub3B0aW9uYWwucHVzaCh7Z29vZ0lQdjY6IHRydWV9KTtcbiAgICB9XG5cbiAgICBpZighcGFzc3dvcmQpXG4gICAgICAgIHBhc3N3b3JkID0gdWlDcmVkZW50aWFscy5wYXNzd29yZDtcblxuICAgIHZhciBhbm9ueW1vdXNDb25uZWN0aW9uRmFpbGVkID0gZmFsc2U7XG4gICAgY29ubmVjdGlvbi5jb25uZWN0KGppZCwgcGFzc3dvcmQsIGZ1bmN0aW9uIChzdGF0dXMsIG1zZykge1xuICAgICAgICBjb25zb2xlLmxvZygnU3Ryb3BoZSBzdGF0dXMgY2hhbmdlZCB0bycsXG4gICAgICAgICAgICBTdHJvcGhlLmdldFN0YXR1c1N0cmluZyhzdGF0dXMpKTtcbiAgICAgICAgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICBpZiAoY29uZmlnLnVzZVN0dW5UdXJuKSB7XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5qaW5nbGUuZ2V0U3R1bkFuZFR1cm5DcmVkZW50aWFscygpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgQVBQLlVJLmRpc2FibGVDb25uZWN0KCk7XG5cbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIk15IEphYmJlciBJRDogXCIgKyBjb25uZWN0aW9uLmppZCk7XG5cbiAgICAgICAgICAgIGlmKHBhc3N3b3JkKVxuICAgICAgICAgICAgICAgIGF1dGhlbnRpY2F0ZWRVc2VyID0gdHJ1ZTtcbiAgICAgICAgICAgIG1heWJlRG9Kb2luKCk7XG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdHVzID09PSBTdHJvcGhlLlN0YXR1cy5DT05ORkFJTCkge1xuICAgICAgICAgICAgaWYobXNnID09PSAneC1zdHJvcGhlLWJhZC1ub24tYW5vbi1qaWQnKSB7XG4gICAgICAgICAgICAgICAgYW5vbnltb3VzQ29ubmVjdGlvbkZhaWxlZCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdHVzID09PSBTdHJvcGhlLlN0YXR1cy5ESVNDT05ORUNURUQpIHtcbiAgICAgICAgICAgIGlmKGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBwcm9tcHQgdXNlciBmb3IgdXNlcm5hbWUgYW5kIHBhc3N3b3JkXG4gICAgICAgICAgICAgICAgWE1QUC5wcm9tcHRMb2dpbigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQVVUSEZBSUwpIHtcbiAgICAgICAgICAgIC8vIHdyb25nIHBhc3N3b3JkIG9yIHVzZXJuYW1lLCBwcm9tcHQgdXNlclxuICAgICAgICAgICAgWE1QUC5wcm9tcHRMb2dpbigpO1xuXG4gICAgICAgIH1cbiAgICB9KTtcbn1cblxuXG5cbmZ1bmN0aW9uIG1heWJlRG9Kb2luKCkge1xuICAgIGlmIChjb25uZWN0aW9uICYmIGNvbm5lY3Rpb24uY29ubmVjdGVkICYmXG4gICAgICAgIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGNvbm5lY3Rpb24uamlkKVxuICAgICAgICAmJiAoQVBQLlJUQy5sb2NhbEF1ZGlvIHx8IEFQUC5SVEMubG9jYWxWaWRlbykpIHtcbiAgICAgICAgLy8gLmNvbm5lY3RlZCBpcyB0cnVlIHdoaWxlIGNvbm5lY3Rpbmc/XG4gICAgICAgIGRvSm9pbigpO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gZG9Kb2luKCkge1xuICAgIHZhciByb29tTmFtZSA9IEFQUC5VSS5nZW5lcmF0ZVJvb21OYW1lKCk7XG5cbiAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgIHJvb21OYW1lLCBBUFAuVUkuY2hlY2tGb3JOaWNrbmFtZUFuZEpvaW4pO1xufVxuXG5mdW5jdGlvbiBpbml0U3Ryb3BoZVBsdWdpbnMoKVxue1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuZW11Y1wiKShYTVBQLCBldmVudEVtaXR0ZXIpO1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuamluZ2xlXCIpKFhNUFAsIGV2ZW50RW1pdHRlcik7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS5tb2RlcmF0ZVwiKShYTVBQKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnV0aWxcIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnJheW9cIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLmxvZ2dlclwiKSgpO1xufVxuXG5mdW5jdGlvbiByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBBUFAuUlRDLmFkZFN0cmVhbUxpc3RlbmVyKG1heWJlRG9Kb2luLFxuICAgICAgICBTdHJlYW1FdmVudFR5cGVzLkVWRU5UX1RZUEVfTE9DQUxfQ1JFQVRFRCk7XG4gICAgQVBQLlVJLmFkZExpc3RlbmVyKFVJRXZlbnRzLk5JQ0tOQU1FX0NIQU5HRUQsIGZ1bmN0aW9uIChuaWNrbmFtZSkge1xuICAgICAgICBYTVBQLmFkZFRvUHJlc2VuY2UoXCJkaXNwbGF5TmFtZVwiLCBuaWNrbmFtZSk7XG4gICAgfSk7XG59XG5cbmZ1bmN0aW9uIHNldHVwRXZlbnRzKCkge1xuICAgICQod2luZG93KS5iaW5kKCdiZWZvcmV1bmxvYWQnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChjb25uZWN0aW9uICYmIGNvbm5lY3Rpb24uY29ubmVjdGVkKSB7XG4gICAgICAgICAgICAvLyBlbnN1cmUgc2lnbm91dFxuICAgICAgICAgICAgJC5hamF4KHtcbiAgICAgICAgICAgICAgICB0eXBlOiAnUE9TVCcsXG4gICAgICAgICAgICAgICAgdXJsOiBjb25maWcuYm9zaCxcbiAgICAgICAgICAgICAgICBhc3luYzogZmFsc2UsXG4gICAgICAgICAgICAgICAgY2FjaGU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGNvbnRlbnRUeXBlOiAnYXBwbGljYXRpb24veG1sJyxcbiAgICAgICAgICAgICAgICBkYXRhOiBcIjxib2R5IHJpZD0nXCIgKyAoY29ubmVjdGlvbi5yaWQgfHwgY29ubmVjdGlvbi5fcHJvdG8ucmlkKVxuICAgICAgICAgICAgICAgICAgICArIFwiJyB4bWxucz0naHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvaHR0cGJpbmQnIHNpZD0nXCJcbiAgICAgICAgICAgICAgICAgICAgKyAoY29ubmVjdGlvbi5zaWQgfHwgY29ubmVjdGlvbi5fcHJvdG8uc2lkKVxuICAgICAgICAgICAgICAgICAgICArIFwiJyB0eXBlPSd0ZXJtaW5hdGUnPlwiICtcbiAgICAgICAgICAgICAgICAgICAgXCI8cHJlc2VuY2UgeG1sbnM9J2phYmJlcjpjbGllbnQnIHR5cGU9J3VuYXZhaWxhYmxlJy8+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjwvYm9keT5cIixcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmdW5jdGlvbiAoZGF0YSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnc2lnbmVkIG91dCcpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGVycm9yOiBmdW5jdGlvbiAoWE1MSHR0cFJlcXVlc3QsIHRleHRTdGF0dXMsIGVycm9yVGhyb3duKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzaWdub3V0IGVycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0U3RhdHVzICsgJyAoJyArIGVycm9yVGhyb3duICsgJyknKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBYTVBQLmRpc3Bvc2VDb25mZXJlbmNlKHRydWUpO1xuICAgIH0pO1xufVxuXG52YXIgWE1QUCA9IHtcbiAgICBzZXNzaW9uVGVybWluYXRlZDogZmFsc2UsXG4gICAgLyoqXG4gICAgICogUmVtZW1iZXJzIGlmIHdlIHdlcmUgbXV0ZWQgYnkgdGhlIGZvY3VzLlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgIGZvcmNlTXV0ZWQ6IGZhbHNlLFxuICAgIHN0YXJ0OiBmdW5jdGlvbiAodWlDcmVkZW50aWFscykge1xuICAgICAgICBzZXR1cEV2ZW50cygpO1xuICAgICAgICBpbml0U3Ryb3BoZVBsdWdpbnMoKTtcbiAgICAgICAgcmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICAgICAgTW9kZXJhdG9yLmluaXQodGhpcywgZXZlbnRFbWl0dGVyKTtcbiAgICAgICAgdmFyIGNvbmZpZ0RvbWFpbiA9IGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4gfHwgY29uZmlnLmhvc3RzLmRvbWFpbjtcbiAgICAgICAgLy8gRm9yY2UgYXV0aGVudGljYXRlZCBkb21haW4gaWYgcm9vbSBpcyBhcHBlbmRlZCB3aXRoICc/bG9naW49dHJ1ZSdcbiAgICAgICAgaWYgKGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4gJiZcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5zZWFyY2guaW5kZXhPZihcImxvZ2luPXRydWVcIikgIT09IC0xKSB7XG4gICAgICAgICAgICBjb25maWdEb21haW4gPSBjb25maWcuaG9zdHMuZG9tYWluO1xuICAgICAgICB9XG4gICAgICAgIHZhciBqaWQgPSB1aUNyZWRlbnRpYWxzLmppZCB8fCBjb25maWdEb21haW4gfHwgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lO1xuICAgICAgICBjb25uZWN0KGppZCwgbnVsbCwgdWlDcmVkZW50aWFscyk7XG4gICAgfSxcbiAgICBwcm9tcHRMb2dpbjogZnVuY3Rpb24gKCkge1xuICAgICAgICBBUFAuVUkuc2hvd0xvZ2luUG9wdXAoY29ubmVjdCk7XG4gICAgfSxcbiAgICBqb2luUm9vbTogZnVuY3Rpb24ocm9vbU5hbWUsIHVzZU5pY2tzLCBuaWNrKVxuICAgIHtcbiAgICAgICAgdmFyIHJvb21qaWQ7XG4gICAgICAgIHJvb21qaWQgPSByb29tTmFtZTtcblxuICAgICAgICBpZiAodXNlTmlja3MpIHtcbiAgICAgICAgICAgIGlmIChuaWNrKSB7XG4gICAgICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyBuaWNrO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByb29tamlkICs9ICcvJyArIFN0cm9waGUuZ2V0Tm9kZUZyb21KaWQoY29ubmVjdGlvbi5qaWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICB2YXIgdG1wSmlkID0gU3Ryb3BoZS5nZXROb2RlRnJvbUppZChjb25uZWN0aW9uLmppZCk7XG5cbiAgICAgICAgICAgIGlmKCFhdXRoZW50aWNhdGVkVXNlcilcbiAgICAgICAgICAgICAgICB0bXBKaWQgPSB0bXBKaWQuc3Vic3RyKDAsIDgpO1xuXG4gICAgICAgICAgICByb29tamlkICs9ICcvJyArIHRtcEppZDtcbiAgICAgICAgfVxuICAgICAgICBjb25uZWN0aW9uLmVtdWMuZG9Kb2luKHJvb21qaWQpO1xuICAgIH0sXG4gICAgbXlKaWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYoIWNvbm5lY3Rpb24pXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQ7XG4gICAgfSxcbiAgICBteVJlc291cmNlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uIHx8ICEgY29ubmVjdGlvbi5lbXVjLm15cm9vbWppZClcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICByZXR1cm4gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQoY29ubmVjdGlvbi5lbXVjLm15cm9vbWppZCk7XG4gICAgfSxcbiAgICBkaXNwb3NlQ29uZmVyZW5jZTogZnVuY3Rpb24gKG9uVW5sb2FkKSB7XG4gICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuRElTUE9TRV9DT05GRVJFTkNFLCBvblVubG9hZCk7XG4gICAgICAgIHZhciBoYW5kbGVyID0gY29ubmVjdGlvbi5qaW5nbGUuYWN0aXZlY2FsbDtcbiAgICAgICAgaWYgKGhhbmRsZXIgJiYgaGFuZGxlci5wZWVyY29ubmVjdGlvbikge1xuICAgICAgICAgICAgLy8gRklYTUU6IHByb2JhYmx5IHJlbW92aW5nIHN0cmVhbXMgaXMgbm90IHJlcXVpcmVkIGFuZCBjbG9zZSgpIHNob3VsZFxuICAgICAgICAgICAgLy8gYmUgZW5vdWdoXG4gICAgICAgICAgICBpZiAoQVBQLlJUQy5sb2NhbEF1ZGlvKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oQVBQLlJUQy5sb2NhbEF1ZGlvLmdldE9yaWdpbmFsU3RyZWFtKCksIG9uVW5sb2FkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChBUFAuUlRDLmxvY2FsVmlkZW8pIHtcbiAgICAgICAgICAgICAgICBoYW5kbGVyLnBlZXJjb25uZWN0aW9uLnJlbW92ZVN0cmVhbShBUFAuUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwgPSBudWxsO1xuICAgICAgICBpZighb25VbmxvYWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIHJlbW92ZUxpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbihyb29tTmFtZSwgY2FsbGJhY2spIHtcbiAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgfSxcbiAgICBpc01vZGVyYXRvcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCk7XG4gICAgfSxcbiAgICBpc1NpcEdhdGV3YXlFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNTaXBHYXRld2F5RW5hYmxlZCgpO1xuICAgIH0sXG4gICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNFeHRlcm5hbEF1dGhFbmFibGVkKCk7XG4gICAgfSxcbiAgICBzd2l0Y2hTdHJlYW1zOiBmdW5jdGlvbiAoc3RyZWFtLCBvbGRTdHJlYW0sIGNhbGxiYWNrKSB7XG4gICAgICAgIGlmIChjb25uZWN0aW9uICYmIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiB3aWxsIGJsb2NrIHN3aXRjaEluUHJvZ3Jlc3Mgb24gdHJ1ZSB2YWx1ZSBpbiBjYXNlIG9mIGV4Y2VwdGlvblxuICAgICAgICAgICAgY29ubmVjdGlvbi5qaW5nbGUuYWN0aXZlY2FsbC5zd2l0Y2hTdHJlYW1zKHN0cmVhbSwgb2xkU3RyZWFtLCBjYWxsYmFjayk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBXZSBhcmUgZG9uZSBpbW1lZGlhdGVseVxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIk5vIGNvbmZlcmVuY2UgaGFuZGxlclwiKTtcbiAgICAgICAgICAgIEFQUC5VSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ0Vycm9yJyxcbiAgICAgICAgICAgICAgICAnVW5hYmxlIHRvIHN3aXRjaCB2aWRlbyBzdHJlYW0uJyk7XG4gICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZXRWaWRlb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgICAgIGlmKGNvbm5lY3Rpb24gJiYgQVBQLlJUQy5sb2NhbFZpZGVvICYmIGNvbm5lY3Rpb24uamluZ2xlLmFjdGl2ZWNhbGwpXG4gICAgICAge1xuICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5hY3RpdmVjYWxsLnNldFZpZGVvTXV0ZShtdXRlLCBjYWxsYmFjaywgb3B0aW9ucyk7XG4gICAgICAgfVxuICAgIH0sXG4gICAgc2V0QXVkaW9NdXRlOiBmdW5jdGlvbiAobXV0ZSwgY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKCEoY29ubmVjdGlvbiAmJiBBUFAuUlRDLmxvY2FsQXVkaW8pKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuXG4gICAgICAgIGlmICh0aGlzLmZvcmNlTXV0ZWQgJiYgIW11dGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIkFza2luZyBmb2N1cyBmb3IgdW5tdXRlXCIpO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQsIG11dGUpO1xuICAgICAgICAgICAgLy8gRklYTUU6IHdhaXQgZm9yIHJlc3VsdCBiZWZvcmUgcmVzZXR0aW5nIG11dGVkIHN0YXR1c1xuICAgICAgICAgICAgdGhpcy5mb3JjZU11dGVkID0gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobXV0ZSA9PSBBUFAuUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAvLyBOb3RoaW5nIHRvIGRvXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSByaWdodCB3YXkgdG8gaGFuZGxlIG11bHRpcGxlIHRyYWNrcy5cbiAgICAgICAgLy8gU28gYXQgbGVhc3QgbWFrZSBzdXJlIHRoYXQgdGhleSBhcmUgYWxsIG11dGVkIG9yIGFsbCB1bm11dGVkIGFuZFxuICAgICAgICAvLyB0aGF0IHdlIHNlbmQgcHJlc2VuY2UganVzdCBvbmNlLlxuICAgICAgICBBUFAuUlRDLmxvY2FsQXVkaW8ubXV0ZSgpO1xuICAgICAgICAvLyBpc011dGVkIGlzIHRoZSBvcHBvc2l0ZSBvZiBhdWRpb0VuYWJsZWRcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEF1ZGlvSW5mb1RvUHJlc2VuY2UobXV0ZSk7XG4gICAgICAgIGNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSxcbiAgICAvLyBSZWFsbHkgbXV0ZSB2aWRlbywgaS5lLiBkb250IGV2ZW4gc2VuZCBibGFjayBmcmFtZXNcbiAgICBtdXRlVmlkZW86IGZ1bmN0aW9uIChwYywgdW5tdXRlKSB7XG4gICAgICAgIC8vIEZJWE1FOiB0aGlzIHByb2JhYmx5IG5lZWRzIGFub3RoZXIgb2YgdGhvc2UgbG92ZWx5IHN0YXRlIHNhZmVndWFyZHMuLi5cbiAgICAgICAgLy8gd2hpY2ggY2hlY2tzIGZvciBpY2Vjb25uID09IGNvbm5lY3RlZCBhbmQgc2lnc3RhdGUgPT0gc3RhYmxlXG4gICAgICAgIHBjLnNldFJlbW90ZURlc2NyaXB0aW9uKHBjLnJlbW90ZURlc2NyaXB0aW9uLFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHBjLmNyZWF0ZUFuc3dlcihcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGFuc3dlcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHNkcCA9IG5ldyBTRFAoYW5zd2VyLnNkcCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2RwLm1lZGlhLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodW5tdXRlKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1zZW5kcmVjdicsICdhPXJlY3Zvbmx5Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2RwLnJhdyA9IHNkcC5zZXNzaW9uICsgc2RwLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuc3dlci5zZHAgPSBzZHAucmF3O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgcGMuc2V0TG9jYWxEZXNjcmlwdGlvbihhbnN3ZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnbXV0ZSBTTEQgb2snKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnbXV0ZSBTTEQgZXJyb3InKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcignRXJyb3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdPb3BzISBTb21ldGhpbmcgd2VudCB3cm9uZyBhbmQgd2UgZmFpbGVkIHRvICcgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdtdXRlISAoU0xEIEZhaWx1cmUpJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBBUFAuVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlVmlkZW8gU1JEIGVycm9yJyk7XG4gICAgICAgICAgICAgICAgQVBQLlVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcignRXJyb3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgJ09vcHMhIFNvbWV0aGluZyB3ZW50IHdyb25nIGFuZCB3ZSBmYWlsZWQgdG8gc3RvcCB2aWRlbyEnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICcoU1JEIEZhaWx1cmUpJyk7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuICAgIHRvZ2dsZVJlY29yZGluZzogZnVuY3Rpb24gKHRva2VuRW1wdHlDYWxsYmFjayxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydGluZ0NhbGxiYWNrLCBzdGFydGVkQ2FsbGJhY2spIHtcbiAgICAgICAgUmVjb3JkaW5nLnRvZ2dsZVJlY29yZGluZyh0b2tlbkVtcHR5Q2FsbGJhY2ssXG4gICAgICAgICAgICBzdGFydGluZ0NhbGxiYWNrLCBzdGFydGVkQ2FsbGJhY2ssIGNvbm5lY3Rpb24pO1xuICAgIH0sXG4gICAgYWRkVG9QcmVzZW5jZTogZnVuY3Rpb24gKG5hbWUsIHZhbHVlLCBkb250U2VuZCkge1xuICAgICAgICBzd2l0Y2ggKG5hbWUpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGNhc2UgXCJkaXNwbGF5TmFtZVwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGREaXNwbGF5TmFtZVRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcImV0aGVycGFkXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEV0aGVycGFkVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwicHJlemlcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkUHJlemlUb1ByZXNlbmNlKHZhbHVlLCAwKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJwcmV6aVNsaWRlXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZEN1cnJlbnRTbGlkZVRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcImNvbm5lY3Rpb25RdWFsaXR5XCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZENvbm5lY3Rpb25JbmZvVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiZW1haWxcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRW1haWxUb1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgIGRlZmF1bHQgOlxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiVW5rbm93biB0YWcgZm9yIHByZXNlbmNlLlwiKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYoIWRvbnRTZW5kKVxuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgc2VuZExvZ3M6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uLmVtdWMuZm9jdXNNdWNKaWQpXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdmFyIGRlZmxhdGUgPSB0cnVlO1xuXG4gICAgICAgIHZhciBjb250ZW50ID0gSlNPTi5zdHJpbmdpZnkoZGF0YSk7XG4gICAgICAgIGlmIChkZWZsYXRlKSB7XG4gICAgICAgICAgICBjb250ZW50ID0gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBQYWtvLmRlZmxhdGVSYXcoY29udGVudCkpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnRlbnQgPSBCYXNlNjQuZW5jb2RlKGNvbnRlbnQpO1xuICAgICAgICAvLyBYRVAtMDMzNy1pc2hcbiAgICAgICAgdmFyIG1lc3NhZ2UgPSAkbXNnKHt0bzogY29ubmVjdGlvbi5lbXVjLmZvY3VzTXVjSmlkLCB0eXBlOiAnbm9ybWFsJ30pO1xuICAgICAgICBtZXNzYWdlLmMoJ2xvZycsIHsgeG1sbnM6ICd1cm46eG1wcDpldmVudGxvZycsXG4gICAgICAgICAgICBpZDogJ1BlZXJDb25uZWN0aW9uU3RhdHMnfSk7XG4gICAgICAgIG1lc3NhZ2UuYygnbWVzc2FnZScpLnQoY29udGVudCkudXAoKTtcbiAgICAgICAgaWYgKGRlZmxhdGUpIHtcbiAgICAgICAgICAgIG1lc3NhZ2UuYygndGFnJywge25hbWU6IFwiZGVmbGF0ZWRcIiwgdmFsdWU6IFwidHJ1ZVwifSkudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlLnVwKCk7XG5cbiAgICAgICAgY29ubmVjdGlvbi5zZW5kKG1lc3NhZ2UpO1xuICAgIH0sXG4gICAgcG9wdWxhdGVEYXRhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGlmIChjb25uZWN0aW9uLmppbmdsZSkge1xuICAgICAgICAgICAgZGF0YSA9IGNvbm5lY3Rpb24uamluZ2xlLnBvcHVsYXRlRGF0YSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgZ2V0TG9nZ2VyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKGNvbm5lY3Rpb24ubG9nZ2VyKVxuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24ubG9nZ2VyLmxvZztcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSxcbiAgICBnZXRQcmV6aTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmdldFByZXppKHRoaXMubXlKaWQoKSk7XG4gICAgfSxcbiAgICByZW1vdmVQcmV6aUZyb21QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgc2VuZENoYXRNZXNzYWdlOiBmdW5jdGlvbiAobWVzc2FnZSwgbmlja25hbWUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRNZXNzYWdlKG1lc3NhZ2UsIG5pY2tuYW1lKTtcbiAgICB9LFxuICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uICh0b3BpYykge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2V0U3ViamVjdCh0b3BpYyk7XG4gICAgfSxcbiAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMubG9ja1Jvb20oa2V5LCBvblN1Y2Nlc3MsIG9uRXJyb3IsIG9uTm90U3VwcG9ydGVkKTtcbiAgICB9LFxuICAgIGRpYWw6IGZ1bmN0aW9uICh0bywgZnJvbSwgcm9vbU5hbWUscm9vbVBhc3MpIHtcbiAgICAgICAgY29ubmVjdGlvbi5yYXlvLmRpYWwodG8sIGZyb20sIHJvb21OYW1lLHJvb21QYXNzKTtcbiAgICB9LFxuICAgIHNldE11dGU6IGZ1bmN0aW9uIChqaWQsIG11dGUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGppZCwgbXV0ZSk7XG4gICAgfSxcbiAgICBlamVjdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25uZWN0aW9uLm1vZGVyYXRlLmVqZWN0KGppZCk7XG4gICAgfSxcbiAgICBmaW5kSmlkRnJvbVJlc291cmNlOiBmdW5jdGlvbiAocmVzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24uZW11Yy5maW5kSmlkRnJvbVJlc291cmNlKHJlc291cmNlKTtcbiAgICB9LFxuICAgIGdldE1lbWJlcnM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzO1xuICAgIH0sXG4gICAgZ2V0SmlkRnJvbVNTUkM6IGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmVtdWMuc3NyYzJqaWRbc3NyY107XG4gICAgfSxcbiAgICBnZXRNVUNKb2luZWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24uZW11Yy5qb2luZWQ7XG4gICAgfSxcbiAgICBnZXRTZXNzaW9uczogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5qaW5nbGUuc2Vzc2lvbnM7XG4gICAgfVxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFhNUFA7XG4iLCIvLyBpMThuZXh0LCB2MS43Ljdcbi8vIENvcHlyaWdodCAoYykyMDE0IEphbiBNw7xobGVtYW5uIChqYW11aGwpLlxuLy8gRGlzdHJpYnV0ZWQgdW5kZXIgTUlUIGxpY2Vuc2Vcbi8vIGh0dHA6Ly9pMThuZXh0LmNvbVxuKGZ1bmN0aW9uKCkge1xuXG4gICAgLy8gYWRkIGluZGV4T2YgdG8gbm9uIEVDTUEtMjYyIHN0YW5kYXJkIGNvbXBsaWFudCBicm93c2Vyc1xuICAgIGlmICghQXJyYXkucHJvdG90eXBlLmluZGV4T2YpIHtcbiAgICAgICAgQXJyYXkucHJvdG90eXBlLmluZGV4T2YgPSBmdW5jdGlvbiAoc2VhcmNoRWxlbWVudCAvKiwgZnJvbUluZGV4ICovICkge1xuICAgICAgICAgICAgXCJ1c2Ugc3RyaWN0XCI7XG4gICAgICAgICAgICBpZiAodGhpcyA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHQgPSBPYmplY3QodGhpcyk7XG4gICAgICAgICAgICB2YXIgbGVuID0gdC5sZW5ndGggPj4+IDA7XG4gICAgICAgICAgICBpZiAobGVuID09PSAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG4gPSAwO1xuICAgICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgbiA9IE51bWJlcihhcmd1bWVudHNbMV0pO1xuICAgICAgICAgICAgICAgIGlmIChuICE9IG4pIHsgLy8gc2hvcnRjdXQgZm9yIHZlcmlmeWluZyBpZiBpdCdzIE5hTlxuICAgICAgICAgICAgICAgICAgICBuID0gMDtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG4gIT0gMCAmJiBuICE9IEluZmluaXR5ICYmIG4gIT0gLUluZmluaXR5KSB7XG4gICAgICAgICAgICAgICAgICAgIG4gPSAobiA+IDAgfHwgLTEpICogTWF0aC5mbG9vcihNYXRoLmFicyhuKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG4gPj0gbGVuKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGsgPSBuID49IDAgPyBuIDogTWF0aC5tYXgobGVuIC0gTWF0aC5hYnMobiksIDApO1xuICAgICAgICAgICAgZm9yICg7IGsgPCBsZW47IGsrKykge1xuICAgICAgICAgICAgICAgIGlmIChrIGluIHQgJiYgdFtrXSA9PT0gc2VhcmNoRWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gLTE7XG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLy8gYWRkIGxhc3RJbmRleE9mIHRvIG5vbiBFQ01BLTI2MiBzdGFuZGFyZCBjb21wbGlhbnQgYnJvd3NlcnNcbiAgICBpZiAoIUFycmF5LnByb3RvdHlwZS5sYXN0SW5kZXhPZikge1xuICAgICAgICBBcnJheS5wcm90b3R5cGUubGFzdEluZGV4T2YgPSBmdW5jdGlvbihzZWFyY2hFbGVtZW50IC8qLCBmcm9tSW5kZXgqLykge1xuICAgICAgICAgICAgXCJ1c2Ugc3RyaWN0XCI7XG4gICAgICAgICAgICBpZiAodGhpcyA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHQgPSBPYmplY3QodGhpcyk7XG4gICAgICAgICAgICB2YXIgbGVuID0gdC5sZW5ndGggPj4+IDA7XG4gICAgICAgICAgICBpZiAobGVuID09PSAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG4gPSBsZW47XG4gICAgICAgICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICBuID0gTnVtYmVyKGFyZ3VtZW50c1sxXSk7XG4gICAgICAgICAgICAgICAgaWYgKG4gIT0gbikge1xuICAgICAgICAgICAgICAgICAgICBuID0gMDtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG4gIT0gMCAmJiBuICE9ICgxIC8gMCkgJiYgbiAhPSAtKDEgLyAwKSkge1xuICAgICAgICAgICAgICAgICAgICBuID0gKG4gPiAwIHx8IC0xKSAqIE1hdGguZmxvb3IoTWF0aC5hYnMobikpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBrID0gbiA+PSAwID8gTWF0aC5taW4obiwgbGVuIC0gMSkgOiBsZW4gLSBNYXRoLmFicyhuKTtcbiAgICAgICAgICAgIGZvciAoOyBrID49IDA7IGstLSkge1xuICAgICAgICAgICAgICAgIGlmIChrIGluIHQgJiYgdFtrXSA9PT0gc2VhcmNoRWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gLTE7XG4gICAgICAgIH07XG4gICAgfVxuICAgIFxuICAgIC8vIEFkZCBzdHJpbmcgdHJpbSBmb3IgSUU4LlxuICAgIGlmICh0eXBlb2YgU3RyaW5nLnByb3RvdHlwZS50cmltICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIFN0cmluZy5wcm90b3R5cGUudHJpbSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpOyBcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHZhciByb290ID0gdGhpc1xuICAgICAgLCAkID0gcm9vdC5qUXVlcnkgfHwgcm9vdC5aZXB0b1xuICAgICAgLCBpMThuID0ge31cbiAgICAgICwgcmVzU3RvcmUgPSB7fVxuICAgICAgLCBjdXJyZW50TG5nXG4gICAgICAsIHJlcGxhY2VtZW50Q291bnRlciA9IDBcbiAgICAgICwgbGFuZ3VhZ2VzID0gW11cbiAgICAgICwgaW5pdGlhbGl6ZWQgPSBmYWxzZVxuICAgICAgLCBzeW5jID0ge307XG5cblxuXG4gICAgLy8gRXhwb3J0IHRoZSBpMThuZXh0IG9iamVjdCBmb3IgKipDb21tb25KUyoqLiBcbiAgICAvLyBJZiB3ZSdyZSBub3QgaW4gQ29tbW9uSlMsIGFkZCBgaTE4bmAgdG8gdGhlXG4gICAgLy8gZ2xvYmFsIG9iamVjdCBvciB0byBqcXVlcnkuXG4gICAgaWYgKHR5cGVvZiBtb2R1bGUgIT09ICd1bmRlZmluZWQnICYmIG1vZHVsZS5leHBvcnRzKSB7XG4gICAgICAgIGlmICghJCkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAkID0gcmVxdWlyZSgnanF1ZXJ5Jyk7XG4gICAgICAgICAgfSBjYXRjaChlKSB7XG4gICAgICAgICAgICAvLyBqdXN0IGlnbm9yZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoJCkge1xuICAgICAgICAgICAgJC5pMThuID0gJC5pMThuIHx8IGkxOG47XG4gICAgICAgIH1cbiAgICAgICAgbW9kdWxlLmV4cG9ydHMgPSBpMThuO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICgkKSB7XG4gICAgICAgICAgICAkLmkxOG4gPSAkLmkxOG4gfHwgaTE4bjtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcm9vdC5pMThuID0gcm9vdC5pMThuIHx8IGkxOG47XG4gICAgfVxuICAgIHN5bmMgPSB7XG4gICAgXG4gICAgICAgIGxvYWQ6IGZ1bmN0aW9uKGxuZ3MsIG9wdGlvbnMsIGNiKSB7XG4gICAgICAgICAgICBpZiAob3B0aW9ucy51c2VMb2NhbFN0b3JhZ2UpIHtcbiAgICAgICAgICAgICAgICBzeW5jLl9sb2FkTG9jYWwobG5ncywgb3B0aW9ucywgZnVuY3Rpb24oZXJyLCBzdG9yZSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWlzc2luZ0xuZ3MgPSBbXTtcbiAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IGxuZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghc3RvcmVbbG5nc1tpXV0pIG1pc3NpbmdMbmdzLnB1c2gobG5nc1tpXSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1pc3NpbmdMbmdzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN5bmMuX2ZldGNoKG1pc3NpbmdMbmdzLCBvcHRpb25zLCBmdW5jdGlvbihlcnIsIGZldGNoZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmLmV4dGVuZChzdG9yZSwgZmV0Y2hlZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3luYy5fc3RvcmVMb2NhbChmZXRjaGVkKTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYihudWxsLCBzdG9yZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNiKG51bGwsIHN0b3JlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzeW5jLl9mZXRjaChsbmdzLCBvcHRpb25zLCBmdW5jdGlvbihlcnIsIHN0b3JlKXtcbiAgICAgICAgICAgICAgICAgICAgY2IobnVsbCwgc3RvcmUpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgIFxuICAgICAgICBfbG9hZExvY2FsOiBmdW5jdGlvbihsbmdzLCBvcHRpb25zLCBjYikge1xuICAgICAgICAgICAgdmFyIHN0b3JlID0ge31cbiAgICAgICAgICAgICAgLCBub3dNUyA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuICAgIFxuICAgICAgICAgICAgaWYod2luZG93LmxvY2FsU3RvcmFnZSkge1xuICAgIFxuICAgICAgICAgICAgICAgIHZhciB0b2RvID0gbG5ncy5sZW5ndGg7XG4gICAgXG4gICAgICAgICAgICAgICAgZi5lYWNoKGxuZ3MsIGZ1bmN0aW9uKGtleSwgbG5nKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBsb2NhbCA9IHdpbmRvdy5sb2NhbFN0b3JhZ2UuZ2V0SXRlbSgncmVzXycgKyBsbmcpO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICBpZiAobG9jYWwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsID0gSlNPTi5wYXJzZShsb2NhbCk7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9jYWwuaTE4blN0YW1wICYmIGxvY2FsLmkxOG5TdGFtcCArIG9wdGlvbnMubG9jYWxTdG9yYWdlRXhwaXJhdGlvblRpbWUgPiBub3dNUykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JlW2xuZ10gPSBsb2NhbDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgICAgICB0b2RvLS07IC8vIHdhaXQgZm9yIGFsbCBkb25lIGJlZm9yIGNhbGxiYWNrXG4gICAgICAgICAgICAgICAgICAgIGlmICh0b2RvID09PSAwKSBjYihudWxsLCBzdG9yZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIF9zdG9yZUxvY2FsOiBmdW5jdGlvbihzdG9yZSkge1xuICAgICAgICAgICAgaWYod2luZG93LmxvY2FsU3RvcmFnZSkge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIG0gaW4gc3RvcmUpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RvcmVbbV0uaTE4blN0YW1wID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG4gICAgICAgICAgICAgICAgICAgIGYubG9jYWxTdG9yYWdlLnNldEl0ZW0oJ3Jlc18nICsgbSwgSlNPTi5zdHJpbmdpZnkoc3RvcmVbbV0pKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIF9mZXRjaDogZnVuY3Rpb24obG5ncywgb3B0aW9ucywgY2IpIHtcbiAgICAgICAgICAgIHZhciBucyA9IG9wdGlvbnMubnNcbiAgICAgICAgICAgICAgLCBzdG9yZSA9IHt9O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoIW9wdGlvbnMuZHluYW1pY0xvYWQpIHtcbiAgICAgICAgICAgICAgICB2YXIgdG9kbyA9IG5zLm5hbWVzcGFjZXMubGVuZ3RoICogbG5ncy5sZW5ndGhcbiAgICAgICAgICAgICAgICAgICwgZXJyb3JzO1xuICAgIFxuICAgICAgICAgICAgICAgIC8vIGxvYWQgZWFjaCBmaWxlIGluZGl2aWR1YWxcbiAgICAgICAgICAgICAgICBmLmVhY2gobnMubmFtZXNwYWNlcywgZnVuY3Rpb24obnNJbmRleCwgbnNWYWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICBmLmVhY2gobG5ncywgZnVuY3Rpb24obG5nSW5kZXgsIGxuZ1ZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIENhbGwgdGhpcyBvbmNlIG91ciB0cmFuc2xhdGlvbiBoYXMgcmV0dXJuZWQuXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgbG9hZENvbXBsZXRlID0gZnVuY3Rpb24oZXJyLCBkYXRhKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcnMgPSBlcnJvcnMgfHwgW107XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9ycy5wdXNoKGVycik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JlW2xuZ1ZhbHVlXSA9IHN0b3JlW2xuZ1ZhbHVlXSB8fCB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9yZVtsbmdWYWx1ZV1bbnNWYWx1ZV0gPSBkYXRhO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvZG8tLTsgLy8gd2FpdCBmb3IgYWxsIGRvbmUgYmVmb3IgY2FsbGJhY2tcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodG9kbyA9PT0gMCkgY2IoZXJyb3JzLCBzdG9yZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICBpZih0eXBlb2Ygb3B0aW9ucy5jdXN0b21Mb2FkID09ICdmdW5jdGlvbicpe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFVzZSB0aGUgc3BlY2lmaWVkIGN1c3RvbSBjYWxsYmFjay5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zLmN1c3RvbUxvYWQobG5nVmFsdWUsIG5zVmFsdWUsIG9wdGlvbnMsIGxvYWRDb21wbGV0ZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vfiAvLyBVc2Ugb3VyIGluYnVpbHQgc3luYy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzeW5jLl9mZXRjaE9uZShsbmdWYWx1ZSwgbnNWYWx1ZSwgb3B0aW9ucywgbG9hZENvbXBsZXRlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIENhbGwgdGhpcyBvbmNlIG91ciB0cmFuc2xhdGlvbiBoYXMgcmV0dXJuZWQuXG4gICAgICAgICAgICAgICAgdmFyIGxvYWRDb21wbGV0ZSA9IGZ1bmN0aW9uKGVyciwgZGF0YSkge1xuICAgICAgICAgICAgICAgICAgICBjYihudWxsLCBkYXRhKTtcbiAgICAgICAgICAgICAgICB9O1xuICAgIFxuICAgICAgICAgICAgICAgIGlmKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvYWQgPT0gJ2Z1bmN0aW9uJyl7XG4gICAgICAgICAgICAgICAgICAgIC8vIFVzZSB0aGUgc3BlY2lmaWVkIGN1c3RvbSBjYWxsYmFjay5cbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2FkKGxuZ3MsIG5zLm5hbWVzcGFjZXMsIG9wdGlvbnMsIGxvYWRDb21wbGV0ZSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHVybCA9IGFwcGx5UmVwbGFjZW1lbnQob3B0aW9ucy5yZXNHZXRQYXRoLCB7IGxuZzogbG5ncy5qb2luKCcrJyksIG5zOiBucy5uYW1lc3BhY2VzLmpvaW4oJysnKSB9KTtcbiAgICAgICAgICAgICAgICAgICAgLy8gbG9hZCBhbGwgbmVlZGVkIHN0dWZmIG9uY2VcbiAgICAgICAgICAgICAgICAgICAgZi5hamF4KHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCB4aHIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmLmxvZygnbG9hZGVkOiAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkQ29tcGxldGUobnVsbCwgZGF0YSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IgOiBmdW5jdGlvbih4aHIsIHN0YXR1cywgZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmLmxvZygnZmFpbGVkIGxvYWRpbmc6ICcgKyB1cmwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRDb21wbGV0ZSgnZmFpbGVkIGxvYWRpbmcgcmVzb3VyY2UuanNvbiBlcnJvcjogJyArIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhVHlwZTogXCJqc29uXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBhc3luYyA6IG9wdGlvbnMuZ2V0QXN5bmNcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSAgICBcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgX2ZldGNoT25lOiBmdW5jdGlvbihsbmcsIG5zLCBvcHRpb25zLCBkb25lKSB7XG4gICAgICAgICAgICB2YXIgdXJsID0gYXBwbHlSZXBsYWNlbWVudChvcHRpb25zLnJlc0dldFBhdGgsIHsgbG5nOiBsbmcsIG5zOiBucyB9KTtcbiAgICAgICAgICAgIGYuYWpheCh7XG4gICAgICAgICAgICAgICAgdXJsOiB1cmwsXG4gICAgICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCB4aHIpIHtcbiAgICAgICAgICAgICAgICAgICAgZi5sb2coJ2xvYWRlZDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIGRvbmUobnVsbCwgZGF0YSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBlcnJvciA6IGZ1bmN0aW9uKHhociwgc3RhdHVzLCBlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBpZiAoKHN0YXR1cyAmJiBzdGF0dXMgPT0gMjAwKSB8fCAoeGhyICYmIHhoci5zdGF0dXMgJiYgeGhyLnN0YXR1cyA9PSAyMDApKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBmaWxlIGxvYWRlZCBidXQgaW52YWxpZCBqc29uLCBzdG9wIHdhc3RlIHRpbWUgIVxuICAgICAgICAgICAgICAgICAgICAgICAgZi5lcnJvcignVGhlcmUgaXMgYSB0eXBvIGluOiAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICgoc3RhdHVzICYmIHN0YXR1cyA9PSA0MDQpIHx8ICh4aHIgJiYgeGhyLnN0YXR1cyAmJiB4aHIuc3RhdHVzID09IDQwNCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGYubG9nKCdEb2VzIG5vdCBleGlzdDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdGhlU3RhdHVzID0gc3RhdHVzID8gc3RhdHVzIDogKCh4aHIgJiYgeGhyLnN0YXR1cykgPyB4aHIuc3RhdHVzIDogbnVsbCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBmLmxvZyh0aGVTdGF0dXMgKyAnIHdoZW4gbG9hZGluZyAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgZG9uZShlcnJvciwge30pO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZGF0YVR5cGU6IFwianNvblwiLFxuICAgICAgICAgICAgICAgIGFzeW5jIDogb3B0aW9ucy5nZXRBc3luY1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIHBvc3RNaXNzaW5nOiBmdW5jdGlvbihsbmcsIG5zLCBrZXksIGRlZmF1bHRWYWx1ZSwgbG5ncykge1xuICAgICAgICAgICAgdmFyIHBheWxvYWQgPSB7fTtcbiAgICAgICAgICAgIHBheWxvYWRba2V5XSA9IGRlZmF1bHRWYWx1ZTtcbiAgICBcbiAgICAgICAgICAgIHZhciB1cmxzID0gW107XG4gICAgXG4gICAgICAgICAgICBpZiAoby5zZW5kTWlzc2luZ1RvID09PSAnZmFsbGJhY2snICYmIG8uZmFsbGJhY2tMbmdbMF0gIT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvLmZhbGxiYWNrTG5nLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIHVybHMucHVzaCh7bG5nOiBvLmZhbGxiYWNrTG5nW2ldLCB1cmw6IGFwcGx5UmVwbGFjZW1lbnQoby5yZXNQb3N0UGF0aCwgeyBsbmc6IG8uZmFsbGJhY2tMbmdbaV0sIG5zOiBucyB9KX0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoby5zZW5kTWlzc2luZ1RvID09PSAnY3VycmVudCcgfHwgKG8uc2VuZE1pc3NpbmdUbyA9PT0gJ2ZhbGxiYWNrJyAmJiBvLmZhbGxiYWNrTG5nWzBdID09PSBmYWxzZSkgKSB7XG4gICAgICAgICAgICAgICAgdXJscy5wdXNoKHtsbmc6IGxuZywgdXJsOiBhcHBseVJlcGxhY2VtZW50KG8ucmVzUG9zdFBhdGgsIHsgbG5nOiBsbmcsIG5zOiBucyB9KX0pO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChvLnNlbmRNaXNzaW5nVG8gPT09ICdhbGwnKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBsbmdzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB1cmxzLnB1c2goe2xuZzogbG5nc1tpXSwgdXJsOiBhcHBseVJlcGxhY2VtZW50KG8ucmVzUG9zdFBhdGgsIHsgbG5nOiBsbmdzW2ldLCBuczogbnMgfSl9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBmb3IgKHZhciB5ID0gMCwgbGVuID0gdXJscy5sZW5ndGg7IHkgPCBsZW47IHkrKykge1xuICAgICAgICAgICAgICAgIHZhciBpdGVtID0gdXJsc1t5XTtcbiAgICAgICAgICAgICAgICBmLmFqYXgoe1xuICAgICAgICAgICAgICAgICAgICB1cmw6IGl0ZW0udXJsLFxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBvLnNlbmRUeXBlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiBwYXlsb2FkLFxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmdW5jdGlvbihkYXRhLCBzdGF0dXMsIHhocikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2coJ3Bvc3RlZCBtaXNzaW5nIGtleSBcXCcnICsga2V5ICsgJ1xcJyB0bzogJyArIGl0ZW0udXJsKTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFkZCBrZXkgdG8gcmVzU3RvcmVcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBrZXlzID0ga2V5LnNwbGl0KCcuJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgeCA9IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdmFsdWUgPSByZXNTdG9yZVtpdGVtLmxuZ11bbnNdO1xuICAgICAgICAgICAgICAgICAgICAgICAgd2hpbGUgKGtleXNbeF0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoeCA9PT0ga2V5cy5sZW5ndGggLSAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWVba2V5c1t4XV0gPSBkZWZhdWx0VmFsdWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZVtrZXlzW3hdXSA9IHZhbHVlW2tleXNbeF1dIHx8IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Kys7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGVycm9yIDogZnVuY3Rpb24oeGhyLCBzdGF0dXMsIGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmLmxvZygnZmFpbGVkIHBvc3RpbmcgbWlzc2luZyBrZXkgXFwnJyArIGtleSArICdcXCcgdG86ICcgKyBpdGVtLnVybCk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGRhdGFUeXBlOiBcImpzb25cIixcbiAgICAgICAgICAgICAgICAgICAgYXN5bmMgOiBvLnBvc3RBc3luY1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgIFxuICAgICAgICByZWxvYWQ6IHJlbG9hZFxuICAgIH07XG4gICAgLy8gZGVmYXVsdHNcbiAgICB2YXIgbyA9IHtcbiAgICAgICAgbG5nOiB1bmRlZmluZWQsXG4gICAgICAgIGxvYWQ6ICdhbGwnLFxuICAgICAgICBwcmVsb2FkOiBbXSxcbiAgICAgICAgbG93ZXJDYXNlTG5nOiBmYWxzZSxcbiAgICAgICAgcmV0dXJuT2JqZWN0VHJlZXM6IGZhbHNlLFxuICAgICAgICBmYWxsYmFja0xuZzogWydkZXYnXSxcbiAgICAgICAgZmFsbGJhY2tOUzogW10sXG4gICAgICAgIGRldGVjdExuZ1FTOiAnc2V0TG5nJyxcbiAgICAgICAgZGV0ZWN0TG5nRnJvbUxvY2FsU3RvcmFnZTogZmFsc2UsXG4gICAgICAgIG5zOiAndHJhbnNsYXRpb24nLFxuICAgICAgICBmYWxsYmFja09uTnVsbDogdHJ1ZSxcbiAgICAgICAgZmFsbGJhY2tPbkVtcHR5OiBmYWxzZSxcbiAgICAgICAgZmFsbGJhY2tUb0RlZmF1bHROUzogZmFsc2UsXG4gICAgICAgIG5zc2VwYXJhdG9yOiAnOicsXG4gICAgICAgIGtleXNlcGFyYXRvcjogJy4nLFxuICAgICAgICBzZWxlY3RvckF0dHI6ICdkYXRhLWkxOG4nLFxuICAgICAgICBkZWJ1ZzogZmFsc2UsXG4gICAgICAgIFxuICAgICAgICByZXNHZXRQYXRoOiAnbG9jYWxlcy9fX2xuZ19fL19fbnNfXy5qc29uJyxcbiAgICAgICAgcmVzUG9zdFBhdGg6ICdsb2NhbGVzL2FkZC9fX2xuZ19fL19fbnNfXycsXG4gICAgXG4gICAgICAgIGdldEFzeW5jOiB0cnVlLFxuICAgICAgICBwb3N0QXN5bmM6IHRydWUsXG4gICAgXG4gICAgICAgIHJlc1N0b3JlOiB1bmRlZmluZWQsXG4gICAgICAgIHVzZUxvY2FsU3RvcmFnZTogZmFsc2UsXG4gICAgICAgIGxvY2FsU3RvcmFnZUV4cGlyYXRpb25UaW1lOiA3KjI0KjYwKjYwKjEwMDAsXG4gICAgXG4gICAgICAgIGR5bmFtaWNMb2FkOiBmYWxzZSxcbiAgICAgICAgc2VuZE1pc3Npbmc6IGZhbHNlLFxuICAgICAgICBzZW5kTWlzc2luZ1RvOiAnZmFsbGJhY2snLCAvLyBjdXJyZW50IHwgYWxsXG4gICAgICAgIHNlbmRUeXBlOiAnUE9TVCcsXG4gICAgXG4gICAgICAgIGludGVycG9sYXRpb25QcmVmaXg6ICdfXycsXG4gICAgICAgIGludGVycG9sYXRpb25TdWZmaXg6ICdfXycsXG4gICAgICAgIGRlZmF1bHRWYXJpYWJsZXM6IGZhbHNlLFxuICAgICAgICByZXVzZVByZWZpeDogJyR0KCcsXG4gICAgICAgIHJldXNlU3VmZml4OiAnKScsXG4gICAgICAgIHBsdXJhbFN1ZmZpeDogJ19wbHVyYWwnLFxuICAgICAgICBwbHVyYWxOb3RGb3VuZDogWydwbHVyYWxfbm90X2ZvdW5kJywgTWF0aC5yYW5kb20oKV0uam9pbignJyksXG4gICAgICAgIGNvbnRleHROb3RGb3VuZDogWydjb250ZXh0X25vdF9mb3VuZCcsIE1hdGgucmFuZG9tKCldLmpvaW4oJycpLFxuICAgICAgICBlc2NhcGVJbnRlcnBvbGF0aW9uOiBmYWxzZSxcbiAgICAgICAgaW5kZWZpbml0ZVN1ZmZpeDogJ19pbmRlZmluaXRlJyxcbiAgICAgICAgaW5kZWZpbml0ZU5vdEZvdW5kOiBbJ2luZGVmaW5pdGVfbm90X2ZvdW5kJywgTWF0aC5yYW5kb20oKV0uam9pbignJyksXG4gICAgXG4gICAgICAgIHNldEpxdWVyeUV4dDogdHJ1ZSxcbiAgICAgICAgZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQ6IHRydWUsXG4gICAgICAgIHVzZURhdGFBdHRyT3B0aW9uczogZmFsc2UsXG4gICAgICAgIGNvb2tpZUV4cGlyYXRpb25UaW1lOiB1bmRlZmluZWQsXG4gICAgICAgIHVzZUNvb2tpZTogdHJ1ZSxcbiAgICAgICAgY29va2llTmFtZTogJ2kxOG5leHQnLFxuICAgICAgICBjb29raWVEb21haW46IHVuZGVmaW5lZCxcbiAgICBcbiAgICAgICAgb2JqZWN0VHJlZUtleUhhbmRsZXI6IHVuZGVmaW5lZCxcbiAgICAgICAgcG9zdFByb2Nlc3M6IHVuZGVmaW5lZCxcbiAgICAgICAgcGFyc2VNaXNzaW5nS2V5OiB1bmRlZmluZWQsXG4gICAgICAgIG1pc3NpbmdLZXlIYW5kbGVyOiBzeW5jLnBvc3RNaXNzaW5nLFxuICAgIFxuICAgICAgICBzaG9ydGN1dEZ1bmN0aW9uOiAnc3ByaW50ZicgLy8gb3I6IGRlZmF1bHRWYWx1ZVxuICAgIH07XG4gICAgZnVuY3Rpb24gX2V4dGVuZCh0YXJnZXQsIHNvdXJjZSkge1xuICAgICAgICBpZiAoIXNvdXJjZSB8fCB0eXBlb2Ygc291cmNlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICByZXR1cm4gdGFyZ2V0O1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGZvciAodmFyIGF0dHIgaW4gc291cmNlKSB7IHRhcmdldFthdHRyXSA9IHNvdXJjZVthdHRyXTsgfVxuICAgICAgICByZXR1cm4gdGFyZ2V0O1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfZGVlcEV4dGVuZCh0YXJnZXQsIHNvdXJjZSkge1xuICAgICAgICBmb3IgKHZhciBwcm9wIGluIHNvdXJjZSlcbiAgICAgICAgICAgIGlmIChwcm9wIGluIHRhcmdldClcbiAgICAgICAgICAgICAgICBfZGVlcEV4dGVuZCh0YXJnZXRbcHJvcF0sIHNvdXJjZVtwcm9wXSk7XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gc291cmNlW3Byb3BdO1xuICAgICAgICByZXR1cm4gdGFyZ2V0O1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfZWFjaChvYmplY3QsIGNhbGxiYWNrLCBhcmdzKSB7XG4gICAgICAgIHZhciBuYW1lLCBpID0gMCxcbiAgICAgICAgICAgIGxlbmd0aCA9IG9iamVjdC5sZW5ndGgsXG4gICAgICAgICAgICBpc09iaiA9IGxlbmd0aCA9PT0gdW5kZWZpbmVkIHx8IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuYXBwbHkob2JqZWN0KSAhPT0gJ1tvYmplY3QgQXJyYXldJyB8fCB0eXBlb2Ygb2JqZWN0ID09PSBcImZ1bmN0aW9uXCI7XG4gICAgXG4gICAgICAgIGlmIChhcmdzKSB7XG4gICAgICAgICAgICBpZiAoaXNPYmopIHtcbiAgICAgICAgICAgICAgICBmb3IgKG5hbWUgaW4gb2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjYWxsYmFjay5hcHBseShvYmplY3RbbmFtZV0sIGFyZ3MpID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZvciAoIDsgaSA8IGxlbmd0aDsgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjYWxsYmFjay5hcHBseShvYmplY3RbaSsrXSwgYXJncykgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gQSBzcGVjaWFsLCBmYXN0LCBjYXNlIGZvciB0aGUgbW9zdCBjb21tb24gdXNlIG9mIGVhY2hcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChpc09iaikge1xuICAgICAgICAgICAgICAgIGZvciAobmFtZSBpbiBvYmplY3QpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrLmNhbGwob2JqZWN0W25hbWVdLCBuYW1lLCBvYmplY3RbbmFtZV0pID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZvciAoIDsgaSA8IGxlbmd0aDsgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjYWxsYmFjay5jYWxsKG9iamVjdFtpXSwgaSwgb2JqZWN0W2krK10pID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmV0dXJuIG9iamVjdDtcbiAgICB9XG4gICAgXG4gICAgdmFyIF9lbnRpdHlNYXAgPSB7XG4gICAgICAgIFwiJlwiOiBcIiZhbXA7XCIsXG4gICAgICAgIFwiPFwiOiBcIiZsdDtcIixcbiAgICAgICAgXCI+XCI6IFwiJmd0O1wiLFxuICAgICAgICAnXCInOiAnJnF1b3Q7JyxcbiAgICAgICAgXCInXCI6ICcmIzM5OycsXG4gICAgICAgIFwiL1wiOiAnJiN4MkY7J1xuICAgIH07XG4gICAgXG4gICAgZnVuY3Rpb24gX2VzY2FwZShkYXRhKSB7XG4gICAgICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiBkYXRhLnJlcGxhY2UoL1smPD5cIidcXC9dL2csIGZ1bmN0aW9uIChzKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIF9lbnRpdHlNYXBbc107XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfWVsc2V7XG4gICAgICAgICAgICByZXR1cm4gZGF0YTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfYWpheChvcHRpb25zKSB7XG4gICAgXG4gICAgICAgIC8vIHYwLjUuMCBvZiBodHRwczovL2dpdGh1Yi5jb20vZ29sb3JvZGVuL2h0dHAuanNcbiAgICAgICAgdmFyIGdldFhociA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICAgICAgLy8gVXNlIHRoZSBuYXRpdmUgWEhSIG9iamVjdCBpZiB0aGUgYnJvd3NlciBzdXBwb3J0cyBpdC5cbiAgICAgICAgICAgIGlmICh3aW5kb3cuWE1MSHR0cFJlcXVlc3QpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobnVsbCwgbmV3IFhNTEh0dHBSZXF1ZXN0KCkpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh3aW5kb3cuQWN0aXZlWE9iamVjdCkge1xuICAgICAgICAgICAgICAgIC8vIEluIEludGVybmV0IEV4cGxvcmVyIGNoZWNrIGZvciBBY3RpdmVYIHZlcnNpb25zIG9mIHRoZSBYSFIgb2JqZWN0LlxuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhudWxsLCBuZXcgQWN0aXZlWE9iamVjdChcIk1zeG1sMi5YTUxIVFRQXCIpKTtcbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhudWxsLCBuZXcgQWN0aXZlWE9iamVjdChcIk1pY3Jvc29mdC5YTUxIVFRQXCIpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAvLyBJZiBubyBYSFIgc3VwcG9ydCB3YXMgZm91bmQsIHRocm93IGFuIGVycm9yLlxuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG5ldyBFcnJvcigpKTtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgdmFyIGVuY29kZVVzaW5nVXJsRW5jb2RpbmcgPSBmdW5jdGlvbiAoZGF0YSkge1xuICAgICAgICAgICAgaWYodHlwZW9mIGRhdGEgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gW107XG4gICAgICAgICAgICBmb3IodmFyIGRhdGFJdGVtIGluIGRhdGEpIHtcbiAgICAgICAgICAgICAgICBpZihkYXRhLmhhc093blByb3BlcnR5KGRhdGFJdGVtKSkge1xuICAgICAgICAgICAgICAgICAgICByZXN1bHQucHVzaChlbmNvZGVVUklDb21wb25lbnQoZGF0YUl0ZW0pICsgJz0nICsgZW5jb2RlVVJJQ29tcG9uZW50KGRhdGFbZGF0YUl0ZW1dKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdC5qb2luKCcmJyk7XG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIHZhciB1dGY4ID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICAgICAgICAgIHRleHQgPSB0ZXh0LnJlcGxhY2UoL1xcclxcbi9nLCAnXFxuJyk7XG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gJyc7XG4gICAgXG4gICAgICAgICAgICBmb3IodmFyIGkgPSAwOyBpIDwgdGV4dC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBjID0gdGV4dC5jaGFyQ29kZUF0KGkpO1xuICAgIFxuICAgICAgICAgICAgICAgIGlmKGMgPCAxMjgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGMpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZigoYyA+IDEyNykgJiYgKGMgPCAyMDQ4KSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoKGMgPj4gNikgfCAxOTIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoKGMgJiA2MykgfCAxMjgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSgoYyA+PiAxMikgfCAyMjQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoKChjID4+IDYpICYgNjMpIHwgMTI4KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChjICYgNjMpIHwgMTI4KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9O1xuICAgIFxuICAgICAgICB2YXIgYmFzZTY0ID0gZnVuY3Rpb24gKHRleHQpIHtcbiAgICAgICAgICAgIHZhciBrZXlTdHIgPSAnQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLz0nO1xuICAgIFxuICAgICAgICAgICAgdGV4dCA9IHV0ZjgodGV4dCk7XG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gJycsXG4gICAgICAgICAgICAgICAgICAgIGNocjEsIGNocjIsIGNocjMsXG4gICAgICAgICAgICAgICAgICAgIGVuYzEsIGVuYzIsIGVuYzMsIGVuYzQsXG4gICAgICAgICAgICAgICAgICAgIGkgPSAwO1xuICAgIFxuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgIGNocjEgPSB0ZXh0LmNoYXJDb2RlQXQoaSsrKTtcbiAgICAgICAgICAgICAgICBjaHIyID0gdGV4dC5jaGFyQ29kZUF0KGkrKyk7XG4gICAgICAgICAgICAgICAgY2hyMyA9IHRleHQuY2hhckNvZGVBdChpKyspO1xuICAgIFxuICAgICAgICAgICAgICAgIGVuYzEgPSBjaHIxID4+IDI7XG4gICAgICAgICAgICAgICAgZW5jMiA9ICgoY2hyMSAmIDMpIDw8IDQpIHwgKGNocjIgPj4gNCk7XG4gICAgICAgICAgICAgICAgZW5jMyA9ICgoY2hyMiAmIDE1KSA8PCAyKSB8IChjaHIzID4+IDYpO1xuICAgICAgICAgICAgICAgIGVuYzQgPSBjaHIzICYgNjM7XG4gICAgXG4gICAgICAgICAgICAgICAgaWYoaXNOYU4oY2hyMikpIHtcbiAgICAgICAgICAgICAgICAgICAgZW5jMyA9IGVuYzQgPSA2NDtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYoaXNOYU4oY2hyMykpIHtcbiAgICAgICAgICAgICAgICAgICAgZW5jNCA9IDY0O1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICByZXN1bHQgKz1cbiAgICAgICAgICAgICAgICAgICAga2V5U3RyLmNoYXJBdChlbmMxKSArXG4gICAgICAgICAgICAgICAgICAgIGtleVN0ci5jaGFyQXQoZW5jMikgK1xuICAgICAgICAgICAgICAgICAgICBrZXlTdHIuY2hhckF0KGVuYzMpICtcbiAgICAgICAgICAgICAgICAgICAga2V5U3RyLmNoYXJBdChlbmM0KTtcbiAgICAgICAgICAgICAgICBjaHIxID0gY2hyMiA9IGNocjMgPSAnJztcbiAgICAgICAgICAgICAgICBlbmMxID0gZW5jMiA9IGVuYzMgPSBlbmM0ID0gJyc7XG4gICAgICAgICAgICB9IHdoaWxlKGkgPCB0ZXh0Lmxlbmd0aCk7XG4gICAgXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9O1xuICAgIFxuICAgICAgICB2YXIgbWVyZ2VIZWFkZXJzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gVXNlIHRoZSBmaXJzdCBoZWFkZXIgb2JqZWN0IGFzIGJhc2UuXG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gYXJndW1lbnRzWzBdO1xuICAgIFxuICAgICAgICAgICAgLy8gSXRlcmF0ZSB0aHJvdWdoIHRoZSByZW1haW5pbmcgaGVhZGVyIG9iamVjdHMgYW5kIGFkZCB0aGVtLlxuICAgICAgICAgICAgZm9yKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBjdXJyZW50SGVhZGVycyA9IGFyZ3VtZW50c1tpXTtcbiAgICAgICAgICAgICAgICBmb3IodmFyIGhlYWRlciBpbiBjdXJyZW50SGVhZGVycykge1xuICAgICAgICAgICAgICAgICAgICBpZihjdXJyZW50SGVhZGVycy5oYXNPd25Qcm9wZXJ0eShoZWFkZXIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRbaGVhZGVyXSA9IGN1cnJlbnRIZWFkZXJzW2hlYWRlcl07XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAvLyBSZXR1cm4gdGhlIG1lcmdlZCBoZWFkZXJzLlxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgdmFyIGFqYXggPSBmdW5jdGlvbiAobWV0aG9kLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAvLyBBZGp1c3QgcGFyYW1ldGVycy5cbiAgICAgICAgICAgIGlmKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sgPSBvcHRpb25zO1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIC8vIFNldCBkZWZhdWx0IHBhcmFtZXRlciB2YWx1ZXMuXG4gICAgICAgICAgICBvcHRpb25zLmNhY2hlID0gb3B0aW9ucy5jYWNoZSB8fCBmYWxzZTtcbiAgICAgICAgICAgIG9wdGlvbnMuZGF0YSA9IG9wdGlvbnMuZGF0YSB8fCB7fTtcbiAgICAgICAgICAgIG9wdGlvbnMuaGVhZGVycyA9IG9wdGlvbnMuaGVhZGVycyB8fCB7fTtcbiAgICAgICAgICAgIG9wdGlvbnMuanNvbnAgPSBvcHRpb25zLmpzb25wIHx8IGZhbHNlO1xuICAgICAgICAgICAgb3B0aW9ucy5hc3luYyA9IG9wdGlvbnMuYXN5bmMgPT09IHVuZGVmaW5lZCA/IHRydWUgOiBvcHRpb25zLmFzeW5jO1xuICAgIFxuICAgICAgICAgICAgLy8gTWVyZ2UgdGhlIHZhcmlvdXMgaGVhZGVyIG9iamVjdHMuXG4gICAgICAgICAgICB2YXIgaGVhZGVycyA9IG1lcmdlSGVhZGVycyh7XG4gICAgICAgICAgICAgICAgJ2FjY2VwdCc6ICcqLyonLFxuICAgICAgICAgICAgICAgICdjb250ZW50LXR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkO2NoYXJzZXQ9VVRGLTgnXG4gICAgICAgICAgICB9LCBhamF4LmhlYWRlcnMsIG9wdGlvbnMuaGVhZGVycyk7XG4gICAgXG4gICAgICAgICAgICAvLyBFbmNvZGUgdGhlIGRhdGEgYWNjb3JkaW5nIHRvIHRoZSBjb250ZW50LXR5cGUuXG4gICAgICAgICAgICB2YXIgcGF5bG9hZDtcbiAgICAgICAgICAgIGlmIChoZWFkZXJzWydjb250ZW50LXR5cGUnXSA9PT0gJ2FwcGxpY2F0aW9uL2pzb24nKSB7XG4gICAgICAgICAgICAgICAgcGF5bG9hZCA9IEpTT04uc3RyaW5naWZ5KG9wdGlvbnMuZGF0YSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHBheWxvYWQgPSBlbmNvZGVVc2luZ1VybEVuY29kaW5nKG9wdGlvbnMuZGF0YSk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAvLyBTcGVjaWFsbHkgcHJlcGFyZSBHRVQgcmVxdWVzdHM6IFNldHVwIHRoZSBxdWVyeSBzdHJpbmcsIGhhbmRsZSBjYWNoaW5nIGFuZCBtYWtlIGEgSlNPTlAgY2FsbFxuICAgICAgICAgICAgLy8gaWYgbmVjY2Vzc2FyeS5cbiAgICAgICAgICAgIGlmKG1ldGhvZCA9PT0gJ0dFVCcpIHtcbiAgICAgICAgICAgICAgICAvLyBTZXR1cCB0aGUgcXVlcnkgc3RyaW5nLlxuICAgICAgICAgICAgICAgIHZhciBxdWVyeVN0cmluZyA9IFtdO1xuICAgICAgICAgICAgICAgIGlmKHBheWxvYWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcXVlcnlTdHJpbmcucHVzaChwYXlsb2FkKTtcbiAgICAgICAgICAgICAgICAgICAgcGF5bG9hZCA9IG51bGw7XG4gICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgIC8vIEhhbmRsZSBjYWNoaW5nLlxuICAgICAgICAgICAgICAgIGlmKCFvcHRpb25zLmNhY2hlKSB7XG4gICAgICAgICAgICAgICAgICAgIHF1ZXJ5U3RyaW5nLnB1c2goJ189JyArIChuZXcgRGF0ZSgpKS5nZXRUaW1lKCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAvLyBJZiBuZWNjZXNzYXJ5IHByZXBhcmUgdGhlIHF1ZXJ5IHN0cmluZyBmb3IgYSBKU09OUCBjYWxsLlxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMuanNvbnApIHtcbiAgICAgICAgICAgICAgICAgICAgcXVlcnlTdHJpbmcucHVzaCgnY2FsbGJhY2s9JyArIG9wdGlvbnMuanNvbnApO1xuICAgICAgICAgICAgICAgICAgICBxdWVyeVN0cmluZy5wdXNoKCdqc29ucD0nICsgb3B0aW9ucy5qc29ucCk7XG4gICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgIC8vIE1lcmdlIHRoZSBxdWVyeSBzdHJpbmcgYW5kIGF0dGFjaCBpdCB0byB0aGUgdXJsLlxuICAgICAgICAgICAgICAgIHF1ZXJ5U3RyaW5nID0gcXVlcnlTdHJpbmcuam9pbignJicpO1xuICAgICAgICAgICAgICAgIGlmIChxdWVyeVN0cmluZy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh1cmwuaW5kZXhPZignPycpID4gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybCArPSAnJicgKyBxdWVyeVN0cmluZztcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybCArPSAnPycgKyBxdWVyeVN0cmluZztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAvLyBNYWtlIGEgSlNPTlAgY2FsbCBpZiBuZWNjZXNzYXJ5LlxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMuanNvbnApIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGhlYWQgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaGVhZCcpWzBdO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc2NyaXB0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7XG4gICAgICAgICAgICAgICAgICAgIHNjcmlwdC50eXBlID0gJ3RleHQvamF2YXNjcmlwdCc7XG4gICAgICAgICAgICAgICAgICAgIHNjcmlwdC5zcmMgPSB1cmw7XG4gICAgICAgICAgICAgICAgICAgIGhlYWQuYXBwZW5kQ2hpbGQoc2NyaXB0KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIC8vIFNpbmNlIHdlIGdvdCBoZXJlLCBpdCBpcyBubyBKU09OUCByZXF1ZXN0LCBzbyBtYWtlIGEgbm9ybWFsIFhIUiByZXF1ZXN0LlxuICAgICAgICAgICAgZ2V0WGhyKGZ1bmN0aW9uIChlcnIsIHhocikge1xuICAgICAgICAgICAgICAgIGlmKGVycikgcmV0dXJuIGNhbGxiYWNrKGVycik7XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gT3BlbiB0aGUgcmVxdWVzdC5cbiAgICAgICAgICAgICAgICB4aHIub3BlbihtZXRob2QsIHVybCwgb3B0aW9ucy5hc3luYyk7XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gU2V0IHRoZSByZXF1ZXN0IGhlYWRlcnMuXG4gICAgICAgICAgICAgICAgZm9yKHZhciBoZWFkZXIgaW4gaGVhZGVycykge1xuICAgICAgICAgICAgICAgICAgICBpZihoZWFkZXJzLmhhc093blByb3BlcnR5KGhlYWRlcikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKGhlYWRlciwgaGVhZGVyc1toZWFkZXJdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgdGhlIHJlcXVlc3QgZXZlbnRzLlxuICAgICAgICAgICAgICAgIHhoci5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKHhoci5yZWFkeVN0YXRlID09PSA0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IHhoci5yZXNwb25zZVRleHQgfHwgJyc7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBubyBjYWxsYmFjayBpcyBnaXZlbiwgcmV0dXJuLlxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIWNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIGFuIG9iamVjdCB0aGF0IHByb3ZpZGVzIGFjY2VzcyB0byB0aGUgZGF0YSBhcyB0ZXh0IGFuZCBKU09OLlxuICAgICAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soeGhyLnN0YXR1cywge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqc29uOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmLmVycm9yKCdDYW4gbm90IHBhcnNlIEpTT04uIFVSTDogJyArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ge307XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH07XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gQWN0dWFsbHkgc2VuZCB0aGUgWEhSIHJlcXVlc3QuXG4gICAgICAgICAgICAgICAgeGhyLnNlbmQocGF5bG9hZCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgLy8gRGVmaW5lIHRoZSBleHRlcm5hbCBpbnRlcmZhY2UuXG4gICAgICAgIHZhciBodHRwID0ge1xuICAgICAgICAgICAgYXV0aEJhc2ljOiBmdW5jdGlvbiAodXNlcm5hbWUsIHBhc3N3b3JkKSB7XG4gICAgICAgICAgICAgICAgYWpheC5oZWFkZXJzWydBdXRob3JpemF0aW9uJ10gPSAnQmFzaWMgJyArIGJhc2U2NCh1c2VybmFtZSArICc6JyArIHBhc3N3b3JkKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBjb25uZWN0OiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdDT05ORUNUJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgZGVsOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdERUxFVEUnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBnZXQ6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFqYXgoJ0dFVCcsIHVybCwgb3B0aW9ucywgY2FsbGJhY2spO1xuICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgIGhlYWQ6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFqYXgoJ0hFQUQnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBoZWFkZXJzOiBmdW5jdGlvbiAoaGVhZGVycykge1xuICAgICAgICAgICAgICAgIGFqYXguaGVhZGVycyA9IGhlYWRlcnMgfHwge307XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgaXNBbGxvd2VkOiBmdW5jdGlvbiAodXJsLCB2ZXJiLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucyh1cmwsIGZ1bmN0aW9uIChzdGF0dXMsIGRhdGEpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soZGF0YS50ZXh0KCkuaW5kZXhPZih2ZXJiKSAhPT0gLTEpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSxcbiAgICBcbiAgICAgICAgICAgIG9wdGlvbnM6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFqYXgoJ09QVElPTlMnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBwYXRjaDogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnUEFUQ0gnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICBwb3N0OiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdQT1NUJywgdXJsLCBvcHRpb25zLCBjYWxsYmFjayk7XG4gICAgICAgICAgICB9LFxuICAgIFxuICAgICAgICAgICAgcHV0OiBmdW5jdGlvbiAodXJsLCBvcHRpb25zLCBjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBhamF4KCdQVVQnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH0sXG4gICAgXG4gICAgICAgICAgICB0cmFjZTogZnVuY3Rpb24gKHVybCwgb3B0aW9ucywgY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWpheCgnVFJBQ0UnLCB1cmwsIG9wdGlvbnMsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICBcbiAgICBcbiAgICAgICAgdmFyIG1ldGhvZGUgPSBvcHRpb25zLnR5cGUgPyBvcHRpb25zLnR5cGUudG9Mb3dlckNhc2UoKSA6ICdnZXQnO1xuICAgIFxuICAgICAgICBodHRwW21ldGhvZGVdKG9wdGlvbnMudXJsLCBvcHRpb25zLCBmdW5jdGlvbiAoc3RhdHVzLCBkYXRhKSB7XG4gICAgICAgICAgICAvLyBmaWxlOiBwcm90b2NvbCBhbHdheXMgZ2l2ZXMgc3RhdHVzIGNvZGUgMCwgc28gY2hlY2sgZm9yIGRhdGFcbiAgICAgICAgICAgIGlmIChzdGF0dXMgPT09IDIwMCB8fCAoc3RhdHVzID09PSAwICYmIGRhdGEudGV4dCgpKSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMuc3VjY2VzcyhkYXRhLmpzb24oKSwgc3RhdHVzLCBudWxsKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb3B0aW9ucy5lcnJvcihkYXRhLnRleHQoKSwgc3RhdHVzLCBudWxsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIFxuICAgIHZhciBfY29va2llID0ge1xuICAgICAgICBjcmVhdGU6IGZ1bmN0aW9uKG5hbWUsdmFsdWUsbWludXRlcyxkb21haW4pIHtcbiAgICAgICAgICAgIHZhciBleHBpcmVzO1xuICAgICAgICAgICAgaWYgKG1pbnV0ZXMpIHtcbiAgICAgICAgICAgICAgICB2YXIgZGF0ZSA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgZGF0ZS5zZXRUaW1lKGRhdGUuZ2V0VGltZSgpKyhtaW51dGVzKjYwKjEwMDApKTtcbiAgICAgICAgICAgICAgICBleHBpcmVzID0gXCI7IGV4cGlyZXM9XCIrZGF0ZS50b0dNVFN0cmluZygpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBleHBpcmVzID0gXCJcIjtcbiAgICAgICAgICAgIGRvbWFpbiA9IChkb21haW4pPyBcImRvbWFpbj1cIitkb21haW4rXCI7XCIgOiBcIlwiO1xuICAgICAgICAgICAgZG9jdW1lbnQuY29va2llID0gbmFtZStcIj1cIit2YWx1ZStleHBpcmVzK1wiO1wiK2RvbWFpbitcInBhdGg9L1wiO1xuICAgICAgICB9LFxuICAgIFxuICAgICAgICByZWFkOiBmdW5jdGlvbihuYW1lKSB7XG4gICAgICAgICAgICB2YXIgbmFtZUVRID0gbmFtZSArIFwiPVwiO1xuICAgICAgICAgICAgdmFyIGNhID0gZG9jdW1lbnQuY29va2llLnNwbGl0KCc7Jyk7XG4gICAgICAgICAgICBmb3IodmFyIGk9MDtpIDwgY2EubGVuZ3RoO2krKykge1xuICAgICAgICAgICAgICAgIHZhciBjID0gY2FbaV07XG4gICAgICAgICAgICAgICAgd2hpbGUgKGMuY2hhckF0KDApPT0nICcpIGMgPSBjLnN1YnN0cmluZygxLGMubGVuZ3RoKTtcbiAgICAgICAgICAgICAgICBpZiAoYy5pbmRleE9mKG5hbWVFUSkgPT09IDApIHJldHVybiBjLnN1YnN0cmluZyhuYW1lRVEubGVuZ3RoLGMubGVuZ3RoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9LFxuICAgIFxuICAgICAgICByZW1vdmU6IGZ1bmN0aW9uKG5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMuY3JlYXRlKG5hbWUsXCJcIiwtMSk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIFxuICAgIHZhciBjb29raWVfbm9vcCA9IHtcbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbihuYW1lLHZhbHVlLG1pbnV0ZXMsZG9tYWluKSB7fSxcbiAgICAgICAgcmVhZDogZnVuY3Rpb24obmFtZSkgeyByZXR1cm4gbnVsbDsgfSxcbiAgICAgICAgcmVtb3ZlOiBmdW5jdGlvbihuYW1lKSB7fVxuICAgIH07XG4gICAgXG4gICAgXG4gICAgXG4gICAgLy8gbW92ZSBkZXBlbmRlbnQgZnVuY3Rpb25zIHRvIGEgY29udGFpbmVyIHNvIHRoYXRcbiAgICAvLyB0aGV5IGNhbiBiZSBvdmVycmlkZW4gZWFzaWVyIGluIG5vIGpxdWVyeSBlbnZpcm9ubWVudCAobm9kZS5qcylcbiAgICB2YXIgZiA9IHtcbiAgICAgICAgZXh0ZW5kOiAkID8gJC5leHRlbmQgOiBfZXh0ZW5kLFxuICAgICAgICBkZWVwRXh0ZW5kOiBfZGVlcEV4dGVuZCxcbiAgICAgICAgZWFjaDogJCA/ICQuZWFjaCA6IF9lYWNoLFxuICAgICAgICBhamF4OiAkID8gJC5hamF4IDogKHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcgPyBfYWpheCA6IGZ1bmN0aW9uKCkge30pLFxuICAgICAgICBjb29raWU6IHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcgPyBfY29va2llIDogY29va2llX25vb3AsXG4gICAgICAgIGRldGVjdExhbmd1YWdlOiBkZXRlY3RMYW5ndWFnZSxcbiAgICAgICAgZXNjYXBlOiBfZXNjYXBlLFxuICAgICAgICBsb2c6IGZ1bmN0aW9uKHN0cikge1xuICAgICAgICAgICAgaWYgKG8uZGVidWcgJiYgdHlwZW9mIGNvbnNvbGUgIT09IFwidW5kZWZpbmVkXCIpIGNvbnNvbGUubG9nKHN0cik7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiBmdW5jdGlvbihzdHIpIHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgY29uc29sZSAhPT0gXCJ1bmRlZmluZWRcIikgY29uc29sZS5lcnJvcihzdHIpO1xuICAgICAgICB9LFxuICAgICAgICBnZXRDb3VudHlJbmRleE9mTG5nOiBmdW5jdGlvbihsbmcpIHtcbiAgICAgICAgICAgIHZhciBsbmdfaW5kZXggPSAwO1xuICAgICAgICAgICAgaWYgKGxuZyA9PT0gJ25iLU5PJyB8fCBsbmcgPT09ICdubi1OTycgfHwgbG5nID09PSAnbmItbm8nIHx8IGxuZyA9PT0gJ25uLW5vJykgbG5nX2luZGV4ID0gMTtcbiAgICAgICAgICAgIHJldHVybiBsbmdfaW5kZXg7XG4gICAgICAgIH0sXG4gICAgICAgIHRvTGFuZ3VhZ2VzOiBmdW5jdGlvbihsbmcpIHtcbiAgICAgICAgICAgIHZhciBsb2cgPSB0aGlzLmxvZztcbiAgICBcbiAgICAgICAgICAgIGZ1bmN0aW9uIGFwcGx5Q2FzZShsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJldCA9IGw7XG4gICAgXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBsID09PSAnc3RyaW5nJyAmJiBsLmluZGV4T2YoJy0nKSA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwYXJ0cyA9IGwuc3BsaXQoJy0nKTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgcmV0ID0gby5sb3dlckNhc2VMbmcgP1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFydHNbMF0udG9Mb3dlckNhc2UoKSArICAnLScgKyBwYXJ0c1sxXS50b0xvd2VyQ2FzZSgpIDpcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRzWzBdLnRvTG93ZXJDYXNlKCkgKyAgJy0nICsgcGFydHNbMV0udG9VcHBlckNhc2UoKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXQgPSBvLmxvd2VyQ2FzZUxuZyA/IGwudG9Mb3dlckNhc2UoKSA6IGw7XG4gICAgICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgICAgIHJldHVybiByZXQ7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICB2YXIgbGFuZ3VhZ2VzID0gW107XG4gICAgICAgICAgICB2YXIgd2hpdGVsaXN0ID0gby5sbmdXaGl0ZWxpc3QgfHwgZmFsc2U7XG4gICAgICAgICAgICB2YXIgYWRkTGFuZ3VhZ2UgPSBmdW5jdGlvbihsYW5ndWFnZSl7XG4gICAgICAgICAgICAgIC8vcmVqZWN0IGxhbmdzIG5vdCB3aGl0ZWxpc3RlZFxuICAgICAgICAgICAgICBpZighd2hpdGVsaXN0IHx8IHdoaXRlbGlzdC5pbmRleE9mKGxhbmd1YWdlKSA+IC0xKXtcbiAgICAgICAgICAgICAgICBsYW5ndWFnZXMucHVzaChsYW5ndWFnZSk7XG4gICAgICAgICAgICAgIH1lbHNle1xuICAgICAgICAgICAgICAgIGxvZygncmVqZWN0aW5nIG5vbi13aGl0ZWxpc3RlZCBsYW5ndWFnZTogJyArIGxhbmd1YWdlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgbG5nID09PSAnc3RyaW5nJyAmJiBsbmcuaW5kZXhPZignLScpID4gLTEpIHtcbiAgICAgICAgICAgICAgICB2YXIgcGFydHMgPSBsbmcuc3BsaXQoJy0nKTtcbiAgICBcbiAgICAgICAgICAgICAgICBpZiAoby5sb2FkICE9PSAndW5zcGVjaWZpYycpIGFkZExhbmd1YWdlKGFwcGx5Q2FzZShsbmcpKTtcbiAgICAgICAgICAgICAgICBpZiAoby5sb2FkICE9PSAnY3VycmVudCcpIGFkZExhbmd1YWdlKGFwcGx5Q2FzZShwYXJ0c1t0aGlzLmdldENvdW50eUluZGV4T2ZMbmcobG5nKV0pKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYWRkTGFuZ3VhZ2UoYXBwbHlDYXNlKGxuZykpO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvLmZhbGxiYWNrTG5nLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKGxhbmd1YWdlcy5pbmRleE9mKG8uZmFsbGJhY2tMbmdbaV0pID09PSAtMSAmJiBvLmZhbGxiYWNrTG5nW2ldKSBsYW5ndWFnZXMucHVzaChhcHBseUNhc2Uoby5mYWxsYmFja0xuZ1tpXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGxhbmd1YWdlcztcbiAgICAgICAgfSxcbiAgICAgICAgcmVnZXhFc2NhcGU6IGZ1bmN0aW9uKHN0cikge1xuICAgICAgICAgICAgcmV0dXJuIHN0ci5yZXBsYWNlKC9bXFwtXFxbXFxdXFwvXFx7XFx9XFwoXFwpXFwqXFwrXFw/XFwuXFxcXFxcXlxcJFxcfF0vZywgXCJcXFxcJCZcIik7XG4gICAgICAgIH0sXG4gICAgICAgIHJlZ2V4UmVwbGFjZW1lbnRFc2NhcGU6IGZ1bmN0aW9uKHN0ck9yRm4pIHtcbiAgICAgICAgICAgIGlmICh0eXBlb2Ygc3RyT3JGbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc3RyT3JGbi5yZXBsYWNlKC9cXCQvZywgXCIkJCQkXCIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc3RyT3JGbjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgbG9jYWxTdG9yYWdlOiB7XG4gICAgICAgICAgICBzZXRJdGVtOiBmdW5jdGlvbihrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHdpbmRvdy5sb2NhbFN0b3JhZ2UpIHtcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5sb2NhbFN0b3JhZ2Uuc2V0SXRlbShrZXksIHZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZi5sb2coJ2ZhaWxlZCB0byBzZXQgdmFsdWUgZm9yIGtleSBcIicgKyBrZXkgKyAnXCIgdG8gbG9jYWxTdG9yYWdlLicpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcbiAgICBmdW5jdGlvbiBpbml0KG9wdGlvbnMsIGNiKSB7XG4gICAgICAgIFxuICAgICAgICBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIGNiID0gb3B0aW9ucztcbiAgICAgICAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICAgICAgfVxuICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgXG4gICAgICAgIC8vIG92ZXJyaWRlIGRlZmF1bHRzIHdpdGggcGFzc2VkIGluIG9wdGlvbnNcbiAgICAgICAgZi5leHRlbmQobywgb3B0aW9ucyk7XG4gICAgICAgIGRlbGV0ZSBvLmZpeExuZzsgLyogcGFzc2VkIGluIGVhY2ggdGltZSAqL1xuICAgIFxuICAgICAgICAvLyBvdmVycmlkZSBmdW5jdGlvbnM6IC5sb2coKSwgLmRldGVjdExhbmd1YWdlKCksIGV0Y1xuICAgICAgICBpZiAoby5mdW5jdGlvbnMpIHtcbiAgICAgICAgICAgIGRlbGV0ZSBvLmZ1bmN0aW9ucztcbiAgICAgICAgICAgIGYuZXh0ZW5kKGYsIG9wdGlvbnMuZnVuY3Rpb25zKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBjcmVhdGUgbmFtZXNwYWNlIG9iamVjdCBpZiBuYW1lc3BhY2UgaXMgcGFzc2VkIGluIGFzIHN0cmluZ1xuICAgICAgICBpZiAodHlwZW9mIG8ubnMgPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIG8ubnMgPSB7IG5hbWVzcGFjZXM6IFtvLm5zXSwgZGVmYXVsdE5zOiBvLm5zfTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBmYWxsYmFjayBuYW1lc3BhY2VzXG4gICAgICAgIGlmICh0eXBlb2Ygby5mYWxsYmFja05TID09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBvLmZhbGxiYWNrTlMgPSBbby5mYWxsYmFja05TXTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBmYWxsYmFjayBsYW5ndWFnZXNcbiAgICAgICAgaWYgKHR5cGVvZiBvLmZhbGxiYWNrTG5nID09ICdzdHJpbmcnIHx8IHR5cGVvZiBvLmZhbGxiYWNrTG5nID09ICdib29sZWFuJykge1xuICAgICAgICAgICAgby5mYWxsYmFja0xuZyA9IFtvLmZhbGxiYWNrTG5nXTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBlc2NhcGUgcHJlZml4L3N1ZmZpeFxuICAgICAgICBvLmludGVycG9sYXRpb25QcmVmaXhFc2NhcGVkID0gZi5yZWdleEVzY2FwZShvLmludGVycG9sYXRpb25QcmVmaXgpO1xuICAgICAgICBvLmludGVycG9sYXRpb25TdWZmaXhFc2NhcGVkID0gZi5yZWdleEVzY2FwZShvLmludGVycG9sYXRpb25TdWZmaXgpO1xuICAgIFxuICAgICAgICBpZiAoIW8ubG5nKSBvLmxuZyA9IGYuZGV0ZWN0TGFuZ3VhZ2UoKTtcbiAgICBcbiAgICAgICAgbGFuZ3VhZ2VzID0gZi50b0xhbmd1YWdlcyhvLmxuZyk7XG4gICAgICAgIGN1cnJlbnRMbmcgPSBsYW5ndWFnZXNbMF07XG4gICAgICAgIGYubG9nKCdjdXJyZW50TG5nIHNldCB0bzogJyArIGN1cnJlbnRMbmcpO1xuICAgIFxuICAgICAgICBpZiAoby51c2VDb29raWUgJiYgZi5jb29raWUucmVhZChvLmNvb2tpZU5hbWUpICE9PSBjdXJyZW50TG5nKXsgLy9jb29raWUgaXMgdW5zZXQgb3IgaW52YWxpZFxuICAgICAgICAgICAgZi5jb29raWUuY3JlYXRlKG8uY29va2llTmFtZSwgY3VycmVudExuZywgby5jb29raWVFeHBpcmF0aW9uVGltZSwgby5jb29raWVEb21haW4pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvLmRldGVjdExuZ0Zyb21Mb2NhbFN0b3JhZ2UgJiYgdHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJyAmJiB3aW5kb3cubG9jYWxTdG9yYWdlKSB7XG4gICAgICAgICAgICBmLmxvY2FsU3RvcmFnZS5zZXRJdGVtKCdpMThuZXh0X2xuZycsIGN1cnJlbnRMbmcpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBsbmdUcmFuc2xhdGUgPSB0cmFuc2xhdGU7XG4gICAgICAgIGlmIChvcHRpb25zLmZpeExuZykge1xuICAgICAgICAgICAgbG5nVHJhbnNsYXRlID0gZnVuY3Rpb24oa2V5LCBvcHRpb25zKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgICAgICAgICAgb3B0aW9ucy5sbmcgPSBvcHRpb25zLmxuZyB8fCBsbmdUcmFuc2xhdGUubG5nO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cmFuc2xhdGUoa2V5LCBvcHRpb25zKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBsbmdUcmFuc2xhdGUubG5nID0gY3VycmVudExuZztcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBwbHVyYWxFeHRlbnNpb25zLnNldEN1cnJlbnRMbmcoY3VycmVudExuZyk7XG4gICAgXG4gICAgICAgIC8vIGFkZCBKUXVlcnkgZXh0ZW5zaW9uc1xuICAgICAgICBpZiAoJCAmJiBvLnNldEpxdWVyeUV4dCkgYWRkSnF1ZXJ5RnVuY3QoKTtcbiAgICBcbiAgICAgICAgLy8galF1ZXJ5IGRlZmVycmVkXG4gICAgICAgIHZhciBkZWZlcnJlZDtcbiAgICAgICAgaWYgKCQgJiYgJC5EZWZlcnJlZCkge1xuICAgICAgICAgICAgZGVmZXJyZWQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gcmV0dXJuIGltbWlkaWF0bHkgaWYgcmVzIGFyZSBwYXNzZWQgaW5cbiAgICAgICAgaWYgKG8ucmVzU3RvcmUpIHtcbiAgICAgICAgICAgIHJlc1N0b3JlID0gby5yZXNTdG9yZTtcbiAgICAgICAgICAgIGluaXRpYWxpemVkID0gdHJ1ZTtcbiAgICAgICAgICAgIGlmIChjYikgY2IobG5nVHJhbnNsYXRlKTtcbiAgICAgICAgICAgIGlmIChkZWZlcnJlZCkgZGVmZXJyZWQucmVzb2x2ZShsbmdUcmFuc2xhdGUpO1xuICAgICAgICAgICAgaWYgKGRlZmVycmVkKSByZXR1cm4gZGVmZXJyZWQucHJvbWlzZSgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIGxhbmd1YWdlcyB0byBsb2FkXG4gICAgICAgIHZhciBsbmdzVG9Mb2FkID0gZi50b0xhbmd1YWdlcyhvLmxuZyk7XG4gICAgICAgIGlmICh0eXBlb2Ygby5wcmVsb2FkID09PSAnc3RyaW5nJykgby5wcmVsb2FkID0gW28ucHJlbG9hZF07XG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBsID0gby5wcmVsb2FkLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgICAgdmFyIHByZXMgPSBmLnRvTGFuZ3VhZ2VzKG8ucHJlbG9hZFtpXSk7XG4gICAgICAgICAgICBmb3IgKHZhciB5ID0gMCwgbGVuID0gcHJlcy5sZW5ndGg7IHkgPCBsZW47IHkrKykge1xuICAgICAgICAgICAgICAgIGlmIChsbmdzVG9Mb2FkLmluZGV4T2YocHJlc1t5XSkgPCAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGxuZ3NUb0xvYWQucHVzaChwcmVzW3ldKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZWxzZSBsb2FkIHRoZW1cbiAgICAgICAgaTE4bi5zeW5jLmxvYWQobG5nc1RvTG9hZCwgbywgZnVuY3Rpb24oZXJyLCBzdG9yZSkge1xuICAgICAgICAgICAgcmVzU3RvcmUgPSBzdG9yZTtcbiAgICAgICAgICAgIGluaXRpYWxpemVkID0gdHJ1ZTtcbiAgICBcbiAgICAgICAgICAgIGlmIChjYikgY2IobG5nVHJhbnNsYXRlKTtcbiAgICAgICAgICAgIGlmIChkZWZlcnJlZCkgZGVmZXJyZWQucmVzb2x2ZShsbmdUcmFuc2xhdGUpO1xuICAgICAgICB9KTtcbiAgICBcbiAgICAgICAgaWYgKGRlZmVycmVkKSByZXR1cm4gZGVmZXJyZWQucHJvbWlzZSgpO1xuICAgIH1cbiAgICBmdW5jdGlvbiBwcmVsb2FkKGxuZ3MsIGNiKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbG5ncyA9PT0gJ3N0cmluZycpIGxuZ3MgPSBbbG5nc107XG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBsID0gbG5ncy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChvLnByZWxvYWQuaW5kZXhPZihsbmdzW2ldKSA8IDApIHtcbiAgICAgICAgICAgICAgICBvLnByZWxvYWQucHVzaChsbmdzW2ldKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaW5pdChjYik7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGFkZFJlc291cmNlQnVuZGxlKGxuZywgbnMsIHJlc291cmNlcywgZGVlcCkge1xuICAgICAgICBpZiAodHlwZW9mIG5zICE9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmVzb3VyY2VzID0gbnM7XG4gICAgICAgICAgICBucyA9IG8ubnMuZGVmYXVsdE5zO1xuICAgICAgICB9IGVsc2UgaWYgKG8ubnMubmFtZXNwYWNlcy5pbmRleE9mKG5zKSA8IDApIHtcbiAgICAgICAgICAgIG8ubnMubmFtZXNwYWNlcy5wdXNoKG5zKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICByZXNTdG9yZVtsbmddID0gcmVzU3RvcmVbbG5nXSB8fCB7fTtcbiAgICAgICAgcmVzU3RvcmVbbG5nXVtuc10gPSByZXNTdG9yZVtsbmddW25zXSB8fCB7fTtcbiAgICBcbiAgICAgICAgaWYgKGRlZXApIHtcbiAgICAgICAgICAgIGYuZGVlcEV4dGVuZChyZXNTdG9yZVtsbmddW25zXSwgcmVzb3VyY2VzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGYuZXh0ZW5kKHJlc1N0b3JlW2xuZ11bbnNdLCByZXNvdXJjZXMpO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGhhc1Jlc291cmNlQnVuZGxlKGxuZywgbnMpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBucyAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIG5zID0gby5ucy5kZWZhdWx0TnM7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmVzU3RvcmVbbG5nXSA9IHJlc1N0b3JlW2xuZ10gfHwge307XG4gICAgICAgIHZhciByZXMgPSByZXNTdG9yZVtsbmddW25zXSB8fCB7fTtcbiAgICBcbiAgICAgICAgdmFyIGhhc1ZhbHVlcyA9IGZhbHNlO1xuICAgICAgICBmb3IodmFyIHByb3AgaW4gcmVzKSB7XG4gICAgICAgICAgICBpZiAocmVzLmhhc093blByb3BlcnR5KHByb3ApKSB7XG4gICAgICAgICAgICAgICAgaGFzVmFsdWVzID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICByZXR1cm4gaGFzVmFsdWVzO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiByZW1vdmVSZXNvdXJjZUJ1bmRsZShsbmcsIG5zKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbnMgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBucyA9IG8ubnMuZGVmYXVsdE5zO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJlc1N0b3JlW2xuZ10gPSByZXNTdG9yZVtsbmddIHx8IHt9O1xuICAgICAgICByZXNTdG9yZVtsbmddW25zXSA9IHt9O1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBhZGRSZXNvdXJjZShsbmcsIG5zLCBrZXksIHZhbHVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbnMgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICByZXNvdXJjZSA9IG5zO1xuICAgICAgICAgICAgbnMgPSBvLm5zLmRlZmF1bHROcztcbiAgICAgICAgfSBlbHNlIGlmIChvLm5zLm5hbWVzcGFjZXMuaW5kZXhPZihucykgPCAwKSB7XG4gICAgICAgICAgICBvLm5zLm5hbWVzcGFjZXMucHVzaChucyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmVzU3RvcmVbbG5nXSA9IHJlc1N0b3JlW2xuZ10gfHwge307XG4gICAgICAgIHJlc1N0b3JlW2xuZ11bbnNdID0gcmVzU3RvcmVbbG5nXVtuc10gfHwge307XG4gICAgXG4gICAgICAgIHZhciBrZXlzID0ga2V5LnNwbGl0KG8ua2V5c2VwYXJhdG9yKTtcbiAgICAgICAgdmFyIHggPSAwO1xuICAgICAgICB2YXIgbm9kZSA9IHJlc1N0b3JlW2xuZ11bbnNdO1xuICAgICAgICB2YXIgb3JpZ1JlZiA9IG5vZGU7XG4gICAgXG4gICAgICAgIHdoaWxlIChrZXlzW3hdKSB7XG4gICAgICAgICAgICBpZiAoeCA9PSBrZXlzLmxlbmd0aCAtIDEpXG4gICAgICAgICAgICAgICAgbm9kZVtrZXlzW3hdXSA9IHZhbHVlO1xuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKG5vZGVba2V5c1t4XV0gPT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAgbm9kZVtrZXlzW3hdXSA9IHt9O1xuICAgIFxuICAgICAgICAgICAgICAgIG5vZGUgPSBub2RlW2tleXNbeF1dO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgeCsrO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGFkZFJlc291cmNlcyhsbmcsIG5zLCByZXNvdXJjZXMpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBucyAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJlc291cmNlID0gbnM7XG4gICAgICAgICAgICBucyA9IG8ubnMuZGVmYXVsdE5zO1xuICAgICAgICB9IGVsc2UgaWYgKG8ubnMubmFtZXNwYWNlcy5pbmRleE9mKG5zKSA8IDApIHtcbiAgICAgICAgICAgIG8ubnMubmFtZXNwYWNlcy5wdXNoKG5zKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBmb3IgKHZhciBtIGluIHJlc291cmNlcykge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiByZXNvdXJjZXNbbV0gPT09ICdzdHJpbmcnKSBhZGRSZXNvdXJjZShsbmcsIG5zLCBtLCByZXNvdXJjZXNbbV0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIHNldERlZmF1bHROYW1lc3BhY2UobnMpIHtcbiAgICAgICAgby5ucy5kZWZhdWx0TnMgPSBucztcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gbG9hZE5hbWVzcGFjZShuYW1lc3BhY2UsIGNiKSB7XG4gICAgICAgIGxvYWROYW1lc3BhY2VzKFtuYW1lc3BhY2VdLCBjYik7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGxvYWROYW1lc3BhY2VzKG5hbWVzcGFjZXMsIGNiKSB7XG4gICAgICAgIHZhciBvcHRzID0ge1xuICAgICAgICAgICAgZHluYW1pY0xvYWQ6IG8uZHluYW1pY0xvYWQsXG4gICAgICAgICAgICByZXNHZXRQYXRoOiBvLnJlc0dldFBhdGgsXG4gICAgICAgICAgICBnZXRBc3luYzogby5nZXRBc3luYyxcbiAgICAgICAgICAgIGN1c3RvbUxvYWQ6IG8uY3VzdG9tTG9hZCxcbiAgICAgICAgICAgIG5zOiB7IG5hbWVzcGFjZXM6IG5hbWVzcGFjZXMsIGRlZmF1bHROczogJyd9IC8qIG5ldyBuYW1lc3BhY2VzIHRvIGxvYWQgKi9cbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgLy8gbGFuZ3VhZ2VzIHRvIGxvYWRcbiAgICAgICAgdmFyIGxuZ3NUb0xvYWQgPSBmLnRvTGFuZ3VhZ2VzKG8ubG5nKTtcbiAgICAgICAgaWYgKHR5cGVvZiBvLnByZWxvYWQgPT09ICdzdHJpbmcnKSBvLnByZWxvYWQgPSBbby5wcmVsb2FkXTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBvLnByZWxvYWQubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgcHJlcyA9IGYudG9MYW5ndWFnZXMoby5wcmVsb2FkW2ldKTtcbiAgICAgICAgICAgIGZvciAodmFyIHkgPSAwLCBsZW4gPSBwcmVzLmxlbmd0aDsgeSA8IGxlbjsgeSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKGxuZ3NUb0xvYWQuaW5kZXhPZihwcmVzW3ldKSA8IDApIHtcbiAgICAgICAgICAgICAgICAgICAgbG5nc1RvTG9hZC5wdXNoKHByZXNbeV0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBjaGVjayBpZiB3ZSBoYXZlIHRvIGxvYWRcbiAgICAgICAgdmFyIGxuZ05lZWRMb2FkID0gW107XG4gICAgICAgIGZvciAodmFyIGEgPSAwLCBsZW5BID0gbG5nc1RvTG9hZC5sZW5ndGg7IGEgPCBsZW5BOyBhKyspIHtcbiAgICAgICAgICAgIHZhciBuZWVkTG9hZCA9IGZhbHNlO1xuICAgICAgICAgICAgdmFyIHJlc1NldCA9IHJlc1N0b3JlW2xuZ3NUb0xvYWRbYV1dO1xuICAgICAgICAgICAgaWYgKHJlc1NldCkge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGIgPSAwLCBsZW5CID0gbmFtZXNwYWNlcy5sZW5ndGg7IGIgPCBsZW5COyBiKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFyZXNTZXRbbmFtZXNwYWNlc1tiXV0pIG5lZWRMb2FkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG5lZWRMb2FkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIGlmIChuZWVkTG9hZCkgbG5nTmVlZExvYWQucHVzaChsbmdzVG9Mb2FkW2FdKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAobG5nTmVlZExvYWQubGVuZ3RoKSB7XG4gICAgICAgICAgICBpMThuLnN5bmMuX2ZldGNoKGxuZ05lZWRMb2FkLCBvcHRzLCBmdW5jdGlvbihlcnIsIHN0b3JlKSB7XG4gICAgICAgICAgICAgICAgdmFyIHRvZG8gPSBuYW1lc3BhY2VzLmxlbmd0aCAqIGxuZ05lZWRMb2FkLmxlbmd0aDtcbiAgICBcbiAgICAgICAgICAgICAgICAvLyBsb2FkIGVhY2ggZmlsZSBpbmRpdmlkdWFsXG4gICAgICAgICAgICAgICAgZi5lYWNoKG5hbWVzcGFjZXMsIGZ1bmN0aW9uKG5zSW5kZXgsIG5zVmFsdWUpIHtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgLy8gYXBwZW5kIG5hbWVzcGFjZSB0byBuYW1lc3BhY2UgYXJyYXlcbiAgICAgICAgICAgICAgICAgICAgaWYgKG8ubnMubmFtZXNwYWNlcy5pbmRleE9mKG5zVmFsdWUpIDwgMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgby5ucy5uYW1lc3BhY2VzLnB1c2gobnNWYWx1ZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAgICAgZi5lYWNoKGxuZ05lZWRMb2FkLCBmdW5jdGlvbihsbmdJbmRleCwgbG5nVmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc1N0b3JlW2xuZ1ZhbHVlXSA9IHJlc1N0b3JlW2xuZ1ZhbHVlXSB8fCB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc1N0b3JlW2xuZ1ZhbHVlXVtuc1ZhbHVlXSA9IHN0b3JlW2xuZ1ZhbHVlXVtuc1ZhbHVlXTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvZG8tLTsgLy8gd2FpdCBmb3IgYWxsIGRvbmUgYmVmb3IgY2FsbGJhY2tcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0b2RvID09PSAwICYmIGNiKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG8udXNlTG9jYWxTdG9yYWdlKSBpMThuLnN5bmMuX3N0b3JlTG9jYWwocmVzU3RvcmUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAoY2IpIGNiKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gc2V0TG5nKGxuZywgb3B0aW9ucywgY2IpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjYiA9IG9wdGlvbnM7XG4gICAgICAgICAgICBvcHRpb25zID0ge307XG4gICAgICAgIH0gZWxzZSBpZiAoIW9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBvcHRpb25zLmxuZyA9IGxuZztcbiAgICAgICAgcmV0dXJuIGluaXQob3B0aW9ucywgY2IpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBsbmcoKSB7XG4gICAgICAgIHJldHVybiBjdXJyZW50TG5nO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiByZWxvYWQoY2IpIHtcbiAgICAgICAgcmVzU3RvcmUgPSB7fTtcbiAgICAgICAgc2V0TG5nKGN1cnJlbnRMbmcsIGNiKTtcbiAgICB9XG4gICAgZnVuY3Rpb24gYWRkSnF1ZXJ5RnVuY3QoKSB7XG4gICAgICAgIC8vICQudCBzaG9ydGN1dFxuICAgICAgICAkLnQgPSAkLnQgfHwgdHJhbnNsYXRlO1xuICAgIFxuICAgICAgICBmdW5jdGlvbiBwYXJzZShlbGUsIGtleSwgb3B0aW9ucykge1xuICAgICAgICAgICAgaWYgKGtleS5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICBcbiAgICAgICAgICAgIHZhciBhdHRyID0gJ3RleHQnO1xuICAgIFxuICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCdbJykgPT09IDApIHtcbiAgICAgICAgICAgICAgICB2YXIgcGFydHMgPSBrZXkuc3BsaXQoJ10nKTtcbiAgICAgICAgICAgICAgICBrZXkgPSBwYXJ0c1sxXTtcbiAgICAgICAgICAgICAgICBhdHRyID0gcGFydHNbMF0uc3Vic3RyKDEsIHBhcnRzWzBdLmxlbmd0aC0xKTtcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignOycpID09PSBrZXkubGVuZ3RoLTEpIHtcbiAgICAgICAgICAgICAgICBrZXkgPSBrZXkuc3Vic3RyKDAsIGtleS5sZW5ndGgtMik7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICB2YXIgb3B0aW9uc1RvVXNlO1xuICAgICAgICAgICAgaWYgKGF0dHIgPT09ICdodG1sJykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnNUb1VzZSA9IG8uZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQgPyAkLmV4dGVuZCh7IGRlZmF1bHRWYWx1ZTogZWxlLmh0bWwoKSB9LCBvcHRpb25zKSA6IG9wdGlvbnM7XG4gICAgICAgICAgICAgICAgZWxlLmh0bWwoJC50KGtleSwgb3B0aW9uc1RvVXNlKSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGF0dHIgPT09ICd0ZXh0Jykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnNUb1VzZSA9IG8uZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQgPyAkLmV4dGVuZCh7IGRlZmF1bHRWYWx1ZTogZWxlLnRleHQoKSB9LCBvcHRpb25zKSA6IG9wdGlvbnM7XG4gICAgICAgICAgICAgICAgZWxlLnRleHQoJC50KGtleSwgb3B0aW9uc1RvVXNlKSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGF0dHIgPT09ICdwcmVwZW5kJykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnNUb1VzZSA9IG8uZGVmYXVsdFZhbHVlRnJvbUNvbnRlbnQgPyAkLmV4dGVuZCh7IGRlZmF1bHRWYWx1ZTogZWxlLmh0bWwoKSB9LCBvcHRpb25zKSA6IG9wdGlvbnM7XG4gICAgICAgICAgICAgICAgZWxlLnByZXBlbmQoJC50KGtleSwgb3B0aW9uc1RvVXNlKSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGF0dHIgPT09ICdhcHBlbmQnKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1RvVXNlID0gby5kZWZhdWx0VmFsdWVGcm9tQ29udGVudCA/ICQuZXh0ZW5kKHsgZGVmYXVsdFZhbHVlOiBlbGUuaHRtbCgpIH0sIG9wdGlvbnMpIDogb3B0aW9ucztcbiAgICAgICAgICAgICAgICBlbGUuYXBwZW5kKCQudChrZXksIG9wdGlvbnNUb1VzZSkpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChhdHRyLmluZGV4T2YoXCJkYXRhLVwiKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHZhciBkYXRhQXR0ciA9IGF0dHIuc3Vic3RyKChcImRhdGEtXCIpLmxlbmd0aCk7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1RvVXNlID0gby5kZWZhdWx0VmFsdWVGcm9tQ29udGVudCA/ICQuZXh0ZW5kKHsgZGVmYXVsdFZhbHVlOiBlbGUuZGF0YShkYXRhQXR0cikgfSwgb3B0aW9ucykgOiBvcHRpb25zO1xuICAgICAgICAgICAgICAgIHZhciB0cmFuc2xhdGVkID0gJC50KGtleSwgb3B0aW9uc1RvVXNlKTtcbiAgICAgICAgICAgICAgICAvL3dlIGNoYW5nZSBpbnRvIHRoZSBkYXRhIGNhY2hlXG4gICAgICAgICAgICAgICAgZWxlLmRhdGEoZGF0YUF0dHIsIHRyYW5zbGF0ZWQpO1xuICAgICAgICAgICAgICAgIC8vd2UgY2hhbmdlIGludG8gdGhlIGRvbVxuICAgICAgICAgICAgICAgIGVsZS5hdHRyKGF0dHIsIHRyYW5zbGF0ZWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zVG9Vc2UgPSBvLmRlZmF1bHRWYWx1ZUZyb21Db250ZW50ID8gJC5leHRlbmQoeyBkZWZhdWx0VmFsdWU6IGVsZS5hdHRyKGF0dHIpIH0sIG9wdGlvbnMpIDogb3B0aW9ucztcbiAgICAgICAgICAgICAgICBlbGUuYXR0cihhdHRyLCAkLnQoa2V5LCBvcHRpb25zVG9Vc2UpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICBmdW5jdGlvbiBsb2NhbGl6ZShlbGUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBrZXkgPSBlbGUuYXR0cihvLnNlbGVjdG9yQXR0cik7XG4gICAgICAgICAgICBpZiAoIWtleSAmJiB0eXBlb2Yga2V5ICE9PSAndW5kZWZpbmVkJyAmJiBrZXkgIT09IGZhbHNlKSBrZXkgPSBlbGUudGV4dCgpIHx8IGVsZS52YWwoKTtcbiAgICAgICAgICAgIGlmICgha2V5KSByZXR1cm47XG4gICAgXG4gICAgICAgICAgICB2YXIgdGFyZ2V0ID0gZWxlXG4gICAgICAgICAgICAgICwgdGFyZ2V0U2VsZWN0b3IgPSBlbGUuZGF0YShcImkxOG4tdGFyZ2V0XCIpO1xuICAgICAgICAgICAgaWYgKHRhcmdldFNlbGVjdG9yKSB7XG4gICAgICAgICAgICAgICAgdGFyZ2V0ID0gZWxlLmZpbmQodGFyZ2V0U2VsZWN0b3IpIHx8IGVsZTtcbiAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgIGlmICghb3B0aW9ucyAmJiBvLnVzZURhdGFBdHRyT3B0aW9ucyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBlbGUuZGF0YShcImkxOG4tb3B0aW9uc1wiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgIFxuICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCc7JykgPj0gMCkge1xuICAgICAgICAgICAgICAgIHZhciBrZXlzID0ga2V5LnNwbGl0KCc7Jyk7XG4gICAgXG4gICAgICAgICAgICAgICAgJC5lYWNoKGtleXMsIGZ1bmN0aW9uKG0sIGspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGsgIT09ICcnKSBwYXJzZSh0YXJnZXQsIGssIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgIFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwYXJzZSh0YXJnZXQsIGtleSwgb3B0aW9ucyk7XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBpZiAoby51c2VEYXRhQXR0ck9wdGlvbnMgPT09IHRydWUpIGVsZS5kYXRhKFwiaTE4bi1vcHRpb25zXCIsIG9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIGZuXG4gICAgICAgICQuZm4uaTE4biA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIC8vIGxvY2FsaXplIGVsZW1lbnQgaXRzZWxmXG4gICAgICAgICAgICAgICAgbG9jYWxpemUoJCh0aGlzKSwgb3B0aW9ucyk7XG4gICAgXG4gICAgICAgICAgICAgICAgLy8gbG9jYWxpemUgY2hpbGRzXG4gICAgICAgICAgICAgICAgdmFyIGVsZW1lbnRzID0gICQodGhpcykuZmluZCgnWycgKyBvLnNlbGVjdG9yQXR0ciArICddJyk7XG4gICAgICAgICAgICAgICAgZWxlbWVudHMuZWFjaChmdW5jdGlvbigpIHsgXG4gICAgICAgICAgICAgICAgICAgIGxvY2FsaXplKCQodGhpcyksIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH07XG4gICAgfVxuICAgIGZ1bmN0aW9uIGFwcGx5UmVwbGFjZW1lbnQoc3RyLCByZXBsYWNlbWVudEhhc2gsIG5lc3RlZEtleSwgb3B0aW9ucykge1xuICAgICAgICBpZiAoIXN0cikgcmV0dXJuIHN0cjtcbiAgICBcbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwgcmVwbGFjZW1lbnRIYXNoOyAvLyBmaXJzdCBjYWxsIHVzZXMgcmVwbGFjZW1lbnQgaGFzaCBjb21iaW5lZCB3aXRoIG9wdGlvbnNcbiAgICAgICAgaWYgKHN0ci5pbmRleE9mKG9wdGlvbnMuaW50ZXJwb2xhdGlvblByZWZpeCB8fCBvLmludGVycG9sYXRpb25QcmVmaXgpIDwgMCkgcmV0dXJuIHN0cjtcbiAgICBcbiAgICAgICAgdmFyIHByZWZpeCA9IG9wdGlvbnMuaW50ZXJwb2xhdGlvblByZWZpeCA/IGYucmVnZXhFc2NhcGUob3B0aW9ucy5pbnRlcnBvbGF0aW9uUHJlZml4KSA6IG8uaW50ZXJwb2xhdGlvblByZWZpeEVzY2FwZWRcbiAgICAgICAgICAsIHN1ZmZpeCA9IG9wdGlvbnMuaW50ZXJwb2xhdGlvblN1ZmZpeCA/IGYucmVnZXhFc2NhcGUob3B0aW9ucy5pbnRlcnBvbGF0aW9uU3VmZml4KSA6IG8uaW50ZXJwb2xhdGlvblN1ZmZpeEVzY2FwZWRcbiAgICAgICAgICAsIHVuRXNjYXBpbmdTdWZmaXggPSAnSFRNTCcrc3VmZml4O1xuICAgIFxuICAgICAgICB2YXIgaGFzaCA9IHJlcGxhY2VtZW50SGFzaC5yZXBsYWNlICYmIHR5cGVvZiByZXBsYWNlbWVudEhhc2gucmVwbGFjZSA9PT0gJ29iamVjdCcgPyByZXBsYWNlbWVudEhhc2gucmVwbGFjZSA6IHJlcGxhY2VtZW50SGFzaDtcbiAgICAgICAgZi5lYWNoKGhhc2gsIGZ1bmN0aW9uKGtleSwgdmFsdWUpIHtcbiAgICAgICAgICAgIHZhciBuZXh0S2V5ID0gbmVzdGVkS2V5ID8gbmVzdGVkS2V5ICsgby5rZXlzZXBhcmF0b3IgKyBrZXkgOiBrZXk7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHN0ciA9IGFwcGx5UmVwbGFjZW1lbnQoc3RyLCB2YWx1ZSwgbmV4dEtleSwgb3B0aW9ucyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLmVzY2FwZUludGVycG9sYXRpb24gfHwgby5lc2NhcGVJbnRlcnBvbGF0aW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0ciA9IHN0ci5yZXBsYWNlKG5ldyBSZWdFeHAoW3ByZWZpeCwgbmV4dEtleSwgdW5Fc2NhcGluZ1N1ZmZpeF0uam9pbignJyksICdnJyksIGYucmVnZXhSZXBsYWNlbWVudEVzY2FwZSh2YWx1ZSkpO1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSBzdHIucmVwbGFjZShuZXcgUmVnRXhwKFtwcmVmaXgsIG5leHRLZXksIHN1ZmZpeF0uam9pbignJyksICdnJyksIGYucmVnZXhSZXBsYWNlbWVudEVzY2FwZShmLmVzY2FwZSh2YWx1ZSkpKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSBzdHIucmVwbGFjZShuZXcgUmVnRXhwKFtwcmVmaXgsIG5leHRLZXksIHN1ZmZpeF0uam9pbignJyksICdnJyksIGYucmVnZXhSZXBsYWNlbWVudEVzY2FwZSh2YWx1ZSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBzdHIgPSBvcHRpb25zLmVzY2FwZUludGVycG9sYXRpb247XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gc3RyO1xuICAgIH1cbiAgICBcbiAgICAvLyBhcHBlbmQgaXQgdG8gZnVuY3Rpb25zXG4gICAgZi5hcHBseVJlcGxhY2VtZW50ID0gYXBwbHlSZXBsYWNlbWVudDtcbiAgICBcbiAgICBmdW5jdGlvbiBhcHBseVJldXNlKHRyYW5zbGF0ZWQsIG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIGNvbW1hID0gJywnO1xuICAgICAgICB2YXIgb3B0aW9uc19vcGVuID0gJ3snO1xuICAgICAgICB2YXIgb3B0aW9uc19jbG9zZSA9ICd9JztcbiAgICBcbiAgICAgICAgdmFyIG9wdHMgPSBmLmV4dGVuZCh7fSwgb3B0aW9ucyk7XG4gICAgICAgIGRlbGV0ZSBvcHRzLnBvc3RQcm9jZXNzO1xuICAgIFxuICAgICAgICB3aGlsZSAodHJhbnNsYXRlZC5pbmRleE9mKG8ucmV1c2VQcmVmaXgpICE9IC0xKSB7XG4gICAgICAgICAgICByZXBsYWNlbWVudENvdW50ZXIrKztcbiAgICAgICAgICAgIGlmIChyZXBsYWNlbWVudENvdW50ZXIgPiBvLm1heFJlY3Vyc2lvbikgeyBicmVhazsgfSAvLyBzYWZldHkgbmV0IGZvciB0b28gbXVjaCByZWN1cnNpb25cbiAgICAgICAgICAgIHZhciBpbmRleF9vZl9vcGVuaW5nID0gdHJhbnNsYXRlZC5sYXN0SW5kZXhPZihvLnJldXNlUHJlZml4KTtcbiAgICAgICAgICAgIHZhciBpbmRleF9vZl9lbmRfb2ZfY2xvc2luZyA9IHRyYW5zbGF0ZWQuaW5kZXhPZihvLnJldXNlU3VmZml4LCBpbmRleF9vZl9vcGVuaW5nKSArIG8ucmV1c2VTdWZmaXgubGVuZ3RoO1xuICAgICAgICAgICAgdmFyIHRva2VuID0gdHJhbnNsYXRlZC5zdWJzdHJpbmcoaW5kZXhfb2Zfb3BlbmluZywgaW5kZXhfb2ZfZW5kX29mX2Nsb3NpbmcpO1xuICAgICAgICAgICAgdmFyIHRva2VuX3dpdGhvdXRfc3ltYm9scyA9IHRva2VuLnJlcGxhY2Uoby5yZXVzZVByZWZpeCwgJycpLnJlcGxhY2Uoby5yZXVzZVN1ZmZpeCwgJycpO1xuICAgIFxuICAgICAgICAgICAgaWYgKGluZGV4X29mX2VuZF9vZl9jbG9zaW5nIDw9IGluZGV4X29mX29wZW5pbmcpIHtcbiAgICAgICAgICAgICAgICBmLmVycm9yKCd0aGVyZSBpcyBhbiBtaXNzaW5nIGNsb3NpbmcgaW4gZm9sbG93aW5nIHRyYW5zbGF0aW9uIHZhbHVlJywgdHJhbnNsYXRlZCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICAgICAgfVxuICAgIFxuICAgICAgICAgICAgaWYgKHRva2VuX3dpdGhvdXRfc3ltYm9scy5pbmRleE9mKGNvbW1hKSAhPSAtMSkge1xuICAgICAgICAgICAgICAgIHZhciBpbmRleF9vZl90b2tlbl9lbmRfb2ZfY2xvc2luZyA9IHRva2VuX3dpdGhvdXRfc3ltYm9scy5pbmRleE9mKGNvbW1hKTtcbiAgICAgICAgICAgICAgICBpZiAodG9rZW5fd2l0aG91dF9zeW1ib2xzLmluZGV4T2Yob3B0aW9uc19vcGVuLCBpbmRleF9vZl90b2tlbl9lbmRfb2ZfY2xvc2luZykgIT0gLTEgJiYgdG9rZW5fd2l0aG91dF9zeW1ib2xzLmluZGV4T2Yob3B0aW9uc19jbG9zZSwgaW5kZXhfb2ZfdG9rZW5fZW5kX29mX2Nsb3NpbmcpICE9IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBpbmRleF9vZl9vcHRzX29wZW5pbmcgPSB0b2tlbl93aXRob3V0X3N5bWJvbHMuaW5kZXhPZihvcHRpb25zX29wZW4sIGluZGV4X29mX3Rva2VuX2VuZF9vZl9jbG9zaW5nKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGluZGV4X29mX29wdHNfZW5kX29mX2Nsb3NpbmcgPSB0b2tlbl93aXRob3V0X3N5bWJvbHMuaW5kZXhPZihvcHRpb25zX2Nsb3NlLCBpbmRleF9vZl9vcHRzX29wZW5pbmcpICsgb3B0aW9uc19jbG9zZS5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRzID0gZi5leHRlbmQob3B0cywgSlNPTi5wYXJzZSh0b2tlbl93aXRob3V0X3N5bWJvbHMuc3Vic3RyaW5nKGluZGV4X29mX29wdHNfb3BlbmluZywgaW5kZXhfb2Zfb3B0c19lbmRfb2ZfY2xvc2luZykpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRva2VuX3dpdGhvdXRfc3ltYm9scyA9IHRva2VuX3dpdGhvdXRfc3ltYm9scy5zdWJzdHJpbmcoMCwgaW5kZXhfb2ZfdG9rZW5fZW5kX29mX2Nsb3NpbmcpO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICB2YXIgdHJhbnNsYXRlZF90b2tlbiA9IF90cmFuc2xhdGUodG9rZW5fd2l0aG91dF9zeW1ib2xzLCBvcHRzKTtcbiAgICAgICAgICAgIHRyYW5zbGF0ZWQgPSB0cmFuc2xhdGVkLnJlcGxhY2UodG9rZW4sIGYucmVnZXhSZXBsYWNlbWVudEVzY2FwZSh0cmFuc2xhdGVkX3Rva2VuKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZWQ7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGhhc0NvbnRleHQob3B0aW9ucykge1xuICAgICAgICByZXR1cm4gKG9wdGlvbnMuY29udGV4dCAmJiAodHlwZW9mIG9wdGlvbnMuY29udGV4dCA9PSAnc3RyaW5nJyB8fCB0eXBlb2Ygb3B0aW9ucy5jb250ZXh0ID09ICdudW1iZXInKSk7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIG5lZWRzUGx1cmFsKG9wdGlvbnMsIGxuZykge1xuICAgICAgICByZXR1cm4gKG9wdGlvbnMuY291bnQgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2Ygb3B0aW9ucy5jb3VudCAhPSAnc3RyaW5nJy8qICYmIHBsdXJhbEV4dGVuc2lvbnMubmVlZHNQbHVyYWwobG5nLCBvcHRpb25zLmNvdW50KSovKTtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gbmVlZHNJbmRlZmluaXRlQXJ0aWNsZShvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiAob3B0aW9ucy5pbmRlZmluaXRlX2FydGljbGUgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2Ygb3B0aW9ucy5pbmRlZmluaXRlX2FydGljbGUgIT0gJ3N0cmluZycgJiYgb3B0aW9ucy5pbmRlZmluaXRlX2FydGljbGUpO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBleGlzdHMoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgIFxuICAgICAgICB2YXIgbm90Rm91bmQgPSBfZ2V0RGVmYXVsdFZhbHVlKGtleSwgb3B0aW9ucylcbiAgICAgICAgICAgICwgZm91bmQgPSBfZmluZChrZXksIG9wdGlvbnMpO1xuICAgIFxuICAgICAgICByZXR1cm4gZm91bmQgIT09IHVuZGVmaW5lZCB8fCBmb3VuZCA9PT0gbm90Rm91bmQ7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIHRyYW5zbGF0ZShrZXksIG9wdGlvbnMpIHtcbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgXG4gICAgICAgIGlmICghaW5pdGlhbGl6ZWQpIHtcbiAgICAgICAgICAgIGYubG9nKCdpMThuZXh0IG5vdCBmaW5pc2hlZCBpbml0aWFsaXphdGlvbi4geW91IG1pZ2h0IGhhdmUgY2FsbGVkIHQgZnVuY3Rpb24gYmVmb3JlIGxvYWRpbmcgcmVzb3VyY2VzIGZpbmlzaGVkLicpXG4gICAgICAgICAgICByZXR1cm4gb3B0aW9ucy5kZWZhdWx0VmFsdWUgfHwgJyc7XG4gICAgICAgIH07XG4gICAgICAgIHJlcGxhY2VtZW50Q291bnRlciA9IDA7XG4gICAgICAgIHJldHVybiBfdHJhbnNsYXRlLmFwcGx5KG51bGwsIGFyZ3VtZW50cyk7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIF9nZXREZWZhdWx0VmFsdWUoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiAob3B0aW9ucy5kZWZhdWx0VmFsdWUgIT09IHVuZGVmaW5lZCkgPyBvcHRpb25zLmRlZmF1bHRWYWx1ZSA6IGtleTtcbiAgICB9XG4gICAgXG4gICAgZnVuY3Rpb24gX2luamVjdFNwcmludGZQcm9jZXNzb3IoKSB7XG4gICAgXG4gICAgICAgIHZhciB2YWx1ZXMgPSBbXTtcbiAgICBcbiAgICAgICAgLy8gbWg6IGJ1aWxkIGFycmF5IGZyb20gc2Vjb25kIGFyZ3VtZW50IG9ud2FyZHNcbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZhbHVlcy5wdXNoKGFyZ3VtZW50c1tpXSk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHBvc3RQcm9jZXNzOiAnc3ByaW50ZicsXG4gICAgICAgICAgICBzcHJpbnRmOiAgICAgdmFsdWVzXG4gICAgICAgIH07XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIF90cmFuc2xhdGUocG90ZW50aWFsS2V5cywgb3B0aW9ucykge1xuICAgICAgICBpZiAob3B0aW9ucyAmJiB0eXBlb2Ygb3B0aW9ucyAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIGlmIChvLnNob3J0Y3V0RnVuY3Rpb24gPT09ICdzcHJpbnRmJykge1xuICAgICAgICAgICAgICAgIC8vIG1oOiBnZXR0ZXh0IGxpa2Ugc3ByaW50ZiBzeW50YXggZm91bmQsIGF1dG9tYXRpY2FsbHkgY3JlYXRlIHNwcmludGYgcHJvY2Vzc29yXG4gICAgICAgICAgICAgICAgb3B0aW9ucyA9IF9pbmplY3RTcHJpbnRmUHJvY2Vzc29yLmFwcGx5KG51bGwsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG8uc2hvcnRjdXRGdW5jdGlvbiA9PT0gJ2RlZmF1bHRWYWx1ZScpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zID0ge1xuICAgICAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU6IG9wdGlvbnNcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAodHlwZW9mIG8uZGVmYXVsdFZhcmlhYmxlcyA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBmLmV4dGVuZCh7fSwgby5kZWZhdWx0VmFyaWFibGVzLCBvcHRpb25zKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAocG90ZW50aWFsS2V5cyA9PT0gdW5kZWZpbmVkIHx8IHBvdGVudGlhbEtleXMgPT09IG51bGwgfHwgcG90ZW50aWFsS2V5cyA9PT0gJycpIHJldHVybiAnJztcbiAgICBcbiAgICAgICAgaWYgKHR5cGVvZiBwb3RlbnRpYWxLZXlzID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcG90ZW50aWFsS2V5cyA9IFtwb3RlbnRpYWxLZXlzXTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICB2YXIga2V5ID0gcG90ZW50aWFsS2V5c1swXTtcbiAgICBcbiAgICAgICAgaWYgKHBvdGVudGlhbEtleXMubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwb3RlbnRpYWxLZXlzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAga2V5ID0gcG90ZW50aWFsS2V5c1tpXTtcbiAgICAgICAgICAgICAgICBpZiAoZXhpc3RzKGtleSwgb3B0aW9ucykpIHtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBub3RGb3VuZCA9IF9nZXREZWZhdWx0VmFsdWUoa2V5LCBvcHRpb25zKVxuICAgICAgICAgICAgLCBmb3VuZCA9IF9maW5kKGtleSwgb3B0aW9ucylcbiAgICAgICAgICAgICwgbG5ncyA9IG9wdGlvbnMubG5nID8gZi50b0xhbmd1YWdlcyhvcHRpb25zLmxuZywgb3B0aW9ucy5mYWxsYmFja0xuZykgOiBsYW5ndWFnZXNcbiAgICAgICAgICAgICwgbnMgPSBvcHRpb25zLm5zIHx8IG8ubnMuZGVmYXVsdE5zXG4gICAgICAgICAgICAsIHBhcnRzO1xuICAgIFxuICAgICAgICAvLyBzcGxpdCBucyBhbmQga2V5XG4gICAgICAgIGlmIChrZXkuaW5kZXhPZihvLm5zc2VwYXJhdG9yKSA+IC0xKSB7XG4gICAgICAgICAgICBwYXJ0cyA9IGtleS5zcGxpdChvLm5zc2VwYXJhdG9yKTtcbiAgICAgICAgICAgIG5zID0gcGFydHNbMF07XG4gICAgICAgICAgICBrZXkgPSBwYXJ0c1sxXTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAoZm91bmQgPT09IHVuZGVmaW5lZCAmJiBvLnNlbmRNaXNzaW5nICYmIHR5cGVvZiBvLm1pc3NpbmdLZXlIYW5kbGVyID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBpZiAob3B0aW9ucy5sbmcpIHtcbiAgICAgICAgICAgICAgICBvLm1pc3NpbmdLZXlIYW5kbGVyKGxuZ3NbMF0sIG5zLCBrZXksIG5vdEZvdW5kLCBsbmdzKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgby5taXNzaW5nS2V5SGFuZGxlcihvLmxuZywgbnMsIGtleSwgbm90Rm91bmQsIGxuZ3MpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBwb3N0UHJvY2Vzc29yID0gb3B0aW9ucy5wb3N0UHJvY2VzcyB8fCBvLnBvc3RQcm9jZXNzO1xuICAgICAgICBpZiAoZm91bmQgIT09IHVuZGVmaW5lZCAmJiBwb3N0UHJvY2Vzc29yKSB7XG4gICAgICAgICAgICBpZiAocG9zdFByb2Nlc3NvcnNbcG9zdFByb2Nlc3Nvcl0pIHtcbiAgICAgICAgICAgICAgICBmb3VuZCA9IHBvc3RQcm9jZXNzb3JzW3Bvc3RQcm9jZXNzb3JdKGZvdW5kLCBrZXksIG9wdGlvbnMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIHByb2Nlc3Mgbm90Rm91bmQgaWYgZnVuY3Rpb24gZXhpc3RzXG4gICAgICAgIHZhciBzcGxpdE5vdEZvdW5kID0gbm90Rm91bmQ7XG4gICAgICAgIGlmIChub3RGb3VuZC5pbmRleE9mKG8ubnNzZXBhcmF0b3IpID4gLTEpIHtcbiAgICAgICAgICAgIHBhcnRzID0gbm90Rm91bmQuc3BsaXQoby5uc3NlcGFyYXRvcik7XG4gICAgICAgICAgICBzcGxpdE5vdEZvdW5kID0gcGFydHNbMV07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHNwbGl0Tm90Rm91bmQgPT09IGtleSAmJiBvLnBhcnNlTWlzc2luZ0tleSkge1xuICAgICAgICAgICAgbm90Rm91bmQgPSBvLnBhcnNlTWlzc2luZ0tleShub3RGb3VuZCk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKGZvdW5kID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIG5vdEZvdW5kID0gYXBwbHlSZXBsYWNlbWVudChub3RGb3VuZCwgb3B0aW9ucyk7XG4gICAgICAgICAgICBub3RGb3VuZCA9IGFwcGx5UmV1c2Uobm90Rm91bmQsIG9wdGlvbnMpO1xuICAgIFxuICAgICAgICAgICAgaWYgKHBvc3RQcm9jZXNzb3IgJiYgcG9zdFByb2Nlc3NvcnNbcG9zdFByb2Nlc3Nvcl0pIHtcbiAgICAgICAgICAgICAgICB2YXIgdmFsID0gX2dldERlZmF1bHRWYWx1ZShrZXksIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIGZvdW5kID0gcG9zdFByb2Nlc3NvcnNbcG9zdFByb2Nlc3Nvcl0odmFsLCBrZXksIG9wdGlvbnMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJldHVybiAoZm91bmQgIT09IHVuZGVmaW5lZCkgPyBmb3VuZCA6IG5vdEZvdW5kO1xuICAgIH1cbiAgICBcbiAgICBmdW5jdGlvbiBfZmluZChrZXksIG9wdGlvbnMpIHtcbiAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgXG4gICAgICAgIHZhciBvcHRpb25XaXRob3V0Q291bnQsIHRyYW5zbGF0ZWRcbiAgICAgICAgICAgICwgbm90Rm91bmQgPSBfZ2V0RGVmYXVsdFZhbHVlKGtleSwgb3B0aW9ucylcbiAgICAgICAgICAgICwgbG5ncyA9IGxhbmd1YWdlcztcbiAgICBcbiAgICAgICAgaWYgKCFyZXNTdG9yZSkgeyByZXR1cm4gbm90Rm91bmQ7IH0gLy8gbm8gcmVzU3RvcmUgdG8gdHJhbnNsYXRlIGZyb21cbiAgICBcbiAgICAgICAgLy8gQ0kgbW9kZVxuICAgICAgICBpZiAobG5nc1swXS50b0xvd2VyQ2FzZSgpID09PSAnY2ltb2RlJykgcmV0dXJuIG5vdEZvdW5kO1xuICAgIFxuICAgICAgICAvLyBwYXNzZWQgaW4gbG5nXG4gICAgICAgIGlmIChvcHRpb25zLmxuZ3MpIGxuZ3MgPSBvcHRpb25zLmxuZ3M7XG4gICAgICAgIGlmIChvcHRpb25zLmxuZykge1xuICAgICAgICAgICAgbG5ncyA9IGYudG9MYW5ndWFnZXMob3B0aW9ucy5sbmcsIG9wdGlvbnMuZmFsbGJhY2tMbmcpO1xuICAgIFxuICAgICAgICAgICAgaWYgKCFyZXNTdG9yZVtsbmdzWzBdXSkge1xuICAgICAgICAgICAgICAgIHZhciBvbGRBc3luYyA9IG8uZ2V0QXN5bmM7XG4gICAgICAgICAgICAgICAgby5nZXRBc3luYyA9IGZhbHNlO1xuICAgIFxuICAgICAgICAgICAgICAgIGkxOG4uc3luYy5sb2FkKGxuZ3MsIG8sIGZ1bmN0aW9uKGVyciwgc3RvcmUpIHtcbiAgICAgICAgICAgICAgICAgICAgZi5leHRlbmQocmVzU3RvcmUsIHN0b3JlKTtcbiAgICAgICAgICAgICAgICAgICAgby5nZXRBc3luYyA9IG9sZEFzeW5jO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIHZhciBucyA9IG9wdGlvbnMubnMgfHwgby5ucy5kZWZhdWx0TnM7XG4gICAgICAgIGlmIChrZXkuaW5kZXhPZihvLm5zc2VwYXJhdG9yKSA+IC0xKSB7XG4gICAgICAgICAgICB2YXIgcGFydHMgPSBrZXkuc3BsaXQoby5uc3NlcGFyYXRvcik7XG4gICAgICAgICAgICBucyA9IHBhcnRzWzBdO1xuICAgICAgICAgICAga2V5ID0gcGFydHNbMV07XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKGhhc0NvbnRleHQob3B0aW9ucykpIHtcbiAgICAgICAgICAgIG9wdGlvbldpdGhvdXRDb3VudCA9IGYuZXh0ZW5kKHt9LCBvcHRpb25zKTtcbiAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25XaXRob3V0Q291bnQuY29udGV4dDtcbiAgICAgICAgICAgIG9wdGlvbldpdGhvdXRDb3VudC5kZWZhdWx0VmFsdWUgPSBvLmNvbnRleHROb3RGb3VuZDtcbiAgICBcbiAgICAgICAgICAgIHZhciBjb250ZXh0S2V5ID0gbnMgKyBvLm5zc2VwYXJhdG9yICsga2V5ICsgJ18nICsgb3B0aW9ucy5jb250ZXh0O1xuICAgIFxuICAgICAgICAgICAgdHJhbnNsYXRlZCA9IHRyYW5zbGF0ZShjb250ZXh0S2V5LCBvcHRpb25XaXRob3V0Q291bnQpO1xuICAgICAgICAgICAgaWYgKHRyYW5zbGF0ZWQgIT0gby5jb250ZXh0Tm90Rm91bmQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYXBwbHlSZXBsYWNlbWVudCh0cmFuc2xhdGVkLCB7IGNvbnRleHQ6IG9wdGlvbnMuY29udGV4dCB9KTsgLy8gYXBwbHkgcmVwbGFjZW1lbnQgZm9yIGNvbnRleHQgb25seVxuICAgICAgICAgICAgfSAvLyBlbHNlIGNvbnRpbnVlIHRyYW5zbGF0aW9uIHdpdGggb3JpZ2luYWwvbm9uQ29udGV4dCBrZXlcbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAobmVlZHNQbHVyYWwob3B0aW9ucywgbG5nc1swXSkpIHtcbiAgICAgICAgICAgIG9wdGlvbldpdGhvdXRDb3VudCA9IGYuZXh0ZW5kKHsgbG5nczogW2xuZ3NbMF1dfSwgb3B0aW9ucyk7XG4gICAgICAgICAgICBkZWxldGUgb3B0aW9uV2l0aG91dENvdW50LmNvdW50O1xuICAgICAgICAgICAgZGVsZXRlIG9wdGlvbldpdGhvdXRDb3VudC5sbmc7XG4gICAgICAgICAgICBvcHRpb25XaXRob3V0Q291bnQuZGVmYXVsdFZhbHVlID0gby5wbHVyYWxOb3RGb3VuZDtcbiAgICBcbiAgICAgICAgICAgIHZhciBwbHVyYWxLZXk7XG4gICAgICAgICAgICBpZiAoIXBsdXJhbEV4dGVuc2lvbnMubmVlZHNQbHVyYWwobG5nc1swXSwgb3B0aW9ucy5jb3VudCkpIHtcbiAgICAgICAgICAgICAgICBwbHVyYWxLZXkgPSBucyArIG8ubnNzZXBhcmF0b3IgKyBrZXk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHBsdXJhbEtleSA9IG5zICsgby5uc3NlcGFyYXRvciArIGtleSArIG8ucGx1cmFsU3VmZml4O1xuICAgICAgICAgICAgICAgIHZhciBwbHVyYWxFeHRlbnNpb24gPSBwbHVyYWxFeHRlbnNpb25zLmdldChsbmdzWzBdLCBvcHRpb25zLmNvdW50KTtcbiAgICAgICAgICAgICAgICBpZiAocGx1cmFsRXh0ZW5zaW9uID49IDApIHtcbiAgICAgICAgICAgICAgICAgICAgcGx1cmFsS2V5ID0gcGx1cmFsS2V5ICsgJ18nICsgcGx1cmFsRXh0ZW5zaW9uO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocGx1cmFsRXh0ZW5zaW9uID09PSAxKSB7XG4gICAgICAgICAgICAgICAgICAgIHBsdXJhbEtleSA9IG5zICsgby5uc3NlcGFyYXRvciArIGtleTsgLy8gc2luZ3VsYXJcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICB0cmFuc2xhdGVkID0gdHJhbnNsYXRlKHBsdXJhbEtleSwgb3B0aW9uV2l0aG91dENvdW50KTtcbiAgICBcbiAgICAgICAgICAgIGlmICh0cmFuc2xhdGVkICE9IG8ucGx1cmFsTm90Rm91bmQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYXBwbHlSZXBsYWNlbWVudCh0cmFuc2xhdGVkLCB7XG4gICAgICAgICAgICAgICAgICAgIGNvdW50OiBvcHRpb25zLmNvdW50LFxuICAgICAgICAgICAgICAgICAgICBpbnRlcnBvbGF0aW9uUHJlZml4OiBvcHRpb25zLmludGVycG9sYXRpb25QcmVmaXgsXG4gICAgICAgICAgICAgICAgICAgIGludGVycG9sYXRpb25TdWZmaXg6IG9wdGlvbnMuaW50ZXJwb2xhdGlvblN1ZmZpeFxuICAgICAgICAgICAgICAgIH0pOyAvLyBhcHBseSByZXBsYWNlbWVudCBmb3IgY291bnQgb25seVxuICAgICAgICAgICAgfSBlbHNlIGlmIChsbmdzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAvLyByZW1vdmUgZmFpbGVkIGxuZ1xuICAgICAgICAgICAgICAgIHZhciBjbG9uZSA9IGxuZ3Muc2xpY2UoKTtcbiAgICAgICAgICAgICAgICBjbG9uZS5zaGlmdCgpO1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBmLmV4dGVuZChvcHRpb25zLCB7IGxuZ3M6IGNsb25lIH0pO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSBvcHRpb25zLmxuZztcbiAgICAgICAgICAgICAgICAvLyByZXRyeSB3aXRoIGZhbGxiYWNrc1xuICAgICAgICAgICAgICAgIHRyYW5zbGF0ZWQgPSB0cmFuc2xhdGUobnMgKyBvLm5zc2VwYXJhdG9yICsga2V5LCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICBpZiAodHJhbnNsYXRlZCAhPSBvLnBsdXJhbE5vdEZvdW5kKSByZXR1cm4gdHJhbnNsYXRlZDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgaWYgKG5lZWRzSW5kZWZpbml0ZUFydGljbGUob3B0aW9ucykpIHtcbiAgICAgICAgICAgIHZhciBvcHRpb25zV2l0aG91dEluZGVmID0gZi5leHRlbmQoe30sIG9wdGlvbnMpO1xuICAgICAgICAgICAgZGVsZXRlIG9wdGlvbnNXaXRob3V0SW5kZWYuaW5kZWZpbml0ZV9hcnRpY2xlO1xuICAgICAgICAgICAgb3B0aW9uc1dpdGhvdXRJbmRlZi5kZWZhdWx0VmFsdWUgPSBvLmluZGVmaW5pdGVOb3RGb3VuZDtcbiAgICAgICAgICAgIC8vIElmIHdlIGRvbid0IGhhdmUgYSBjb3VudCwgd2Ugd2FudCB0aGUgaW5kZWZpbml0ZSwgaWYgd2UgZG8gaGF2ZSBhIGNvdW50LCBhbmQgbmVlZHNQbHVyYWwgaXMgZmFsc2VcbiAgICAgICAgICAgIHZhciBpbmRlZmluaXRlS2V5ID0gbnMgKyBvLm5zc2VwYXJhdG9yICsga2V5ICsgKCgob3B0aW9ucy5jb3VudCAmJiAhbmVlZHNQbHVyYWwob3B0aW9ucywgbG5nc1swXSkpIHx8ICFvcHRpb25zLmNvdW50KSA/IG8uaW5kZWZpbml0ZVN1ZmZpeCA6IFwiXCIpO1xuICAgICAgICAgICAgdHJhbnNsYXRlZCA9IHRyYW5zbGF0ZShpbmRlZmluaXRlS2V5LCBvcHRpb25zV2l0aG91dEluZGVmKTtcbiAgICAgICAgICAgIGlmICh0cmFuc2xhdGVkICE9IG8uaW5kZWZpbml0ZU5vdEZvdW5kKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgdmFyIGZvdW5kO1xuICAgICAgICB2YXIga2V5cyA9IGtleS5zcGxpdChvLmtleXNlcGFyYXRvcik7XG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrICkge1xuICAgICAgICAgICAgaWYgKGZvdW5kICE9PSB1bmRlZmluZWQpIGJyZWFrO1xuICAgIFxuICAgICAgICAgICAgdmFyIGwgPSBsbmdzW2ldO1xuICAgIFxuICAgICAgICAgICAgdmFyIHggPSAwO1xuICAgICAgICAgICAgdmFyIHZhbHVlID0gcmVzU3RvcmVbbF0gJiYgcmVzU3RvcmVbbF1bbnNdO1xuICAgICAgICAgICAgd2hpbGUgKGtleXNbeF0pIHtcbiAgICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlICYmIHZhbHVlW2tleXNbeF1dO1xuICAgICAgICAgICAgICAgIHgrKztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHZhbHVlVHlwZSA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuYXBwbHkodmFsdWUpO1xuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gYXBwbHlSZXBsYWNlbWVudCh2YWx1ZSwgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gYXBwbHlSZXVzZSh2YWx1ZSwgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZVR5cGUgPT09ICdbb2JqZWN0IEFycmF5XScgJiYgIW8ucmV0dXJuT2JqZWN0VHJlZXMgJiYgIW9wdGlvbnMucmV0dXJuT2JqZWN0VHJlZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZS5qb2luKCdcXG4nKTtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBhcHBseVJlcGxhY2VtZW50KHZhbHVlLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBhcHBseVJldXNlKHZhbHVlLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlID09PSBudWxsICYmIG8uZmFsbGJhY2tPbk51bGwgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIW8ucmV0dXJuT2JqZWN0VHJlZXMgJiYgIW9wdGlvbnMucmV0dXJuT2JqZWN0VHJlZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChvLm9iamVjdFRyZWVLZXlIYW5kbGVyICYmIHR5cGVvZiBvLm9iamVjdFRyZWVLZXlIYW5kbGVyID09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IG8ub2JqZWN0VHJlZUtleUhhbmRsZXIoa2V5LCB2YWx1ZSwgbCwgbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICdrZXkgXFwnJyArIG5zICsgJzonICsga2V5ICsgJyAoJyArIGwgKyAnKVxcJyAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JldHVybmVkIGFuIG9iamVjdCBpbnN0ZWFkIG9mIHN0cmluZy4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYubG9nKHZhbHVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZVR5cGUgIT09ICdbb2JqZWN0IE51bWJlcl0nICYmIHZhbHVlVHlwZSAhPT0gJ1tvYmplY3QgRnVuY3Rpb25dJyAmJiB2YWx1ZVR5cGUgIT09ICdbb2JqZWN0IFJlZ0V4cF0nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgY29weSA9ICh2YWx1ZVR5cGUgPT09ICdbb2JqZWN0IEFycmF5XScpID8gW10gOiB7fTsgLy8gYXBwbHkgY2hpbGQgdHJhbnNsYXRpb24gb24gYSBjb3B5XG4gICAgICAgICAgICAgICAgICAgICAgICBmLmVhY2godmFsdWUsIGZ1bmN0aW9uKG0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3B5W21dID0gX3RyYW5zbGF0ZShucyArIG8ubnNzZXBhcmF0b3IgKyBrZXkgKyBvLmtleXNlcGFyYXRvciArIG0sIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IGNvcHk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiYgdmFsdWUudHJpbSgpID09PSAnJyAmJiBvLmZhbGxiYWNrT25FbXB0eSA9PT0gdHJ1ZSlcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgXG4gICAgICAgICAgICAgICAgZm91bmQgPSB2YWx1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIFxuICAgICAgICBpZiAoZm91bmQgPT09IHVuZGVmaW5lZCAmJiAhb3B0aW9ucy5pc0ZhbGxiYWNrTG9va3VwICYmIChvLmZhbGxiYWNrVG9EZWZhdWx0TlMgPT09IHRydWUgfHwgKG8uZmFsbGJhY2tOUyAmJiBvLmZhbGxiYWNrTlMubGVuZ3RoID4gMCkpKSB7XG4gICAgICAgICAgICAvLyBzZXQgZmxhZyBmb3IgZmFsbGJhY2sgbG9va3VwIC0gYXZvaWQgcmVjdXJzaW9uXG4gICAgICAgICAgICBvcHRpb25zLmlzRmFsbGJhY2tMb29rdXAgPSB0cnVlO1xuICAgIFxuICAgICAgICAgICAgaWYgKG8uZmFsbGJhY2tOUy5sZW5ndGgpIHtcbiAgICBcbiAgICAgICAgICAgICAgICBmb3IgKHZhciB5ID0gMCwgbGVuWSA9IG8uZmFsbGJhY2tOUy5sZW5ndGg7IHkgPCBsZW5ZOyB5KyspIHtcbiAgICAgICAgICAgICAgICAgICAgZm91bmQgPSBfZmluZChvLmZhbGxiYWNrTlNbeV0gKyBvLm5zc2VwYXJhdG9yICsga2V5LCBvcHRpb25zKTtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKGZvdW5kIHx8IChmb3VuZD09PVwiXCIgJiYgby5mYWxsYmFja09uRW1wdHkgPT09IGZhbHNlKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLyogY29tcGFyZSB2YWx1ZSB3aXRob3V0IG5hbWVzcGFjZSAqL1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZvdW5kVmFsdWUgPSBmb3VuZC5pbmRleE9mKG8ubnNzZXBhcmF0b3IpID4gLTEgPyBmb3VuZC5zcGxpdChvLm5zc2VwYXJhdG9yKVsxXSA6IGZvdW5kXG4gICAgICAgICAgICAgICAgICAgICAgICAgICwgbm90Rm91bmRWYWx1ZSA9IG5vdEZvdW5kLmluZGV4T2Yoby5uc3NlcGFyYXRvcikgPiAtMSA/IG5vdEZvdW5kLnNwbGl0KG8ubnNzZXBhcmF0b3IpWzFdIDogbm90Rm91bmQ7XG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZm91bmRWYWx1ZSAhPT0gbm90Rm91bmRWYWx1ZSkgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZvdW5kID0gX2ZpbmQoa2V5LCBvcHRpb25zKTsgLy8gZmFsbGJhY2sgdG8gZGVmYXVsdCBOU1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgb3B0aW9ucy5pc0ZhbGxiYWNrTG9va3VwID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgcmV0dXJuIGZvdW5kO1xuICAgIH1cbiAgICBmdW5jdGlvbiBkZXRlY3RMYW5ndWFnZSgpIHtcbiAgICAgICAgdmFyIGRldGVjdGVkTG5nO1xuICAgICAgICB2YXIgd2hpdGVsaXN0ID0gby5sbmdXaGl0ZWxpc3QgfHwgW107XG4gICAgICAgIHZhciB1c2VyTG5nQ2hvaWNlcyA9IFtdO1xuICAgIFxuICAgICAgICAvLyBnZXQgZnJvbSBxc1xuICAgICAgICB2YXIgcXNQYXJtID0gW107XG4gICAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIHZhciBxdWVyeSA9IHdpbmRvdy5sb2NhdGlvbi5zZWFyY2guc3Vic3RyaW5nKDEpO1xuICAgICAgICAgICAgICAgIHZhciBwYXJhbXMgPSBxdWVyeS5zcGxpdCgnJicpO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGk9MDsgaTxwYXJhbXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBvcyA9IHBhcmFtc1tpXS5pbmRleE9mKCc9Jyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChwb3MgPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIga2V5ID0gcGFyYW1zW2ldLnN1YnN0cmluZygwLHBvcyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoa2V5ID09IG8uZGV0ZWN0TG5nUVMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VyTG5nQ2hvaWNlcy5wdXNoKHBhcmFtc1tpXS5zdWJzdHJpbmcocG9zKzEpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pKCk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgLy8gZ2V0IGZyb20gY29va2llXG4gICAgICAgIGlmIChvLnVzZUNvb2tpZSAmJiB0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICB2YXIgYyA9IGYuY29va2llLnJlYWQoby5jb29raWVOYW1lKTtcbiAgICAgICAgICAgIGlmIChjKSB1c2VyTG5nQ2hvaWNlcy5wdXNoKGMpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIC8vIGdldCBmcm9tIGxvY2FsU3RvcmFnZVxuICAgICAgICBpZiAoby5kZXRlY3RMbmdGcm9tTG9jYWxTdG9yYWdlICYmIHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnICYmIHdpbmRvdy5sb2NhbFN0b3JhZ2UpIHtcbiAgICAgICAgICAgIHVzZXJMbmdDaG9pY2VzLnB1c2god2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKCdpMThuZXh0X2xuZycpKTtcbiAgICAgICAgfVxuICAgIFxuICAgICAgICAvLyBnZXQgZnJvbSBuYXZpZ2F0b3JcbiAgICAgICAgaWYgKHR5cGVvZiBuYXZpZ2F0b3IgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBpZiAobmF2aWdhdG9yLmxhbmd1YWdlcykgeyAvLyBjaHJvbWUgb25seTsgbm90IGFuIGFycmF5LCBzbyBjYW4ndCB1c2UgLnB1c2guYXBwbHkgaW5zdGVhZCBvZiBpdGVyYXRpbmdcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpPTA7aTxuYXZpZ2F0b3IubGFuZ3VhZ2VzLmxlbmd0aDtpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdXNlckxuZ0Nob2ljZXMucHVzaChuYXZpZ2F0b3IubGFuZ3VhZ2VzW2ldKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobmF2aWdhdG9yLnVzZXJMYW5ndWFnZSkge1xuICAgICAgICAgICAgICAgIHVzZXJMbmdDaG9pY2VzLnB1c2gobmF2aWdhdG9yLnVzZXJMYW5ndWFnZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobmF2aWdhdG9yLmxhbmd1YWdlKSB7XG4gICAgICAgICAgICAgICAgdXNlckxuZ0Nob2ljZXMucHVzaChuYXZpZ2F0b3IubGFuZ3VhZ2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXG4gICAgICAgIChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGk9MDtpPHVzZXJMbmdDaG9pY2VzLmxlbmd0aDtpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgbG5nID0gdXNlckxuZ0Nob2ljZXNbaV07XG4gICAgXG4gICAgICAgICAgICAgICAgaWYgKGxuZy5pbmRleE9mKCctJykgPiAtMSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgcGFydHMgPSBsbmcuc3BsaXQoJy0nKTtcbiAgICAgICAgICAgICAgICAgICAgbG5nID0gby5sb3dlckNhc2VMbmcgP1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFydHNbMF0udG9Mb3dlckNhc2UoKSArICAnLScgKyBwYXJ0c1sxXS50b0xvd2VyQ2FzZSgpIDpcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRzWzBdLnRvTG93ZXJDYXNlKCkgKyAgJy0nICsgcGFydHNbMV0udG9VcHBlckNhc2UoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgaWYgKHdoaXRlbGlzdC5sZW5ndGggPT09IDAgfHwgd2hpdGVsaXN0LmluZGV4T2YobG5nKSA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIGRldGVjdGVkTG5nID0gbG5nO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pKCk7XG4gICAgXG4gICAgICAgIC8vZmFsbGJhY2tcbiAgICAgICAgaWYgKCFkZXRlY3RlZExuZyl7XG4gICAgICAgICAgZGV0ZWN0ZWRMbmcgPSBvLmZhbGxiYWNrTG5nWzBdO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gZGV0ZWN0ZWRMbmc7XG4gICAgfVxuICAgIC8vIGRlZmluaXRpb24gaHR0cDovL3RyYW5zbGF0ZS5zb3VyY2Vmb3JnZS5uZXQvd2lraS9sMTBuL3BsdXJhbGZvcm1zXG4gICAgXG4gICAgLyogW2NvZGUsIG5hbWUsIG51bWJlcnMsIHBsdXJhbHNUeXBlXSAqL1xuICAgIHZhciBfcnVsZXMgPSBbXG4gICAgICAgIFtcImFjaFwiLCBcIkFjaG9saVwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcImFmXCIsIFwiQWZyaWthYW5zXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJha1wiLCBcIkFrYW5cIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJhbVwiLCBcIkFtaGFyaWNcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJhblwiLCBcIkFyYWdvbmVzZVwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wiYXJcIiwgXCJBcmFiaWNcIiwgWzAsMSwyLDMsMTEsMTAwXSw1XSxcbiAgICAgICAgW1wiYXJuXCIsIFwiTWFwdWR1bmd1blwiLFsxLDJdLCAxXSxcbiAgICAgICAgW1wiYXN0XCIsIFwiQXN0dXJpYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJheVwiLCBcIkF5bWFyw6FcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wiYXpcIiwgXCJBemVyYmFpamFuaVwiLFsxLDJdLDJdLFxuICAgICAgICBbXCJiZVwiLCBcIkJlbGFydXNpYW5cIixbMSwyLDVdLDRdLFxuICAgICAgICBbXCJiZ1wiLCBcIkJ1bGdhcmlhblwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wiYm5cIiwgXCJCZW5nYWxpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiYm9cIiwgXCJUaWJldGFuXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImJyXCIsIFwiQnJldG9uXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wiYnNcIiwgXCJCb3NuaWFuXCIsIFsxLDIsNV0sNF0sXG4gICAgICAgIFtcImNhXCIsIFwiQ2F0YWxhblwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImNnZ1wiLCBcIkNoaWdhXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImNzXCIsIFwiQ3plY2hcIiwgWzEsMiw1XSw2XSxcbiAgICAgICAgW1wiY3NiXCIsIFwiS2FzaHViaWFuXCIsWzEsMiw1XSw3XSxcbiAgICAgICAgW1wiY3lcIiwgXCJXZWxzaFwiLCBbMSwyLDMsOF0sOF0sXG4gICAgICAgIFtcImRhXCIsIFwiRGFuaXNoXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZGVcIiwgXCJHZXJtYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJkZXZcIiwgXCJEZXZlbG9wbWVudCBGYWxsYmFja1wiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImR6XCIsIFwiRHpvbmdraGFcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wiZWxcIiwgXCJHcmVla1wiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImVuXCIsIFwiRW5nbGlzaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImVvXCIsIFwiRXNwZXJhbnRvXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJlc1wiLCBcIlNwYW5pc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJlc19hclwiLFwiQXJnZW50aW5lYW4gU3BhbmlzaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImV0XCIsIFwiRXN0b25pYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJldVwiLCBcIkJhc3F1ZVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImZhXCIsIFwiUGVyc2lhblwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJmaVwiLCBcIkZpbm5pc2hcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJmaWxcIiwgXCJGaWxpcGlub1wiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcImZvXCIsIFwiRmFyb2VzZVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImZyXCIsIFwiRnJlbmNoXCIsIFsxLDJdLCA5XSxcbiAgICAgICAgW1wiZnVyXCIsIFwiRnJpdWxpYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJmeVwiLCBcIkZyaXNpYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJnYVwiLCBcIklyaXNoXCIsIFsxLDIsMyw3LDExXSwxMF0sXG4gICAgICAgIFtcImdkXCIsIFwiU2NvdHRpc2ggR2FlbGljXCIsWzEsMiwzLDIwXSwxMV0sXG4gICAgICAgIFtcImdsXCIsIFwiR2FsaWNpYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJndVwiLCBcIkd1amFyYXRpXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiZ3VuXCIsIFwiR3VuXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wiaGFcIiwgXCJIYXVzYVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImhlXCIsIFwiSGVicmV3XCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiaGlcIiwgXCJIaW5kaVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcImhyXCIsIFwiQ3JvYXRpYW5cIiwgWzEsMiw1XSw0XSxcbiAgICAgICAgW1wiaHVcIiwgXCJIdW5nYXJpYW5cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcImh5XCIsIFwiQXJtZW5pYW5cIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJpYVwiLCBcIkludGVybGluZ3VhXCIsWzEsMl0sMl0sXG4gICAgICAgIFtcImlkXCIsIFwiSW5kb25lc2lhblwiLFsxXSwgM10sXG4gICAgICAgIFtcImlzXCIsIFwiSWNlbGFuZGljXCIsWzEsMl0sIDEyXSxcbiAgICAgICAgW1wiaXRcIiwgXCJJdGFsaWFuXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wiamFcIiwgXCJKYXBhbmVzZVwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJqYm9cIiwgXCJMb2piYW5cIiwgWzFdLCAzXSxcbiAgICAgICAgW1wianZcIiwgXCJKYXZhbmVzZVwiLCBbMCwxXSwgMTNdLFxuICAgICAgICBbXCJrYVwiLCBcIkdlb3JnaWFuXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImtrXCIsIFwiS2F6YWtoXCIsIFsxXSwgM10sXG4gICAgICAgIFtcImttXCIsIFwiS2htZXJcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wia25cIiwgXCJLYW5uYWRhXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wia29cIiwgXCJLb3JlYW5cIiwgWzFdLCAzXSxcbiAgICAgICAgW1wia3VcIiwgXCJLdXJkaXNoXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wia3dcIiwgXCJDb3JuaXNoXCIsIFsxLDIsMyw0XSwxNF0sXG4gICAgICAgIFtcImt5XCIsIFwiS3lyZ3l6XCIsIFsxXSwgM10sXG4gICAgICAgIFtcImxiXCIsIFwiTGV0emVidXJnZXNjaFwiLFsxLDJdLDJdLFxuICAgICAgICBbXCJsblwiLCBcIkxpbmdhbGFcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJsb1wiLCBcIkxhb1wiLCBbMV0sIDNdLFxuICAgICAgICBbXCJsdFwiLCBcIkxpdGh1YW5pYW5cIixbMSwyLDEwXSwxNV0sXG4gICAgICAgIFtcImx2XCIsIFwiTGF0dmlhblwiLCBbMSwyLDBdLDE2XSxcbiAgICAgICAgW1wibWFpXCIsIFwiTWFpdGhpbGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJtZmVcIiwgXCJNYXVyaXRpYW4gQ3Jlb2xlXCIsWzEsMl0sMV0sXG4gICAgICAgIFtcIm1nXCIsIFwiTWFsYWdhc3lcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJtaVwiLCBcIk1hb3JpXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wibWtcIiwgXCJNYWNlZG9uaWFuXCIsWzEsMl0sMTddLFxuICAgICAgICBbXCJtbFwiLCBcIk1hbGF5YWxhbVwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wibW5cIiwgXCJNb25nb2xpYW5cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcIm1ua1wiLCBcIk1hbmRpbmthXCIsIFswLDEsMl0sMThdLFxuICAgICAgICBbXCJtclwiLCBcIk1hcmF0aGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJtc1wiLCBcIk1hbGF5XCIsIFsxXSwgM10sXG4gICAgICAgIFtcIm10XCIsIFwiTWFsdGVzZVwiLCBbMSwyLDExLDIwXSwxOV0sXG4gICAgICAgIFtcIm5haFwiLCBcIk5haHVhdGxcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJuYXBcIiwgXCJOZWFwb2xpdGFuXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJuYlwiLCBcIk5vcndlZ2lhbiBCb2ttYWxcIixbMSwyXSwyXSxcbiAgICAgICAgW1wibmVcIiwgXCJOZXBhbGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJubFwiLCBcIkR1dGNoXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wibm5cIiwgXCJOb3J3ZWdpYW4gTnlub3Jza1wiLFsxLDJdLDJdLFxuICAgICAgICBbXCJub1wiLCBcIk5vcndlZ2lhblwiLFsxLDJdLCAyXSxcbiAgICAgICAgW1wibnNvXCIsIFwiTm9ydGhlcm4gU290aG9cIixbMSwyXSwyXSxcbiAgICAgICAgW1wib2NcIiwgXCJPY2NpdGFuXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wib3JcIiwgXCJPcml5YVwiLCBbMiwxXSwgMl0sXG4gICAgICAgIFtcInBhXCIsIFwiUHVuamFiaVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInBhcFwiLCBcIlBhcGlhbWVudG9cIixbMSwyXSwgMl0sXG4gICAgICAgIFtcInBsXCIsIFwiUG9saXNoXCIsIFsxLDIsNV0sN10sXG4gICAgICAgIFtcInBtc1wiLCBcIlBpZW1vbnRlc2VcIixbMSwyXSwgMl0sXG4gICAgICAgIFtcInBzXCIsIFwiUGFzaHRvXCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wicHRcIiwgXCJQb3J0dWd1ZXNlXCIsWzEsMl0sIDJdLFxuICAgICAgICBbXCJwdF9iclwiLFwiQnJhemlsaWFuIFBvcnR1Z3Vlc2VcIixbMSwyXSwgMl0sXG4gICAgICAgIFtcInJtXCIsIFwiUm9tYW5zaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInJvXCIsIFwiUm9tYW5pYW5cIiwgWzEsMiwyMF0sMjBdLFxuICAgICAgICBbXCJydVwiLCBcIlJ1c3NpYW5cIiwgWzEsMiw1XSw0XSxcbiAgICAgICAgW1wic2FoXCIsIFwiWWFrdXRcIiwgWzFdLCAzXSxcbiAgICAgICAgW1wic2NvXCIsIFwiU2NvdHNcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJzZVwiLCBcIk5vcnRoZXJuIFNhbWlcIixbMSwyXSwgMl0sXG4gICAgICAgIFtcInNpXCIsIFwiU2luaGFsYVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInNrXCIsIFwiU2xvdmFrXCIsIFsxLDIsNV0sNl0sXG4gICAgICAgIFtcInNsXCIsIFwiU2xvdmVuaWFuXCIsWzUsMSwyLDNdLDIxXSxcbiAgICAgICAgW1wic29cIiwgXCJTb21hbGlcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJzb25cIiwgXCJTb25naGF5XCIsIFsxLDJdLCAyXSxcbiAgICAgICAgW1wic3FcIiwgXCJBbGJhbmlhblwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInNyXCIsIFwiU2VyYmlhblwiLCBbMSwyLDVdLDRdLFxuICAgICAgICBbXCJzdVwiLCBcIlN1bmRhbmVzZVwiLFsxXSwgM10sXG4gICAgICAgIFtcInN2XCIsIFwiU3dlZGlzaFwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInN3XCIsIFwiU3dhaGlsaVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInRhXCIsIFwiVGFtaWxcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ0ZVwiLCBcIlRlbHVndVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInRnXCIsIFwiVGFqaWtcIiwgWzEsMl0sIDFdLFxuICAgICAgICBbXCJ0aFwiLCBcIlRoYWlcIiwgWzFdLCAzXSxcbiAgICAgICAgW1widGlcIiwgXCJUaWdyaW55YVwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcInRrXCIsIFwiVHVya21lblwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInRyXCIsIFwiVHVya2lzaFwiLCBbMSwyXSwgMV0sXG4gICAgICAgIFtcInR0XCIsIFwiVGF0YXJcIiwgWzFdLCAzXSxcbiAgICAgICAgW1widWdcIiwgXCJVeWdodXJcIiwgWzFdLCAzXSxcbiAgICAgICAgW1widWtcIiwgXCJVa3JhaW5pYW5cIixbMSwyLDVdLDRdLFxuICAgICAgICBbXCJ1clwiLCBcIlVyZHVcIiwgWzEsMl0sIDJdLFxuICAgICAgICBbXCJ1elwiLCBcIlV6YmVrXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1widmlcIiwgXCJWaWV0bmFtZXNlXCIsWzFdLCAzXSxcbiAgICAgICAgW1wid2FcIiwgXCJXYWxsb29uXCIsIFsxLDJdLCAxXSxcbiAgICAgICAgW1wid29cIiwgXCJXb2xvZlwiLCBbMV0sIDNdLFxuICAgICAgICBbXCJ5b1wiLCBcIllvcnViYVwiLCBbMSwyXSwgMl0sXG4gICAgICAgIFtcInpoXCIsIFwiQ2hpbmVzZVwiLCBbMV0sIDNdXG4gICAgXTtcbiAgICBcbiAgICB2YXIgX3J1bGVzUGx1cmFsc1R5cGVzID0ge1xuICAgICAgICAxOiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuID4gMSk7fSxcbiAgICAgICAgMjogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiAhPSAxKTt9LFxuICAgICAgICAzOiBmdW5jdGlvbihuKSB7cmV0dXJuIDA7fSxcbiAgICAgICAgNDogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiUxMD09MSAmJiBuJTEwMCE9MTEgPyAwIDogbiUxMD49MiAmJiBuJTEwPD00ICYmIChuJTEwMDwxMCB8fCBuJTEwMD49MjApID8gMSA6IDIpO30sXG4gICAgICAgIDU6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG49PT0wID8gMCA6IG49PTEgPyAxIDogbj09MiA/IDIgOiBuJTEwMD49MyAmJiBuJTEwMDw9MTAgPyAzIDogbiUxMDA+PTExID8gNCA6IDUpO30sXG4gICAgICAgIDY6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKChuPT0xKSA/IDAgOiAobj49MiAmJiBuPD00KSA/IDEgOiAyKTt9LFxuICAgICAgICA3OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuPT0xID8gMCA6IG4lMTA+PTIgJiYgbiUxMDw9NCAmJiAobiUxMDA8MTAgfHwgbiUxMDA+PTIwKSA/IDEgOiAyKTt9LFxuICAgICAgICA4OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcigobj09MSkgPyAwIDogKG49PTIpID8gMSA6IChuICE9IDggJiYgbiAhPSAxMSkgPyAyIDogMyk7fSxcbiAgICAgICAgOTogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiA+PSAyKTt9LFxuICAgICAgICAxMDogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobj09MSA/IDAgOiBuPT0yID8gMSA6IG48NyA/IDIgOiBuPDExID8gMyA6IDQpIDt9LFxuICAgICAgICAxMTogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIoKG49PTEgfHwgbj09MTEpID8gMCA6IChuPT0yIHx8IG49PTEyKSA/IDEgOiAobiA+IDIgJiYgbiA8IDIwKSA/IDIgOiAzKTt9LFxuICAgICAgICAxMjogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiUxMCE9MSB8fCBuJTEwMD09MTEpO30sXG4gICAgICAgIDEzOiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcihuICE9PSAwKTt9LFxuICAgICAgICAxNDogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIoKG49PTEpID8gMCA6IChuPT0yKSA/IDEgOiAobiA9PSAzKSA/IDIgOiAzKTt9LFxuICAgICAgICAxNTogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiUxMD09MSAmJiBuJTEwMCE9MTEgPyAwIDogbiUxMD49MiAmJiAobiUxMDA8MTAgfHwgbiUxMDA+PTIwKSA/IDEgOiAyKTt9LFxuICAgICAgICAxNjogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobiUxMD09MSAmJiBuJTEwMCE9MTEgPyAwIDogbiAhPT0gMCA/IDEgOiAyKTt9LFxuICAgICAgICAxNzogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobj09MSB8fCBuJTEwPT0xID8gMCA6IDEpO30sXG4gICAgICAgIDE4OiBmdW5jdGlvbihuKSB7cmV0dXJuIE51bWJlcigwID8gMCA6IG49PTEgPyAxIDogMik7fSxcbiAgICAgICAgMTk6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG49PTEgPyAwIDogbj09PTAgfHwgKCBuJTEwMD4xICYmIG4lMTAwPDExKSA/IDEgOiAobiUxMDA+MTAgJiYgbiUxMDA8MjAgKSA/IDIgOiAzKTt9LFxuICAgICAgICAyMDogZnVuY3Rpb24obikge3JldHVybiBOdW1iZXIobj09MSA/IDAgOiAobj09PTAgfHwgKG4lMTAwID4gMCAmJiBuJTEwMCA8IDIwKSkgPyAxIDogMik7fSxcbiAgICAgICAgMjE6IGZ1bmN0aW9uKG4pIHtyZXR1cm4gTnVtYmVyKG4lMTAwPT0xID8gMSA6IG4lMTAwPT0yID8gMiA6IG4lMTAwPT0zIHx8IG4lMTAwPT00ID8gMyA6IDApOyB9XG4gICAgfTtcbiAgICBcbiAgICB2YXIgcGx1cmFsRXh0ZW5zaW9ucyA9IHtcbiAgICBcbiAgICAgICAgcnVsZXM6IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbCwgcnVsZXMgPSB7fTtcbiAgICAgICAgICAgIGZvciAobD1fcnVsZXMubGVuZ3RoOyBsLS0gOykge1xuICAgICAgICAgICAgICAgIHJ1bGVzW19ydWxlc1tsXVswXV0gPSB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IF9ydWxlc1tsXVsxXSxcbiAgICAgICAgICAgICAgICAgICAgbnVtYmVyczogX3J1bGVzW2xdWzJdLFxuICAgICAgICAgICAgICAgICAgICBwbHVyYWxzOiBfcnVsZXNQbHVyYWxzVHlwZXNbX3J1bGVzW2xdWzNdXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBydWxlcztcbiAgICAgICAgfSgpKSxcbiAgICBcbiAgICAgICAgLy8geW91IGNhbiBhZGQgeW91ciBvd24gcGx1cmFsRXh0ZW5zaW9uc1xuICAgICAgICBhZGRSdWxlOiBmdW5jdGlvbihsbmcsIG9iaikge1xuICAgICAgICAgICAgcGx1cmFsRXh0ZW5zaW9ucy5ydWxlc1tsbmddID0gb2JqO1xuICAgICAgICB9LFxuICAgIFxuICAgICAgICBzZXRDdXJyZW50TG5nOiBmdW5jdGlvbihsbmcpIHtcbiAgICAgICAgICAgIGlmICghcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZSB8fCBwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlLmxuZyAhPT0gbG5nKSB7XG4gICAgICAgICAgICAgICAgdmFyIHBhcnRzID0gbG5nLnNwbGl0KCctJyk7XG4gICAgXG4gICAgICAgICAgICAgICAgcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZSA9IHtcbiAgICAgICAgICAgICAgICAgICAgbG5nOiBsbmcsXG4gICAgICAgICAgICAgICAgICAgIHJ1bGU6IHBsdXJhbEV4dGVuc2lvbnMucnVsZXNbcGFydHNbMF1dXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICBcbiAgICAgICAgbmVlZHNQbHVyYWw6IGZ1bmN0aW9uKGxuZywgY291bnQpIHtcbiAgICAgICAgICAgIHZhciBwYXJ0cyA9IGxuZy5zcGxpdCgnLScpO1xuICAgIFxuICAgICAgICAgICAgdmFyIGV4dDtcbiAgICAgICAgICAgIGlmIChwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlICYmIHBsdXJhbEV4dGVuc2lvbnMuY3VycmVudFJ1bGUubG5nID09PSBsbmcpIHtcbiAgICAgICAgICAgICAgICBleHQgPSBwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlLnJ1bGU7IFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBleHQgPSBwbHVyYWxFeHRlbnNpb25zLnJ1bGVzW3BhcnRzW2YuZ2V0Q291bnR5SW5kZXhPZkxuZyhsbmcpXV07XG4gICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICBpZiAoZXh0ICYmIGV4dC5udW1iZXJzLmxlbmd0aCA8PSAxKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5nZXQobG5nLCBjb3VudCkgIT09IDE7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgXG4gICAgICAgIGdldDogZnVuY3Rpb24obG5nLCBjb3VudCkge1xuICAgICAgICAgICAgdmFyIHBhcnRzID0gbG5nLnNwbGl0KCctJyk7XG4gICAgXG4gICAgICAgICAgICBmdW5jdGlvbiBnZXRSZXN1bHQobCwgYykge1xuICAgICAgICAgICAgICAgIHZhciBleHQ7XG4gICAgICAgICAgICAgICAgaWYgKHBsdXJhbEV4dGVuc2lvbnMuY3VycmVudFJ1bGUgJiYgcGx1cmFsRXh0ZW5zaW9ucy5jdXJyZW50UnVsZS5sbmcgPT09IGxuZykge1xuICAgICAgICAgICAgICAgICAgICBleHQgPSBwbHVyYWxFeHRlbnNpb25zLmN1cnJlbnRSdWxlLnJ1bGU7IFxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGV4dCA9IHBsdXJhbEV4dGVuc2lvbnMucnVsZXNbbF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChleHQpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChleHQubm9BYnMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSBleHQucGx1cmFscyhjKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSBleHQucGx1cmFscyhNYXRoLmFicyhjKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIHZhciBudW1iZXIgPSBleHQubnVtYmVyc1tpXTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGV4dC5udW1iZXJzLmxlbmd0aCA9PT0gMiAmJiBleHQubnVtYmVyc1swXSA9PT0gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG51bWJlciA9PT0gMikgeyBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSAtMTsgLy8gcmVndWxhciBwbHVyYWxcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAobnVtYmVyID09PSAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTsgLy8gc2luZ3VsYXJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfS8vY29uc29sZS5sb2coY291bnQgKyAnLScgKyBudW1iZXIpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbnVtYmVyO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjID09PSAxID8gJzEnIDogJy0xJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgIHJldHVybiBnZXRSZXN1bHQocGFydHNbZi5nZXRDb3VudHlJbmRleE9mTG5nKGxuZyldLCBjb3VudCk7XG4gICAgICAgIH1cbiAgICBcbiAgICB9O1xuICAgIHZhciBwb3N0UHJvY2Vzc29ycyA9IHt9O1xuICAgIHZhciBhZGRQb3N0UHJvY2Vzc29yID0gZnVuY3Rpb24obmFtZSwgZmMpIHtcbiAgICAgICAgcG9zdFByb2Nlc3NvcnNbbmFtZV0gPSBmYztcbiAgICB9O1xuICAgIC8vIHNwcmludGYgc3VwcG9ydFxuICAgIHZhciBzcHJpbnRmID0gKGZ1bmN0aW9uKCkge1xuICAgICAgICBmdW5jdGlvbiBnZXRfdHlwZSh2YXJpYWJsZSkge1xuICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbCh2YXJpYWJsZSkuc2xpY2UoOCwgLTEpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIH1cbiAgICAgICAgZnVuY3Rpb24gc3RyX3JlcGVhdChpbnB1dCwgbXVsdGlwbGllcikge1xuICAgICAgICAgICAgZm9yICh2YXIgb3V0cHV0ID0gW107IG11bHRpcGxpZXIgPiAwOyBvdXRwdXRbLS1tdWx0aXBsaWVyXSA9IGlucHV0KSB7LyogZG8gbm90aGluZyAqL31cbiAgICAgICAgICAgIHJldHVybiBvdXRwdXQuam9pbignJyk7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgdmFyIHN0cl9mb3JtYXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghc3RyX2Zvcm1hdC5jYWNoZS5oYXNPd25Qcm9wZXJ0eShhcmd1bWVudHNbMF0pKSB7XG4gICAgICAgICAgICAgICAgc3RyX2Zvcm1hdC5jYWNoZVthcmd1bWVudHNbMF1dID0gc3RyX2Zvcm1hdC5wYXJzZShhcmd1bWVudHNbMF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHN0cl9mb3JtYXQuZm9ybWF0LmNhbGwobnVsbCwgc3RyX2Zvcm1hdC5jYWNoZVthcmd1bWVudHNbMF1dLCBhcmd1bWVudHMpO1xuICAgICAgICB9O1xuICAgIFxuICAgICAgICBzdHJfZm9ybWF0LmZvcm1hdCA9IGZ1bmN0aW9uKHBhcnNlX3RyZWUsIGFyZ3YpIHtcbiAgICAgICAgICAgIHZhciBjdXJzb3IgPSAxLCB0cmVlX2xlbmd0aCA9IHBhcnNlX3RyZWUubGVuZ3RoLCBub2RlX3R5cGUgPSAnJywgYXJnLCBvdXRwdXQgPSBbXSwgaSwgaywgbWF0Y2gsIHBhZCwgcGFkX2NoYXJhY3RlciwgcGFkX2xlbmd0aDtcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCB0cmVlX2xlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgbm9kZV90eXBlID0gZ2V0X3R5cGUocGFyc2VfdHJlZVtpXSk7XG4gICAgICAgICAgICAgICAgaWYgKG5vZGVfdHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICAgICAgb3V0cHV0LnB1c2gocGFyc2VfdHJlZVtpXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKG5vZGVfdHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgICAgICAgICAgICAgICBtYXRjaCA9IHBhcnNlX3RyZWVbaV07IC8vIGNvbnZlbmllbmNlIHB1cnBvc2VzIG9ubHlcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1hdGNoWzJdKSB7IC8vIGtleXdvcmQgYXJndW1lbnRcbiAgICAgICAgICAgICAgICAgICAgICAgIGFyZyA9IGFyZ3ZbY3Vyc29yXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBtYXRjaFsyXS5sZW5ndGg7IGsrKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghYXJnLmhhc093blByb3BlcnR5KG1hdGNoWzJdW2tdKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyhzcHJpbnRmKCdbc3ByaW50Zl0gcHJvcGVydHkgXCIlc1wiIGRvZXMgbm90IGV4aXN0JywgbWF0Y2hbMl1ba10pKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJnID0gYXJnW21hdGNoWzJdW2tdXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmIChtYXRjaFsxXSkgeyAvLyBwb3NpdGlvbmFsIGFyZ3VtZW50IChleHBsaWNpdClcbiAgICAgICAgICAgICAgICAgICAgICAgIGFyZyA9IGFyZ3ZbbWF0Y2hbMV1dO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2UgeyAvLyBwb3NpdGlvbmFsIGFyZ3VtZW50IChpbXBsaWNpdClcbiAgICAgICAgICAgICAgICAgICAgICAgIGFyZyA9IGFyZ3ZbY3Vyc29yKytdO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgXG4gICAgICAgICAgICAgICAgICAgIGlmICgvW15zXS8udGVzdChtYXRjaFs4XSkgJiYgKGdldF90eXBlKGFyZykgIT0gJ251bWJlcicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyhzcHJpbnRmKCdbc3ByaW50Zl0gZXhwZWN0aW5nIG51bWJlciBidXQgZm91bmQgJXMnLCBnZXRfdHlwZShhcmcpKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChtYXRjaFs4XSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnYic6IGFyZyA9IGFyZy50b1N0cmluZygyKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdjJzogYXJnID0gU3RyaW5nLmZyb21DaGFyQ29kZShhcmcpOyBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ2QnOiBhcmcgPSBwYXJzZUludChhcmcsIDEwKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdlJzogYXJnID0gbWF0Y2hbN10gPyBhcmcudG9FeHBvbmVudGlhbChtYXRjaFs3XSkgOiBhcmcudG9FeHBvbmVudGlhbCgpOyBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ2YnOiBhcmcgPSBtYXRjaFs3XSA/IHBhcnNlRmxvYXQoYXJnKS50b0ZpeGVkKG1hdGNoWzddKSA6IHBhcnNlRmxvYXQoYXJnKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdvJzogYXJnID0gYXJnLnRvU3RyaW5nKDgpOyBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3MnOiBhcmcgPSAoKGFyZyA9IFN0cmluZyhhcmcpKSAmJiBtYXRjaFs3XSA/IGFyZy5zdWJzdHJpbmcoMCwgbWF0Y2hbN10pIDogYXJnKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICd1JzogYXJnID0gTWF0aC5hYnMoYXJnKTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICd4JzogYXJnID0gYXJnLnRvU3RyaW5nKDE2KTsgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdYJzogYXJnID0gYXJnLnRvU3RyaW5nKDE2KS50b1VwcGVyQ2FzZSgpOyBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBhcmcgPSAoL1tkZWZdLy50ZXN0KG1hdGNoWzhdKSAmJiBtYXRjaFszXSAmJiBhcmcgPj0gMCA/ICcrJysgYXJnIDogYXJnKTtcbiAgICAgICAgICAgICAgICAgICAgcGFkX2NoYXJhY3RlciA9IG1hdGNoWzRdID8gbWF0Y2hbNF0gPT0gJzAnID8gJzAnIDogbWF0Y2hbNF0uY2hhckF0KDEpIDogJyAnO1xuICAgICAgICAgICAgICAgICAgICBwYWRfbGVuZ3RoID0gbWF0Y2hbNl0gLSBTdHJpbmcoYXJnKS5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgIHBhZCA9IG1hdGNoWzZdID8gc3RyX3JlcGVhdChwYWRfY2hhcmFjdGVyLCBwYWRfbGVuZ3RoKSA6ICcnO1xuICAgICAgICAgICAgICAgICAgICBvdXRwdXQucHVzaChtYXRjaFs1XSA/IGFyZyArIHBhZCA6IHBhZCArIGFyZyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIG91dHB1dC5qb2luKCcnKTtcbiAgICAgICAgfTtcbiAgICBcbiAgICAgICAgc3RyX2Zvcm1hdC5jYWNoZSA9IHt9O1xuICAgIFxuICAgICAgICBzdHJfZm9ybWF0LnBhcnNlID0gZnVuY3Rpb24oZm10KSB7XG4gICAgICAgICAgICB2YXIgX2ZtdCA9IGZtdCwgbWF0Y2ggPSBbXSwgcGFyc2VfdHJlZSA9IFtdLCBhcmdfbmFtZXMgPSAwO1xuICAgICAgICAgICAgd2hpbGUgKF9mbXQpIHtcbiAgICAgICAgICAgICAgICBpZiAoKG1hdGNoID0gL15bXlxceDI1XSsvLmV4ZWMoX2ZtdCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHBhcnNlX3RyZWUucHVzaChtYXRjaFswXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKChtYXRjaCA9IC9eXFx4MjV7Mn0vLmV4ZWMoX2ZtdCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHBhcnNlX3RyZWUucHVzaCgnJScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIGlmICgobWF0Y2ggPSAvXlxceDI1KD86KFsxLTldXFxkKilcXCR8XFwoKFteXFwpXSspXFwpKT8oXFwrKT8oMHwnW14kXSk/KC0pPyhcXGQrKT8oPzpcXC4oXFxkKykpPyhbYi1mb3N1eFhdKS8uZXhlYyhfZm10KSkgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1hdGNoWzJdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhcmdfbmFtZXMgfD0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmaWVsZF9saXN0ID0gW10sIHJlcGxhY2VtZW50X2ZpZWxkID0gbWF0Y2hbMl0sIGZpZWxkX21hdGNoID0gW107XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoKGZpZWxkX21hdGNoID0gL14oW2Etel9dW2Etel9cXGRdKikvaS5leGVjKHJlcGxhY2VtZW50X2ZpZWxkKSkgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZF9saXN0LnB1c2goZmllbGRfbWF0Y2hbMV0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWxlICgocmVwbGFjZW1lbnRfZmllbGQgPSByZXBsYWNlbWVudF9maWVsZC5zdWJzdHJpbmcoZmllbGRfbWF0Y2hbMF0ubGVuZ3RoKSkgIT09ICcnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICgoZmllbGRfbWF0Y2ggPSAvXlxcLihbYS16X11bYS16X1xcZF0qKS9pLmV4ZWMocmVwbGFjZW1lbnRfZmllbGQpKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRfbGlzdC5wdXNoKGZpZWxkX21hdGNoWzFdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmICgoZmllbGRfbWF0Y2ggPSAvXlxcWyhcXGQrKVxcXS8uZXhlYyhyZXBsYWNlbWVudF9maWVsZCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZF9saXN0LnB1c2goZmllbGRfbWF0Y2hbMV0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3coJ1tzcHJpbnRmXSBodWg/Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdygnW3NwcmludGZdIGh1aD8nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoWzJdID0gZmllbGRfbGlzdDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ19uYW1lcyB8PSAyO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChhcmdfbmFtZXMgPT09IDMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93KCdbc3ByaW50Zl0gbWl4aW5nIHBvc2l0aW9uYWwgYW5kIG5hbWVkIHBsYWNlaG9sZGVycyBpcyBub3QgKHlldCkgc3VwcG9ydGVkJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcGFyc2VfdHJlZS5wdXNoKG1hdGNoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93KCdbc3ByaW50Zl0gaHVoPycpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBfZm10ID0gX2ZtdC5zdWJzdHJpbmcobWF0Y2hbMF0ubGVuZ3RoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBwYXJzZV90cmVlO1xuICAgICAgICB9O1xuICAgIFxuICAgICAgICByZXR1cm4gc3RyX2Zvcm1hdDtcbiAgICB9KSgpO1xuICAgIFxuICAgIHZhciB2c3ByaW50ZiA9IGZ1bmN0aW9uKGZtdCwgYXJndikge1xuICAgICAgICBhcmd2LnVuc2hpZnQoZm10KTtcbiAgICAgICAgcmV0dXJuIHNwcmludGYuYXBwbHkobnVsbCwgYXJndik7XG4gICAgfTtcbiAgICBcbiAgICBhZGRQb3N0UHJvY2Vzc29yKFwic3ByaW50ZlwiLCBmdW5jdGlvbih2YWwsIGtleSwgb3B0cykge1xuICAgICAgICBpZiAoIW9wdHMuc3ByaW50ZikgcmV0dXJuIHZhbDtcbiAgICBcbiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuYXBwbHkob3B0cy5zcHJpbnRmKSA9PT0gJ1tvYmplY3QgQXJyYXldJykge1xuICAgICAgICAgICAgcmV0dXJuIHZzcHJpbnRmKHZhbCwgb3B0cy5zcHJpbnRmKTtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2Ygb3B0cy5zcHJpbnRmID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgcmV0dXJuIHNwcmludGYodmFsLCBvcHRzLnNwcmludGYpO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfSk7XG4gICAgLy8gcHVibGljIGFwaSBpbnRlcmZhY2VcbiAgICBpMThuLmluaXQgPSBpbml0O1xuICAgIGkxOG4uc2V0TG5nID0gc2V0TG5nO1xuICAgIGkxOG4ucHJlbG9hZCA9IHByZWxvYWQ7XG4gICAgaTE4bi5hZGRSZXNvdXJjZUJ1bmRsZSA9IGFkZFJlc291cmNlQnVuZGxlO1xuICAgIGkxOG4uaGFzUmVzb3VyY2VCdW5kbGUgPSBoYXNSZXNvdXJjZUJ1bmRsZTtcbiAgICBpMThuLmFkZFJlc291cmNlID0gYWRkUmVzb3VyY2U7XG4gICAgaTE4bi5hZGRSZXNvdXJjZXMgPSBhZGRSZXNvdXJjZXM7XG4gICAgaTE4bi5yZW1vdmVSZXNvdXJjZUJ1bmRsZSA9IHJlbW92ZVJlc291cmNlQnVuZGxlO1xuICAgIGkxOG4ubG9hZE5hbWVzcGFjZSA9IGxvYWROYW1lc3BhY2U7XG4gICAgaTE4bi5sb2FkTmFtZXNwYWNlcyA9IGxvYWROYW1lc3BhY2VzO1xuICAgIGkxOG4uc2V0RGVmYXVsdE5hbWVzcGFjZSA9IHNldERlZmF1bHROYW1lc3BhY2U7XG4gICAgaTE4bi50ID0gdHJhbnNsYXRlO1xuICAgIGkxOG4udHJhbnNsYXRlID0gdHJhbnNsYXRlO1xuICAgIGkxOG4uZXhpc3RzID0gZXhpc3RzO1xuICAgIGkxOG4uZGV0ZWN0TGFuZ3VhZ2UgPSBmLmRldGVjdExhbmd1YWdlO1xuICAgIGkxOG4ucGx1cmFsRXh0ZW5zaW9ucyA9IHBsdXJhbEV4dGVuc2lvbnM7XG4gICAgaTE4bi5zeW5jID0gc3luYztcbiAgICBpMThuLmZ1bmN0aW9ucyA9IGY7XG4gICAgaTE4bi5sbmcgPSBsbmc7XG4gICAgaTE4bi5hZGRQb3N0UHJvY2Vzc29yID0gYWRkUG9zdFByb2Nlc3NvcjtcbiAgICBpMThuLm9wdGlvbnMgPSBvO1xuXG59KSgpOyIsIi8vIFRvcCBsZXZlbCBmaWxlIGlzIGp1c3QgYSBtaXhpbiBvZiBzdWJtb2R1bGVzICYgY29uc3RhbnRzXG4ndXNlIHN0cmljdCc7XG5cbnZhciBhc3NpZ24gICAgPSByZXF1aXJlKCcuL2xpYi91dGlscy9jb21tb24nKS5hc3NpZ247XG5cbnZhciBkZWZsYXRlICAgPSByZXF1aXJlKCcuL2xpYi9kZWZsYXRlJyk7XG52YXIgaW5mbGF0ZSAgID0gcmVxdWlyZSgnLi9saWIvaW5mbGF0ZScpO1xudmFyIGNvbnN0YW50cyA9IHJlcXVpcmUoJy4vbGliL3psaWIvY29uc3RhbnRzJyk7XG5cbnZhciBwYWtvID0ge307XG5cbmFzc2lnbihwYWtvLCBkZWZsYXRlLCBpbmZsYXRlLCBjb25zdGFudHMpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHBha287IiwiJ3VzZSBzdHJpY3QnO1xuXG5cbnZhciB6bGliX2RlZmxhdGUgPSByZXF1aXJlKCcuL3psaWIvZGVmbGF0ZS5qcycpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi91dGlscy9jb21tb24nKTtcbnZhciBzdHJpbmdzID0gcmVxdWlyZSgnLi91dGlscy9zdHJpbmdzJyk7XG52YXIgbXNnID0gcmVxdWlyZSgnLi96bGliL21lc3NhZ2VzJyk7XG52YXIgenN0cmVhbSA9IHJlcXVpcmUoJy4vemxpYi96c3RyZWFtJyk7XG5cblxuLyogUHVibGljIGNvbnN0YW50cyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbnZhciBaX05PX0ZMVVNIICAgICAgPSAwO1xudmFyIFpfRklOSVNIICAgICAgICA9IDQ7XG5cbnZhciBaX09LICAgICAgICAgICAgPSAwO1xudmFyIFpfU1RSRUFNX0VORCAgICA9IDE7XG5cbnZhciBaX0RFRkFVTFRfQ09NUFJFU1NJT04gPSAtMTtcblxudmFyIFpfREVGQVVMVF9TVFJBVEVHWSAgICA9IDA7XG5cbnZhciBaX0RFRkxBVEVEICA9IDg7XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxuLyoqXG4gKiBjbGFzcyBEZWZsYXRlXG4gKlxuICogR2VuZXJpYyBKUy1zdHlsZSB3cmFwcGVyIGZvciB6bGliIGNhbGxzLiBJZiB5b3UgZG9uJ3QgbmVlZFxuICogc3RyZWFtaW5nIGJlaGF2aW91ciAtIHVzZSBtb3JlIHNpbXBsZSBmdW5jdGlvbnM6IFtbZGVmbGF0ZV1dLFxuICogW1tkZWZsYXRlUmF3XV0gYW5kIFtbZ3ppcF1dLlxuICoqL1xuXG4vKiBpbnRlcm5hbFxuICogRGVmbGF0ZS5jaHVua3MgLT4gQXJyYXlcbiAqXG4gKiBDaHVua3Mgb2Ygb3V0cHV0IGRhdGEsIGlmIFtbRGVmbGF0ZSNvbkRhdGFdXSBub3Qgb3ZlcnJpZGVuLlxuICoqL1xuXG4vKipcbiAqIERlZmxhdGUucmVzdWx0IC0+IFVpbnQ4QXJyYXl8QXJyYXlcbiAqXG4gKiBDb21wcmVzc2VkIHJlc3VsdCwgZ2VuZXJhdGVkIGJ5IGRlZmF1bHQgW1tEZWZsYXRlI29uRGF0YV1dXG4gKiBhbmQgW1tEZWZsYXRlI29uRW5kXV0gaGFuZGxlcnMuIEZpbGxlZCBhZnRlciB5b3UgcHVzaCBsYXN0IGNodW5rXG4gKiAoY2FsbCBbW0RlZmxhdGUjcHVzaF1dIHdpdGggYFpfRklOSVNIYCAvIGB0cnVlYCBwYXJhbSkuXG4gKiovXG5cbi8qKlxuICogRGVmbGF0ZS5lcnIgLT4gTnVtYmVyXG4gKlxuICogRXJyb3IgY29kZSBhZnRlciBkZWZsYXRlIGZpbmlzaGVkLiAwIChaX09LKSBvbiBzdWNjZXNzLlxuICogWW91IHdpbGwgbm90IG5lZWQgaXQgaW4gcmVhbCBsaWZlLCBiZWNhdXNlIGRlZmxhdGUgZXJyb3JzXG4gKiBhcmUgcG9zc2libGUgb25seSBvbiB3cm9uZyBvcHRpb25zIG9yIGJhZCBgb25EYXRhYCAvIGBvbkVuZGBcbiAqIGN1c3RvbSBoYW5kbGVycy5cbiAqKi9cblxuLyoqXG4gKiBEZWZsYXRlLm1zZyAtPiBTdHJpbmdcbiAqXG4gKiBFcnJvciBtZXNzYWdlLCBpZiBbW0RlZmxhdGUuZXJyXV0gIT0gMFxuICoqL1xuXG5cbi8qKlxuICogbmV3IERlZmxhdGUob3B0aW9ucylcbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBkZWZsYXRlIG9wdGlvbnMuXG4gKlxuICogQ3JlYXRlcyBuZXcgZGVmbGF0b3IgaW5zdGFuY2Ugd2l0aCBzcGVjaWZpZWQgcGFyYW1zLiBUaHJvd3MgZXhjZXB0aW9uXG4gKiBvbiBiYWQgcGFyYW1zLiBTdXBwb3J0ZWQgb3B0aW9uczpcbiAqXG4gKiAtIGBsZXZlbGBcbiAqIC0gYHdpbmRvd0JpdHNgXG4gKiAtIGBtZW1MZXZlbGBcbiAqIC0gYHN0cmF0ZWd5YFxuICpcbiAqIFtodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWRdKGh0dHA6Ly96bGliLm5ldC9tYW51YWwuaHRtbCNBZHZhbmNlZClcbiAqIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZXNlLlxuICpcbiAqIEFkZGl0aW9uYWwgb3B0aW9ucywgZm9yIGludGVybmFsIG5lZWRzOlxuICpcbiAqIC0gYGNodW5rU2l6ZWAgLSBzaXplIG9mIGdlbmVyYXRlZCBkYXRhIGNodW5rcyAoMTZLIGJ5IGRlZmF1bHQpXG4gKiAtIGByYXdgIChCb29sZWFuKSAtIGRvIHJhdyBkZWZsYXRlXG4gKiAtIGBnemlwYCAoQm9vbGVhbikgLSBjcmVhdGUgZ3ppcCB3cmFwcGVyXG4gKiAtIGB0b2AgKFN0cmluZykgLSBpZiBlcXVhbCB0byAnc3RyaW5nJywgdGhlbiByZXN1bHQgd2lsbCBiZSBcImJpbmFyeSBzdHJpbmdcIlxuICogICAgKGVhY2ggY2hhciBjb2RlIFswLi4yNTVdKVxuICogLSBgaGVhZGVyYCAoT2JqZWN0KSAtIGN1c3RvbSBoZWFkZXIgZm9yIGd6aXBcbiAqICAgLSBgdGV4dGAgKEJvb2xlYW4pIC0gdHJ1ZSBpZiBjb21wcmVzc2VkIGRhdGEgYmVsaWV2ZWQgdG8gYmUgdGV4dFxuICogICAtIGB0aW1lYCAoTnVtYmVyKSAtIG1vZGlmaWNhdGlvbiB0aW1lLCB1bml4IHRpbWVzdGFtcFxuICogICAtIGBvc2AgKE51bWJlcikgLSBvcGVyYXRpb24gc3lzdGVtIGNvZGVcbiAqICAgLSBgZXh0cmFgIChBcnJheSkgLSBhcnJheSBvZiBieXRlcyB3aXRoIGV4dHJhIGRhdGEgKG1heCA2NTUzNilcbiAqICAgLSBgbmFtZWAgKFN0cmluZykgLSBmaWxlIG5hbWUgKGJpbmFyeSBzdHJpbmcpXG4gKiAgIC0gYGNvbW1lbnRgIChTdHJpbmcpIC0gY29tbWVudCAoYmluYXJ5IHN0cmluZylcbiAqICAgLSBgaGNyY2AgKEJvb2xlYW4pIC0gdHJ1ZSBpZiBoZWFkZXIgY3JjIHNob3VsZCBiZSBhZGRlZFxuICpcbiAqICMjIyMjIEV4YW1wbGU6XG4gKlxuICogYGBgamF2YXNjcmlwdFxuICogdmFyIHBha28gPSByZXF1aXJlKCdwYWtvJylcbiAqICAgLCBjaHVuazEgPSBVaW50OEFycmF5KFsxLDIsMyw0LDUsNiw3LDgsOV0pXG4gKiAgICwgY2h1bmsyID0gVWludDhBcnJheShbMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTldKTtcbiAqXG4gKiB2YXIgZGVmbGF0ZSA9IG5ldyBwYWtvLkRlZmxhdGUoeyBsZXZlbDogM30pO1xuICpcbiAqIGRlZmxhdGUucHVzaChjaHVuazEsIGZhbHNlKTtcbiAqIGRlZmxhdGUucHVzaChjaHVuazIsIHRydWUpOyAgLy8gdHJ1ZSAtPiBsYXN0IGNodW5rXG4gKlxuICogaWYgKGRlZmxhdGUuZXJyKSB7IHRocm93IG5ldyBFcnJvcihkZWZsYXRlLmVycik7IH1cbiAqXG4gKiBjb25zb2xlLmxvZyhkZWZsYXRlLnJlc3VsdCk7XG4gKiBgYGBcbiAqKi9cbnZhciBEZWZsYXRlID0gZnVuY3Rpb24ob3B0aW9ucykge1xuXG4gIHRoaXMub3B0aW9ucyA9IHV0aWxzLmFzc2lnbih7XG4gICAgbGV2ZWw6IFpfREVGQVVMVF9DT01QUkVTU0lPTixcbiAgICBtZXRob2Q6IFpfREVGTEFURUQsXG4gICAgY2h1bmtTaXplOiAxNjM4NCxcbiAgICB3aW5kb3dCaXRzOiAxNSxcbiAgICBtZW1MZXZlbDogOCxcbiAgICBzdHJhdGVneTogWl9ERUZBVUxUX1NUUkFURUdZLFxuICAgIHRvOiAnJ1xuICB9LCBvcHRpb25zIHx8IHt9KTtcblxuICB2YXIgb3B0ID0gdGhpcy5vcHRpb25zO1xuXG4gIGlmIChvcHQucmF3ICYmIChvcHQud2luZG93Qml0cyA+IDApKSB7XG4gICAgb3B0LndpbmRvd0JpdHMgPSAtb3B0LndpbmRvd0JpdHM7XG4gIH1cblxuICBlbHNlIGlmIChvcHQuZ3ppcCAmJiAob3B0LndpbmRvd0JpdHMgPiAwKSAmJiAob3B0LndpbmRvd0JpdHMgPCAxNikpIHtcbiAgICBvcHQud2luZG93Qml0cyArPSAxNjtcbiAgfVxuXG4gIHRoaXMuZXJyICAgID0gMDsgICAgICAvLyBlcnJvciBjb2RlLCBpZiBoYXBwZW5zICgwID0gWl9PSylcbiAgdGhpcy5tc2cgICAgPSAnJzsgICAgIC8vIGVycm9yIG1lc3NhZ2VcbiAgdGhpcy5lbmRlZCAgPSBmYWxzZTsgIC8vIHVzZWQgdG8gYXZvaWQgbXVsdGlwbGUgb25FbmQoKSBjYWxsc1xuICB0aGlzLmNodW5rcyA9IFtdOyAgICAgLy8gY2h1bmtzIG9mIGNvbXByZXNzZWQgZGF0YVxuXG4gIHRoaXMuc3RybSA9IG5ldyB6c3RyZWFtKCk7XG4gIHRoaXMuc3RybS5hdmFpbF9vdXQgPSAwO1xuXG4gIHZhciBzdGF0dXMgPSB6bGliX2RlZmxhdGUuZGVmbGF0ZUluaXQyKFxuICAgIHRoaXMuc3RybSxcbiAgICBvcHQubGV2ZWwsXG4gICAgb3B0Lm1ldGhvZCxcbiAgICBvcHQud2luZG93Qml0cyxcbiAgICBvcHQubWVtTGV2ZWwsXG4gICAgb3B0LnN0cmF0ZWd5XG4gICk7XG5cbiAgaWYgKHN0YXR1cyAhPT0gWl9PSykge1xuICAgIHRocm93IG5ldyBFcnJvcihtc2dbc3RhdHVzXSk7XG4gIH1cblxuICBpZiAob3B0LmhlYWRlcikge1xuICAgIHpsaWJfZGVmbGF0ZS5kZWZsYXRlU2V0SGVhZGVyKHRoaXMuc3RybSwgb3B0LmhlYWRlcik7XG4gIH1cbn07XG5cbi8qKlxuICogRGVmbGF0ZSNwdXNoKGRhdGFbLCBtb2RlXSkgLT4gQm9vbGVhblxuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEuIFN0cmluZ3Mgd2lsbCBiZSBjb252ZXJ0ZWQgdG9cbiAqICAgdXRmOCBieXRlIHNlcXVlbmNlLlxuICogLSBtb2RlIChOdW1iZXJ8Qm9vbGVhbik6IDAuLjYgZm9yIGNvcnJlc3BvbmRpbmcgWl9OT19GTFVTSC4uWl9UUkVFIG1vZGVzLlxuICogICBTZWUgY29uc3RhbnRzLiBTa2lwcGVkIG9yIGBmYWxzZWAgbWVhbnMgWl9OT19GTFVTSCwgYHRydWVgIG1lYW5zaCBaX0ZJTklTSC5cbiAqXG4gKiBTZW5kcyBpbnB1dCBkYXRhIHRvIGRlZmxhdGUgcGlwZSwgZ2VuZXJhdGluZyBbW0RlZmxhdGUjb25EYXRhXV0gY2FsbHMgd2l0aFxuICogbmV3IGNvbXByZXNzZWQgY2h1bmtzLiBSZXR1cm5zIGB0cnVlYCBvbiBzdWNjZXNzLiBUaGUgbGFzdCBkYXRhIGJsb2NrIG11c3QgaGF2ZVxuICogbW9kZSBaX0ZJTklTSCAob3IgYHRydWVgKS4gVGhhdCBmbHVzaCBpbnRlcm5hbCBwZW5kaW5nIGJ1ZmZlcnMgYW5kIGNhbGxcbiAqIFtbRGVmbGF0ZSNvbkVuZF1dLlxuICpcbiAqIE9uIGZhaWwgY2FsbCBbW0RlZmxhdGUjb25FbmRdXSB3aXRoIGVycm9yIGNvZGUgYW5kIHJldHVybiBmYWxzZS5cbiAqXG4gKiBXZSBzdHJvbmdseSByZWNvbW1lbmQgdG8gdXNlIGBVaW50OEFycmF5YCBvbiBpbnB1dCBmb3IgYmVzdCBzcGVlZCAob3V0cHV0XG4gKiBhcnJheSBmb3JtYXQgaXMgZGV0ZWN0ZWQgYXV0b21hdGljYWxseSkuIEFsc28sIGRvbid0IHNraXAgbGFzdCBwYXJhbSBhbmQgYWx3YXlzXG4gKiB1c2UgdGhlIHNhbWUgdHlwZSBpbiB5b3VyIGNvZGUgKGJvb2xlYW4gb3IgbnVtYmVyKS4gVGhhdCB3aWxsIGltcHJvdmUgSlMgc3BlZWQuXG4gKlxuICogRm9yIHJlZ3VsYXIgYEFycmF5YC1zIG1ha2Ugc3VyZSBhbGwgZWxlbWVudHMgYXJlIFswLi4yNTVdLlxuICpcbiAqICMjIyMjIEV4YW1wbGVcbiAqXG4gKiBgYGBqYXZhc2NyaXB0XG4gKiBwdXNoKGNodW5rLCBmYWxzZSk7IC8vIHB1c2ggb25lIG9mIGRhdGEgY2h1bmtzXG4gKiAuLi5cbiAqIHB1c2goY2h1bmssIHRydWUpOyAgLy8gcHVzaCBsYXN0IGNodW5rXG4gKiBgYGBcbiAqKi9cbkRlZmxhdGUucHJvdG90eXBlLnB1c2ggPSBmdW5jdGlvbihkYXRhLCBtb2RlKSB7XG4gIHZhciBzdHJtID0gdGhpcy5zdHJtO1xuICB2YXIgY2h1bmtTaXplID0gdGhpcy5vcHRpb25zLmNodW5rU2l6ZTtcbiAgdmFyIHN0YXR1cywgX21vZGU7XG5cbiAgaWYgKHRoaXMuZW5kZWQpIHsgcmV0dXJuIGZhbHNlOyB9XG5cbiAgX21vZGUgPSAobW9kZSA9PT0gfn5tb2RlKSA/IG1vZGUgOiAoKG1vZGUgPT09IHRydWUpID8gWl9GSU5JU0ggOiBaX05PX0ZMVVNIKTtcblxuICAvLyBDb252ZXJ0IGRhdGEgaWYgbmVlZGVkXG4gIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAvLyBJZiB3ZSBuZWVkIHRvIGNvbXByZXNzIHRleHQsIGNoYW5nZSBlbmNvZGluZyB0byB1dGY4LlxuICAgIHN0cm0uaW5wdXQgPSBzdHJpbmdzLnN0cmluZzJidWYoZGF0YSk7XG4gIH0gZWxzZSB7XG4gICAgc3RybS5pbnB1dCA9IGRhdGE7XG4gIH1cblxuICBzdHJtLm5leHRfaW4gPSAwO1xuICBzdHJtLmF2YWlsX2luID0gc3RybS5pbnB1dC5sZW5ndGg7XG5cbiAgZG8ge1xuICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgc3RybS5vdXRwdXQgPSBuZXcgdXRpbHMuQnVmOChjaHVua1NpemUpO1xuICAgICAgc3RybS5uZXh0X291dCA9IDA7XG4gICAgICBzdHJtLmF2YWlsX291dCA9IGNodW5rU2l6ZTtcbiAgICB9XG4gICAgc3RhdHVzID0gemxpYl9kZWZsYXRlLmRlZmxhdGUoc3RybSwgX21vZGUpOyAgICAvKiBubyBiYWQgcmV0dXJuIHZhbHVlICovXG5cbiAgICBpZiAoc3RhdHVzICE9PSBaX1NUUkVBTV9FTkQgJiYgc3RhdHVzICE9PSBaX09LKSB7XG4gICAgICB0aGlzLm9uRW5kKHN0YXR1cyk7XG4gICAgICB0aGlzLmVuZGVkID0gdHJ1ZTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKHN0cm0uYXZhaWxfb3V0ID09PSAwIHx8IChzdHJtLmF2YWlsX2luID09PSAwICYmIF9tb2RlID09PSBaX0ZJTklTSCkpIHtcbiAgICAgIGlmICh0aGlzLm9wdGlvbnMudG8gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHRoaXMub25EYXRhKHN0cmluZ3MuYnVmMmJpbnN0cmluZyh1dGlscy5zaHJpbmtCdWYoc3RybS5vdXRwdXQsIHN0cm0ubmV4dF9vdXQpKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLm9uRGF0YSh1dGlscy5zaHJpbmtCdWYoc3RybS5vdXRwdXQsIHN0cm0ubmV4dF9vdXQpKTtcbiAgICAgIH1cbiAgICB9XG4gIH0gd2hpbGUgKChzdHJtLmF2YWlsX2luID4gMCB8fCBzdHJtLmF2YWlsX291dCA9PT0gMCkgJiYgc3RhdHVzICE9PSBaX1NUUkVBTV9FTkQpO1xuXG4gIC8vIEZpbmFsaXplIG9uIHRoZSBsYXN0IGNodW5rLlxuICBpZiAoX21vZGUgPT09IFpfRklOSVNIKSB7XG4gICAgc3RhdHVzID0gemxpYl9kZWZsYXRlLmRlZmxhdGVFbmQodGhpcy5zdHJtKTtcbiAgICB0aGlzLm9uRW5kKHN0YXR1cyk7XG4gICAgdGhpcy5lbmRlZCA9IHRydWU7XG4gICAgcmV0dXJuIHN0YXR1cyA9PT0gWl9PSztcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufTtcblxuXG4vKipcbiAqIERlZmxhdGUjb25EYXRhKGNodW5rKSAtPiBWb2lkXG4gKiAtIGNodW5rIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IG91cHV0IGRhdGEuIFR5cGUgb2YgYXJyYXkgZGVwZW5kc1xuICogICBvbiBqcyBlbmdpbmUgc3VwcG9ydC4gV2hlbiBzdHJpbmcgb3V0cHV0IHJlcXVlc3RlZCwgZWFjaCBjaHVua1xuICogICB3aWxsIGJlIHN0cmluZy5cbiAqXG4gKiBCeSBkZWZhdWx0LCBzdG9yZXMgZGF0YSBibG9ja3MgaW4gYGNodW5rc1tdYCBwcm9wZXJ0eSBhbmQgZ2x1ZVxuICogdGhvc2UgaW4gYG9uRW5kYC4gT3ZlcnJpZGUgdGhpcyBoYW5kbGVyLCBpZiB5b3UgbmVlZCBhbm90aGVyIGJlaGF2aW91ci5cbiAqKi9cbkRlZmxhdGUucHJvdG90eXBlLm9uRGF0YSA9IGZ1bmN0aW9uKGNodW5rKSB7XG4gIHRoaXMuY2h1bmtzLnB1c2goY2h1bmspO1xufTtcblxuXG4vKipcbiAqIERlZmxhdGUjb25FbmQoc3RhdHVzKSAtPiBWb2lkXG4gKiAtIHN0YXR1cyAoTnVtYmVyKTogZGVmbGF0ZSBzdGF0dXMuIDAgKFpfT0spIG9uIHN1Y2Nlc3MsXG4gKiAgIG90aGVyIGlmIG5vdC5cbiAqXG4gKiBDYWxsZWQgb25jZSBhZnRlciB5b3UgdGVsbCBkZWZsYXRlIHRoYXQgaW5wdXQgc3RyZWFtIGNvbXBsZXRlXG4gKiBvciBlcnJvciBoYXBwZW5uZWQuIEJ5IGRlZmF1bHQgLSBqb2luIGNvbGxlY3RlZCBjaHVua3MsXG4gKiBmcmVlIG1lbW9yeSBhbmQgZmlsbCBgcmVzdWx0c2AgLyBgZXJyYCBwcm9wZXJ0aWVzLlxuICoqL1xuRGVmbGF0ZS5wcm90b3R5cGUub25FbmQgPSBmdW5jdGlvbihzdGF0dXMpIHtcbiAgLy8gT24gc3VjY2VzcyAtIGpvaW5cbiAgaWYgKHN0YXR1cyA9PT0gWl9PSykge1xuICAgIGlmICh0aGlzLm9wdGlvbnMudG8gPT09ICdzdHJpbmcnKSB7XG4gICAgICB0aGlzLnJlc3VsdCA9IHRoaXMuY2h1bmtzLmpvaW4oJycpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlc3VsdCA9IHV0aWxzLmZsYXR0ZW5DaHVua3ModGhpcy5jaHVua3MpO1xuICAgIH1cbiAgfVxuICB0aGlzLmNodW5rcyA9IFtdO1xuICB0aGlzLmVyciA9IHN0YXR1cztcbiAgdGhpcy5tc2cgPSB0aGlzLnN0cm0ubXNnO1xufTtcblxuXG4vKipcbiAqIGRlZmxhdGUoZGF0YVssIG9wdGlvbnNdKSAtPiBVaW50OEFycmF5fEFycmF5fFN0cmluZ1xuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEgdG8gY29tcHJlc3MuXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgZGVmbGF0ZSBvcHRpb25zLlxuICpcbiAqIENvbXByZXNzIGBkYXRhYCB3aXRoIGRlZmxhdGUgYWxyb3J5dGhtIGFuZCBgb3B0aW9uc2AuXG4gKlxuICogU3VwcG9ydGVkIG9wdGlvbnMgYXJlOlxuICpcbiAqIC0gbGV2ZWxcbiAqIC0gd2luZG93Qml0c1xuICogLSBtZW1MZXZlbFxuICogLSBzdHJhdGVneVxuICpcbiAqIFtodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWRdKGh0dHA6Ly96bGliLm5ldC9tYW51YWwuaHRtbCNBZHZhbmNlZClcbiAqIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZXNlLlxuICpcbiAqIFN1Z2FyIChvcHRpb25zKTpcbiAqXG4gKiAtIGByYXdgIChCb29sZWFuKSAtIHNheSB0aGF0IHdlIHdvcmsgd2l0aCByYXcgc3RyZWFtLCBpZiB5b3UgZG9uJ3Qgd2lzaCB0byBzcGVjaWZ5XG4gKiAgIG5lZ2F0aXZlIHdpbmRvd0JpdHMgaW1wbGljaXRseS5cbiAqIC0gYHRvYCAoU3RyaW5nKSAtIGlmIGVxdWFsIHRvICdzdHJpbmcnLCB0aGVuIHJlc3VsdCB3aWxsIGJlIFwiYmluYXJ5IHN0cmluZ1wiXG4gKiAgICAoZWFjaCBjaGFyIGNvZGUgWzAuLjI1NV0pXG4gKlxuICogIyMjIyMgRXhhbXBsZTpcbiAqXG4gKiBgYGBqYXZhc2NyaXB0XG4gKiB2YXIgcGFrbyA9IHJlcXVpcmUoJ3Bha28nKVxuICogICAsIGRhdGEgPSBVaW50OEFycmF5KFsxLDIsMyw0LDUsNiw3LDgsOV0pO1xuICpcbiAqIGNvbnNvbGUubG9nKHBha28uZGVmbGF0ZShkYXRhKSk7XG4gKiBgYGBcbiAqKi9cbmZ1bmN0aW9uIGRlZmxhdGUoaW5wdXQsIG9wdGlvbnMpIHtcbiAgdmFyIGRlZmxhdG9yID0gbmV3IERlZmxhdGUob3B0aW9ucyk7XG5cbiAgZGVmbGF0b3IucHVzaChpbnB1dCwgdHJ1ZSk7XG5cbiAgLy8gVGhhdCB3aWxsIG5ldmVyIGhhcHBlbnMsIGlmIHlvdSBkb24ndCBjaGVhdCB3aXRoIG9wdGlvbnMgOilcbiAgaWYgKGRlZmxhdG9yLmVycikgeyB0aHJvdyBkZWZsYXRvci5tc2c7IH1cblxuICByZXR1cm4gZGVmbGF0b3IucmVzdWx0O1xufVxuXG5cbi8qKlxuICogZGVmbGF0ZVJhdyhkYXRhWywgb3B0aW9uc10pIC0+IFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogaW5wdXQgZGF0YSB0byBjb21wcmVzcy5cbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBkZWZsYXRlIG9wdGlvbnMuXG4gKlxuICogVGhlIHNhbWUgYXMgW1tkZWZsYXRlXV0sIGJ1dCBjcmVhdGVzIHJhdyBkYXRhLCB3aXRob3V0IHdyYXBwZXJcbiAqIChoZWFkZXIgYW5kIGFkbGVyMzIgY3JjKS5cbiAqKi9cbmZ1bmN0aW9uIGRlZmxhdGVSYXcoaW5wdXQsIG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gIG9wdGlvbnMucmF3ID0gdHJ1ZTtcbiAgcmV0dXJuIGRlZmxhdGUoaW5wdXQsIG9wdGlvbnMpO1xufVxuXG5cbi8qKlxuICogZ3ppcChkYXRhWywgb3B0aW9uc10pIC0+IFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogaW5wdXQgZGF0YSB0byBjb21wcmVzcy5cbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBkZWZsYXRlIG9wdGlvbnMuXG4gKlxuICogVGhlIHNhbWUgYXMgW1tkZWZsYXRlXV0sIGJ1dCBjcmVhdGUgZ3ppcCB3cmFwcGVyIGluc3RlYWQgb2ZcbiAqIGRlZmxhdGUgb25lLlxuICoqL1xuZnVuY3Rpb24gZ3ppcChpbnB1dCwgb3B0aW9ucykge1xuICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgb3B0aW9ucy5nemlwID0gdHJ1ZTtcbiAgcmV0dXJuIGRlZmxhdGUoaW5wdXQsIG9wdGlvbnMpO1xufVxuXG5cbmV4cG9ydHMuRGVmbGF0ZSA9IERlZmxhdGU7XG5leHBvcnRzLmRlZmxhdGUgPSBkZWZsYXRlO1xuZXhwb3J0cy5kZWZsYXRlUmF3ID0gZGVmbGF0ZVJhdztcbmV4cG9ydHMuZ3ppcCA9IGd6aXA7IiwiJ3VzZSBzdHJpY3QnO1xuXG5cbnZhciB6bGliX2luZmxhdGUgPSByZXF1aXJlKCcuL3psaWIvaW5mbGF0ZS5qcycpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi91dGlscy9jb21tb24nKTtcbnZhciBzdHJpbmdzID0gcmVxdWlyZSgnLi91dGlscy9zdHJpbmdzJyk7XG52YXIgYyA9IHJlcXVpcmUoJy4vemxpYi9jb25zdGFudHMnKTtcbnZhciBtc2cgPSByZXF1aXJlKCcuL3psaWIvbWVzc2FnZXMnKTtcbnZhciB6c3RyZWFtID0gcmVxdWlyZSgnLi96bGliL3pzdHJlYW0nKTtcbnZhciBnemhlYWRlciA9IHJlcXVpcmUoJy4vemxpYi9nemhlYWRlcicpO1xuXG5cbi8qKlxuICogY2xhc3MgSW5mbGF0ZVxuICpcbiAqIEdlbmVyaWMgSlMtc3R5bGUgd3JhcHBlciBmb3IgemxpYiBjYWxscy4gSWYgeW91IGRvbid0IG5lZWRcbiAqIHN0cmVhbWluZyBiZWhhdmlvdXIgLSB1c2UgbW9yZSBzaW1wbGUgZnVuY3Rpb25zOiBbW2luZmxhdGVdXVxuICogYW5kIFtbaW5mbGF0ZVJhd11dLlxuICoqL1xuXG4vKiBpbnRlcm5hbFxuICogaW5mbGF0ZS5jaHVua3MgLT4gQXJyYXlcbiAqXG4gKiBDaHVua3Mgb2Ygb3V0cHV0IGRhdGEsIGlmIFtbSW5mbGF0ZSNvbkRhdGFdXSBub3Qgb3ZlcnJpZGVuLlxuICoqL1xuXG4vKipcbiAqIEluZmxhdGUucmVzdWx0IC0+IFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nXG4gKlxuICogVW5jb21wcmVzc2VkIHJlc3VsdCwgZ2VuZXJhdGVkIGJ5IGRlZmF1bHQgW1tJbmZsYXRlI29uRGF0YV1dXG4gKiBhbmQgW1tJbmZsYXRlI29uRW5kXV0gaGFuZGxlcnMuIEZpbGxlZCBhZnRlciB5b3UgcHVzaCBsYXN0IGNodW5rXG4gKiAoY2FsbCBbW0luZmxhdGUjcHVzaF1dIHdpdGggYFpfRklOSVNIYCAvIGB0cnVlYCBwYXJhbSkuXG4gKiovXG5cbi8qKlxuICogSW5mbGF0ZS5lcnIgLT4gTnVtYmVyXG4gKlxuICogRXJyb3IgY29kZSBhZnRlciBpbmZsYXRlIGZpbmlzaGVkLiAwIChaX09LKSBvbiBzdWNjZXNzLlxuICogU2hvdWxkIGJlIGNoZWNrZWQgaWYgYnJva2VuIGRhdGEgcG9zc2libGUuXG4gKiovXG5cbi8qKlxuICogSW5mbGF0ZS5tc2cgLT4gU3RyaW5nXG4gKlxuICogRXJyb3IgbWVzc2FnZSwgaWYgW1tJbmZsYXRlLmVycl1dICE9IDBcbiAqKi9cblxuXG4vKipcbiAqIG5ldyBJbmZsYXRlKG9wdGlvbnMpXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgaW5mbGF0ZSBvcHRpb25zLlxuICpcbiAqIENyZWF0ZXMgbmV3IGluZmxhdG9yIGluc3RhbmNlIHdpdGggc3BlY2lmaWVkIHBhcmFtcy4gVGhyb3dzIGV4Y2VwdGlvblxuICogb24gYmFkIHBhcmFtcy4gU3VwcG9ydGVkIG9wdGlvbnM6XG4gKlxuICogLSBgd2luZG93Qml0c2BcbiAqXG4gKiBbaHR0cDovL3psaWIubmV0L21hbnVhbC5odG1sI0FkdmFuY2VkXShodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWQpXG4gKiBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGVzZS5cbiAqXG4gKiBBZGRpdGlvbmFsIG9wdGlvbnMsIGZvciBpbnRlcm5hbCBuZWVkczpcbiAqXG4gKiAtIGBjaHVua1NpemVgIC0gc2l6ZSBvZiBnZW5lcmF0ZWQgZGF0YSBjaHVua3MgKDE2SyBieSBkZWZhdWx0KVxuICogLSBgcmF3YCAoQm9vbGVhbikgLSBkbyByYXcgaW5mbGF0ZVxuICogLSBgdG9gIChTdHJpbmcpIC0gaWYgZXF1YWwgdG8gJ3N0cmluZycsIHRoZW4gcmVzdWx0IHdpbGwgYmUgY29udmVydGVkXG4gKiAgIGZyb20gdXRmOCB0byB1dGYxNiAoamF2YXNjcmlwdCkgc3RyaW5nLiBXaGVuIHN0cmluZyBvdXRwdXQgcmVxdWVzdGVkLFxuICogICBjaHVuayBsZW5ndGggY2FuIGRpZmZlciBmcm9tIGBjaHVua1NpemVgLCBkZXBlbmRpbmcgb24gY29udGVudC5cbiAqXG4gKiBCeSBkZWZhdWx0LCB3aGVuIG5vIG9wdGlvbnMgc2V0LCBhdXRvZGV0ZWN0IGRlZmxhdGUvZ3ppcCBkYXRhIGZvcm1hdCB2aWFcbiAqIHdyYXBwZXIgaGVhZGVyLlxuICpcbiAqICMjIyMjIEV4YW1wbGU6XG4gKlxuICogYGBgamF2YXNjcmlwdFxuICogdmFyIHBha28gPSByZXF1aXJlKCdwYWtvJylcbiAqICAgLCBjaHVuazEgPSBVaW50OEFycmF5KFsxLDIsMyw0LDUsNiw3LDgsOV0pXG4gKiAgICwgY2h1bmsyID0gVWludDhBcnJheShbMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTldKTtcbiAqXG4gKiB2YXIgaW5mbGF0ZSA9IG5ldyBwYWtvLkluZmxhdGUoeyBsZXZlbDogM30pO1xuICpcbiAqIGluZmxhdGUucHVzaChjaHVuazEsIGZhbHNlKTtcbiAqIGluZmxhdGUucHVzaChjaHVuazIsIHRydWUpOyAgLy8gdHJ1ZSAtPiBsYXN0IGNodW5rXG4gKlxuICogaWYgKGluZmxhdGUuZXJyKSB7IHRocm93IG5ldyBFcnJvcihpbmZsYXRlLmVycik7IH1cbiAqXG4gKiBjb25zb2xlLmxvZyhpbmZsYXRlLnJlc3VsdCk7XG4gKiBgYGBcbiAqKi9cbnZhciBJbmZsYXRlID0gZnVuY3Rpb24ob3B0aW9ucykge1xuXG4gIHRoaXMub3B0aW9ucyA9IHV0aWxzLmFzc2lnbih7XG4gICAgY2h1bmtTaXplOiAxNjM4NCxcbiAgICB3aW5kb3dCaXRzOiAwLFxuICAgIHRvOiAnJ1xuICB9LCBvcHRpb25zIHx8IHt9KTtcblxuICB2YXIgb3B0ID0gdGhpcy5vcHRpb25zO1xuXG4gIC8vIEZvcmNlIHdpbmRvdyBzaXplIGZvciBgcmF3YCBkYXRhLCBpZiBub3Qgc2V0IGRpcmVjdGx5LFxuICAvLyBiZWNhdXNlIHdlIGhhdmUgbm8gaGVhZGVyIGZvciBhdXRvZGV0ZWN0LlxuICBpZiAob3B0LnJhdyAmJiAob3B0LndpbmRvd0JpdHMgPj0gMCkgJiYgKG9wdC53aW5kb3dCaXRzIDwgMTYpKSB7XG4gICAgb3B0LndpbmRvd0JpdHMgPSAtb3B0LndpbmRvd0JpdHM7XG4gICAgaWYgKG9wdC53aW5kb3dCaXRzID09PSAwKSB7IG9wdC53aW5kb3dCaXRzID0gLTE1OyB9XG4gIH1cblxuICAvLyBJZiBgd2luZG93Qml0c2Agbm90IGRlZmluZWQgKGFuZCBtb2RlIG5vdCByYXcpIC0gc2V0IGF1dG9kZXRlY3QgZmxhZyBmb3IgZ3ppcC9kZWZsYXRlXG4gIGlmICgob3B0LndpbmRvd0JpdHMgPj0gMCkgJiYgKG9wdC53aW5kb3dCaXRzIDwgMTYpICYmXG4gICAgICAhKG9wdGlvbnMgJiYgb3B0aW9ucy53aW5kb3dCaXRzKSkge1xuICAgIG9wdC53aW5kb3dCaXRzICs9IDMyO1xuICB9XG5cbiAgLy8gR3ppcCBoZWFkZXIgaGFzIG5vIGluZm8gYWJvdXQgd2luZG93cyBzaXplLCB3ZSBjYW4gZG8gYXV0b2RldGVjdCBvbmx5XG4gIC8vIGZvciBkZWZsYXRlLiBTbywgaWYgd2luZG93IHNpemUgbm90IHNldCwgZm9yY2UgaXQgdG8gbWF4IHdoZW4gZ3ppcCBwb3NzaWJsZVxuICBpZiAoKG9wdC53aW5kb3dCaXRzID4gMTUpICYmIChvcHQud2luZG93Qml0cyA8IDQ4KSkge1xuICAgIC8vIGJpdCAzICgxNikgLT4gZ3ppcHBlZCBkYXRhXG4gICAgLy8gYml0IDQgKDMyKSAtPiBhdXRvZGV0ZWN0IGd6aXAvZGVmbGF0ZVxuICAgIGlmICgob3B0LndpbmRvd0JpdHMgJiAxNSkgPT09IDApIHtcbiAgICAgIG9wdC53aW5kb3dCaXRzIHw9IDE1O1xuICAgIH1cbiAgfVxuXG4gIHRoaXMuZXJyICAgID0gMDsgICAgICAvLyBlcnJvciBjb2RlLCBpZiBoYXBwZW5zICgwID0gWl9PSylcbiAgdGhpcy5tc2cgICAgPSAnJzsgICAgIC8vIGVycm9yIG1lc3NhZ2VcbiAgdGhpcy5lbmRlZCAgPSBmYWxzZTsgIC8vIHVzZWQgdG8gYXZvaWQgbXVsdGlwbGUgb25FbmQoKSBjYWxsc1xuICB0aGlzLmNodW5rcyA9IFtdOyAgICAgLy8gY2h1bmtzIG9mIGNvbXByZXNzZWQgZGF0YVxuXG4gIHRoaXMuc3RybSAgID0gbmV3IHpzdHJlYW0oKTtcbiAgdGhpcy5zdHJtLmF2YWlsX291dCA9IDA7XG5cbiAgdmFyIHN0YXR1cyAgPSB6bGliX2luZmxhdGUuaW5mbGF0ZUluaXQyKFxuICAgIHRoaXMuc3RybSxcbiAgICBvcHQud2luZG93Qml0c1xuICApO1xuXG4gIGlmIChzdGF0dXMgIT09IGMuWl9PSykge1xuICAgIHRocm93IG5ldyBFcnJvcihtc2dbc3RhdHVzXSk7XG4gIH1cblxuICB0aGlzLmhlYWRlciA9IG5ldyBnemhlYWRlcigpO1xuXG4gIHpsaWJfaW5mbGF0ZS5pbmZsYXRlR2V0SGVhZGVyKHRoaXMuc3RybSwgdGhpcy5oZWFkZXIpO1xufTtcblxuLyoqXG4gKiBJbmZsYXRlI3B1c2goZGF0YVssIG1vZGVdKSAtPiBCb29sZWFuXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogaW5wdXQgZGF0YVxuICogLSBtb2RlIChOdW1iZXJ8Qm9vbGVhbik6IDAuLjYgZm9yIGNvcnJlc3BvbmRpbmcgWl9OT19GTFVTSC4uWl9UUkVFIG1vZGVzLlxuICogICBTZWUgY29uc3RhbnRzLiBTa2lwcGVkIG9yIGBmYWxzZWAgbWVhbnMgWl9OT19GTFVTSCwgYHRydWVgIG1lYW5zaCBaX0ZJTklTSC5cbiAqXG4gKiBTZW5kcyBpbnB1dCBkYXRhIHRvIGluZmxhdGUgcGlwZSwgZ2VuZXJhdGluZyBbW0luZmxhdGUjb25EYXRhXV0gY2FsbHMgd2l0aFxuICogbmV3IG91dHB1dCBjaHVua3MuIFJldHVybnMgYHRydWVgIG9uIHN1Y2Nlc3MuIFRoZSBsYXN0IGRhdGEgYmxvY2sgbXVzdCBoYXZlXG4gKiBtb2RlIFpfRklOSVNIIChvciBgdHJ1ZWApLiBUaGF0IGZsdXNoIGludGVybmFsIHBlbmRpbmcgYnVmZmVycyBhbmQgY2FsbFxuICogW1tJbmZsYXRlI29uRW5kXV0uXG4gKlxuICogT24gZmFpbCBjYWxsIFtbSW5mbGF0ZSNvbkVuZF1dIHdpdGggZXJyb3IgY29kZSBhbmQgcmV0dXJuIGZhbHNlLlxuICpcbiAqIFdlIHN0cm9uZ2x5IHJlY29tbWVuZCB0byB1c2UgYFVpbnQ4QXJyYXlgIG9uIGlucHV0IGZvciBiZXN0IHNwZWVkIChvdXRwdXRcbiAqIGZvcm1hdCBpcyBkZXRlY3RlZCBhdXRvbWF0aWNhbGx5KS4gQWxzbywgZG9uJ3Qgc2tpcCBsYXN0IHBhcmFtIGFuZCBhbHdheXNcbiAqIHVzZSB0aGUgc2FtZSB0eXBlIGluIHlvdXIgY29kZSAoYm9vbGVhbiBvciBudW1iZXIpLiBUaGF0IHdpbGwgaW1wcm92ZSBKUyBzcGVlZC5cbiAqXG4gKiBGb3IgcmVndWxhciBgQXJyYXlgLXMgbWFrZSBzdXJlIGFsbCBlbGVtZW50cyBhcmUgWzAuLjI1NV0uXG4gKlxuICogIyMjIyMgRXhhbXBsZVxuICpcbiAqIGBgYGphdmFzY3JpcHRcbiAqIHB1c2goY2h1bmssIGZhbHNlKTsgLy8gcHVzaCBvbmUgb2YgZGF0YSBjaHVua3NcbiAqIC4uLlxuICogcHVzaChjaHVuaywgdHJ1ZSk7ICAvLyBwdXNoIGxhc3QgY2h1bmtcbiAqIGBgYFxuICoqL1xuSW5mbGF0ZS5wcm90b3R5cGUucHVzaCA9IGZ1bmN0aW9uKGRhdGEsIG1vZGUpIHtcbiAgdmFyIHN0cm0gPSB0aGlzLnN0cm07XG4gIHZhciBjaHVua1NpemUgPSB0aGlzLm9wdGlvbnMuY2h1bmtTaXplO1xuICB2YXIgc3RhdHVzLCBfbW9kZTtcbiAgdmFyIG5leHRfb3V0X3V0ZjgsIHRhaWwsIHV0ZjhzdHI7XG5cbiAgaWYgKHRoaXMuZW5kZWQpIHsgcmV0dXJuIGZhbHNlOyB9XG4gIF9tb2RlID0gKG1vZGUgPT09IH5+bW9kZSkgPyBtb2RlIDogKChtb2RlID09PSB0cnVlKSA/IGMuWl9GSU5JU0ggOiBjLlpfTk9fRkxVU0gpO1xuXG4gIC8vIENvbnZlcnQgZGF0YSBpZiBuZWVkZWRcbiAgaWYgKHR5cGVvZiBkYXRhID09PSAnc3RyaW5nJykge1xuICAgIC8vIE9ubHkgYmluYXJ5IHN0cmluZ3MgY2FuIGJlIGRlY29tcHJlc3NlZCBvbiBwcmFjdGljZVxuICAgIHN0cm0uaW5wdXQgPSBzdHJpbmdzLmJpbnN0cmluZzJidWYoZGF0YSk7XG4gIH0gZWxzZSB7XG4gICAgc3RybS5pbnB1dCA9IGRhdGE7XG4gIH1cblxuICBzdHJtLm5leHRfaW4gPSAwO1xuICBzdHJtLmF2YWlsX2luID0gc3RybS5pbnB1dC5sZW5ndGg7XG5cbiAgZG8ge1xuICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgc3RybS5vdXRwdXQgPSBuZXcgdXRpbHMuQnVmOChjaHVua1NpemUpO1xuICAgICAgc3RybS5uZXh0X291dCA9IDA7XG4gICAgICBzdHJtLmF2YWlsX291dCA9IGNodW5rU2l6ZTtcbiAgICB9XG5cbiAgICBzdGF0dXMgPSB6bGliX2luZmxhdGUuaW5mbGF0ZShzdHJtLCBjLlpfTk9fRkxVU0gpOyAgICAvKiBubyBiYWQgcmV0dXJuIHZhbHVlICovXG5cbiAgICBpZiAoc3RhdHVzICE9PSBjLlpfU1RSRUFNX0VORCAmJiBzdGF0dXMgIT09IGMuWl9PSykge1xuICAgICAgdGhpcy5vbkVuZChzdGF0dXMpO1xuICAgICAgdGhpcy5lbmRlZCA9IHRydWU7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKHN0cm0ubmV4dF9vdXQpIHtcbiAgICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCB8fCBzdGF0dXMgPT09IGMuWl9TVFJFQU1fRU5EIHx8IChzdHJtLmF2YWlsX2luID09PSAwICYmIF9tb2RlID09PSBjLlpfRklOSVNIKSkge1xuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMudG8gPT09ICdzdHJpbmcnKSB7XG5cbiAgICAgICAgICBuZXh0X291dF91dGY4ID0gc3RyaW5ncy51dGY4Ym9yZGVyKHN0cm0ub3V0cHV0LCBzdHJtLm5leHRfb3V0KTtcblxuICAgICAgICAgIHRhaWwgPSBzdHJtLm5leHRfb3V0IC0gbmV4dF9vdXRfdXRmODtcbiAgICAgICAgICB1dGY4c3RyID0gc3RyaW5ncy5idWYyc3RyaW5nKHN0cm0ub3V0cHV0LCBuZXh0X291dF91dGY4KTtcblxuICAgICAgICAgIC8vIG1vdmUgdGFpbFxuICAgICAgICAgIHN0cm0ubmV4dF9vdXQgPSB0YWlsO1xuICAgICAgICAgIHN0cm0uYXZhaWxfb3V0ID0gY2h1bmtTaXplIC0gdGFpbDtcbiAgICAgICAgICBpZiAodGFpbCkgeyB1dGlscy5hcnJheVNldChzdHJtLm91dHB1dCwgc3RybS5vdXRwdXQsIG5leHRfb3V0X3V0ZjgsIHRhaWwsIDApOyB9XG5cbiAgICAgICAgICB0aGlzLm9uRGF0YSh1dGY4c3RyKTtcblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMub25EYXRhKHV0aWxzLnNocmlua0J1ZihzdHJtLm91dHB1dCwgc3RybS5uZXh0X291dCkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9IHdoaWxlICgoc3RybS5hdmFpbF9pbiA+IDApICYmIHN0YXR1cyAhPT0gYy5aX1NUUkVBTV9FTkQpO1xuXG4gIGlmIChzdGF0dXMgPT09IGMuWl9TVFJFQU1fRU5EKSB7XG4gICAgX21vZGUgPSBjLlpfRklOSVNIO1xuICB9XG4gIC8vIEZpbmFsaXplIG9uIHRoZSBsYXN0IGNodW5rLlxuICBpZiAoX21vZGUgPT09IGMuWl9GSU5JU0gpIHtcbiAgICBzdGF0dXMgPSB6bGliX2luZmxhdGUuaW5mbGF0ZUVuZCh0aGlzLnN0cm0pO1xuICAgIHRoaXMub25FbmQoc3RhdHVzKTtcbiAgICB0aGlzLmVuZGVkID0gdHJ1ZTtcbiAgICByZXR1cm4gc3RhdHVzID09PSBjLlpfT0s7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cblxuLyoqXG4gKiBJbmZsYXRlI29uRGF0YShjaHVuaykgLT4gVm9pZFxuICogLSBjaHVuayAoVWludDhBcnJheXxBcnJheXxTdHJpbmcpOiBvdXB1dCBkYXRhLiBUeXBlIG9mIGFycmF5IGRlcGVuZHNcbiAqICAgb24ganMgZW5naW5lIHN1cHBvcnQuIFdoZW4gc3RyaW5nIG91dHB1dCByZXF1ZXN0ZWQsIGVhY2ggY2h1bmtcbiAqICAgd2lsbCBiZSBzdHJpbmcuXG4gKlxuICogQnkgZGVmYXVsdCwgc3RvcmVzIGRhdGEgYmxvY2tzIGluIGBjaHVua3NbXWAgcHJvcGVydHkgYW5kIGdsdWVcbiAqIHRob3NlIGluIGBvbkVuZGAuIE92ZXJyaWRlIHRoaXMgaGFuZGxlciwgaWYgeW91IG5lZWQgYW5vdGhlciBiZWhhdmlvdXIuXG4gKiovXG5JbmZsYXRlLnByb3RvdHlwZS5vbkRhdGEgPSBmdW5jdGlvbihjaHVuaykge1xuICB0aGlzLmNodW5rcy5wdXNoKGNodW5rKTtcbn07XG5cblxuLyoqXG4gKiBJbmZsYXRlI29uRW5kKHN0YXR1cykgLT4gVm9pZFxuICogLSBzdGF0dXMgKE51bWJlcik6IGluZmxhdGUgc3RhdHVzLiAwIChaX09LKSBvbiBzdWNjZXNzLFxuICogICBvdGhlciBpZiBub3QuXG4gKlxuICogQ2FsbGVkIG9uY2UgYWZ0ZXIgeW91IHRlbGwgaW5mbGF0ZSB0aGF0IGlucHV0IHN0cmVhbSBjb21wbGV0ZVxuICogb3IgZXJyb3IgaGFwcGVubmVkLiBCeSBkZWZhdWx0IC0gam9pbiBjb2xsZWN0ZWQgY2h1bmtzLFxuICogZnJlZSBtZW1vcnkgYW5kIGZpbGwgYHJlc3VsdHNgIC8gYGVycmAgcHJvcGVydGllcy5cbiAqKi9cbkluZmxhdGUucHJvdG90eXBlLm9uRW5kID0gZnVuY3Rpb24oc3RhdHVzKSB7XG4gIC8vIE9uIHN1Y2Nlc3MgLSBqb2luXG4gIGlmIChzdGF0dXMgPT09IGMuWl9PSykge1xuICAgIGlmICh0aGlzLm9wdGlvbnMudG8gPT09ICdzdHJpbmcnKSB7XG4gICAgICAvLyBHbHVlICYgY29udmVydCBoZXJlLCB1bnRpbCB3ZSB0ZWFjaCBwYWtvIHRvIHNlbmRcbiAgICAgIC8vIHV0ZjggYWxsaWduZWQgc3RyaW5ncyB0byBvbkRhdGFcbiAgICAgIHRoaXMucmVzdWx0ID0gdGhpcy5jaHVua3Muam9pbignJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucmVzdWx0ID0gdXRpbHMuZmxhdHRlbkNodW5rcyh0aGlzLmNodW5rcyk7XG4gICAgfVxuICB9XG4gIHRoaXMuY2h1bmtzID0gW107XG4gIHRoaXMuZXJyID0gc3RhdHVzO1xuICB0aGlzLm1zZyA9IHRoaXMuc3RybS5tc2c7XG59O1xuXG5cbi8qKlxuICogaW5mbGF0ZShkYXRhWywgb3B0aW9uc10pIC0+IFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nXG4gKiAtIGRhdGEgKFVpbnQ4QXJyYXl8QXJyYXl8U3RyaW5nKTogaW5wdXQgZGF0YSB0byBkZWNvbXByZXNzLlxuICogLSBvcHRpb25zIChPYmplY3QpOiB6bGliIGluZmxhdGUgb3B0aW9ucy5cbiAqXG4gKiBEZWNvbXByZXNzIGBkYXRhYCB3aXRoIGluZmxhdGUvdW5nemlwIGFuZCBgb3B0aW9uc2AuIEF1dG9kZXRlY3RcbiAqIGZvcm1hdCB2aWEgd3JhcHBlciBoZWFkZXIgYnkgZGVmYXVsdC4gVGhhdCdzIHdoeSB3ZSBkb24ndCBwcm92aWRlXG4gKiBzZXBhcmF0ZSBgdW5nemlwYCBtZXRob2QuXG4gKlxuICogU3VwcG9ydGVkIG9wdGlvbnMgYXJlOlxuICpcbiAqIC0gd2luZG93Qml0c1xuICpcbiAqIFtodHRwOi8vemxpYi5uZXQvbWFudWFsLmh0bWwjQWR2YW5jZWRdKGh0dHA6Ly96bGliLm5ldC9tYW51YWwuaHRtbCNBZHZhbmNlZClcbiAqIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICpcbiAqIFN1Z2FyIChvcHRpb25zKTpcbiAqXG4gKiAtIGByYXdgIChCb29sZWFuKSAtIHNheSB0aGF0IHdlIHdvcmsgd2l0aCByYXcgc3RyZWFtLCBpZiB5b3UgZG9uJ3Qgd2lzaCB0byBzcGVjaWZ5XG4gKiAgIG5lZ2F0aXZlIHdpbmRvd0JpdHMgaW1wbGljaXRseS5cbiAqIC0gYHRvYCAoU3RyaW5nKSAtIGlmIGVxdWFsIHRvICdzdHJpbmcnLCB0aGVuIHJlc3VsdCB3aWxsIGJlIGNvbnZlcnRlZFxuICogICBmcm9tIHV0ZjggdG8gdXRmMTYgKGphdmFzY3JpcHQpIHN0cmluZy4gV2hlbiBzdHJpbmcgb3V0cHV0IHJlcXVlc3RlZCxcbiAqICAgY2h1bmsgbGVuZ3RoIGNhbiBkaWZmZXIgZnJvbSBgY2h1bmtTaXplYCwgZGVwZW5kaW5nIG9uIGNvbnRlbnQuXG4gKlxuICpcbiAqICMjIyMjIEV4YW1wbGU6XG4gKlxuICogYGBgamF2YXNjcmlwdFxuICogdmFyIHBha28gPSByZXF1aXJlKCdwYWtvJylcbiAqICAgLCBpbnB1dCA9IHBha28uZGVmbGF0ZShbMSwyLDMsNCw1LDYsNyw4LDldKVxuICogICAsIG91dHB1dDtcbiAqXG4gKiB0cnkge1xuICogICBvdXRwdXQgPSBwYWtvLmluZmxhdGUoaW5wdXQpO1xuICogfSBjYXRjaCAoZXJyKVxuICogICBjb25zb2xlLmxvZyhlcnIpO1xuICogfVxuICogYGBgXG4gKiovXG5mdW5jdGlvbiBpbmZsYXRlKGlucHV0LCBvcHRpb25zKSB7XG4gIHZhciBpbmZsYXRvciA9IG5ldyBJbmZsYXRlKG9wdGlvbnMpO1xuXG4gIGluZmxhdG9yLnB1c2goaW5wdXQsIHRydWUpO1xuXG4gIC8vIFRoYXQgd2lsbCBuZXZlciBoYXBwZW5zLCBpZiB5b3UgZG9uJ3QgY2hlYXQgd2l0aCBvcHRpb25zIDopXG4gIGlmIChpbmZsYXRvci5lcnIpIHsgdGhyb3cgaW5mbGF0b3IubXNnOyB9XG5cbiAgcmV0dXJuIGluZmxhdG9yLnJlc3VsdDtcbn1cblxuXG4vKipcbiAqIGluZmxhdGVSYXcoZGF0YVssIG9wdGlvbnNdKSAtPiBVaW50OEFycmF5fEFycmF5fFN0cmluZ1xuICogLSBkYXRhIChVaW50OEFycmF5fEFycmF5fFN0cmluZyk6IGlucHV0IGRhdGEgdG8gZGVjb21wcmVzcy5cbiAqIC0gb3B0aW9ucyAoT2JqZWN0KTogemxpYiBpbmZsYXRlIG9wdGlvbnMuXG4gKlxuICogVGhlIHNhbWUgYXMgW1tpbmZsYXRlXV0sIGJ1dCBjcmVhdGVzIHJhdyBkYXRhLCB3aXRob3V0IHdyYXBwZXJcbiAqIChoZWFkZXIgYW5kIGFkbGVyMzIgY3JjKS5cbiAqKi9cbmZ1bmN0aW9uIGluZmxhdGVSYXcoaW5wdXQsIG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gIG9wdGlvbnMucmF3ID0gdHJ1ZTtcbiAgcmV0dXJuIGluZmxhdGUoaW5wdXQsIG9wdGlvbnMpO1xufVxuXG5cbi8qKlxuICogdW5nemlwKGRhdGFbLCBvcHRpb25zXSkgLT4gVWludDhBcnJheXxBcnJheXxTdHJpbmdcbiAqIC0gZGF0YSAoVWludDhBcnJheXxBcnJheXxTdHJpbmcpOiBpbnB1dCBkYXRhIHRvIGRlY29tcHJlc3MuXG4gKiAtIG9wdGlvbnMgKE9iamVjdCk6IHpsaWIgaW5mbGF0ZSBvcHRpb25zLlxuICpcbiAqIEp1c3Qgc2hvcnRjdXQgdG8gW1tpbmZsYXRlXV0sIGJlY2F1c2UgaXQgYXV0b2RldGVjdHMgZm9ybWF0XG4gKiBieSBoZWFkZXIuY29udGVudC4gRG9uZSBmb3IgY29udmVuaWVuY2UuXG4gKiovXG5cblxuZXhwb3J0cy5JbmZsYXRlID0gSW5mbGF0ZTtcbmV4cG9ydHMuaW5mbGF0ZSA9IGluZmxhdGU7XG5leHBvcnRzLmluZmxhdGVSYXcgPSBpbmZsYXRlUmF3O1xuZXhwb3J0cy51bmd6aXAgID0gaW5mbGF0ZTtcbiIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgVFlQRURfT0sgPSAgKHR5cGVvZiBVaW50OEFycmF5ICE9PSAndW5kZWZpbmVkJykgJiZcbiAgICAgICAgICAgICAgICAodHlwZW9mIFVpbnQxNkFycmF5ICE9PSAndW5kZWZpbmVkJykgJiZcbiAgICAgICAgICAgICAgICAodHlwZW9mIEludDMyQXJyYXkgIT09ICd1bmRlZmluZWQnKTtcblxuXG5leHBvcnRzLmFzc2lnbiA9IGZ1bmN0aW9uIChvYmogLypmcm9tMSwgZnJvbTIsIGZyb20zLCAuLi4qLykge1xuICB2YXIgc291cmNlcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XG4gIHdoaWxlIChzb3VyY2VzLmxlbmd0aCkge1xuICAgIHZhciBzb3VyY2UgPSBzb3VyY2VzLnNoaWZ0KCk7XG4gICAgaWYgKCFzb3VyY2UpIHsgY29udGludWU7IH1cblxuICAgIGlmICh0eXBlb2Yoc291cmNlKSAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3Ioc291cmNlICsgJ211c3QgYmUgbm9uLW9iamVjdCcpO1xuICAgIH1cblxuICAgIGZvciAodmFyIHAgaW4gc291cmNlKSB7XG4gICAgICBpZiAoc291cmNlLmhhc093blByb3BlcnR5KHApKSB7XG4gICAgICAgIG9ialtwXSA9IHNvdXJjZVtwXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gb2JqO1xufTtcblxuXG4vLyByZWR1Y2UgYnVmZmVyIHNpemUsIGF2b2lkaW5nIG1lbSBjb3B5XG5leHBvcnRzLnNocmlua0J1ZiA9IGZ1bmN0aW9uIChidWYsIHNpemUpIHtcbiAgaWYgKGJ1Zi5sZW5ndGggPT09IHNpemUpIHsgcmV0dXJuIGJ1ZjsgfVxuICBpZiAoYnVmLnN1YmFycmF5KSB7IHJldHVybiBidWYuc3ViYXJyYXkoMCwgc2l6ZSk7IH1cbiAgYnVmLmxlbmd0aCA9IHNpemU7XG4gIHJldHVybiBidWY7XG59O1xuXG5cbnZhciBmblR5cGVkID0ge1xuICBhcnJheVNldDogZnVuY3Rpb24gKGRlc3QsIHNyYywgc3JjX29mZnMsIGxlbiwgZGVzdF9vZmZzKSB7XG4gICAgaWYgKHNyYy5zdWJhcnJheSAmJiBkZXN0LnN1YmFycmF5KSB7XG4gICAgICBkZXN0LnNldChzcmMuc3ViYXJyYXkoc3JjX29mZnMsIHNyY19vZmZzK2xlbiksIGRlc3Rfb2Zmcyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEZhbGxiYWNrIHRvIG9yZGluYXJ5IGFycmF5XG4gICAgZm9yKHZhciBpPTA7IGk8bGVuOyBpKyspIHtcbiAgICAgIGRlc3RbZGVzdF9vZmZzICsgaV0gPSBzcmNbc3JjX29mZnMgKyBpXTtcbiAgICB9XG4gIH0sXG4gIC8vIEpvaW4gYXJyYXkgb2YgY2h1bmtzIHRvIHNpbmdsZSBhcnJheS5cbiAgZmxhdHRlbkNodW5rczogZnVuY3Rpb24oY2h1bmtzKSB7XG4gICAgdmFyIGksIGwsIGxlbiwgcG9zLCBjaHVuaywgcmVzdWx0O1xuXG4gICAgLy8gY2FsY3VsYXRlIGRhdGEgbGVuZ3RoXG4gICAgbGVuID0gMDtcbiAgICBmb3IgKGk9MCwgbD1jaHVua3MubGVuZ3RoOyBpPGw7IGkrKykge1xuICAgICAgbGVuICs9IGNodW5rc1tpXS5sZW5ndGg7XG4gICAgfVxuXG4gICAgLy8gam9pbiBjaHVua3NcbiAgICByZXN1bHQgPSBuZXcgVWludDhBcnJheShsZW4pO1xuICAgIHBvcyA9IDA7XG4gICAgZm9yIChpPTAsIGw9Y2h1bmtzLmxlbmd0aDsgaTxsOyBpKyspIHtcbiAgICAgIGNodW5rID0gY2h1bmtzW2ldO1xuICAgICAgcmVzdWx0LnNldChjaHVuaywgcG9zKTtcbiAgICAgIHBvcyArPSBjaHVuay5sZW5ndGg7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcblxudmFyIGZuVW50eXBlZCA9IHtcbiAgYXJyYXlTZXQ6IGZ1bmN0aW9uIChkZXN0LCBzcmMsIHNyY19vZmZzLCBsZW4sIGRlc3Rfb2Zmcykge1xuICAgIGZvcih2YXIgaT0wOyBpPGxlbjsgaSsrKSB7XG4gICAgICBkZXN0W2Rlc3Rfb2ZmcyArIGldID0gc3JjW3NyY19vZmZzICsgaV07XG4gICAgfVxuICB9LFxuICAvLyBKb2luIGFycmF5IG9mIGNodW5rcyB0byBzaW5nbGUgYXJyYXkuXG4gIGZsYXR0ZW5DaHVua3M6IGZ1bmN0aW9uKGNodW5rcykge1xuICAgIHJldHVybiBbXS5jb25jYXQuYXBwbHkoW10sIGNodW5rcyk7XG4gIH1cbn07XG5cblxuLy8gRW5hYmxlL0Rpc2FibGUgdHlwZWQgYXJyYXlzIHVzZSwgZm9yIHRlc3Rpbmdcbi8vXG5leHBvcnRzLnNldFR5cGVkID0gZnVuY3Rpb24gKG9uKSB7XG4gIGlmIChvbikge1xuICAgIGV4cG9ydHMuQnVmOCAgPSBVaW50OEFycmF5O1xuICAgIGV4cG9ydHMuQnVmMTYgPSBVaW50MTZBcnJheTtcbiAgICBleHBvcnRzLkJ1ZjMyID0gSW50MzJBcnJheTtcbiAgICBleHBvcnRzLmFzc2lnbihleHBvcnRzLCBmblR5cGVkKTtcbiAgfSBlbHNlIHtcbiAgICBleHBvcnRzLkJ1ZjggID0gQXJyYXk7XG4gICAgZXhwb3J0cy5CdWYxNiA9IEFycmF5O1xuICAgIGV4cG9ydHMuQnVmMzIgPSBBcnJheTtcbiAgICBleHBvcnRzLmFzc2lnbihleHBvcnRzLCBmblVudHlwZWQpO1xuICB9XG59O1xuXG5leHBvcnRzLnNldFR5cGVkKFRZUEVEX09LKTsiLCIvLyBTdHJpbmcgZW5jb2RlL2RlY29kZSBoZWxwZXJzXG4ndXNlIHN0cmljdCc7XG5cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi9jb21tb24nKTtcblxuXG4vLyBRdWljayBjaGVjayBpZiB3ZSBjYW4gdXNlIGZhc3QgYXJyYXkgdG8gYmluIHN0cmluZyBjb252ZXJzaW9uXG4vL1xuLy8gLSBhcHBseShBcnJheSkgY2FuIGZhaWwgb24gQW5kcm9pZCAyLjJcbi8vIC0gYXBwbHkoVWludDhBcnJheSkgY2FuIGZhaWwgb24gaU9TIDUuMSBTYWZhcnlcbi8vXG52YXIgU1RSX0FQUExZX09LID0gdHJ1ZTtcbnZhciBTVFJfQVBQTFlfVUlBX09LID0gdHJ1ZTtcblxudHJ5IHsgU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBbMF0pOyB9IGNhdGNoKF9fKSB7IFNUUl9BUFBMWV9PSyA9IGZhbHNlOyB9XG50cnkgeyBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIG5ldyBVaW50OEFycmF5KDEpKTsgfSBjYXRjaChfXykgeyBTVFJfQVBQTFlfVUlBX09LID0gZmFsc2U7IH1cblxuXG4vLyBUYWJsZSB3aXRoIHV0ZjggbGVuZ3RocyAoY2FsY3VsYXRlZCBieSBmaXJzdCBieXRlIG9mIHNlcXVlbmNlKVxuLy8gTm90ZSwgdGhhdCA1ICYgNi1ieXRlIHZhbHVlcyBhbmQgc29tZSA0LWJ5dGUgdmFsdWVzIGNhbiBub3QgYmUgcmVwcmVzZW50ZWQgaW4gSlMsXG4vLyBiZWNhdXNlIG1heCBwb3NzaWJsZSBjb2RlcG9pbnQgaXMgMHgxMGZmZmZcbnZhciBfdXRmOGxlbiA9IG5ldyB1dGlscy5CdWY4KDI1Nik7XG5mb3IgKHZhciBpPTA7IGk8MjU2OyBpKyspIHtcbiAgX3V0ZjhsZW5baV0gPSAoaSA+PSAyNTIgPyA2IDogaSA+PSAyNDggPyA1IDogaSA+PSAyNDAgPyA0IDogaSA+PSAyMjQgPyAzIDogaSA+PSAxOTIgPyAyIDogMSk7XG59XG5fdXRmOGxlblsyNTRdPV91dGY4bGVuWzI1NF09MTsgLy8gSW52YWxpZCBzZXF1ZW5jZSBzdGFydFxuXG5cbi8vIGNvbnZlcnQgc3RyaW5nIHRvIGFycmF5ICh0eXBlZCwgd2hlbiBwb3NzaWJsZSlcbmV4cG9ydHMuc3RyaW5nMmJ1ZiA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgdmFyIGJ1ZiwgYywgYzIsIG1fcG9zLCBpLCBzdHJfbGVuID0gc3RyLmxlbmd0aCwgYnVmX2xlbiA9IDA7XG5cbiAgLy8gY291bnQgYmluYXJ5IHNpemVcbiAgZm9yIChtX3BvcyA9IDA7IG1fcG9zIDwgc3RyX2xlbjsgbV9wb3MrKykge1xuICAgIGMgPSBzdHIuY2hhckNvZGVBdChtX3Bvcyk7XG4gICAgaWYgKChjICYgMHhmYzAwKSA9PT0gMHhkODAwICYmIChtX3BvcysxIDwgc3RyX2xlbikpIHtcbiAgICAgIGMyID0gc3RyLmNoYXJDb2RlQXQobV9wb3MrMSk7XG4gICAgICBpZiAoKGMyICYgMHhmYzAwKSA9PT0gMHhkYzAwKSB7XG4gICAgICAgIGMgPSAweDEwMDAwICsgKChjIC0gMHhkODAwKSA8PCAxMCkgKyAoYzIgLSAweGRjMDApO1xuICAgICAgICBtX3BvcysrO1xuICAgICAgfVxuICAgIH1cbiAgICBidWZfbGVuICs9IGMgPCAweDgwID8gMSA6IGMgPCAweDgwMCA/IDIgOiBjIDwgMHgxMDAwMCA/IDMgOiA0O1xuICB9XG5cbiAgLy8gYWxsb2NhdGUgYnVmZmVyXG4gIGJ1ZiA9IG5ldyB1dGlscy5CdWY4KGJ1Zl9sZW4pO1xuXG4gIC8vIGNvbnZlcnRcbiAgZm9yIChpPTAsIG1fcG9zID0gMDsgaSA8IGJ1Zl9sZW47IG1fcG9zKyspIHtcbiAgICBjID0gc3RyLmNoYXJDb2RlQXQobV9wb3MpO1xuICAgIGlmICgoYyAmIDB4ZmMwMCkgPT09IDB4ZDgwMCAmJiAobV9wb3MrMSA8IHN0cl9sZW4pKSB7XG4gICAgICBjMiA9IHN0ci5jaGFyQ29kZUF0KG1fcG9zKzEpO1xuICAgICAgaWYgKChjMiAmIDB4ZmMwMCkgPT09IDB4ZGMwMCkge1xuICAgICAgICBjID0gMHgxMDAwMCArICgoYyAtIDB4ZDgwMCkgPDwgMTApICsgKGMyIC0gMHhkYzAwKTtcbiAgICAgICAgbV9wb3MrKztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGMgPCAweDgwKSB7XG4gICAgICAvKiBvbmUgYnl0ZSAqL1xuICAgICAgYnVmW2krK10gPSBjO1xuICAgIH0gZWxzZSBpZiAoYyA8IDB4ODAwKSB7XG4gICAgICAvKiB0d28gYnl0ZXMgKi9cbiAgICAgIGJ1ZltpKytdID0gMHhDMCB8IChjID4+PiA2KTtcbiAgICAgIGJ1ZltpKytdID0gMHg4MCB8IChjICYgMHgzZik7XG4gICAgfSBlbHNlIGlmIChjIDwgMHgxMDAwMCkge1xuICAgICAgLyogdGhyZWUgYnl0ZXMgKi9cbiAgICAgIGJ1ZltpKytdID0gMHhFMCB8IChjID4+PiAxMik7XG4gICAgICBidWZbaSsrXSA9IDB4ODAgfCAoYyA+Pj4gNiAmIDB4M2YpO1xuICAgICAgYnVmW2krK10gPSAweDgwIHwgKGMgJiAweDNmKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLyogZm91ciBieXRlcyAqL1xuICAgICAgYnVmW2krK10gPSAweGYwIHwgKGMgPj4+IDE4KTtcbiAgICAgIGJ1ZltpKytdID0gMHg4MCB8IChjID4+PiAxMiAmIDB4M2YpO1xuICAgICAgYnVmW2krK10gPSAweDgwIHwgKGMgPj4+IDYgJiAweDNmKTtcbiAgICAgIGJ1ZltpKytdID0gMHg4MCB8IChjICYgMHgzZik7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGJ1Zjtcbn07XG5cbi8vIEhlbHBlciAodXNlZCBpbiAyIHBsYWNlcylcbmZ1bmN0aW9uIGJ1ZjJiaW5zdHJpbmcoYnVmLCBsZW4pIHtcbiAgLy8gdXNlIGZhbGxiYWNrIGZvciBiaWcgYXJyYXlzIHRvIGF2b2lkIHN0YWNrIG92ZXJmbG93XG4gIGlmIChsZW4gPCA2NTUzNykge1xuICAgIGlmICgoYnVmLnN1YmFycmF5ICYmIFNUUl9BUFBMWV9VSUFfT0spIHx8ICghYnVmLnN1YmFycmF5ICYmIFNUUl9BUFBMWV9PSykpIHtcbiAgICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIHV0aWxzLnNocmlua0J1ZihidWYsIGxlbikpO1xuICAgIH1cbiAgfVxuXG4gIHZhciByZXN1bHQgPSAnJztcbiAgZm9yKHZhciBpPTA7IGkgPCBsZW47IGkrKykge1xuICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ1ZltpXSk7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuXG4vLyBDb252ZXJ0IGJ5dGUgYXJyYXkgdG8gYmluYXJ5IHN0cmluZ1xuZXhwb3J0cy5idWYyYmluc3RyaW5nID0gZnVuY3Rpb24oYnVmKSB7XG4gIHJldHVybiBidWYyYmluc3RyaW5nKGJ1ZiwgYnVmLmxlbmd0aCk7XG59O1xuXG5cbi8vIENvbnZlcnQgYmluYXJ5IHN0cmluZyAodHlwZWQsIHdoZW4gcG9zc2libGUpXG5leHBvcnRzLmJpbnN0cmluZzJidWYgPSBmdW5jdGlvbihzdHIpIHtcbiAgdmFyIGJ1ZiA9IG5ldyB1dGlscy5CdWY4KHN0ci5sZW5ndGgpO1xuICBmb3IodmFyIGk9MCwgbGVuPWJ1Zi5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgIGJ1ZltpXSA9IHN0ci5jaGFyQ29kZUF0KGkpO1xuICB9XG4gIHJldHVybiBidWY7XG59O1xuXG5cbi8vIGNvbnZlcnQgYXJyYXkgdG8gc3RyaW5nXG5leHBvcnRzLmJ1ZjJzdHJpbmcgPSBmdW5jdGlvbiAoYnVmLCBtYXgpIHtcbiAgdmFyIGksIG91dCwgYywgY19sZW47XG4gIHZhciBsZW4gPSBtYXggfHwgYnVmLmxlbmd0aDtcblxuICAvLyBSZXNlcnZlIG1heCBwb3NzaWJsZSBsZW5ndGggKDIgd29yZHMgcGVyIGNoYXIpXG4gIC8vIE5COiBieSB1bmtub3duIHJlYXNvbnMsIEFycmF5IGlzIHNpZ25pZmljYW50bHkgZmFzdGVyIGZvclxuICAvLyAgICAgU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseSB0aGFuIFVpbnQxNkFycmF5LlxuICB2YXIgdXRmMTZidWYgPSBuZXcgQXJyYXkobGVuKjIpO1xuXG4gIGZvciAob3V0PTAsIGk9MDsgaTxsZW47KSB7XG4gICAgYyA9IGJ1ZltpKytdO1xuICAgIC8vIHF1aWNrIHByb2Nlc3MgYXNjaWlcbiAgICBpZiAoYyA8IDB4ODApIHsgdXRmMTZidWZbb3V0KytdID0gYzsgY29udGludWU7IH1cblxuICAgIGNfbGVuID0gX3V0ZjhsZW5bY107XG4gICAgLy8gc2tpcCA1ICYgNiBieXRlIGNvZGVzXG4gICAgaWYgKGNfbGVuID4gNCkgeyB1dGYxNmJ1ZltvdXQrK10gPSAweGZmZmQ7IGkgKz0gY19sZW4tMTsgY29udGludWU7IH1cblxuICAgIC8vIGFwcGx5IG1hc2sgb24gZmlyc3QgYnl0ZVxuICAgIGMgJj0gY19sZW4gPT09IDIgPyAweDFmIDogY19sZW4gPT09IDMgPyAweDBmIDogMHgwNztcbiAgICAvLyBqb2luIHRoZSByZXN0XG4gICAgd2hpbGUgKGNfbGVuID4gMSAmJiBpIDwgbGVuKSB7XG4gICAgICBjID0gKGMgPDwgNikgfCAoYnVmW2krK10gJiAweDNmKTtcbiAgICAgIGNfbGVuLS07XG4gICAgfVxuXG4gICAgLy8gdGVybWluYXRlZCBieSBlbmQgb2Ygc3RyaW5nP1xuICAgIGlmIChjX2xlbiA+IDEpIHsgdXRmMTZidWZbb3V0KytdID0gMHhmZmZkOyBjb250aW51ZTsgfVxuXG4gICAgaWYgKGMgPCAweDEwMDAwKSB7XG4gICAgICB1dGYxNmJ1ZltvdXQrK10gPSBjO1xuICAgIH0gZWxzZSB7XG4gICAgICBjIC09IDB4MTAwMDA7XG4gICAgICB1dGYxNmJ1ZltvdXQrK10gPSAweGQ4MDAgfCAoKGMgPj4gMTApICYgMHgzZmYpO1xuICAgICAgdXRmMTZidWZbb3V0KytdID0gMHhkYzAwIHwgKGMgJiAweDNmZik7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGJ1ZjJiaW5zdHJpbmcodXRmMTZidWYsIG91dCk7XG59O1xuXG5cbi8vIENhbGN1bGF0ZSBtYXggcG9zc2libGUgcG9zaXRpb24gaW4gdXRmOCBidWZmZXIsXG4vLyB0aGF0IHdpbGwgbm90IGJyZWFrIHNlcXVlbmNlLiBJZiB0aGF0J3Mgbm90IHBvc3NpYmxlXG4vLyAtICh2ZXJ5IHNtYWxsIGxpbWl0cykgcmV0dXJuIG1heCBzaXplIGFzIGlzLlxuLy9cbi8vIGJ1ZltdIC0gdXRmOCBieXRlcyBhcnJheVxuLy8gbWF4ICAgLSBsZW5ndGggbGltaXQgKG1hbmRhdG9yeSk7XG5leHBvcnRzLnV0Zjhib3JkZXIgPSBmdW5jdGlvbihidWYsIG1heCkge1xuICB2YXIgcG9zO1xuXG4gIG1heCA9IG1heCB8fCBidWYubGVuZ3RoO1xuICBpZiAobWF4ID4gYnVmLmxlbmd0aCkgeyBtYXggPSBidWYubGVuZ3RoOyB9XG5cbiAgLy8gZ28gYmFjayBmcm9tIGxhc3QgcG9zaXRpb24sIHVudGlsIHN0YXJ0IG9mIHNlcXVlbmNlIGZvdW5kXG4gIHBvcyA9IG1heC0xO1xuICB3aGlsZSAocG9zID49IDAgJiYgKGJ1Zltwb3NdICYgMHhDMCkgPT09IDB4ODApIHsgcG9zLS07IH1cblxuICAvLyBGdWNrdXAgLSB2ZXJ5IHNtYWxsIGFuZCBicm9rZW4gc2VxdWVuY2UsXG4gIC8vIHJldHVybiBtYXgsIGJlY2F1c2Ugd2Ugc2hvdWxkIHJldHVybiBzb21ldGhpbmcgYW55d2F5LlxuICBpZiAocG9zIDwgMCkgeyByZXR1cm4gbWF4OyB9XG5cbiAgLy8gSWYgd2UgY2FtZSB0byBzdGFydCBvZiBidWZmZXIgLSB0aGF0IG1lYW5zIHZ1ZmZlciBpcyB0b28gc21hbGwsXG4gIC8vIHJldHVybiBtYXggdG9vLlxuICBpZiAocG9zID09PSAwKSB7IHJldHVybiBtYXg7IH1cblxuICByZXR1cm4gKHBvcyArIF91dGY4bGVuW2J1Zltwb3NdXSA+IG1heCkgPyBwb3MgOiBtYXg7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG4vLyBOb3RlOiBhZGxlcjMyIHRha2VzIDEyJSBmb3IgbGV2ZWwgMCBhbmQgMiUgZm9yIGxldmVsIDYuXG4vLyBJdCBkb2Vzbid0IHdvcnRoIHRvIG1ha2UgYWRkaXRpb25hbCBvcHRpbWl6YXRpb25hIGFzIGluIG9yaWdpbmFsLlxuLy8gU21hbGwgc2l6ZSBpcyBwcmVmZXJhYmxlLlxuXG5mdW5jdGlvbiBhZGxlcjMyKGFkbGVyLCBidWYsIGxlbiwgcG9zKSB7XG4gIHZhciBzMSA9IChhZGxlciAmIDB4ZmZmZikgfDBcbiAgICAsIHMyID0gKChhZGxlciA+Pj4gMTYpICYgMHhmZmZmKSB8MFxuICAgICwgbiA9IDA7XG5cbiAgd2hpbGUgKGxlbiAhPT0gMCkge1xuICAgIC8vIFNldCBsaW1pdCB+IHR3aWNlIGxlc3MgdGhhbiA1NTUyLCB0byBrZWVwXG4gICAgLy8gczIgaW4gMzEtYml0cywgYmVjYXVzZSB3ZSBmb3JjZSBzaWduZWQgaW50cy5cbiAgICAvLyBpbiBvdGhlciBjYXNlICU9IHdpbGwgZmFpbC5cbiAgICBuID0gbGVuID4gMjAwMCA/IDIwMDAgOiBsZW47XG4gICAgbGVuIC09IG47XG5cbiAgICBkbyB7XG4gICAgICBzMSA9IChzMSArIGJ1Zltwb3MrK10pIHwwO1xuICAgICAgczIgPSAoczIgKyBzMSkgfDA7XG4gICAgfSB3aGlsZSAoLS1uKTtcblxuICAgIHMxICU9IDY1NTIxO1xuICAgIHMyICU9IDY1NTIxO1xuICB9XG5cbiAgcmV0dXJuIChzMSB8IChzMiA8PCAxNikpIHwwO1xufVxuXG5cbm1vZHVsZS5leHBvcnRzID0gYWRsZXIzMjsiLCJtb2R1bGUuZXhwb3J0cyA9IHtcblxuICAvKiBBbGxvd2VkIGZsdXNoIHZhbHVlczsgc2VlIGRlZmxhdGUoKSBhbmQgaW5mbGF0ZSgpIGJlbG93IGZvciBkZXRhaWxzICovXG4gIFpfTk9fRkxVU0g6ICAgICAgICAgMCxcbiAgWl9QQVJUSUFMX0ZMVVNIOiAgICAxLFxuICBaX1NZTkNfRkxVU0g6ICAgICAgIDIsXG4gIFpfRlVMTF9GTFVTSDogICAgICAgMyxcbiAgWl9GSU5JU0g6ICAgICAgICAgICA0LFxuICBaX0JMT0NLOiAgICAgICAgICAgIDUsXG4gIFpfVFJFRVM6ICAgICAgICAgICAgNixcblxuICAvKiBSZXR1cm4gY29kZXMgZm9yIHRoZSBjb21wcmVzc2lvbi9kZWNvbXByZXNzaW9uIGZ1bmN0aW9ucy4gTmVnYXRpdmUgdmFsdWVzXG4gICogYXJlIGVycm9ycywgcG9zaXRpdmUgdmFsdWVzIGFyZSB1c2VkIGZvciBzcGVjaWFsIGJ1dCBub3JtYWwgZXZlbnRzLlxuICAqL1xuICBaX09LOiAgICAgICAgICAgICAgIDAsXG4gIFpfU1RSRUFNX0VORDogICAgICAgMSxcbiAgWl9ORUVEX0RJQ1Q6ICAgICAgICAyLFxuICBaX0VSUk5POiAgICAgICAgICAgLTEsXG4gIFpfU1RSRUFNX0VSUk9SOiAgICAtMixcbiAgWl9EQVRBX0VSUk9SOiAgICAgIC0zLFxuICAvL1pfTUVNX0VSUk9SOiAgICAgLTQsXG4gIFpfQlVGX0VSUk9SOiAgICAgICAtNSxcbiAgLy9aX1ZFUlNJT05fRVJST1I6IC02LFxuXG4gIC8qIGNvbXByZXNzaW9uIGxldmVscyAqL1xuICBaX05PX0NPTVBSRVNTSU9OOiAgICAgICAgIDAsXG4gIFpfQkVTVF9TUEVFRDogICAgICAgICAgICAgMSxcbiAgWl9CRVNUX0NPTVBSRVNTSU9OOiAgICAgICA5LFxuICBaX0RFRkFVTFRfQ09NUFJFU1NJT046ICAgLTEsXG5cblxuICBaX0ZJTFRFUkVEOiAgICAgICAgICAgICAgIDEsXG4gIFpfSFVGRk1BTl9PTkxZOiAgICAgICAgICAgMixcbiAgWl9STEU6ICAgICAgICAgICAgICAgICAgICAzLFxuICBaX0ZJWEVEOiAgICAgICAgICAgICAgICAgIDQsXG4gIFpfREVGQVVMVF9TVFJBVEVHWTogICAgICAgMCxcblxuICAvKiBQb3NzaWJsZSB2YWx1ZXMgb2YgdGhlIGRhdGFfdHlwZSBmaWVsZCAodGhvdWdoIHNlZSBpbmZsYXRlKCkpICovXG4gIFpfQklOQVJZOiAgICAgICAgICAgICAgICAgMCxcbiAgWl9URVhUOiAgICAgICAgICAgICAgICAgICAxLFxuICAvL1pfQVNDSUk6ICAgICAgICAgICAgICAgIDEsIC8vID0gWl9URVhUIChkZXByZWNhdGVkKVxuICBaX1VOS05PV046ICAgICAgICAgICAgICAgIDIsXG5cbiAgLyogVGhlIGRlZmxhdGUgY29tcHJlc3Npb24gbWV0aG9kICovXG4gIFpfREVGTEFURUQ6ICAgICAgICAgICAgICAgOFxuICAvL1pfTlVMTDogICAgICAgICAgICAgICAgIG51bGwgLy8gVXNlIC0xIG9yIG51bGwgaW5saW5lLCBkZXBlbmRpbmcgb24gdmFyIHR5cGVcbn07IiwiJ3VzZSBzdHJpY3QnO1xuXG4vLyBOb3RlOiB3ZSBjYW4ndCBnZXQgc2lnbmlmaWNhbnQgc3BlZWQgYm9vc3QgaGVyZS5cbi8vIFNvIHdyaXRlIGNvZGUgdG8gbWluaW1pemUgc2l6ZSAtIG5vIHByZWdlbmVyYXRlZCB0YWJsZXNcbi8vIGFuZCBhcnJheSB0b29scyBkZXBlbmRlbmNpZXMuXG5cblxuLy8gVXNlIG9yZGluYXJ5IGFycmF5LCBzaW5jZSB1bnR5cGVkIG1ha2VzIG5vIGJvb3N0IGhlcmVcbmZ1bmN0aW9uIG1ha2VUYWJsZSgpIHtcbiAgdmFyIGMsIHRhYmxlID0gW107XG5cbiAgZm9yKHZhciBuID0wOyBuIDwgMjU2OyBuKyspe1xuICAgIGMgPSBuO1xuICAgIGZvcih2YXIgayA9MDsgayA8IDg7IGsrKyl7XG4gICAgICBjID0gKChjJjEpID8gKDB4RURCODgzMjAgXiAoYyA+Pj4gMSkpIDogKGMgPj4+IDEpKTtcbiAgICB9XG4gICAgdGFibGVbbl0gPSBjO1xuICB9XG5cbiAgcmV0dXJuIHRhYmxlO1xufVxuXG4vLyBDcmVhdGUgdGFibGUgb24gbG9hZC4gSnVzdCAyNTUgc2lnbmVkIGxvbmdzLiBOb3QgYSBwcm9ibGVtLlxudmFyIGNyY1RhYmxlID0gbWFrZVRhYmxlKCk7XG5cblxuZnVuY3Rpb24gY3JjMzIoY3JjLCBidWYsIGxlbiwgcG9zKSB7XG4gIHZhciB0ID0gY3JjVGFibGVcbiAgICAsIGVuZCA9IHBvcyArIGxlbjtcblxuICBjcmMgPSBjcmMgXiAoLTEpO1xuXG4gIGZvciAodmFyIGkgPSBwb3M7IGkgPCBlbmQ7IGkrKyApIHtcbiAgICBjcmMgPSAoY3JjID4+PiA4KSBeIHRbKGNyYyBeIGJ1ZltpXSkgJiAweEZGXTtcbiAgfVxuXG4gIHJldHVybiAoY3JjIF4gKC0xKSk7IC8vID4+PiAwO1xufVxuXG5cbm1vZHVsZS5leHBvcnRzID0gY3JjMzI7IiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbHMgICA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbW1vbicpO1xudmFyIHRyZWVzICAgPSByZXF1aXJlKCcuL3RyZWVzJyk7XG52YXIgYWRsZXIzMiA9IHJlcXVpcmUoJy4vYWRsZXIzMicpO1xudmFyIGNyYzMyICAgPSByZXF1aXJlKCcuL2NyYzMyJyk7XG52YXIgbXNnICAgPSByZXF1aXJlKCcuL21lc3NhZ2VzJyk7XG5cbi8qIFB1YmxpYyBjb25zdGFudHMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5cbi8qIEFsbG93ZWQgZmx1c2ggdmFsdWVzOyBzZWUgZGVmbGF0ZSgpIGFuZCBpbmZsYXRlKCkgYmVsb3cgZm9yIGRldGFpbHMgKi9cbnZhciBaX05PX0ZMVVNIICAgICAgPSAwO1xudmFyIFpfUEFSVElBTF9GTFVTSCA9IDE7XG4vL3ZhciBaX1NZTkNfRkxVU0ggICAgPSAyO1xudmFyIFpfRlVMTF9GTFVTSCAgICA9IDM7XG52YXIgWl9GSU5JU0ggICAgICAgID0gNDtcbnZhciBaX0JMT0NLICAgICAgICAgPSA1O1xuLy92YXIgWl9UUkVFUyAgICAgICAgID0gNjtcblxuXG4vKiBSZXR1cm4gY29kZXMgZm9yIHRoZSBjb21wcmVzc2lvbi9kZWNvbXByZXNzaW9uIGZ1bmN0aW9ucy4gTmVnYXRpdmUgdmFsdWVzXG4gKiBhcmUgZXJyb3JzLCBwb3NpdGl2ZSB2YWx1ZXMgYXJlIHVzZWQgZm9yIHNwZWNpYWwgYnV0IG5vcm1hbCBldmVudHMuXG4gKi9cbnZhciBaX09LICAgICAgICAgICAgPSAwO1xudmFyIFpfU1RSRUFNX0VORCAgICA9IDE7XG4vL3ZhciBaX05FRURfRElDVCAgICAgPSAyO1xuLy92YXIgWl9FUlJOTyAgICAgICAgID0gLTE7XG52YXIgWl9TVFJFQU1fRVJST1IgID0gLTI7XG52YXIgWl9EQVRBX0VSUk9SICAgID0gLTM7XG4vL3ZhciBaX01FTV9FUlJPUiAgICAgPSAtNDtcbnZhciBaX0JVRl9FUlJPUiAgICAgPSAtNTtcbi8vdmFyIFpfVkVSU0lPTl9FUlJPUiA9IC02O1xuXG5cbi8qIGNvbXByZXNzaW9uIGxldmVscyAqL1xuLy92YXIgWl9OT19DT01QUkVTU0lPTiAgICAgID0gMDtcbi8vdmFyIFpfQkVTVF9TUEVFRCAgICAgICAgICA9IDE7XG4vL3ZhciBaX0JFU1RfQ09NUFJFU1NJT04gICAgPSA5O1xudmFyIFpfREVGQVVMVF9DT01QUkVTU0lPTiA9IC0xO1xuXG5cbnZhciBaX0ZJTFRFUkVEICAgICAgICAgICAgPSAxO1xudmFyIFpfSFVGRk1BTl9PTkxZICAgICAgICA9IDI7XG52YXIgWl9STEUgICAgICAgICAgICAgICAgID0gMztcbnZhciBaX0ZJWEVEICAgICAgICAgICAgICAgPSA0O1xudmFyIFpfREVGQVVMVF9TVFJBVEVHWSAgICA9IDA7XG5cbi8qIFBvc3NpYmxlIHZhbHVlcyBvZiB0aGUgZGF0YV90eXBlIGZpZWxkICh0aG91Z2ggc2VlIGluZmxhdGUoKSkgKi9cbi8vdmFyIFpfQklOQVJZICAgICAgICAgICAgICA9IDA7XG4vL3ZhciBaX1RFWFQgICAgICAgICAgICAgICAgPSAxO1xuLy92YXIgWl9BU0NJSSAgICAgICAgICAgICAgID0gMTsgLy8gPSBaX1RFWFRcbnZhciBaX1VOS05PV04gICAgICAgICAgICAgPSAyO1xuXG5cbi8qIFRoZSBkZWZsYXRlIGNvbXByZXNzaW9uIG1ldGhvZCAqL1xudmFyIFpfREVGTEFURUQgID0gODtcblxuLyo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuXG52YXIgTUFYX01FTV9MRVZFTCA9IDk7XG4vKiBNYXhpbXVtIHZhbHVlIGZvciBtZW1MZXZlbCBpbiBkZWZsYXRlSW5pdDIgKi9cbnZhciBNQVhfV0JJVFMgPSAxNTtcbi8qIDMySyBMWjc3IHdpbmRvdyAqL1xudmFyIERFRl9NRU1fTEVWRUwgPSA4O1xuXG5cbnZhciBMRU5HVEhfQ09ERVMgID0gMjk7XG4vKiBudW1iZXIgb2YgbGVuZ3RoIGNvZGVzLCBub3QgY291bnRpbmcgdGhlIHNwZWNpYWwgRU5EX0JMT0NLIGNvZGUgKi9cbnZhciBMSVRFUkFMUyAgICAgID0gMjU2O1xuLyogbnVtYmVyIG9mIGxpdGVyYWwgYnl0ZXMgMC4uMjU1ICovXG52YXIgTF9DT0RFUyAgICAgICA9IExJVEVSQUxTICsgMSArIExFTkdUSF9DT0RFUztcbi8qIG51bWJlciBvZiBMaXRlcmFsIG9yIExlbmd0aCBjb2RlcywgaW5jbHVkaW5nIHRoZSBFTkRfQkxPQ0sgY29kZSAqL1xudmFyIERfQ09ERVMgICAgICAgPSAzMDtcbi8qIG51bWJlciBvZiBkaXN0YW5jZSBjb2RlcyAqL1xudmFyIEJMX0NPREVTICAgICAgPSAxOTtcbi8qIG51bWJlciBvZiBjb2RlcyB1c2VkIHRvIHRyYW5zZmVyIHRoZSBiaXQgbGVuZ3RocyAqL1xudmFyIEhFQVBfU0laRSAgICAgPSAyKkxfQ09ERVMgKyAxO1xuLyogbWF4aW11bSBoZWFwIHNpemUgKi9cbnZhciBNQVhfQklUUyAgPSAxNTtcbi8qIEFsbCBjb2RlcyBtdXN0IG5vdCBleGNlZWQgTUFYX0JJVFMgYml0cyAqL1xuXG52YXIgTUlOX01BVENIID0gMztcbnZhciBNQVhfTUFUQ0ggPSAyNTg7XG52YXIgTUlOX0xPT0tBSEVBRCA9IChNQVhfTUFUQ0ggKyBNSU5fTUFUQ0ggKyAxKTtcblxudmFyIFBSRVNFVF9ESUNUID0gMHgyMDtcblxudmFyIElOSVRfU1RBVEUgPSA0MjtcbnZhciBFWFRSQV9TVEFURSA9IDY5O1xudmFyIE5BTUVfU1RBVEUgPSA3MztcbnZhciBDT01NRU5UX1NUQVRFID0gOTE7XG52YXIgSENSQ19TVEFURSA9IDEwMztcbnZhciBCVVNZX1NUQVRFID0gMTEzO1xudmFyIEZJTklTSF9TVEFURSA9IDY2NjtcblxudmFyIEJTX05FRURfTU9SRSAgICAgID0gMTsgLyogYmxvY2sgbm90IGNvbXBsZXRlZCwgbmVlZCBtb3JlIGlucHV0IG9yIG1vcmUgb3V0cHV0ICovXG52YXIgQlNfQkxPQ0tfRE9ORSAgICAgPSAyOyAvKiBibG9jayBmbHVzaCBwZXJmb3JtZWQgKi9cbnZhciBCU19GSU5JU0hfU1RBUlRFRCA9IDM7IC8qIGZpbmlzaCBzdGFydGVkLCBuZWVkIG9ubHkgbW9yZSBvdXRwdXQgYXQgbmV4dCBkZWZsYXRlICovXG52YXIgQlNfRklOSVNIX0RPTkUgICAgPSA0OyAvKiBmaW5pc2ggZG9uZSwgYWNjZXB0IG5vIG1vcmUgaW5wdXQgb3Igb3V0cHV0ICovXG5cbnZhciBPU19DT0RFID0gMHgwMzsgLy8gVW5peCA6KSAuIERvbid0IGRldGVjdCwgdXNlIHRoaXMgZGVmYXVsdC5cblxuZnVuY3Rpb24gZXJyKHN0cm0sIGVycm9yQ29kZSkge1xuICBzdHJtLm1zZyA9IG1zZ1tlcnJvckNvZGVdO1xuICByZXR1cm4gZXJyb3JDb2RlO1xufVxuXG5mdW5jdGlvbiByYW5rKGYpIHtcbiAgcmV0dXJuICgoZikgPDwgMSkgLSAoKGYpID4gNCA/IDkgOiAwKTtcbn1cblxuZnVuY3Rpb24gemVybyhidWYpIHsgdmFyIGxlbiA9IGJ1Zi5sZW5ndGg7IHdoaWxlICgtLWxlbiA+PSAwKSB7IGJ1ZltsZW5dID0gMDsgfSB9XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogRmx1c2ggYXMgbXVjaCBwZW5kaW5nIG91dHB1dCBhcyBwb3NzaWJsZS4gQWxsIGRlZmxhdGUoKSBvdXRwdXQgZ29lc1xuICogdGhyb3VnaCB0aGlzIGZ1bmN0aW9uIHNvIHNvbWUgYXBwbGljYXRpb25zIG1heSB3aXNoIHRvIG1vZGlmeSBpdFxuICogdG8gYXZvaWQgYWxsb2NhdGluZyBhIGxhcmdlIHN0cm0tPm91dHB1dCBidWZmZXIgYW5kIGNvcHlpbmcgaW50byBpdC5cbiAqIChTZWUgYWxzbyByZWFkX2J1ZigpKS5cbiAqL1xuZnVuY3Rpb24gZmx1c2hfcGVuZGluZyhzdHJtKSB7XG4gIHZhciBzID0gc3RybS5zdGF0ZTtcblxuICAvL190cl9mbHVzaF9iaXRzKHMpO1xuICB2YXIgbGVuID0gcy5wZW5kaW5nO1xuICBpZiAobGVuID4gc3RybS5hdmFpbF9vdXQpIHtcbiAgICBsZW4gPSBzdHJtLmF2YWlsX291dDtcbiAgfVxuICBpZiAobGVuID09PSAwKSB7IHJldHVybjsgfVxuXG4gIHV0aWxzLmFycmF5U2V0KHN0cm0ub3V0cHV0LCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmdfb3V0LCBsZW4sIHN0cm0ubmV4dF9vdXQpO1xuICBzdHJtLm5leHRfb3V0ICs9IGxlbjtcbiAgcy5wZW5kaW5nX291dCArPSBsZW47XG4gIHN0cm0udG90YWxfb3V0ICs9IGxlbjtcbiAgc3RybS5hdmFpbF9vdXQgLT0gbGVuO1xuICBzLnBlbmRpbmcgLT0gbGVuO1xuICBpZiAocy5wZW5kaW5nID09PSAwKSB7XG4gICAgcy5wZW5kaW5nX291dCA9IDA7XG4gIH1cbn1cblxuXG5mdW5jdGlvbiBmbHVzaF9ibG9ja19vbmx5IChzLCBsYXN0KSB7XG4gIHRyZWVzLl90cl9mbHVzaF9ibG9jayhzLCAocy5ibG9ja19zdGFydCA+PSAwID8gcy5ibG9ja19zdGFydCA6IC0xKSwgcy5zdHJzdGFydCAtIHMuYmxvY2tfc3RhcnQsIGxhc3QpO1xuICBzLmJsb2NrX3N0YXJ0ID0gcy5zdHJzdGFydDtcbiAgZmx1c2hfcGVuZGluZyhzLnN0cm0pO1xufVxuXG5cbmZ1bmN0aW9uIHB1dF9ieXRlKHMsIGIpIHtcbiAgcy5wZW5kaW5nX2J1ZltzLnBlbmRpbmcrK10gPSBiO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFB1dCBhIHNob3J0IGluIHRoZSBwZW5kaW5nIGJ1ZmZlci4gVGhlIDE2LWJpdCB2YWx1ZSBpcyBwdXQgaW4gTVNCIG9yZGVyLlxuICogSU4gYXNzZXJ0aW9uOiB0aGUgc3RyZWFtIHN0YXRlIGlzIGNvcnJlY3QgYW5kIHRoZXJlIGlzIGVub3VnaCByb29tIGluXG4gKiBwZW5kaW5nX2J1Zi5cbiAqL1xuZnVuY3Rpb24gcHV0U2hvcnRNU0IocywgYikge1xuLy8gIHB1dF9ieXRlKHMsIChCeXRlKShiID4+IDgpKTtcbi8vICBwdXRfYnl0ZShzLCAoQnl0ZSkoYiAmIDB4ZmYpKTtcbiAgcy5wZW5kaW5nX2J1ZltzLnBlbmRpbmcrK10gPSAoYiA+Pj4gOCkgJiAweGZmO1xuICBzLnBlbmRpbmdfYnVmW3MucGVuZGluZysrXSA9IGIgJiAweGZmO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogUmVhZCBhIG5ldyBidWZmZXIgZnJvbSB0aGUgY3VycmVudCBpbnB1dCBzdHJlYW0sIHVwZGF0ZSB0aGUgYWRsZXIzMlxuICogYW5kIHRvdGFsIG51bWJlciBvZiBieXRlcyByZWFkLiAgQWxsIGRlZmxhdGUoKSBpbnB1dCBnb2VzIHRocm91Z2hcbiAqIHRoaXMgZnVuY3Rpb24gc28gc29tZSBhcHBsaWNhdGlvbnMgbWF5IHdpc2ggdG8gbW9kaWZ5IGl0IHRvIGF2b2lkXG4gKiBhbGxvY2F0aW5nIGEgbGFyZ2Ugc3RybS0+aW5wdXQgYnVmZmVyIGFuZCBjb3B5aW5nIGZyb20gaXQuXG4gKiAoU2VlIGFsc28gZmx1c2hfcGVuZGluZygpKS5cbiAqL1xuZnVuY3Rpb24gcmVhZF9idWYoc3RybSwgYnVmLCBzdGFydCwgc2l6ZSkge1xuICB2YXIgbGVuID0gc3RybS5hdmFpbF9pbjtcblxuICBpZiAobGVuID4gc2l6ZSkgeyBsZW4gPSBzaXplOyB9XG4gIGlmIChsZW4gPT09IDApIHsgcmV0dXJuIDA7IH1cblxuICBzdHJtLmF2YWlsX2luIC09IGxlbjtcblxuICB1dGlscy5hcnJheVNldChidWYsIHN0cm0uaW5wdXQsIHN0cm0ubmV4dF9pbiwgbGVuLCBzdGFydCk7XG4gIGlmIChzdHJtLnN0YXRlLndyYXAgPT09IDEpIHtcbiAgICBzdHJtLmFkbGVyID0gYWRsZXIzMihzdHJtLmFkbGVyLCBidWYsIGxlbiwgc3RhcnQpO1xuICB9XG5cbiAgZWxzZSBpZiAoc3RybS5zdGF0ZS53cmFwID09PSAyKSB7XG4gICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIGJ1ZiwgbGVuLCBzdGFydCk7XG4gIH1cblxuICBzdHJtLm5leHRfaW4gKz0gbGVuO1xuICBzdHJtLnRvdGFsX2luICs9IGxlbjtcblxuICByZXR1cm4gbGVuO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2V0IG1hdGNoX3N0YXJ0IHRvIHRoZSBsb25nZXN0IG1hdGNoIHN0YXJ0aW5nIGF0IHRoZSBnaXZlbiBzdHJpbmcgYW5kXG4gKiByZXR1cm4gaXRzIGxlbmd0aC4gTWF0Y2hlcyBzaG9ydGVyIG9yIGVxdWFsIHRvIHByZXZfbGVuZ3RoIGFyZSBkaXNjYXJkZWQsXG4gKiBpbiB3aGljaCBjYXNlIHRoZSByZXN1bHQgaXMgZXF1YWwgdG8gcHJldl9sZW5ndGggYW5kIG1hdGNoX3N0YXJ0IGlzXG4gKiBnYXJiYWdlLlxuICogSU4gYXNzZXJ0aW9uczogY3VyX21hdGNoIGlzIHRoZSBoZWFkIG9mIHRoZSBoYXNoIGNoYWluIGZvciB0aGUgY3VycmVudFxuICogICBzdHJpbmcgKHN0cnN0YXJ0KSBhbmQgaXRzIGRpc3RhbmNlIGlzIDw9IE1BWF9ESVNULCBhbmQgcHJldl9sZW5ndGggPj0gMVxuICogT1VUIGFzc2VydGlvbjogdGhlIG1hdGNoIGxlbmd0aCBpcyBub3QgZ3JlYXRlciB0aGFuIHMtPmxvb2thaGVhZC5cbiAqL1xuZnVuY3Rpb24gbG9uZ2VzdF9tYXRjaChzLCBjdXJfbWF0Y2gpIHtcbiAgdmFyIGNoYWluX2xlbmd0aCA9IHMubWF4X2NoYWluX2xlbmd0aDsgICAgICAvKiBtYXggaGFzaCBjaGFpbiBsZW5ndGggKi9cbiAgdmFyIHNjYW4gPSBzLnN0cnN0YXJ0OyAvKiBjdXJyZW50IHN0cmluZyAqL1xuICB2YXIgbWF0Y2g7ICAgICAgICAgICAgICAgICAgICAgICAvKiBtYXRjaGVkIHN0cmluZyAqL1xuICB2YXIgbGVuOyAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIGxlbmd0aCBvZiBjdXJyZW50IG1hdGNoICovXG4gIHZhciBiZXN0X2xlbiA9IHMucHJldl9sZW5ndGg7ICAgICAgICAgICAgICAvKiBiZXN0IG1hdGNoIGxlbmd0aCBzbyBmYXIgKi9cbiAgdmFyIG5pY2VfbWF0Y2ggPSBzLm5pY2VfbWF0Y2g7ICAgICAgICAgICAgIC8qIHN0b3AgaWYgbWF0Y2ggbG9uZyBlbm91Z2ggKi9cbiAgdmFyIGxpbWl0ID0gKHMuc3Ryc3RhcnQgPiAocy53X3NpemUgLSBNSU5fTE9PS0FIRUFEKSkgP1xuICAgICAgcy5zdHJzdGFydCAtIChzLndfc2l6ZSAtIE1JTl9MT09LQUhFQUQpIDogMC8qTklMKi87XG5cbiAgdmFyIF93aW4gPSBzLndpbmRvdzsgLy8gc2hvcnRjdXRcblxuICB2YXIgd21hc2sgPSBzLndfbWFzaztcbiAgdmFyIHByZXYgID0gcy5wcmV2O1xuXG4gIC8qIFN0b3Agd2hlbiBjdXJfbWF0Y2ggYmVjb21lcyA8PSBsaW1pdC4gVG8gc2ltcGxpZnkgdGhlIGNvZGUsXG4gICAqIHdlIHByZXZlbnQgbWF0Y2hlcyB3aXRoIHRoZSBzdHJpbmcgb2Ygd2luZG93IGluZGV4IDAuXG4gICAqL1xuXG4gIHZhciBzdHJlbmQgPSBzLnN0cnN0YXJ0ICsgTUFYX01BVENIO1xuICB2YXIgc2Nhbl9lbmQxICA9IF93aW5bc2NhbiArIGJlc3RfbGVuIC0gMV07XG4gIHZhciBzY2FuX2VuZCAgID0gX3dpbltzY2FuICsgYmVzdF9sZW5dO1xuXG4gIC8qIFRoZSBjb2RlIGlzIG9wdGltaXplZCBmb3IgSEFTSF9CSVRTID49IDggYW5kIE1BWF9NQVRDSC0yIG11bHRpcGxlIG9mIDE2LlxuICAgKiBJdCBpcyBlYXN5IHRvIGdldCByaWQgb2YgdGhpcyBvcHRpbWl6YXRpb24gaWYgbmVjZXNzYXJ5LlxuICAgKi9cbiAgLy8gQXNzZXJ0KHMtPmhhc2hfYml0cyA+PSA4ICYmIE1BWF9NQVRDSCA9PSAyNTgsIFwiQ29kZSB0b28gY2xldmVyXCIpO1xuXG4gIC8qIERvIG5vdCB3YXN0ZSB0b28gbXVjaCB0aW1lIGlmIHdlIGFscmVhZHkgaGF2ZSBhIGdvb2QgbWF0Y2g6ICovXG4gIGlmIChzLnByZXZfbGVuZ3RoID49IHMuZ29vZF9tYXRjaCkge1xuICAgIGNoYWluX2xlbmd0aCA+Pj0gMjtcbiAgfVxuICAvKiBEbyBub3QgbG9vayBmb3IgbWF0Y2hlcyBiZXlvbmQgdGhlIGVuZCBvZiB0aGUgaW5wdXQuIFRoaXMgaXMgbmVjZXNzYXJ5XG4gICAqIHRvIG1ha2UgZGVmbGF0ZSBkZXRlcm1pbmlzdGljLlxuICAgKi9cbiAgaWYgKG5pY2VfbWF0Y2ggPiBzLmxvb2thaGVhZCkgeyBuaWNlX21hdGNoID0gcy5sb29rYWhlYWQ7IH1cblxuICAvLyBBc3NlcnQoKHVsZylzLT5zdHJzdGFydCA8PSBzLT53aW5kb3dfc2l6ZS1NSU5fTE9PS0FIRUFELCBcIm5lZWQgbG9va2FoZWFkXCIpO1xuXG4gIGRvIHtcbiAgICAvLyBBc3NlcnQoY3VyX21hdGNoIDwgcy0+c3Ryc3RhcnQsIFwibm8gZnV0dXJlXCIpO1xuICAgIG1hdGNoID0gY3VyX21hdGNoO1xuXG4gICAgLyogU2tpcCB0byBuZXh0IG1hdGNoIGlmIHRoZSBtYXRjaCBsZW5ndGggY2Fubm90IGluY3JlYXNlXG4gICAgICogb3IgaWYgdGhlIG1hdGNoIGxlbmd0aCBpcyBsZXNzIHRoYW4gMi4gIE5vdGUgdGhhdCB0aGUgY2hlY2tzIGJlbG93XG4gICAgICogZm9yIGluc3VmZmljaWVudCBsb29rYWhlYWQgb25seSBvY2N1ciBvY2Nhc2lvbmFsbHkgZm9yIHBlcmZvcm1hbmNlXG4gICAgICogcmVhc29ucy4gIFRoZXJlZm9yZSB1bmluaXRpYWxpemVkIG1lbW9yeSB3aWxsIGJlIGFjY2Vzc2VkLCBhbmRcbiAgICAgKiBjb25kaXRpb25hbCBqdW1wcyB3aWxsIGJlIG1hZGUgdGhhdCBkZXBlbmQgb24gdGhvc2UgdmFsdWVzLlxuICAgICAqIEhvd2V2ZXIgdGhlIGxlbmd0aCBvZiB0aGUgbWF0Y2ggaXMgbGltaXRlZCB0byB0aGUgbG9va2FoZWFkLCBzb1xuICAgICAqIHRoZSBvdXRwdXQgb2YgZGVmbGF0ZSBpcyBub3QgYWZmZWN0ZWQgYnkgdGhlIHVuaW5pdGlhbGl6ZWQgdmFsdWVzLlxuICAgICAqL1xuXG4gICAgaWYgKF93aW5bbWF0Y2ggKyBiZXN0X2xlbl0gICAgICE9PSBzY2FuX2VuZCAgfHxcbiAgICAgICAgX3dpblttYXRjaCArIGJlc3RfbGVuIC0gMV0gIT09IHNjYW5fZW5kMSB8fFxuICAgICAgICBfd2luW21hdGNoXSAgICAgICAgICAgICAgICAhPT0gX3dpbltzY2FuXSB8fFxuICAgICAgICBfd2luWysrbWF0Y2hdICAgICAgICAgICAgICAhPT0gX3dpbltzY2FuICsgMV0pIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8qIFRoZSBjaGVjayBhdCBiZXN0X2xlbi0xIGNhbiBiZSByZW1vdmVkIGJlY2F1c2UgaXQgd2lsbCBiZSBtYWRlXG4gICAgICogYWdhaW4gbGF0ZXIuIChUaGlzIGhldXJpc3RpYyBpcyBub3QgYWx3YXlzIGEgd2luLilcbiAgICAgKiBJdCBpcyBub3QgbmVjZXNzYXJ5IHRvIGNvbXBhcmUgc2NhblsyXSBhbmQgbWF0Y2hbMl0gc2luY2UgdGhleVxuICAgICAqIGFyZSBhbHdheXMgZXF1YWwgd2hlbiB0aGUgb3RoZXIgYnl0ZXMgbWF0Y2gsIGdpdmVuIHRoYXRcbiAgICAgKiB0aGUgaGFzaCBrZXlzIGFyZSBlcXVhbCBhbmQgdGhhdCBIQVNIX0JJVFMgPj0gOC5cbiAgICAgKi9cbiAgICBzY2FuICs9IDI7XG4gICAgbWF0Y2grKztcbiAgICAvLyBBc3NlcnQoKnNjYW4gPT0gKm1hdGNoLCBcIm1hdGNoWzJdP1wiKTtcblxuICAgIC8qIFdlIGNoZWNrIGZvciBpbnN1ZmZpY2llbnQgbG9va2FoZWFkIG9ubHkgZXZlcnkgOHRoIGNvbXBhcmlzb247XG4gICAgICogdGhlIDI1NnRoIGNoZWNrIHdpbGwgYmUgbWFkZSBhdCBzdHJzdGFydCsyNTguXG4gICAgICovXG4gICAgZG8ge1xuICAgICAgLypqc2hpbnQgbm9lbXB0eTpmYWxzZSovXG4gICAgfSB3aGlsZSAoX3dpblsrK3NjYW5dID09PSBfd2luWysrbWF0Y2hdICYmIF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJlxuICAgICAgICAgICAgIF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJiBfd2luWysrc2Nhbl0gPT09IF93aW5bKyttYXRjaF0gJiZcbiAgICAgICAgICAgICBfd2luWysrc2Nhbl0gPT09IF93aW5bKyttYXRjaF0gJiYgX3dpblsrK3NjYW5dID09PSBfd2luWysrbWF0Y2hdICYmXG4gICAgICAgICAgICAgX3dpblsrK3NjYW5dID09PSBfd2luWysrbWF0Y2hdICYmIF93aW5bKytzY2FuXSA9PT0gX3dpblsrK21hdGNoXSAmJlxuICAgICAgICAgICAgIHNjYW4gPCBzdHJlbmQpO1xuXG4gICAgLy8gQXNzZXJ0KHNjYW4gPD0gcy0+d2luZG93Kyh1bnNpZ25lZCkocy0+d2luZG93X3NpemUtMSksIFwid2lsZCBzY2FuXCIpO1xuXG4gICAgbGVuID0gTUFYX01BVENIIC0gKHN0cmVuZCAtIHNjYW4pO1xuICAgIHNjYW4gPSBzdHJlbmQgLSBNQVhfTUFUQ0g7XG5cbiAgICBpZiAobGVuID4gYmVzdF9sZW4pIHtcbiAgICAgIHMubWF0Y2hfc3RhcnQgPSBjdXJfbWF0Y2g7XG4gICAgICBiZXN0X2xlbiA9IGxlbjtcbiAgICAgIGlmIChsZW4gPj0gbmljZV9tYXRjaCkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHNjYW5fZW5kMSAgPSBfd2luW3NjYW4gKyBiZXN0X2xlbiAtIDFdO1xuICAgICAgc2Nhbl9lbmQgICA9IF93aW5bc2NhbiArIGJlc3RfbGVuXTtcbiAgICB9XG4gIH0gd2hpbGUgKChjdXJfbWF0Y2ggPSBwcmV2W2N1cl9tYXRjaCAmIHdtYXNrXSkgPiBsaW1pdCAmJiAtLWNoYWluX2xlbmd0aCAhPT0gMCk7XG5cbiAgaWYgKGJlc3RfbGVuIDw9IHMubG9va2FoZWFkKSB7XG4gICAgcmV0dXJuIGJlc3RfbGVuO1xuICB9XG4gIHJldHVybiBzLmxvb2thaGVhZDtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEZpbGwgdGhlIHdpbmRvdyB3aGVuIHRoZSBsb29rYWhlYWQgYmVjb21lcyBpbnN1ZmZpY2llbnQuXG4gKiBVcGRhdGVzIHN0cnN0YXJ0IGFuZCBsb29rYWhlYWQuXG4gKlxuICogSU4gYXNzZXJ0aW9uOiBsb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFEXG4gKiBPVVQgYXNzZXJ0aW9uczogc3Ryc3RhcnQgPD0gd2luZG93X3NpemUtTUlOX0xPT0tBSEVBRFxuICogICAgQXQgbGVhc3Qgb25lIGJ5dGUgaGFzIGJlZW4gcmVhZCwgb3IgYXZhaWxfaW4gPT0gMDsgcmVhZHMgYXJlXG4gKiAgICBwZXJmb3JtZWQgZm9yIGF0IGxlYXN0IHR3byBieXRlcyAocmVxdWlyZWQgZm9yIHRoZSB6aXAgdHJhbnNsYXRlX2VvbFxuICogICAgb3B0aW9uIC0tIG5vdCBzdXBwb3J0ZWQgaGVyZSkuXG4gKi9cbmZ1bmN0aW9uIGZpbGxfd2luZG93KHMpIHtcbiAgdmFyIF93X3NpemUgPSBzLndfc2l6ZTtcbiAgdmFyIHAsIG4sIG0sIG1vcmUsIHN0cjtcblxuICAvL0Fzc2VydChzLT5sb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFELCBcImFscmVhZHkgZW5vdWdoIGxvb2thaGVhZFwiKTtcblxuICBkbyB7XG4gICAgbW9yZSA9IHMud2luZG93X3NpemUgLSBzLmxvb2thaGVhZCAtIHMuc3Ryc3RhcnQ7XG5cbiAgICAvLyBKUyBpbnRzIGhhdmUgMzIgYml0LCBibG9jayBiZWxvdyBub3QgbmVlZGVkXG4gICAgLyogRGVhbCB3aXRoICFAIyQlIDY0SyBsaW1pdDogKi9cbiAgICAvL2lmIChzaXplb2YoaW50KSA8PSAyKSB7XG4gICAgLy8gICAgaWYgKG1vcmUgPT0gMCAmJiBzLT5zdHJzdGFydCA9PSAwICYmIHMtPmxvb2thaGVhZCA9PSAwKSB7XG4gICAgLy8gICAgICAgIG1vcmUgPSB3c2l6ZTtcbiAgICAvL1xuICAgIC8vICB9IGVsc2UgaWYgKG1vcmUgPT0gKHVuc2lnbmVkKSgtMSkpIHtcbiAgICAvLyAgICAgICAgLyogVmVyeSB1bmxpa2VseSwgYnV0IHBvc3NpYmxlIG9uIDE2IGJpdCBtYWNoaW5lIGlmXG4gICAgLy8gICAgICAgICAqIHN0cnN0YXJ0ID09IDAgJiYgbG9va2FoZWFkID09IDEgKGlucHV0IGRvbmUgYSBieXRlIGF0IHRpbWUpXG4gICAgLy8gICAgICAgICAqL1xuICAgIC8vICAgICAgICBtb3JlLS07XG4gICAgLy8gICAgfVxuICAgIC8vfVxuXG5cbiAgICAvKiBJZiB0aGUgd2luZG93IGlzIGFsbW9zdCBmdWxsIGFuZCB0aGVyZSBpcyBpbnN1ZmZpY2llbnQgbG9va2FoZWFkLFxuICAgICAqIG1vdmUgdGhlIHVwcGVyIGhhbGYgdG8gdGhlIGxvd2VyIG9uZSB0byBtYWtlIHJvb20gaW4gdGhlIHVwcGVyIGhhbGYuXG4gICAgICovXG4gICAgaWYgKHMuc3Ryc3RhcnQgPj0gX3dfc2l6ZSArIChfd19zaXplIC0gTUlOX0xPT0tBSEVBRCkpIHtcblxuICAgICAgdXRpbHMuYXJyYXlTZXQocy53aW5kb3csIHMud2luZG93LCBfd19zaXplLCBfd19zaXplLCAwKTtcbiAgICAgIHMubWF0Y2hfc3RhcnQgLT0gX3dfc2l6ZTtcbiAgICAgIHMuc3Ryc3RhcnQgLT0gX3dfc2l6ZTtcbiAgICAgIC8qIHdlIG5vdyBoYXZlIHN0cnN0YXJ0ID49IE1BWF9ESVNUICovXG4gICAgICBzLmJsb2NrX3N0YXJ0IC09IF93X3NpemU7XG5cbiAgICAgIC8qIFNsaWRlIHRoZSBoYXNoIHRhYmxlIChjb3VsZCBiZSBhdm9pZGVkIHdpdGggMzIgYml0IHZhbHVlc1xuICAgICAgIGF0IHRoZSBleHBlbnNlIG9mIG1lbW9yeSB1c2FnZSkuIFdlIHNsaWRlIGV2ZW4gd2hlbiBsZXZlbCA9PSAwXG4gICAgICAgdG8ga2VlcCB0aGUgaGFzaCB0YWJsZSBjb25zaXN0ZW50IGlmIHdlIHN3aXRjaCBiYWNrIHRvIGxldmVsID4gMFxuICAgICAgIGxhdGVyLiAoVXNpbmcgbGV2ZWwgMCBwZXJtYW5lbnRseSBpcyBub3QgYW4gb3B0aW1hbCB1c2FnZSBvZlxuICAgICAgIHpsaWIsIHNvIHdlIGRvbid0IGNhcmUgYWJvdXQgdGhpcyBwYXRob2xvZ2ljYWwgY2FzZS4pXG4gICAgICAgKi9cblxuICAgICAgbiA9IHMuaGFzaF9zaXplO1xuICAgICAgcCA9IG47XG4gICAgICBkbyB7XG4gICAgICAgIG0gPSBzLmhlYWRbLS1wXTtcbiAgICAgICAgcy5oZWFkW3BdID0gKG0gPj0gX3dfc2l6ZSA/IG0gLSBfd19zaXplIDogMCk7XG4gICAgICB9IHdoaWxlICgtLW4pO1xuXG4gICAgICBuID0gX3dfc2l6ZTtcbiAgICAgIHAgPSBuO1xuICAgICAgZG8ge1xuICAgICAgICBtID0gcy5wcmV2Wy0tcF07XG4gICAgICAgIHMucHJldltwXSA9IChtID49IF93X3NpemUgPyBtIC0gX3dfc2l6ZSA6IDApO1xuICAgICAgICAvKiBJZiBuIGlzIG5vdCBvbiBhbnkgaGFzaCBjaGFpbiwgcHJldltuXSBpcyBnYXJiYWdlIGJ1dFxuICAgICAgICAgKiBpdHMgdmFsdWUgd2lsbCBuZXZlciBiZSB1c2VkLlxuICAgICAgICAgKi9cbiAgICAgIH0gd2hpbGUgKC0tbik7XG5cbiAgICAgIG1vcmUgKz0gX3dfc2l6ZTtcbiAgICB9XG4gICAgaWYgKHMuc3RybS5hdmFpbF9pbiA9PT0gMCkge1xuICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgLyogSWYgdGhlcmUgd2FzIG5vIHNsaWRpbmc6XG4gICAgICogICAgc3Ryc3RhcnQgPD0gV1NJWkUrTUFYX0RJU1QtMSAmJiBsb29rYWhlYWQgPD0gTUlOX0xPT0tBSEVBRCAtIDEgJiZcbiAgICAgKiAgICBtb3JlID09IHdpbmRvd19zaXplIC0gbG9va2FoZWFkIC0gc3Ryc3RhcnRcbiAgICAgKiA9PiBtb3JlID49IHdpbmRvd19zaXplIC0gKE1JTl9MT09LQUhFQUQtMSArIFdTSVpFICsgTUFYX0RJU1QtMSlcbiAgICAgKiA9PiBtb3JlID49IHdpbmRvd19zaXplIC0gMipXU0laRSArIDJcbiAgICAgKiBJbiB0aGUgQklHX01FTSBvciBNTUFQIGNhc2UgKG5vdCB5ZXQgc3VwcG9ydGVkKSxcbiAgICAgKiAgIHdpbmRvd19zaXplID09IGlucHV0X3NpemUgKyBNSU5fTE9PS0FIRUFEICAmJlxuICAgICAqICAgc3Ryc3RhcnQgKyBzLT5sb29rYWhlYWQgPD0gaW5wdXRfc2l6ZSA9PiBtb3JlID49IE1JTl9MT09LQUhFQUQuXG4gICAgICogT3RoZXJ3aXNlLCB3aW5kb3dfc2l6ZSA9PSAyKldTSVpFIHNvIG1vcmUgPj0gMi5cbiAgICAgKiBJZiB0aGVyZSB3YXMgc2xpZGluZywgbW9yZSA+PSBXU0laRS4gU28gaW4gYWxsIGNhc2VzLCBtb3JlID49IDIuXG4gICAgICovXG4gICAgLy9Bc3NlcnQobW9yZSA+PSAyLCBcIm1vcmUgPCAyXCIpO1xuICAgIG4gPSByZWFkX2J1ZihzLnN0cm0sIHMud2luZG93LCBzLnN0cnN0YXJ0ICsgcy5sb29rYWhlYWQsIG1vcmUpO1xuICAgIHMubG9va2FoZWFkICs9IG47XG5cbiAgICAvKiBJbml0aWFsaXplIHRoZSBoYXNoIHZhbHVlIG5vdyB0aGF0IHdlIGhhdmUgc29tZSBpbnB1dDogKi9cbiAgICBpZiAocy5sb29rYWhlYWQgKyBzLmluc2VydCA+PSBNSU5fTUFUQ0gpIHtcbiAgICAgIHN0ciA9IHMuc3Ryc3RhcnQgLSBzLmluc2VydDtcbiAgICAgIHMuaW5zX2ggPSBzLndpbmRvd1tzdHJdO1xuXG4gICAgICAvKiBVUERBVEVfSEFTSChzLCBzLT5pbnNfaCwgcy0+d2luZG93W3N0ciArIDFdKTsgKi9cbiAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3N0ciArIDFdKSAmIHMuaGFzaF9tYXNrO1xuLy8jaWYgTUlOX01BVENIICE9IDNcbi8vICAgICAgICBDYWxsIHVwZGF0ZV9oYXNoKCkgTUlOX01BVENILTMgbW9yZSB0aW1lc1xuLy8jZW5kaWZcbiAgICAgIHdoaWxlIChzLmluc2VydCkge1xuICAgICAgICAvKiBVUERBVEVfSEFTSChzLCBzLT5pbnNfaCwgcy0+d2luZG93W3N0ciArIE1JTl9NQVRDSC0xXSk7ICovXG4gICAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3N0ciArIE1JTl9NQVRDSC0xXSkgJiBzLmhhc2hfbWFzaztcblxuICAgICAgICBzLnByZXZbc3RyICYgcy53X21hc2tdID0gcy5oZWFkW3MuaW5zX2hdO1xuICAgICAgICBzLmhlYWRbcy5pbnNfaF0gPSBzdHI7XG4gICAgICAgIHN0cisrO1xuICAgICAgICBzLmluc2VydC0tO1xuICAgICAgICBpZiAocy5sb29rYWhlYWQgKyBzLmluc2VydCA8IE1JTl9NQVRDSCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIC8qIElmIHRoZSB3aG9sZSBpbnB1dCBoYXMgbGVzcyB0aGFuIE1JTl9NQVRDSCBieXRlcywgaW5zX2ggaXMgZ2FyYmFnZSxcbiAgICAgKiBidXQgdGhpcyBpcyBub3QgaW1wb3J0YW50IHNpbmNlIG9ubHkgbGl0ZXJhbCBieXRlcyB3aWxsIGJlIGVtaXR0ZWQuXG4gICAgICovXG5cbiAgfSB3aGlsZSAocy5sb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFEICYmIHMuc3RybS5hdmFpbF9pbiAhPT0gMCk7XG5cbiAgLyogSWYgdGhlIFdJTl9JTklUIGJ5dGVzIGFmdGVyIHRoZSBlbmQgb2YgdGhlIGN1cnJlbnQgZGF0YSBoYXZlIG5ldmVyIGJlZW5cbiAgICogd3JpdHRlbiwgdGhlbiB6ZXJvIHRob3NlIGJ5dGVzIGluIG9yZGVyIHRvIGF2b2lkIG1lbW9yeSBjaGVjayByZXBvcnRzIG9mXG4gICAqIHRoZSB1c2Ugb2YgdW5pbml0aWFsaXplZCAob3IgdW5pbml0aWFsaXNlZCBhcyBKdWxpYW4gd3JpdGVzKSBieXRlcyBieVxuICAgKiB0aGUgbG9uZ2VzdCBtYXRjaCByb3V0aW5lcy4gIFVwZGF0ZSB0aGUgaGlnaCB3YXRlciBtYXJrIGZvciB0aGUgbmV4dFxuICAgKiB0aW1lIHRocm91Z2ggaGVyZS4gIFdJTl9JTklUIGlzIHNldCB0byBNQVhfTUFUQ0ggc2luY2UgdGhlIGxvbmdlc3QgbWF0Y2hcbiAgICogcm91dGluZXMgYWxsb3cgc2Nhbm5pbmcgdG8gc3Ryc3RhcnQgKyBNQVhfTUFUQ0gsIGlnbm9yaW5nIGxvb2thaGVhZC5cbiAgICovXG4vLyAgaWYgKHMuaGlnaF93YXRlciA8IHMud2luZG93X3NpemUpIHtcbi8vICAgIHZhciBjdXJyID0gcy5zdHJzdGFydCArIHMubG9va2FoZWFkO1xuLy8gICAgdmFyIGluaXQgPSAwO1xuLy9cbi8vICAgIGlmIChzLmhpZ2hfd2F0ZXIgPCBjdXJyKSB7XG4vLyAgICAgIC8qIFByZXZpb3VzIGhpZ2ggd2F0ZXIgbWFyayBiZWxvdyBjdXJyZW50IGRhdGEgLS0gemVybyBXSU5fSU5JVFxuLy8gICAgICAgKiBieXRlcyBvciB1cCB0byBlbmQgb2Ygd2luZG93LCB3aGljaGV2ZXIgaXMgbGVzcy5cbi8vICAgICAgICovXG4vLyAgICAgIGluaXQgPSBzLndpbmRvd19zaXplIC0gY3Vycjtcbi8vICAgICAgaWYgKGluaXQgPiBXSU5fSU5JVClcbi8vICAgICAgICBpbml0ID0gV0lOX0lOSVQ7XG4vLyAgICAgIHptZW16ZXJvKHMtPndpbmRvdyArIGN1cnIsICh1bnNpZ25lZClpbml0KTtcbi8vICAgICAgcy0+aGlnaF93YXRlciA9IGN1cnIgKyBpbml0O1xuLy8gICAgfVxuLy8gICAgZWxzZSBpZiAocy0+aGlnaF93YXRlciA8ICh1bGcpY3VyciArIFdJTl9JTklUKSB7XG4vLyAgICAgIC8qIEhpZ2ggd2F0ZXIgbWFyayBhdCBvciBhYm92ZSBjdXJyZW50IGRhdGEsIGJ1dCBiZWxvdyBjdXJyZW50IGRhdGFcbi8vICAgICAgICogcGx1cyBXSU5fSU5JVCAtLSB6ZXJvIG91dCB0byBjdXJyZW50IGRhdGEgcGx1cyBXSU5fSU5JVCwgb3IgdXBcbi8vICAgICAgICogdG8gZW5kIG9mIHdpbmRvdywgd2hpY2hldmVyIGlzIGxlc3MuXG4vLyAgICAgICAqL1xuLy8gICAgICBpbml0ID0gKHVsZyljdXJyICsgV0lOX0lOSVQgLSBzLT5oaWdoX3dhdGVyO1xuLy8gICAgICBpZiAoaW5pdCA+IHMtPndpbmRvd19zaXplIC0gcy0+aGlnaF93YXRlcilcbi8vICAgICAgICBpbml0ID0gcy0+d2luZG93X3NpemUgLSBzLT5oaWdoX3dhdGVyO1xuLy8gICAgICB6bWVtemVybyhzLT53aW5kb3cgKyBzLT5oaWdoX3dhdGVyLCAodW5zaWduZWQpaW5pdCk7XG4vLyAgICAgIHMtPmhpZ2hfd2F0ZXIgKz0gaW5pdDtcbi8vICAgIH1cbi8vICB9XG4vL1xuLy8gIEFzc2VydCgodWxnKXMtPnN0cnN0YXJ0IDw9IHMtPndpbmRvd19zaXplIC0gTUlOX0xPT0tBSEVBRCxcbi8vICAgIFwibm90IGVub3VnaCByb29tIGZvciBzZWFyY2hcIik7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogQ29weSB3aXRob3V0IGNvbXByZXNzaW9uIGFzIG11Y2ggYXMgcG9zc2libGUgZnJvbSB0aGUgaW5wdXQgc3RyZWFtLCByZXR1cm5cbiAqIHRoZSBjdXJyZW50IGJsb2NrIHN0YXRlLlxuICogVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCBpbnNlcnQgbmV3IHN0cmluZ3MgaW4gdGhlIGRpY3Rpb25hcnkgc2luY2VcbiAqIHVuY29tcHJlc3NpYmxlIGRhdGEgaXMgcHJvYmFibHkgbm90IHVzZWZ1bC4gVGhpcyBmdW5jdGlvbiBpcyB1c2VkXG4gKiBvbmx5IGZvciB0aGUgbGV2ZWw9MCBjb21wcmVzc2lvbiBvcHRpb24uXG4gKiBOT1RFOiB0aGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBvcHRpbWl6ZWQgdG8gYXZvaWQgZXh0cmEgY29weWluZyBmcm9tXG4gKiB3aW5kb3cgdG8gcGVuZGluZ19idWYuXG4gKi9cbmZ1bmN0aW9uIGRlZmxhdGVfc3RvcmVkKHMsIGZsdXNoKSB7XG4gIC8qIFN0b3JlZCBibG9ja3MgYXJlIGxpbWl0ZWQgdG8gMHhmZmZmIGJ5dGVzLCBwZW5kaW5nX2J1ZiBpcyBsaW1pdGVkXG4gICAqIHRvIHBlbmRpbmdfYnVmX3NpemUsIGFuZCBlYWNoIHN0b3JlZCBibG9jayBoYXMgYSA1IGJ5dGUgaGVhZGVyOlxuICAgKi9cbiAgdmFyIG1heF9ibG9ja19zaXplID0gMHhmZmZmO1xuXG4gIGlmIChtYXhfYmxvY2tfc2l6ZSA+IHMucGVuZGluZ19idWZfc2l6ZSAtIDUpIHtcbiAgICBtYXhfYmxvY2tfc2l6ZSA9IHMucGVuZGluZ19idWZfc2l6ZSAtIDU7XG4gIH1cblxuICAvKiBDb3B5IGFzIG11Y2ggYXMgcG9zc2libGUgZnJvbSBpbnB1dCB0byBvdXRwdXQ6ICovXG4gIGZvciAoOzspIHtcbiAgICAvKiBGaWxsIHRoZSB3aW5kb3cgYXMgbXVjaCBhcyBwb3NzaWJsZTogKi9cbiAgICBpZiAocy5sb29rYWhlYWQgPD0gMSkge1xuXG4gICAgICAvL0Fzc2VydChzLT5zdHJzdGFydCA8IHMtPndfc2l6ZStNQVhfRElTVChzKSB8fFxuICAgICAgLy8gIHMtPmJsb2NrX3N0YXJ0ID49IChsb25nKXMtPndfc2l6ZSwgXCJzbGlkZSB0b28gbGF0ZVwiKTtcbi8vICAgICAgaWYgKCEocy5zdHJzdGFydCA8IHMud19zaXplICsgKHMud19zaXplIC0gTUlOX0xPT0tBSEVBRCkgfHxcbi8vICAgICAgICBzLmJsb2NrX3N0YXJ0ID49IHMud19zaXplKSkge1xuLy8gICAgICAgIHRocm93ICBuZXcgRXJyb3IoXCJzbGlkZSB0b28gbGF0ZVwiKTtcbi8vICAgICAgfVxuXG4gICAgICBmaWxsX3dpbmRvdyhzKTtcbiAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCAmJiBmbHVzaCA9PT0gWl9OT19GTFVTSCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuXG4gICAgICBpZiAocy5sb29rYWhlYWQgPT09IDApIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvKiBmbHVzaCB0aGUgY3VycmVudCBibG9jayAqL1xuICAgIH1cbiAgICAvL0Fzc2VydChzLT5ibG9ja19zdGFydCA+PSAwTCwgXCJibG9jayBnb25lXCIpO1xuLy8gICAgaWYgKHMuYmxvY2tfc3RhcnQgPCAwKSB0aHJvdyBuZXcgRXJyb3IoXCJibG9jayBnb25lXCIpO1xuXG4gICAgcy5zdHJzdGFydCArPSBzLmxvb2thaGVhZDtcbiAgICBzLmxvb2thaGVhZCA9IDA7XG5cbiAgICAvKiBFbWl0IGEgc3RvcmVkIGJsb2NrIGlmIHBlbmRpbmdfYnVmIHdpbGwgYmUgZnVsbDogKi9cbiAgICB2YXIgbWF4X3N0YXJ0ID0gcy5ibG9ja19zdGFydCArIG1heF9ibG9ja19zaXplO1xuXG4gICAgaWYgKHMuc3Ryc3RhcnQgPT09IDAgfHwgcy5zdHJzdGFydCA+PSBtYXhfc3RhcnQpIHtcbiAgICAgIC8qIHN0cnN0YXJ0ID09IDAgaXMgcG9zc2libGUgd2hlbiB3cmFwYXJvdW5kIG9uIDE2LWJpdCBtYWNoaW5lICovXG4gICAgICBzLmxvb2thaGVhZCA9IHMuc3Ryc3RhcnQgLSBtYXhfc3RhcnQ7XG4gICAgICBzLnN0cnN0YXJ0ID0gbWF4X3N0YXJ0O1xuICAgICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgLyoqKi9cblxuXG4gICAgfVxuICAgIC8qIEZsdXNoIGlmIHdlIG1heSBoYXZlIHRvIHNsaWRlLCBvdGhlcndpc2UgYmxvY2tfc3RhcnQgbWF5IGJlY29tZVxuICAgICAqIG5lZ2F0aXZlIGFuZCB0aGUgZGF0YSB3aWxsIGJlIGdvbmU6XG4gICAgICovXG4gICAgaWYgKHMuc3Ryc3RhcnQgLSBzLmJsb2NrX3N0YXJ0ID49IChzLndfc2l6ZSAtIE1JTl9MT09LQUhFQUQpKSB7XG4gICAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDApOyAqKiovXG4gICAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgICB9XG4gICAgICAvKioqL1xuICAgIH1cbiAgfVxuXG4gIHMuaW5zZXJ0ID0gMDtcblxuICBpZiAoZmx1c2ggPT09IFpfRklOSVNIKSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAxKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgdHJ1ZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19GSU5JU0hfU1RBUlRFRDtcbiAgICB9XG4gICAgLyoqKi9cbiAgICByZXR1cm4gQlNfRklOSVNIX0RPTkU7XG4gIH1cblxuICBpZiAocy5zdHJzdGFydCA+IHMuYmxvY2tfc3RhcnQpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDApOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgfVxuICAgIC8qKiovXG4gIH1cblxuICByZXR1cm4gQlNfTkVFRF9NT1JFO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvbXByZXNzIGFzIG11Y2ggYXMgcG9zc2libGUgZnJvbSB0aGUgaW5wdXQgc3RyZWFtLCByZXR1cm4gdGhlIGN1cnJlbnRcbiAqIGJsb2NrIHN0YXRlLlxuICogVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCBwZXJmb3JtIGxhenkgZXZhbHVhdGlvbiBvZiBtYXRjaGVzIGFuZCBpbnNlcnRzXG4gKiBuZXcgc3RyaW5ncyBpbiB0aGUgZGljdGlvbmFyeSBvbmx5IGZvciB1bm1hdGNoZWQgc3RyaW5ncyBvciBmb3Igc2hvcnRcbiAqIG1hdGNoZXMuIEl0IGlzIHVzZWQgb25seSBmb3IgdGhlIGZhc3QgY29tcHJlc3Npb24gb3B0aW9ucy5cbiAqL1xuZnVuY3Rpb24gZGVmbGF0ZV9mYXN0KHMsIGZsdXNoKSB7XG4gIHZhciBoYXNoX2hlYWQ7ICAgICAgICAvKiBoZWFkIG9mIHRoZSBoYXNoIGNoYWluICovXG4gIHZhciBiZmx1c2g7ICAgICAgICAgICAvKiBzZXQgaWYgY3VycmVudCBibG9jayBtdXN0IGJlIGZsdXNoZWQgKi9cblxuICBmb3IgKDs7KSB7XG4gICAgLyogTWFrZSBzdXJlIHRoYXQgd2UgYWx3YXlzIGhhdmUgZW5vdWdoIGxvb2thaGVhZCwgZXhjZXB0XG4gICAgICogYXQgdGhlIGVuZCBvZiB0aGUgaW5wdXQgZmlsZS4gV2UgbmVlZCBNQVhfTUFUQ0ggYnl0ZXNcbiAgICAgKiBmb3IgdGhlIG5leHQgbWF0Y2gsIHBsdXMgTUlOX01BVENIIGJ5dGVzIHRvIGluc2VydCB0aGVcbiAgICAgKiBzdHJpbmcgZm9sbG93aW5nIHRoZSBuZXh0IG1hdGNoLlxuICAgICAqL1xuICAgIGlmIChzLmxvb2thaGVhZCA8IE1JTl9MT09LQUhFQUQpIHtcbiAgICAgIGZpbGxfd2luZG93KHMpO1xuICAgICAgaWYgKHMubG9va2FoZWFkIDwgTUlOX0xPT0tBSEVBRCAmJiBmbHVzaCA9PT0gWl9OT19GTFVTSCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgaWYgKHMubG9va2FoZWFkID09PSAwKSB7XG4gICAgICAgIGJyZWFrOyAvKiBmbHVzaCB0aGUgY3VycmVudCBibG9jayAqL1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qIEluc2VydCB0aGUgc3RyaW5nIHdpbmRvd1tzdHJzdGFydCAuLiBzdHJzdGFydCsyXSBpbiB0aGVcbiAgICAgKiBkaWN0aW9uYXJ5LCBhbmQgc2V0IGhhc2hfaGVhZCB0byB0aGUgaGVhZCBvZiB0aGUgaGFzaCBjaGFpbjpcbiAgICAgKi9cbiAgICBoYXNoX2hlYWQgPSAwLypOSUwqLztcbiAgICBpZiAocy5sb29rYWhlYWQgPj0gTUlOX01BVENIKSB7XG4gICAgICAvKioqIElOU0VSVF9TVFJJTkcocywgcy5zdHJzdGFydCwgaGFzaF9oZWFkKTsgKioqL1xuICAgICAgcy5pbnNfaCA9ICgocy5pbnNfaCA8PCBzLmhhc2hfc2hpZnQpIF4gcy53aW5kb3dbcy5zdHJzdGFydCArIE1JTl9NQVRDSCAtIDFdKSAmIHMuaGFzaF9tYXNrO1xuICAgICAgaGFzaF9oZWFkID0gcy5wcmV2W3Muc3Ryc3RhcnQgJiBzLndfbWFza10gPSBzLmhlYWRbcy5pbnNfaF07XG4gICAgICBzLmhlYWRbcy5pbnNfaF0gPSBzLnN0cnN0YXJ0O1xuICAgICAgLyoqKi9cbiAgICB9XG5cbiAgICAvKiBGaW5kIHRoZSBsb25nZXN0IG1hdGNoLCBkaXNjYXJkaW5nIHRob3NlIDw9IHByZXZfbGVuZ3RoLlxuICAgICAqIEF0IHRoaXMgcG9pbnQgd2UgaGF2ZSBhbHdheXMgbWF0Y2hfbGVuZ3RoIDwgTUlOX01BVENIXG4gICAgICovXG4gICAgaWYgKGhhc2hfaGVhZCAhPT0gMC8qTklMKi8gJiYgKChzLnN0cnN0YXJ0IC0gaGFzaF9oZWFkKSA8PSAocy53X3NpemUgLSBNSU5fTE9PS0FIRUFEKSkpIHtcbiAgICAgIC8qIFRvIHNpbXBsaWZ5IHRoZSBjb2RlLCB3ZSBwcmV2ZW50IG1hdGNoZXMgd2l0aCB0aGUgc3RyaW5nXG4gICAgICAgKiBvZiB3aW5kb3cgaW5kZXggMCAoaW4gcGFydGljdWxhciB3ZSBoYXZlIHRvIGF2b2lkIGEgbWF0Y2hcbiAgICAgICAqIG9mIHRoZSBzdHJpbmcgd2l0aCBpdHNlbGYgYXQgdGhlIHN0YXJ0IG9mIHRoZSBpbnB1dCBmaWxlKS5cbiAgICAgICAqL1xuICAgICAgcy5tYXRjaF9sZW5ndGggPSBsb25nZXN0X21hdGNoKHMsIGhhc2hfaGVhZCk7XG4gICAgICAvKiBsb25nZXN0X21hdGNoKCkgc2V0cyBtYXRjaF9zdGFydCAqL1xuICAgIH1cbiAgICBpZiAocy5tYXRjaF9sZW5ndGggPj0gTUlOX01BVENIKSB7XG4gICAgICAvLyBjaGVja19tYXRjaChzLCBzLnN0cnN0YXJ0LCBzLm1hdGNoX3N0YXJ0LCBzLm1hdGNoX2xlbmd0aCk7IC8vIGZvciBkZWJ1ZyBvbmx5XG5cbiAgICAgIC8qKiogX3RyX3RhbGx5X2Rpc3Qocywgcy5zdHJzdGFydCAtIHMubWF0Y2hfc3RhcnQsXG4gICAgICAgICAgICAgICAgICAgICBzLm1hdGNoX2xlbmd0aCAtIE1JTl9NQVRDSCwgYmZsdXNoKTsgKioqL1xuICAgICAgYmZsdXNoID0gdHJlZXMuX3RyX3RhbGx5KHMsIHMuc3Ryc3RhcnQgLSBzLm1hdGNoX3N0YXJ0LCBzLm1hdGNoX2xlbmd0aCAtIE1JTl9NQVRDSCk7XG5cbiAgICAgIHMubG9va2FoZWFkIC09IHMubWF0Y2hfbGVuZ3RoO1xuXG4gICAgICAvKiBJbnNlcnQgbmV3IHN0cmluZ3MgaW4gdGhlIGhhc2ggdGFibGUgb25seSBpZiB0aGUgbWF0Y2ggbGVuZ3RoXG4gICAgICAgKiBpcyBub3QgdG9vIGxhcmdlLiBUaGlzIHNhdmVzIHRpbWUgYnV0IGRlZ3JhZGVzIGNvbXByZXNzaW9uLlxuICAgICAgICovXG4gICAgICBpZiAocy5tYXRjaF9sZW5ndGggPD0gcy5tYXhfbGF6eV9tYXRjaC8qbWF4X2luc2VydF9sZW5ndGgqLyAmJiBzLmxvb2thaGVhZCA+PSBNSU5fTUFUQ0gpIHtcbiAgICAgICAgcy5tYXRjaF9sZW5ndGgtLTsgLyogc3RyaW5nIGF0IHN0cnN0YXJ0IGFscmVhZHkgaW4gdGFibGUgKi9cbiAgICAgICAgZG8ge1xuICAgICAgICAgIHMuc3Ryc3RhcnQrKztcbiAgICAgICAgICAvKioqIElOU0VSVF9TVFJJTkcocywgcy5zdHJzdGFydCwgaGFzaF9oZWFkKTsgKioqL1xuICAgICAgICAgIHMuaW5zX2ggPSAoKHMuaW5zX2ggPDwgcy5oYXNoX3NoaWZ0KSBeIHMud2luZG93W3Muc3Ryc3RhcnQgKyBNSU5fTUFUQ0ggLSAxXSkgJiBzLmhhc2hfbWFzaztcbiAgICAgICAgICBoYXNoX2hlYWQgPSBzLnByZXZbcy5zdHJzdGFydCAmIHMud19tYXNrXSA9IHMuaGVhZFtzLmluc19oXTtcbiAgICAgICAgICBzLmhlYWRbcy5pbnNfaF0gPSBzLnN0cnN0YXJ0O1xuICAgICAgICAgIC8qKiovXG4gICAgICAgICAgLyogc3Ryc3RhcnQgbmV2ZXIgZXhjZWVkcyBXU0laRS1NQVhfTUFUQ0gsIHNvIHRoZXJlIGFyZVxuICAgICAgICAgICAqIGFsd2F5cyBNSU5fTUFUQ0ggYnl0ZXMgYWhlYWQuXG4gICAgICAgICAgICovXG4gICAgICAgIH0gd2hpbGUgKC0tcy5tYXRjaF9sZW5ndGggIT09IDApO1xuICAgICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgICB9IGVsc2VcbiAgICAgIHtcbiAgICAgICAgcy5zdHJzdGFydCArPSBzLm1hdGNoX2xlbmd0aDtcbiAgICAgICAgcy5tYXRjaF9sZW5ndGggPSAwO1xuICAgICAgICBzLmluc19oID0gcy53aW5kb3dbcy5zdHJzdGFydF07XG4gICAgICAgIC8qIFVQREFURV9IQVNIKHMsIHMuaW5zX2gsIHMud2luZG93W3Muc3Ryc3RhcnQrMV0pOyAqL1xuICAgICAgICBzLmluc19oID0gKChzLmluc19oIDw8IHMuaGFzaF9zaGlmdCkgXiBzLndpbmRvd1tzLnN0cnN0YXJ0ICsgMV0pICYgcy5oYXNoX21hc2s7XG5cbi8vI2lmIE1JTl9NQVRDSCAhPSAzXG4vLyAgICAgICAgICAgICAgICBDYWxsIFVQREFURV9IQVNIKCkgTUlOX01BVENILTMgbW9yZSB0aW1lc1xuLy8jZW5kaWZcbiAgICAgICAgLyogSWYgbG9va2FoZWFkIDwgTUlOX01BVENILCBpbnNfaCBpcyBnYXJiYWdlLCBidXQgaXQgZG9lcyBub3RcbiAgICAgICAgICogbWF0dGVyIHNpbmNlIGl0IHdpbGwgYmUgcmVjb21wdXRlZCBhdCBuZXh0IGRlZmxhdGUgY2FsbC5cbiAgICAgICAgICovXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qIE5vIG1hdGNoLCBvdXRwdXQgYSBsaXRlcmFsIGJ5dGUgKi9cbiAgICAgIC8vVHJhY2V2digoc3RkZXJyLFwiJWNcIiwgcy53aW5kb3dbcy5zdHJzdGFydF0pKTtcbiAgICAgIC8qKiogX3RyX3RhbGx5X2xpdChzLCBzLndpbmRvd1tzLnN0cnN0YXJ0XSwgYmZsdXNoKTsgKioqL1xuICAgICAgYmZsdXNoID0gdHJlZXMuX3RyX3RhbGx5KHMsIDAsIHMud2luZG93W3Muc3Ryc3RhcnRdKTtcblxuICAgICAgcy5sb29rYWhlYWQtLTtcbiAgICAgIHMuc3Ryc3RhcnQrKztcbiAgICB9XG4gICAgaWYgKGJmbHVzaCkge1xuICAgICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgLyoqKi9cbiAgICB9XG4gIH1cbiAgcy5pbnNlcnQgPSAoKHMuc3Ryc3RhcnQgPCAoTUlOX01BVENILTEpKSA/IHMuc3Ryc3RhcnQgOiBNSU5fTUFUQ0gtMSk7XG4gIGlmIChmbHVzaCA9PT0gWl9GSU5JU0gpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDEpOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCB0cnVlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX0ZJTklTSF9TVEFSVEVEO1xuICAgIH1cbiAgICAvKioqL1xuICAgIHJldHVybiBCU19GSU5JU0hfRE9ORTtcbiAgfVxuICBpZiAocy5sYXN0X2xpdCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICB9XG4gICAgLyoqKi9cbiAgfVxuICByZXR1cm4gQlNfQkxPQ0tfRE9ORTtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTYW1lIGFzIGFib3ZlLCBidXQgYWNoaWV2ZXMgYmV0dGVyIGNvbXByZXNzaW9uLiBXZSB1c2UgYSBsYXp5XG4gKiBldmFsdWF0aW9uIGZvciBtYXRjaGVzOiBhIG1hdGNoIGlzIGZpbmFsbHkgYWRvcHRlZCBvbmx5IGlmIHRoZXJlIGlzXG4gKiBubyBiZXR0ZXIgbWF0Y2ggYXQgdGhlIG5leHQgd2luZG93IHBvc2l0aW9uLlxuICovXG5mdW5jdGlvbiBkZWZsYXRlX3Nsb3cocywgZmx1c2gpIHtcbiAgdmFyIGhhc2hfaGVhZDsgICAgICAgICAgLyogaGVhZCBvZiBoYXNoIGNoYWluICovXG4gIHZhciBiZmx1c2g7ICAgICAgICAgICAgICAvKiBzZXQgaWYgY3VycmVudCBibG9jayBtdXN0IGJlIGZsdXNoZWQgKi9cblxuICB2YXIgbWF4X2luc2VydDtcblxuICAvKiBQcm9jZXNzIHRoZSBpbnB1dCBibG9jay4gKi9cbiAgZm9yICg7Oykge1xuICAgIC8qIE1ha2Ugc3VyZSB0aGF0IHdlIGFsd2F5cyBoYXZlIGVub3VnaCBsb29rYWhlYWQsIGV4Y2VwdFxuICAgICAqIGF0IHRoZSBlbmQgb2YgdGhlIGlucHV0IGZpbGUuIFdlIG5lZWQgTUFYX01BVENIIGJ5dGVzXG4gICAgICogZm9yIHRoZSBuZXh0IG1hdGNoLCBwbHVzIE1JTl9NQVRDSCBieXRlcyB0byBpbnNlcnQgdGhlXG4gICAgICogc3RyaW5nIGZvbGxvd2luZyB0aGUgbmV4dCBtYXRjaC5cbiAgICAgKi9cbiAgICBpZiAocy5sb29rYWhlYWQgPCBNSU5fTE9PS0FIRUFEKSB7XG4gICAgICBmaWxsX3dpbmRvdyhzKTtcbiAgICAgIGlmIChzLmxvb2thaGVhZCA8IE1JTl9MT09LQUhFQUQgJiYgZmx1c2ggPT09IFpfTk9fRkxVU0gpIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkgeyBicmVhazsgfSAvKiBmbHVzaCB0aGUgY3VycmVudCBibG9jayAqL1xuICAgIH1cblxuICAgIC8qIEluc2VydCB0aGUgc3RyaW5nIHdpbmRvd1tzdHJzdGFydCAuLiBzdHJzdGFydCsyXSBpbiB0aGVcbiAgICAgKiBkaWN0aW9uYXJ5LCBhbmQgc2V0IGhhc2hfaGVhZCB0byB0aGUgaGVhZCBvZiB0aGUgaGFzaCBjaGFpbjpcbiAgICAgKi9cbiAgICBoYXNoX2hlYWQgPSAwLypOSUwqLztcbiAgICBpZiAocy5sb29rYWhlYWQgPj0gTUlOX01BVENIKSB7XG4gICAgICAvKioqIElOU0VSVF9TVFJJTkcocywgcy5zdHJzdGFydCwgaGFzaF9oZWFkKTsgKioqL1xuICAgICAgcy5pbnNfaCA9ICgocy5pbnNfaCA8PCBzLmhhc2hfc2hpZnQpIF4gcy53aW5kb3dbcy5zdHJzdGFydCArIE1JTl9NQVRDSCAtIDFdKSAmIHMuaGFzaF9tYXNrO1xuICAgICAgaGFzaF9oZWFkID0gcy5wcmV2W3Muc3Ryc3RhcnQgJiBzLndfbWFza10gPSBzLmhlYWRbcy5pbnNfaF07XG4gICAgICBzLmhlYWRbcy5pbnNfaF0gPSBzLnN0cnN0YXJ0O1xuICAgICAgLyoqKi9cbiAgICB9XG5cbiAgICAvKiBGaW5kIHRoZSBsb25nZXN0IG1hdGNoLCBkaXNjYXJkaW5nIHRob3NlIDw9IHByZXZfbGVuZ3RoLlxuICAgICAqL1xuICAgIHMucHJldl9sZW5ndGggPSBzLm1hdGNoX2xlbmd0aDtcbiAgICBzLnByZXZfbWF0Y2ggPSBzLm1hdGNoX3N0YXJ0O1xuICAgIHMubWF0Y2hfbGVuZ3RoID0gTUlOX01BVENILTE7XG5cbiAgICBpZiAoaGFzaF9oZWFkICE9PSAwLypOSUwqLyAmJiBzLnByZXZfbGVuZ3RoIDwgcy5tYXhfbGF6eV9tYXRjaCAmJlxuICAgICAgICBzLnN0cnN0YXJ0IC0gaGFzaF9oZWFkIDw9IChzLndfc2l6ZS1NSU5fTE9PS0FIRUFEKS8qTUFYX0RJU1QocykqLykge1xuICAgICAgLyogVG8gc2ltcGxpZnkgdGhlIGNvZGUsIHdlIHByZXZlbnQgbWF0Y2hlcyB3aXRoIHRoZSBzdHJpbmdcbiAgICAgICAqIG9mIHdpbmRvdyBpbmRleCAwIChpbiBwYXJ0aWN1bGFyIHdlIGhhdmUgdG8gYXZvaWQgYSBtYXRjaFxuICAgICAgICogb2YgdGhlIHN0cmluZyB3aXRoIGl0c2VsZiBhdCB0aGUgc3RhcnQgb2YgdGhlIGlucHV0IGZpbGUpLlxuICAgICAgICovXG4gICAgICBzLm1hdGNoX2xlbmd0aCA9IGxvbmdlc3RfbWF0Y2gocywgaGFzaF9oZWFkKTtcbiAgICAgIC8qIGxvbmdlc3RfbWF0Y2goKSBzZXRzIG1hdGNoX3N0YXJ0ICovXG5cbiAgICAgIGlmIChzLm1hdGNoX2xlbmd0aCA8PSA1ICYmXG4gICAgICAgICAocy5zdHJhdGVneSA9PT0gWl9GSUxURVJFRCB8fCAocy5tYXRjaF9sZW5ndGggPT09IE1JTl9NQVRDSCAmJiBzLnN0cnN0YXJ0IC0gcy5tYXRjaF9zdGFydCA+IDQwOTYvKlRPT19GQVIqLykpKSB7XG5cbiAgICAgICAgLyogSWYgcHJldl9tYXRjaCBpcyBhbHNvIE1JTl9NQVRDSCwgbWF0Y2hfc3RhcnQgaXMgZ2FyYmFnZVxuICAgICAgICAgKiBidXQgd2Ugd2lsbCBpZ25vcmUgdGhlIGN1cnJlbnQgbWF0Y2ggYW55d2F5LlxuICAgICAgICAgKi9cbiAgICAgICAgcy5tYXRjaF9sZW5ndGggPSBNSU5fTUFUQ0gtMTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyogSWYgdGhlcmUgd2FzIGEgbWF0Y2ggYXQgdGhlIHByZXZpb3VzIHN0ZXAgYW5kIHRoZSBjdXJyZW50XG4gICAgICogbWF0Y2ggaXMgbm90IGJldHRlciwgb3V0cHV0IHRoZSBwcmV2aW91cyBtYXRjaDpcbiAgICAgKi9cbiAgICBpZiAocy5wcmV2X2xlbmd0aCA+PSBNSU5fTUFUQ0ggJiYgcy5tYXRjaF9sZW5ndGggPD0gcy5wcmV2X2xlbmd0aCkge1xuICAgICAgbWF4X2luc2VydCA9IHMuc3Ryc3RhcnQgKyBzLmxvb2thaGVhZCAtIE1JTl9NQVRDSDtcbiAgICAgIC8qIERvIG5vdCBpbnNlcnQgc3RyaW5ncyBpbiBoYXNoIHRhYmxlIGJleW9uZCB0aGlzLiAqL1xuXG4gICAgICAvL2NoZWNrX21hdGNoKHMsIHMuc3Ryc3RhcnQtMSwgcy5wcmV2X21hdGNoLCBzLnByZXZfbGVuZ3RoKTtcblxuICAgICAgLyoqKl90cl90YWxseV9kaXN0KHMsIHMuc3Ryc3RhcnQgLSAxIC0gcy5wcmV2X21hdGNoLFxuICAgICAgICAgICAgICAgICAgICAgcy5wcmV2X2xlbmd0aCAtIE1JTl9NQVRDSCwgYmZsdXNoKTsqKiovXG4gICAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgcy5zdHJzdGFydCAtIDEtIHMucHJldl9tYXRjaCwgcy5wcmV2X2xlbmd0aCAtIE1JTl9NQVRDSCk7XG4gICAgICAvKiBJbnNlcnQgaW4gaGFzaCB0YWJsZSBhbGwgc3RyaW5ncyB1cCB0byB0aGUgZW5kIG9mIHRoZSBtYXRjaC5cbiAgICAgICAqIHN0cnN0YXJ0LTEgYW5kIHN0cnN0YXJ0IGFyZSBhbHJlYWR5IGluc2VydGVkLiBJZiB0aGVyZSBpcyBub3RcbiAgICAgICAqIGVub3VnaCBsb29rYWhlYWQsIHRoZSBsYXN0IHR3byBzdHJpbmdzIGFyZSBub3QgaW5zZXJ0ZWQgaW5cbiAgICAgICAqIHRoZSBoYXNoIHRhYmxlLlxuICAgICAgICovXG4gICAgICBzLmxvb2thaGVhZCAtPSBzLnByZXZfbGVuZ3RoLTE7XG4gICAgICBzLnByZXZfbGVuZ3RoIC09IDI7XG4gICAgICBkbyB7XG4gICAgICAgIGlmICgrK3Muc3Ryc3RhcnQgPD0gbWF4X2luc2VydCkge1xuICAgICAgICAgIC8qKiogSU5TRVJUX1NUUklORyhzLCBzLnN0cnN0YXJ0LCBoYXNoX2hlYWQpOyAqKiovXG4gICAgICAgICAgcy5pbnNfaCA9ICgocy5pbnNfaCA8PCBzLmhhc2hfc2hpZnQpIF4gcy53aW5kb3dbcy5zdHJzdGFydCArIE1JTl9NQVRDSCAtIDFdKSAmIHMuaGFzaF9tYXNrO1xuICAgICAgICAgIGhhc2hfaGVhZCA9IHMucHJldltzLnN0cnN0YXJ0ICYgcy53X21hc2tdID0gcy5oZWFkW3MuaW5zX2hdO1xuICAgICAgICAgIHMuaGVhZFtzLmluc19oXSA9IHMuc3Ryc3RhcnQ7XG4gICAgICAgICAgLyoqKi9cbiAgICAgICAgfVxuICAgICAgfSB3aGlsZSAoLS1zLnByZXZfbGVuZ3RoICE9PSAwKTtcbiAgICAgIHMubWF0Y2hfYXZhaWxhYmxlID0gMDtcbiAgICAgIHMubWF0Y2hfbGVuZ3RoID0gTUlOX01BVENILTE7XG4gICAgICBzLnN0cnN0YXJ0Kys7XG5cbiAgICAgIGlmIChiZmx1c2gpIHtcbiAgICAgICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgICAgICBmbHVzaF9ibG9ja19vbmx5KHMsIGZhbHNlKTtcbiAgICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgICB9XG4gICAgICAgIC8qKiovXG4gICAgICB9XG5cbiAgICB9IGVsc2UgaWYgKHMubWF0Y2hfYXZhaWxhYmxlKSB7XG4gICAgICAvKiBJZiB0aGVyZSB3YXMgbm8gbWF0Y2ggYXQgdGhlIHByZXZpb3VzIHBvc2l0aW9uLCBvdXRwdXQgYVxuICAgICAgICogc2luZ2xlIGxpdGVyYWwuIElmIHRoZXJlIHdhcyBhIG1hdGNoIGJ1dCB0aGUgY3VycmVudCBtYXRjaFxuICAgICAgICogaXMgbG9uZ2VyLCB0cnVuY2F0ZSB0aGUgcHJldmlvdXMgbWF0Y2ggdG8gYSBzaW5nbGUgbGl0ZXJhbC5cbiAgICAgICAqL1xuICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsXCIlY1wiLCBzLT53aW5kb3dbcy0+c3Ryc3RhcnQtMV0pKTtcbiAgICAgIC8qKiogX3RyX3RhbGx5X2xpdChzLCBzLndpbmRvd1tzLnN0cnN0YXJ0LTFdLCBiZmx1c2gpOyAqKiovXG4gICAgICBiZmx1c2ggPSB0cmVlcy5fdHJfdGFsbHkocywgMCwgcy53aW5kb3dbcy5zdHJzdGFydC0xXSk7XG5cbiAgICAgIGlmIChiZmx1c2gpIHtcbiAgICAgICAgLyoqKiBGTFVTSF9CTE9DS19PTkxZKHMsIDApICoqKi9cbiAgICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICAgIC8qKiovXG4gICAgICB9XG4gICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgICBzLmxvb2thaGVhZC0tO1xuICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLyogVGhlcmUgaXMgbm8gcHJldmlvdXMgbWF0Y2ggdG8gY29tcGFyZSB3aXRoLCB3YWl0IGZvclxuICAgICAgICogdGhlIG5leHQgc3RlcCB0byBkZWNpZGUuXG4gICAgICAgKi9cbiAgICAgIHMubWF0Y2hfYXZhaWxhYmxlID0gMTtcbiAgICAgIHMuc3Ryc3RhcnQrKztcbiAgICAgIHMubG9va2FoZWFkLS07XG4gICAgfVxuICB9XG4gIC8vQXNzZXJ0IChmbHVzaCAhPSBaX05PX0ZMVVNILCBcIm5vIGZsdXNoP1wiKTtcbiAgaWYgKHMubWF0Y2hfYXZhaWxhYmxlKSB7XG4gICAgLy9UcmFjZXZ2KChzdGRlcnIsXCIlY1wiLCBzLT53aW5kb3dbcy0+c3Ryc3RhcnQtMV0pKTtcbiAgICAvKioqIF90cl90YWxseV9saXQocywgcy53aW5kb3dbcy5zdHJzdGFydC0xXSwgYmZsdXNoKTsgKioqL1xuICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCAwLCBzLndpbmRvd1tzLnN0cnN0YXJ0LTFdKTtcblxuICAgIHMubWF0Y2hfYXZhaWxhYmxlID0gMDtcbiAgfVxuICBzLmluc2VydCA9IHMuc3Ryc3RhcnQgPCBNSU5fTUFUQ0gtMSA/IHMuc3Ryc3RhcnQgOiBNSU5fTUFUQ0gtMTtcbiAgaWYgKGZsdXNoID09PSBaX0ZJTklTSCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMSk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIHRydWUpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfRklOSVNIX1NUQVJURUQ7XG4gICAgfVxuICAgIC8qKiovXG4gICAgcmV0dXJuIEJTX0ZJTklTSF9ET05FO1xuICB9XG4gIGlmIChzLmxhc3RfbGl0KSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgIH1cbiAgICAvKioqL1xuICB9XG5cbiAgcmV0dXJuIEJTX0JMT0NLX0RPTkU7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBGb3IgWl9STEUsIHNpbXBseSBsb29rIGZvciBydW5zIG9mIGJ5dGVzLCBnZW5lcmF0ZSBtYXRjaGVzIG9ubHkgb2YgZGlzdGFuY2VcbiAqIG9uZS4gIERvIG5vdCBtYWludGFpbiBhIGhhc2ggdGFibGUuICAoSXQgd2lsbCBiZSByZWdlbmVyYXRlZCBpZiB0aGlzIHJ1biBvZlxuICogZGVmbGF0ZSBzd2l0Y2hlcyBhd2F5IGZyb20gWl9STEUuKVxuICovXG5mdW5jdGlvbiBkZWZsYXRlX3JsZShzLCBmbHVzaCkge1xuICB2YXIgYmZsdXNoOyAgICAgICAgICAgIC8qIHNldCBpZiBjdXJyZW50IGJsb2NrIG11c3QgYmUgZmx1c2hlZCAqL1xuICB2YXIgcHJldjsgICAgICAgICAgICAgIC8qIGJ5dGUgYXQgZGlzdGFuY2Ugb25lIHRvIG1hdGNoICovXG4gIHZhciBzY2FuLCBzdHJlbmQ7ICAgICAgLyogc2NhbiBnb2VzIHVwIHRvIHN0cmVuZCBmb3IgbGVuZ3RoIG9mIHJ1biAqL1xuXG4gIHZhciBfd2luID0gcy53aW5kb3c7XG5cbiAgZm9yICg7Oykge1xuICAgIC8qIE1ha2Ugc3VyZSB0aGF0IHdlIGFsd2F5cyBoYXZlIGVub3VnaCBsb29rYWhlYWQsIGV4Y2VwdFxuICAgICAqIGF0IHRoZSBlbmQgb2YgdGhlIGlucHV0IGZpbGUuIFdlIG5lZWQgTUFYX01BVENIIGJ5dGVzXG4gICAgICogZm9yIHRoZSBsb25nZXN0IHJ1biwgcGx1cyBvbmUgZm9yIHRoZSB1bnJvbGxlZCBsb29wLlxuICAgICAqL1xuICAgIGlmIChzLmxvb2thaGVhZCA8PSBNQVhfTUFUQ0gpIHtcbiAgICAgIGZpbGxfd2luZG93KHMpO1xuICAgICAgaWYgKHMubG9va2FoZWFkIDw9IE1BWF9NQVRDSCAmJiBmbHVzaCA9PT0gWl9OT19GTFVTSCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgaWYgKHMubG9va2FoZWFkID09PSAwKSB7IGJyZWFrOyB9IC8qIGZsdXNoIHRoZSBjdXJyZW50IGJsb2NrICovXG4gICAgfVxuXG4gICAgLyogU2VlIGhvdyBtYW55IHRpbWVzIHRoZSBwcmV2aW91cyBieXRlIHJlcGVhdHMgKi9cbiAgICBzLm1hdGNoX2xlbmd0aCA9IDA7XG4gICAgaWYgKHMubG9va2FoZWFkID49IE1JTl9NQVRDSCAmJiBzLnN0cnN0YXJ0ID4gMCkge1xuICAgICAgc2NhbiA9IHMuc3Ryc3RhcnQgLSAxO1xuICAgICAgcHJldiA9IF93aW5bc2Nhbl07XG4gICAgICBpZiAocHJldiA9PT0gX3dpblsrK3NjYW5dICYmIHByZXYgPT09IF93aW5bKytzY2FuXSAmJiBwcmV2ID09PSBfd2luWysrc2Nhbl0pIHtcbiAgICAgICAgc3RyZW5kID0gcy5zdHJzdGFydCArIE1BWF9NQVRDSDtcbiAgICAgICAgZG8ge1xuICAgICAgICAgIC8qanNoaW50IG5vZW1wdHk6ZmFsc2UqL1xuICAgICAgICB9IHdoaWxlIChwcmV2ID09PSBfd2luWysrc2Nhbl0gJiYgcHJldiA9PT0gX3dpblsrK3NjYW5dICYmXG4gICAgICAgICAgICAgICAgIHByZXYgPT09IF93aW5bKytzY2FuXSAmJiBwcmV2ID09PSBfd2luWysrc2Nhbl0gJiZcbiAgICAgICAgICAgICAgICAgcHJldiA9PT0gX3dpblsrK3NjYW5dICYmIHByZXYgPT09IF93aW5bKytzY2FuXSAmJlxuICAgICAgICAgICAgICAgICBwcmV2ID09PSBfd2luWysrc2Nhbl0gJiYgcHJldiA9PT0gX3dpblsrK3NjYW5dICYmXG4gICAgICAgICAgICAgICAgIHNjYW4gPCBzdHJlbmQpO1xuICAgICAgICBzLm1hdGNoX2xlbmd0aCA9IE1BWF9NQVRDSCAtIChzdHJlbmQgLSBzY2FuKTtcbiAgICAgICAgaWYgKHMubWF0Y2hfbGVuZ3RoID4gcy5sb29rYWhlYWQpIHtcbiAgICAgICAgICBzLm1hdGNoX2xlbmd0aCA9IHMubG9va2FoZWFkO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvL0Fzc2VydChzY2FuIDw9IHMtPndpbmRvdysodUludCkocy0+d2luZG93X3NpemUtMSksIFwid2lsZCBzY2FuXCIpO1xuICAgIH1cblxuICAgIC8qIEVtaXQgbWF0Y2ggaWYgaGF2ZSBydW4gb2YgTUlOX01BVENIIG9yIGxvbmdlciwgZWxzZSBlbWl0IGxpdGVyYWwgKi9cbiAgICBpZiAocy5tYXRjaF9sZW5ndGggPj0gTUlOX01BVENIKSB7XG4gICAgICAvL2NoZWNrX21hdGNoKHMsIHMuc3Ryc3RhcnQsIHMuc3Ryc3RhcnQgLSAxLCBzLm1hdGNoX2xlbmd0aCk7XG5cbiAgICAgIC8qKiogX3RyX3RhbGx5X2Rpc3QocywgMSwgcy5tYXRjaF9sZW5ndGggLSBNSU5fTUFUQ0gsIGJmbHVzaCk7ICoqKi9cbiAgICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCAxLCBzLm1hdGNoX2xlbmd0aCAtIE1JTl9NQVRDSCk7XG5cbiAgICAgIHMubG9va2FoZWFkIC09IHMubWF0Y2hfbGVuZ3RoO1xuICAgICAgcy5zdHJzdGFydCArPSBzLm1hdGNoX2xlbmd0aDtcbiAgICAgIHMubWF0Y2hfbGVuZ3RoID0gMDtcbiAgICB9IGVsc2Uge1xuICAgICAgLyogTm8gbWF0Y2gsIG91dHB1dCBhIGxpdGVyYWwgYnl0ZSAqL1xuICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsXCIlY1wiLCBzLT53aW5kb3dbcy0+c3Ryc3RhcnRdKSk7XG4gICAgICAvKioqIF90cl90YWxseV9saXQocywgcy53aW5kb3dbcy5zdHJzdGFydF0sIGJmbHVzaCk7ICoqKi9cbiAgICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCAwLCBzLndpbmRvd1tzLnN0cnN0YXJ0XSk7XG5cbiAgICAgIHMubG9va2FoZWFkLS07XG4gICAgICBzLnN0cnN0YXJ0Kys7XG4gICAgfVxuICAgIGlmIChiZmx1c2gpIHtcbiAgICAgIC8qKiogRkxVU0hfQkxPQ0socywgMCk7ICoqKi9cbiAgICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJTX05FRURfTU9SRTtcbiAgICAgIH1cbiAgICAgIC8qKiovXG4gICAgfVxuICB9XG4gIHMuaW5zZXJ0ID0gMDtcbiAgaWYgKGZsdXNoID09PSBaX0ZJTklTSCkge1xuICAgIC8qKiogRkxVU0hfQkxPQ0socywgMSk7ICoqKi9cbiAgICBmbHVzaF9ibG9ja19vbmx5KHMsIHRydWUpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfRklOSVNIX1NUQVJURUQ7XG4gICAgfVxuICAgIC8qKiovXG4gICAgcmV0dXJuIEJTX0ZJTklTSF9ET05FO1xuICB9XG4gIGlmIChzLmxhc3RfbGl0KSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgZmFsc2UpO1xuICAgIGlmIChzLnN0cm0uYXZhaWxfb3V0ID09PSAwKSB7XG4gICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgIH1cbiAgICAvKioqL1xuICB9XG4gIHJldHVybiBCU19CTE9DS19ET05FO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEZvciBaX0hVRkZNQU5fT05MWSwgZG8gbm90IGxvb2sgZm9yIG1hdGNoZXMuICBEbyBub3QgbWFpbnRhaW4gYSBoYXNoIHRhYmxlLlxuICogKEl0IHdpbGwgYmUgcmVnZW5lcmF0ZWQgaWYgdGhpcyBydW4gb2YgZGVmbGF0ZSBzd2l0Y2hlcyBhd2F5IGZyb20gSHVmZm1hbi4pXG4gKi9cbmZ1bmN0aW9uIGRlZmxhdGVfaHVmZihzLCBmbHVzaCkge1xuICB2YXIgYmZsdXNoOyAgICAgICAgICAgICAvKiBzZXQgaWYgY3VycmVudCBibG9jayBtdXN0IGJlIGZsdXNoZWQgKi9cblxuICBmb3IgKDs7KSB7XG4gICAgLyogTWFrZSBzdXJlIHRoYXQgd2UgaGF2ZSBhIGxpdGVyYWwgdG8gd3JpdGUuICovXG4gICAgaWYgKHMubG9va2FoZWFkID09PSAwKSB7XG4gICAgICBmaWxsX3dpbmRvdyhzKTtcbiAgICAgIGlmIChzLmxvb2thaGVhZCA9PT0gMCkge1xuICAgICAgICBpZiAoZmx1c2ggPT09IFpfTk9fRkxVU0gpIHtcbiAgICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrOyAgICAgIC8qIGZsdXNoIHRoZSBjdXJyZW50IGJsb2NrICovXG4gICAgICB9XG4gICAgfVxuXG4gICAgLyogT3V0cHV0IGEgbGl0ZXJhbCBieXRlICovXG4gICAgcy5tYXRjaF9sZW5ndGggPSAwO1xuICAgIC8vVHJhY2V2digoc3RkZXJyLFwiJWNcIiwgcy0+d2luZG93W3MtPnN0cnN0YXJ0XSkpO1xuICAgIC8qKiogX3RyX3RhbGx5X2xpdChzLCBzLndpbmRvd1tzLnN0cnN0YXJ0XSwgYmZsdXNoKTsgKioqL1xuICAgIGJmbHVzaCA9IHRyZWVzLl90cl90YWxseShzLCAwLCBzLndpbmRvd1tzLnN0cnN0YXJ0XSk7XG4gICAgcy5sb29rYWhlYWQtLTtcbiAgICBzLnN0cnN0YXJ0Kys7XG4gICAgaWYgKGJmbHVzaCkge1xuICAgICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAwKTsgKioqL1xuICAgICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgICBpZiAocy5zdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gQlNfTkVFRF9NT1JFO1xuICAgICAgfVxuICAgICAgLyoqKi9cbiAgICB9XG4gIH1cbiAgcy5pbnNlcnQgPSAwO1xuICBpZiAoZmx1c2ggPT09IFpfRklOSVNIKSB7XG4gICAgLyoqKiBGTFVTSF9CTE9DSyhzLCAxKTsgKioqL1xuICAgIGZsdXNoX2Jsb2NrX29ubHkocywgdHJ1ZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19GSU5JU0hfU1RBUlRFRDtcbiAgICB9XG4gICAgLyoqKi9cbiAgICByZXR1cm4gQlNfRklOSVNIX0RPTkU7XG4gIH1cbiAgaWYgKHMubGFzdF9saXQpIHtcbiAgICAvKioqIEZMVVNIX0JMT0NLKHMsIDApOyAqKiovXG4gICAgZmx1c2hfYmxvY2tfb25seShzLCBmYWxzZSk7XG4gICAgaWYgKHMuc3RybS5hdmFpbF9vdXQgPT09IDApIHtcbiAgICAgIHJldHVybiBCU19ORUVEX01PUkU7XG4gICAgfVxuICAgIC8qKiovXG4gIH1cbiAgcmV0dXJuIEJTX0JMT0NLX0RPTkU7XG59XG5cbi8qIFZhbHVlcyBmb3IgbWF4X2xhenlfbWF0Y2gsIGdvb2RfbWF0Y2ggYW5kIG1heF9jaGFpbl9sZW5ndGgsIGRlcGVuZGluZyBvblxuICogdGhlIGRlc2lyZWQgcGFjayBsZXZlbCAoMC4uOSkuIFRoZSB2YWx1ZXMgZ2l2ZW4gYmVsb3cgaGF2ZSBiZWVuIHR1bmVkIHRvXG4gKiBleGNsdWRlIHdvcnN0IGNhc2UgcGVyZm9ybWFuY2UgZm9yIHBhdGhvbG9naWNhbCBmaWxlcy4gQmV0dGVyIHZhbHVlcyBtYXkgYmVcbiAqIGZvdW5kIGZvciBzcGVjaWZpYyBmaWxlcy5cbiAqL1xudmFyIENvbmZpZyA9IGZ1bmN0aW9uIChnb29kX2xlbmd0aCwgbWF4X2xhenksIG5pY2VfbGVuZ3RoLCBtYXhfY2hhaW4sIGZ1bmMpIHtcbiAgdGhpcy5nb29kX2xlbmd0aCA9IGdvb2RfbGVuZ3RoO1xuICB0aGlzLm1heF9sYXp5ID0gbWF4X2xhenk7XG4gIHRoaXMubmljZV9sZW5ndGggPSBuaWNlX2xlbmd0aDtcbiAgdGhpcy5tYXhfY2hhaW4gPSBtYXhfY2hhaW47XG4gIHRoaXMuZnVuYyA9IGZ1bmM7XG59O1xuXG52YXIgY29uZmlndXJhdGlvbl90YWJsZTtcblxuY29uZmlndXJhdGlvbl90YWJsZSA9IFtcbiAgLyogICAgICBnb29kIGxhenkgbmljZSBjaGFpbiAqL1xuICBuZXcgQ29uZmlnKDAsIDAsIDAsIDAsIGRlZmxhdGVfc3RvcmVkKSwgICAgICAgICAgLyogMCBzdG9yZSBvbmx5ICovXG4gIG5ldyBDb25maWcoNCwgNCwgOCwgNCwgZGVmbGF0ZV9mYXN0KSwgICAgICAgICAgICAvKiAxIG1heCBzcGVlZCwgbm8gbGF6eSBtYXRjaGVzICovXG4gIG5ldyBDb25maWcoNCwgNSwgMTYsIDgsIGRlZmxhdGVfZmFzdCksICAgICAgICAgICAvKiAyICovXG4gIG5ldyBDb25maWcoNCwgNiwgMzIsIDMyLCBkZWZsYXRlX2Zhc3QpLCAgICAgICAgICAvKiAzICovXG5cbiAgbmV3IENvbmZpZyg0LCA0LCAxNiwgMTYsIGRlZmxhdGVfc2xvdyksICAgICAgICAgIC8qIDQgbGF6eSBtYXRjaGVzICovXG4gIG5ldyBDb25maWcoOCwgMTYsIDMyLCAzMiwgZGVmbGF0ZV9zbG93KSwgICAgICAgICAvKiA1ICovXG4gIG5ldyBDb25maWcoOCwgMTYsIDEyOCwgMTI4LCBkZWZsYXRlX3Nsb3cpLCAgICAgICAvKiA2ICovXG4gIG5ldyBDb25maWcoOCwgMzIsIDEyOCwgMjU2LCBkZWZsYXRlX3Nsb3cpLCAgICAgICAvKiA3ICovXG4gIG5ldyBDb25maWcoMzIsIDEyOCwgMjU4LCAxMDI0LCBkZWZsYXRlX3Nsb3cpLCAgICAvKiA4ICovXG4gIG5ldyBDb25maWcoMzIsIDI1OCwgMjU4LCA0MDk2LCBkZWZsYXRlX3Nsb3cpICAgICAvKiA5IG1heCBjb21wcmVzc2lvbiAqL1xuXTtcblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEluaXRpYWxpemUgdGhlIFwibG9uZ2VzdCBtYXRjaFwiIHJvdXRpbmVzIGZvciBhIG5ldyB6bGliIHN0cmVhbVxuICovXG5mdW5jdGlvbiBsbV9pbml0KHMpIHtcbiAgcy53aW5kb3dfc2l6ZSA9IDIgKiBzLndfc2l6ZTtcblxuICAvKioqIENMRUFSX0hBU0gocyk7ICoqKi9cbiAgemVybyhzLmhlYWQpOyAvLyBGaWxsIHdpdGggTklMICg9IDApO1xuXG4gIC8qIFNldCB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIHBhcmFtZXRlcnM6XG4gICAqL1xuICBzLm1heF9sYXp5X21hdGNoID0gY29uZmlndXJhdGlvbl90YWJsZVtzLmxldmVsXS5tYXhfbGF6eTtcbiAgcy5nb29kX21hdGNoID0gY29uZmlndXJhdGlvbl90YWJsZVtzLmxldmVsXS5nb29kX2xlbmd0aDtcbiAgcy5uaWNlX21hdGNoID0gY29uZmlndXJhdGlvbl90YWJsZVtzLmxldmVsXS5uaWNlX2xlbmd0aDtcbiAgcy5tYXhfY2hhaW5fbGVuZ3RoID0gY29uZmlndXJhdGlvbl90YWJsZVtzLmxldmVsXS5tYXhfY2hhaW47XG5cbiAgcy5zdHJzdGFydCA9IDA7XG4gIHMuYmxvY2tfc3RhcnQgPSAwO1xuICBzLmxvb2thaGVhZCA9IDA7XG4gIHMuaW5zZXJ0ID0gMDtcbiAgcy5tYXRjaF9sZW5ndGggPSBzLnByZXZfbGVuZ3RoID0gTUlOX01BVENIIC0gMTtcbiAgcy5tYXRjaF9hdmFpbGFibGUgPSAwO1xuICBzLmluc19oID0gMDtcbn1cblxuXG5mdW5jdGlvbiBEZWZsYXRlU3RhdGUoKSB7XG4gIHRoaXMuc3RybSA9IG51bGw7ICAgICAgICAgICAgLyogcG9pbnRlciBiYWNrIHRvIHRoaXMgemxpYiBzdHJlYW0gKi9cbiAgdGhpcy5zdGF0dXMgPSAwOyAgICAgICAgICAgIC8qIGFzIHRoZSBuYW1lIGltcGxpZXMgKi9cbiAgdGhpcy5wZW5kaW5nX2J1ZiA9IG51bGw7ICAgICAgLyogb3V0cHV0IHN0aWxsIHBlbmRpbmcgKi9cbiAgdGhpcy5wZW5kaW5nX2J1Zl9zaXplID0gMDsgIC8qIHNpemUgb2YgcGVuZGluZ19idWYgKi9cbiAgdGhpcy5wZW5kaW5nX291dCA9IDA7ICAgICAgIC8qIG5leHQgcGVuZGluZyBieXRlIHRvIG91dHB1dCB0byB0aGUgc3RyZWFtICovXG4gIHRoaXMucGVuZGluZyA9IDA7ICAgICAgICAgICAvKiBuYiBvZiBieXRlcyBpbiB0aGUgcGVuZGluZyBidWZmZXIgKi9cbiAgdGhpcy53cmFwID0gMDsgICAgICAgICAgICAgIC8qIGJpdCAwIHRydWUgZm9yIHpsaWIsIGJpdCAxIHRydWUgZm9yIGd6aXAgKi9cbiAgdGhpcy5nemhlYWQgPSBudWxsOyAgICAgICAgIC8qIGd6aXAgaGVhZGVyIGluZm9ybWF0aW9uIHRvIHdyaXRlICovXG4gIHRoaXMuZ3ppbmRleCA9IDA7ICAgICAgICAgICAvKiB3aGVyZSBpbiBleHRyYSwgbmFtZSwgb3IgY29tbWVudCAqL1xuICB0aGlzLm1ldGhvZCA9IFpfREVGTEFURUQ7IC8qIGNhbiBvbmx5IGJlIERFRkxBVEVEICovXG4gIHRoaXMubGFzdF9mbHVzaCA9IC0xOyAgIC8qIHZhbHVlIG9mIGZsdXNoIHBhcmFtIGZvciBwcmV2aW91cyBkZWZsYXRlIGNhbGwgKi9cblxuICB0aGlzLndfc2l6ZSA9IDA7ICAvKiBMWjc3IHdpbmRvdyBzaXplICgzMksgYnkgZGVmYXVsdCkgKi9cbiAgdGhpcy53X2JpdHMgPSAwOyAgLyogbG9nMih3X3NpemUpICAoOC4uMTYpICovXG4gIHRoaXMud19tYXNrID0gMDsgIC8qIHdfc2l6ZSAtIDEgKi9cblxuICB0aGlzLndpbmRvdyA9IG51bGw7XG4gIC8qIFNsaWRpbmcgd2luZG93LiBJbnB1dCBieXRlcyBhcmUgcmVhZCBpbnRvIHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgd2luZG93LFxuICAgKiBhbmQgbW92ZSB0byB0aGUgZmlyc3QgaGFsZiBsYXRlciB0byBrZWVwIGEgZGljdGlvbmFyeSBvZiBhdCBsZWFzdCB3U2l6ZVxuICAgKiBieXRlcy4gV2l0aCB0aGlzIG9yZ2FuaXphdGlvbiwgbWF0Y2hlcyBhcmUgbGltaXRlZCB0byBhIGRpc3RhbmNlIG9mXG4gICAqIHdTaXplLU1BWF9NQVRDSCBieXRlcywgYnV0IHRoaXMgZW5zdXJlcyB0aGF0IElPIGlzIGFsd2F5c1xuICAgKiBwZXJmb3JtZWQgd2l0aCBhIGxlbmd0aCBtdWx0aXBsZSBvZiB0aGUgYmxvY2sgc2l6ZS5cbiAgICovXG5cbiAgdGhpcy53aW5kb3dfc2l6ZSA9IDA7XG4gIC8qIEFjdHVhbCBzaXplIG9mIHdpbmRvdzogMip3U2l6ZSwgZXhjZXB0IHdoZW4gdGhlIHVzZXIgaW5wdXQgYnVmZmVyXG4gICAqIGlzIGRpcmVjdGx5IHVzZWQgYXMgc2xpZGluZyB3aW5kb3cuXG4gICAqL1xuXG4gIHRoaXMucHJldiA9IG51bGw7XG4gIC8qIExpbmsgdG8gb2xkZXIgc3RyaW5nIHdpdGggc2FtZSBoYXNoIGluZGV4LiBUbyBsaW1pdCB0aGUgc2l6ZSBvZiB0aGlzXG4gICAqIGFycmF5IHRvIDY0SywgdGhpcyBsaW5rIGlzIG1haW50YWluZWQgb25seSBmb3IgdGhlIGxhc3QgMzJLIHN0cmluZ3MuXG4gICAqIEFuIGluZGV4IGluIHRoaXMgYXJyYXkgaXMgdGh1cyBhIHdpbmRvdyBpbmRleCBtb2R1bG8gMzJLLlxuICAgKi9cblxuICB0aGlzLmhlYWQgPSBudWxsOyAgIC8qIEhlYWRzIG9mIHRoZSBoYXNoIGNoYWlucyBvciBOSUwuICovXG5cbiAgdGhpcy5pbnNfaCA9IDA7ICAgICAgIC8qIGhhc2ggaW5kZXggb2Ygc3RyaW5nIHRvIGJlIGluc2VydGVkICovXG4gIHRoaXMuaGFzaF9zaXplID0gMDsgICAvKiBudW1iZXIgb2YgZWxlbWVudHMgaW4gaGFzaCB0YWJsZSAqL1xuICB0aGlzLmhhc2hfYml0cyA9IDA7ICAgLyogbG9nMihoYXNoX3NpemUpICovXG4gIHRoaXMuaGFzaF9tYXNrID0gMDsgICAvKiBoYXNoX3NpemUtMSAqL1xuXG4gIHRoaXMuaGFzaF9zaGlmdCA9IDA7XG4gIC8qIE51bWJlciBvZiBiaXRzIGJ5IHdoaWNoIGluc19oIG11c3QgYmUgc2hpZnRlZCBhdCBlYWNoIGlucHV0XG4gICAqIHN0ZXAuIEl0IG11c3QgYmUgc3VjaCB0aGF0IGFmdGVyIE1JTl9NQVRDSCBzdGVwcywgdGhlIG9sZGVzdFxuICAgKiBieXRlIG5vIGxvbmdlciB0YWtlcyBwYXJ0IGluIHRoZSBoYXNoIGtleSwgdGhhdCBpczpcbiAgICogICBoYXNoX3NoaWZ0ICogTUlOX01BVENIID49IGhhc2hfYml0c1xuICAgKi9cblxuICB0aGlzLmJsb2NrX3N0YXJ0ID0gMDtcbiAgLyogV2luZG93IHBvc2l0aW9uIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGN1cnJlbnQgb3V0cHV0IGJsb2NrLiBHZXRzXG4gICAqIG5lZ2F0aXZlIHdoZW4gdGhlIHdpbmRvdyBpcyBtb3ZlZCBiYWNrd2FyZHMuXG4gICAqL1xuXG4gIHRoaXMubWF0Y2hfbGVuZ3RoID0gMDsgICAgICAvKiBsZW5ndGggb2YgYmVzdCBtYXRjaCAqL1xuICB0aGlzLnByZXZfbWF0Y2ggPSAwOyAgICAgICAgLyogcHJldmlvdXMgbWF0Y2ggKi9cbiAgdGhpcy5tYXRjaF9hdmFpbGFibGUgPSAwOyAgIC8qIHNldCBpZiBwcmV2aW91cyBtYXRjaCBleGlzdHMgKi9cbiAgdGhpcy5zdHJzdGFydCA9IDA7ICAgICAgICAgIC8qIHN0YXJ0IG9mIHN0cmluZyB0byBpbnNlcnQgKi9cbiAgdGhpcy5tYXRjaF9zdGFydCA9IDA7ICAgICAgIC8qIHN0YXJ0IG9mIG1hdGNoaW5nIHN0cmluZyAqL1xuICB0aGlzLmxvb2thaGVhZCA9IDA7ICAgICAgICAgLyogbnVtYmVyIG9mIHZhbGlkIGJ5dGVzIGFoZWFkIGluIHdpbmRvdyAqL1xuXG4gIHRoaXMucHJldl9sZW5ndGggPSAwO1xuICAvKiBMZW5ndGggb2YgdGhlIGJlc3QgbWF0Y2ggYXQgcHJldmlvdXMgc3RlcC4gTWF0Y2hlcyBub3QgZ3JlYXRlciB0aGFuIHRoaXNcbiAgICogYXJlIGRpc2NhcmRlZC4gVGhpcyBpcyB1c2VkIGluIHRoZSBsYXp5IG1hdGNoIGV2YWx1YXRpb24uXG4gICAqL1xuXG4gIHRoaXMubWF4X2NoYWluX2xlbmd0aCA9IDA7XG4gIC8qIFRvIHNwZWVkIHVwIGRlZmxhdGlvbiwgaGFzaCBjaGFpbnMgYXJlIG5ldmVyIHNlYXJjaGVkIGJleW9uZCB0aGlzXG4gICAqIGxlbmd0aC4gIEEgaGlnaGVyIGxpbWl0IGltcHJvdmVzIGNvbXByZXNzaW9uIHJhdGlvIGJ1dCBkZWdyYWRlcyB0aGVcbiAgICogc3BlZWQuXG4gICAqL1xuXG4gIHRoaXMubWF4X2xhenlfbWF0Y2ggPSAwO1xuICAvKiBBdHRlbXB0IHRvIGZpbmQgYSBiZXR0ZXIgbWF0Y2ggb25seSB3aGVuIHRoZSBjdXJyZW50IG1hdGNoIGlzIHN0cmljdGx5XG4gICAqIHNtYWxsZXIgdGhhbiB0aGlzIHZhbHVlLiBUaGlzIG1lY2hhbmlzbSBpcyB1c2VkIG9ubHkgZm9yIGNvbXByZXNzaW9uXG4gICAqIGxldmVscyA+PSA0LlxuICAgKi9cbiAgLy8gVGhhdCdzIGFsaWFzIHRvIG1heF9sYXp5X21hdGNoLCBkb24ndCB1c2UgZGlyZWN0bHlcbiAgLy90aGlzLm1heF9pbnNlcnRfbGVuZ3RoID0gMDtcbiAgLyogSW5zZXJ0IG5ldyBzdHJpbmdzIGluIHRoZSBoYXNoIHRhYmxlIG9ubHkgaWYgdGhlIG1hdGNoIGxlbmd0aCBpcyBub3RcbiAgICogZ3JlYXRlciB0aGFuIHRoaXMgbGVuZ3RoLiBUaGlzIHNhdmVzIHRpbWUgYnV0IGRlZ3JhZGVzIGNvbXByZXNzaW9uLlxuICAgKiBtYXhfaW5zZXJ0X2xlbmd0aCBpcyB1c2VkIG9ubHkgZm9yIGNvbXByZXNzaW9uIGxldmVscyA8PSAzLlxuICAgKi9cblxuICB0aGlzLmxldmVsID0gMDsgICAgIC8qIGNvbXByZXNzaW9uIGxldmVsICgxLi45KSAqL1xuICB0aGlzLnN0cmF0ZWd5ID0gMDsgIC8qIGZhdm9yIG9yIGZvcmNlIEh1ZmZtYW4gY29kaW5nKi9cblxuICB0aGlzLmdvb2RfbWF0Y2ggPSAwO1xuICAvKiBVc2UgYSBmYXN0ZXIgc2VhcmNoIHdoZW4gdGhlIHByZXZpb3VzIG1hdGNoIGlzIGxvbmdlciB0aGFuIHRoaXMgKi9cblxuICB0aGlzLm5pY2VfbWF0Y2ggPSAwOyAvKiBTdG9wIHNlYXJjaGluZyB3aGVuIGN1cnJlbnQgbWF0Y2ggZXhjZWVkcyB0aGlzICovXG5cbiAgICAgICAgICAgICAgLyogdXNlZCBieSB0cmVlcy5jOiAqL1xuXG4gIC8qIERpZG4ndCB1c2UgY3RfZGF0YSB0eXBlZGVmIGJlbG93IHRvIHN1cHByZXNzIGNvbXBpbGVyIHdhcm5pbmcgKi9cblxuICAvLyBzdHJ1Y3QgY3RfZGF0YV9zIGR5bl9sdHJlZVtIRUFQX1NJWkVdOyAgIC8qIGxpdGVyYWwgYW5kIGxlbmd0aCB0cmVlICovXG4gIC8vIHN0cnVjdCBjdF9kYXRhX3MgZHluX2R0cmVlWzIqRF9DT0RFUysxXTsgLyogZGlzdGFuY2UgdHJlZSAqL1xuICAvLyBzdHJ1Y3QgY3RfZGF0YV9zIGJsX3RyZWVbMipCTF9DT0RFUysxXTsgIC8qIEh1ZmZtYW4gdHJlZSBmb3IgYml0IGxlbmd0aHMgKi9cblxuICAvLyBVc2UgZmxhdCBhcnJheSBvZiBET1VCTEUgc2l6ZSwgd2l0aCBpbnRlcmxlYXZlZCBmYXRhLFxuICAvLyBiZWNhdXNlIEpTIGRvZXMgbm90IHN1cHBvcnQgZWZmZWN0aXZlXG4gIHRoaXMuZHluX2x0cmVlICA9IG5ldyB1dGlscy5CdWYxNihIRUFQX1NJWkUgKiAyKTtcbiAgdGhpcy5keW5fZHRyZWUgID0gbmV3IHV0aWxzLkJ1ZjE2KCgyKkRfQ09ERVMrMSkgKiAyKTtcbiAgdGhpcy5ibF90cmVlICAgID0gbmV3IHV0aWxzLkJ1ZjE2KCgyKkJMX0NPREVTKzEpICogMik7XG4gIHplcm8odGhpcy5keW5fbHRyZWUpO1xuICB6ZXJvKHRoaXMuZHluX2R0cmVlKTtcbiAgemVybyh0aGlzLmJsX3RyZWUpO1xuXG4gIHRoaXMubF9kZXNjICAgPSBudWxsOyAgICAgICAgIC8qIGRlc2MuIGZvciBsaXRlcmFsIHRyZWUgKi9cbiAgdGhpcy5kX2Rlc2MgICA9IG51bGw7ICAgICAgICAgLyogZGVzYy4gZm9yIGRpc3RhbmNlIHRyZWUgKi9cbiAgdGhpcy5ibF9kZXNjICA9IG51bGw7ICAgICAgICAgLyogZGVzYy4gZm9yIGJpdCBsZW5ndGggdHJlZSAqL1xuXG4gIC8vdXNoIGJsX2NvdW50W01BWF9CSVRTKzFdO1xuICB0aGlzLmJsX2NvdW50ID0gbmV3IHV0aWxzLkJ1ZjE2KE1BWF9CSVRTKzEpO1xuICAvKiBudW1iZXIgb2YgY29kZXMgYXQgZWFjaCBiaXQgbGVuZ3RoIGZvciBhbiBvcHRpbWFsIHRyZWUgKi9cblxuICAvL2ludCBoZWFwWzIqTF9DT0RFUysxXTsgICAgICAvKiBoZWFwIHVzZWQgdG8gYnVpbGQgdGhlIEh1ZmZtYW4gdHJlZXMgKi9cbiAgdGhpcy5oZWFwID0gbmV3IHV0aWxzLkJ1ZjE2KDIqTF9DT0RFUysxKTsgIC8qIGhlYXAgdXNlZCB0byBidWlsZCB0aGUgSHVmZm1hbiB0cmVlcyAqL1xuICB6ZXJvKHRoaXMuaGVhcCk7XG5cbiAgdGhpcy5oZWFwX2xlbiA9IDA7ICAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBoZWFwICovXG4gIHRoaXMuaGVhcF9tYXggPSAwOyAgICAgICAgICAgICAgIC8qIGVsZW1lbnQgb2YgbGFyZ2VzdCBmcmVxdWVuY3kgKi9cbiAgLyogVGhlIHNvbnMgb2YgaGVhcFtuXSBhcmUgaGVhcFsyKm5dIGFuZCBoZWFwWzIqbisxXS4gaGVhcFswXSBpcyBub3QgdXNlZC5cbiAgICogVGhlIHNhbWUgaGVhcCBhcnJheSBpcyB1c2VkIHRvIGJ1aWxkIGFsbCB0cmVlcy5cbiAgICovXG5cbiAgdGhpcy5kZXB0aCA9IG5ldyB1dGlscy5CdWYxNigyKkxfQ09ERVMrMSk7IC8vdWNoIGRlcHRoWzIqTF9DT0RFUysxXTtcbiAgemVybyh0aGlzLmRlcHRoKTtcbiAgLyogRGVwdGggb2YgZWFjaCBzdWJ0cmVlIHVzZWQgYXMgdGllIGJyZWFrZXIgZm9yIHRyZWVzIG9mIGVxdWFsIGZyZXF1ZW5jeVxuICAgKi9cblxuICB0aGlzLmxfYnVmID0gMDsgICAgICAgICAgLyogYnVmZmVyIGluZGV4IGZvciBsaXRlcmFscyBvciBsZW5ndGhzICovXG5cbiAgdGhpcy5saXRfYnVmc2l6ZSA9IDA7XG4gIC8qIFNpemUgb2YgbWF0Y2ggYnVmZmVyIGZvciBsaXRlcmFscy9sZW5ndGhzLiAgVGhlcmUgYXJlIDQgcmVhc29ucyBmb3JcbiAgICogbGltaXRpbmcgbGl0X2J1ZnNpemUgdG8gNjRLOlxuICAgKiAgIC0gZnJlcXVlbmNpZXMgY2FuIGJlIGtlcHQgaW4gMTYgYml0IGNvdW50ZXJzXG4gICAqICAgLSBpZiBjb21wcmVzc2lvbiBpcyBub3Qgc3VjY2Vzc2Z1bCBmb3IgdGhlIGZpcnN0IGJsb2NrLCBhbGwgaW5wdXRcbiAgICogICAgIGRhdGEgaXMgc3RpbGwgaW4gdGhlIHdpbmRvdyBzbyB3ZSBjYW4gc3RpbGwgZW1pdCBhIHN0b3JlZCBibG9jayBldmVuXG4gICAqICAgICB3aGVuIGlucHV0IGNvbWVzIGZyb20gc3RhbmRhcmQgaW5wdXQuICAoVGhpcyBjYW4gYWxzbyBiZSBkb25lIGZvclxuICAgKiAgICAgYWxsIGJsb2NrcyBpZiBsaXRfYnVmc2l6ZSBpcyBub3QgZ3JlYXRlciB0aGFuIDMySy4pXG4gICAqICAgLSBpZiBjb21wcmVzc2lvbiBpcyBub3Qgc3VjY2Vzc2Z1bCBmb3IgYSBmaWxlIHNtYWxsZXIgdGhhbiA2NEssIHdlIGNhblxuICAgKiAgICAgZXZlbiBlbWl0IGEgc3RvcmVkIGZpbGUgaW5zdGVhZCBvZiBhIHN0b3JlZCBibG9jayAoc2F2aW5nIDUgYnl0ZXMpLlxuICAgKiAgICAgVGhpcyBpcyBhcHBsaWNhYmxlIG9ubHkgZm9yIHppcCAobm90IGd6aXAgb3IgemxpYikuXG4gICAqICAgLSBjcmVhdGluZyBuZXcgSHVmZm1hbiB0cmVlcyBsZXNzIGZyZXF1ZW50bHkgbWF5IG5vdCBwcm92aWRlIGZhc3RcbiAgICogICAgIGFkYXB0YXRpb24gdG8gY2hhbmdlcyBpbiB0aGUgaW5wdXQgZGF0YSBzdGF0aXN0aWNzLiAoVGFrZSBmb3JcbiAgICogICAgIGV4YW1wbGUgYSBiaW5hcnkgZmlsZSB3aXRoIHBvb3JseSBjb21wcmVzc2libGUgY29kZSBmb2xsb3dlZCBieVxuICAgKiAgICAgYSBoaWdobHkgY29tcHJlc3NpYmxlIHN0cmluZyB0YWJsZS4pIFNtYWxsZXIgYnVmZmVyIHNpemVzIGdpdmVcbiAgICogICAgIGZhc3QgYWRhcHRhdGlvbiBidXQgaGF2ZSBvZiBjb3Vyc2UgdGhlIG92ZXJoZWFkIG9mIHRyYW5zbWl0dGluZ1xuICAgKiAgICAgdHJlZXMgbW9yZSBmcmVxdWVudGx5LlxuICAgKiAgIC0gSSBjYW4ndCBjb3VudCBhYm92ZSA0XG4gICAqL1xuXG4gIHRoaXMubGFzdF9saXQgPSAwOyAgICAgIC8qIHJ1bm5pbmcgaW5kZXggaW4gbF9idWYgKi9cblxuICB0aGlzLmRfYnVmID0gMDtcbiAgLyogQnVmZmVyIGluZGV4IGZvciBkaXN0YW5jZXMuIFRvIHNpbXBsaWZ5IHRoZSBjb2RlLCBkX2J1ZiBhbmQgbF9idWYgaGF2ZVxuICAgKiB0aGUgc2FtZSBudW1iZXIgb2YgZWxlbWVudHMuIFRvIHVzZSBkaWZmZXJlbnQgbGVuZ3RocywgYW4gZXh0cmEgZmxhZ1xuICAgKiBhcnJheSB3b3VsZCBiZSBuZWNlc3NhcnkuXG4gICAqL1xuXG4gIHRoaXMub3B0X2xlbiA9IDA7ICAgICAgIC8qIGJpdCBsZW5ndGggb2YgY3VycmVudCBibG9jayB3aXRoIG9wdGltYWwgdHJlZXMgKi9cbiAgdGhpcy5zdGF0aWNfbGVuID0gMDsgICAgLyogYml0IGxlbmd0aCBvZiBjdXJyZW50IGJsb2NrIHdpdGggc3RhdGljIHRyZWVzICovXG4gIHRoaXMubWF0Y2hlcyA9IDA7ICAgICAgIC8qIG51bWJlciBvZiBzdHJpbmcgbWF0Y2hlcyBpbiBjdXJyZW50IGJsb2NrICovXG4gIHRoaXMuaW5zZXJ0ID0gMDsgICAgICAgIC8qIGJ5dGVzIGF0IGVuZCBvZiB3aW5kb3cgbGVmdCB0byBpbnNlcnQgKi9cblxuXG4gIHRoaXMuYmlfYnVmID0gMDtcbiAgLyogT3V0cHV0IGJ1ZmZlci4gYml0cyBhcmUgaW5zZXJ0ZWQgc3RhcnRpbmcgYXQgdGhlIGJvdHRvbSAobGVhc3RcbiAgICogc2lnbmlmaWNhbnQgYml0cykuXG4gICAqL1xuICB0aGlzLmJpX3ZhbGlkID0gMDtcbiAgLyogTnVtYmVyIG9mIHZhbGlkIGJpdHMgaW4gYmlfYnVmLiAgQWxsIGJpdHMgYWJvdmUgdGhlIGxhc3QgdmFsaWQgYml0XG4gICAqIGFyZSBhbHdheXMgemVyby5cbiAgICovXG5cbiAgLy8gVXNlZCBmb3Igd2luZG93IG1lbW9yeSBpbml0LiBXZSBzYWZlbHkgaWdub3JlIGl0IGZvciBKUy4gVGhhdCBtYWtlc1xuICAvLyBzZW5zZSBvbmx5IGZvciBwb2ludGVycyBhbmQgbWVtb3J5IGNoZWNrIHRvb2xzLlxuICAvL3RoaXMuaGlnaF93YXRlciA9IDA7XG4gIC8qIEhpZ2ggd2F0ZXIgbWFyayBvZmZzZXQgaW4gd2luZG93IGZvciBpbml0aWFsaXplZCBieXRlcyAtLSBieXRlcyBhYm92ZVxuICAgKiB0aGlzIGFyZSBzZXQgdG8gemVybyBpbiBvcmRlciB0byBhdm9pZCBtZW1vcnkgY2hlY2sgd2FybmluZ3Mgd2hlblxuICAgKiBsb25nZXN0IG1hdGNoIHJvdXRpbmVzIGFjY2VzcyBieXRlcyBwYXN0IHRoZSBpbnB1dC4gIFRoaXMgaXMgdGhlblxuICAgKiB1cGRhdGVkIHRvIHRoZSBuZXcgaGlnaCB3YXRlciBtYXJrLlxuICAgKi9cbn1cblxuXG5mdW5jdGlvbiBkZWZsYXRlUmVzZXRLZWVwKHN0cm0pIHtcbiAgdmFyIHM7XG5cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlKSB7XG4gICAgcmV0dXJuIGVycihzdHJtLCBaX1NUUkVBTV9FUlJPUik7XG4gIH1cblxuICBzdHJtLnRvdGFsX2luID0gc3RybS50b3RhbF9vdXQgPSAwO1xuICBzdHJtLmRhdGFfdHlwZSA9IFpfVU5LTk9XTjtcblxuICBzID0gc3RybS5zdGF0ZTtcbiAgcy5wZW5kaW5nID0gMDtcbiAgcy5wZW5kaW5nX291dCA9IDA7XG5cbiAgaWYgKHMud3JhcCA8IDApIHtcbiAgICBzLndyYXAgPSAtcy53cmFwO1xuICAgIC8qIHdhcyBtYWRlIG5lZ2F0aXZlIGJ5IGRlZmxhdGUoLi4uLCBaX0ZJTklTSCk7ICovXG4gIH1cbiAgcy5zdGF0dXMgPSAocy53cmFwID8gSU5JVF9TVEFURSA6IEJVU1lfU1RBVEUpO1xuICBzdHJtLmFkbGVyID0gKHMud3JhcCA9PT0gMikgP1xuICAgIDAgIC8vIGNyYzMyKDAsIFpfTlVMTCwgMClcbiAgOlxuICAgIDE7IC8vIGFkbGVyMzIoMCwgWl9OVUxMLCAwKVxuICBzLmxhc3RfZmx1c2ggPSBaX05PX0ZMVVNIO1xuICB0cmVlcy5fdHJfaW5pdChzKTtcbiAgcmV0dXJuIFpfT0s7XG59XG5cblxuZnVuY3Rpb24gZGVmbGF0ZVJlc2V0KHN0cm0pIHtcbiAgdmFyIHJldCA9IGRlZmxhdGVSZXNldEtlZXAoc3RybSk7XG4gIGlmIChyZXQgPT09IFpfT0spIHtcbiAgICBsbV9pbml0KHN0cm0uc3RhdGUpO1xuICB9XG4gIHJldHVybiByZXQ7XG59XG5cblxuZnVuY3Rpb24gZGVmbGF0ZVNldEhlYWRlcihzdHJtLCBoZWFkKSB7XG4gIGlmICghc3RybSB8fCAhc3RybS5zdGF0ZSkgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cbiAgaWYgKHN0cm0uc3RhdGUud3JhcCAhPT0gMikgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cbiAgc3RybS5zdGF0ZS5nemhlYWQgPSBoZWFkO1xuICByZXR1cm4gWl9PSztcbn1cblxuXG5mdW5jdGlvbiBkZWZsYXRlSW5pdDIoc3RybSwgbGV2ZWwsIG1ldGhvZCwgd2luZG93Qml0cywgbWVtTGV2ZWwsIHN0cmF0ZWd5KSB7XG4gIGlmICghc3RybSkgeyAvLyA9PT0gWl9OVUxMXG4gICAgcmV0dXJuIFpfU1RSRUFNX0VSUk9SO1xuICB9XG4gIHZhciB3cmFwID0gMTtcblxuICBpZiAobGV2ZWwgPT09IFpfREVGQVVMVF9DT01QUkVTU0lPTikge1xuICAgIGxldmVsID0gNjtcbiAgfVxuXG4gIGlmICh3aW5kb3dCaXRzIDwgMCkgeyAvKiBzdXBwcmVzcyB6bGliIHdyYXBwZXIgKi9cbiAgICB3cmFwID0gMDtcbiAgICB3aW5kb3dCaXRzID0gLXdpbmRvd0JpdHM7XG4gIH1cblxuICBlbHNlIGlmICh3aW5kb3dCaXRzID4gMTUpIHtcbiAgICB3cmFwID0gMjsgICAgICAgICAgIC8qIHdyaXRlIGd6aXAgd3JhcHBlciBpbnN0ZWFkICovXG4gICAgd2luZG93Qml0cyAtPSAxNjtcbiAgfVxuXG5cbiAgaWYgKG1lbUxldmVsIDwgMSB8fCBtZW1MZXZlbCA+IE1BWF9NRU1fTEVWRUwgfHwgbWV0aG9kICE9PSBaX0RFRkxBVEVEIHx8XG4gICAgd2luZG93Qml0cyA8IDggfHwgd2luZG93Qml0cyA+IDE1IHx8IGxldmVsIDwgMCB8fCBsZXZlbCA+IDkgfHxcbiAgICBzdHJhdGVneSA8IDAgfHwgc3RyYXRlZ3kgPiBaX0ZJWEVEKSB7XG4gICAgcmV0dXJuIGVycihzdHJtLCBaX1NUUkVBTV9FUlJPUik7XG4gIH1cblxuXG4gIGlmICh3aW5kb3dCaXRzID09PSA4KSB7XG4gICAgd2luZG93Qml0cyA9IDk7XG4gIH1cbiAgLyogdW50aWwgMjU2LWJ5dGUgd2luZG93IGJ1ZyBmaXhlZCAqL1xuXG4gIHZhciBzID0gbmV3IERlZmxhdGVTdGF0ZSgpO1xuXG4gIHN0cm0uc3RhdGUgPSBzO1xuICBzLnN0cm0gPSBzdHJtO1xuXG4gIHMud3JhcCA9IHdyYXA7XG4gIHMuZ3poZWFkID0gbnVsbDtcbiAgcy53X2JpdHMgPSB3aW5kb3dCaXRzO1xuICBzLndfc2l6ZSA9IDEgPDwgcy53X2JpdHM7XG4gIHMud19tYXNrID0gcy53X3NpemUgLSAxO1xuXG4gIHMuaGFzaF9iaXRzID0gbWVtTGV2ZWwgKyA3O1xuICBzLmhhc2hfc2l6ZSA9IDEgPDwgcy5oYXNoX2JpdHM7XG4gIHMuaGFzaF9tYXNrID0gcy5oYXNoX3NpemUgLSAxO1xuICBzLmhhc2hfc2hpZnQgPSB+figocy5oYXNoX2JpdHMgKyBNSU5fTUFUQ0ggLSAxKSAvIE1JTl9NQVRDSCk7XG5cbiAgcy53aW5kb3cgPSBuZXcgdXRpbHMuQnVmOChzLndfc2l6ZSAqIDIpO1xuICBzLmhlYWQgPSBuZXcgdXRpbHMuQnVmMTYocy5oYXNoX3NpemUpO1xuICBzLnByZXYgPSBuZXcgdXRpbHMuQnVmMTYocy53X3NpemUpO1xuXG4gIC8vIERvbid0IG5lZWQgbWVtIGluaXQgbWFnaWMgZm9yIEpTLlxuICAvL3MuaGlnaF93YXRlciA9IDA7ICAvKiBub3RoaW5nIHdyaXR0ZW4gdG8gcy0+d2luZG93IHlldCAqL1xuXG4gIHMubGl0X2J1ZnNpemUgPSAxIDw8IChtZW1MZXZlbCArIDYpOyAvKiAxNksgZWxlbWVudHMgYnkgZGVmYXVsdCAqL1xuXG4gIHMucGVuZGluZ19idWZfc2l6ZSA9IHMubGl0X2J1ZnNpemUgKiA0O1xuICBzLnBlbmRpbmdfYnVmID0gbmV3IHV0aWxzLkJ1Zjgocy5wZW5kaW5nX2J1Zl9zaXplKTtcblxuICBzLmRfYnVmID0gcy5saXRfYnVmc2l6ZSA+PiAxO1xuICBzLmxfYnVmID0gKDEgKyAyKSAqIHMubGl0X2J1ZnNpemU7XG5cbiAgcy5sZXZlbCA9IGxldmVsO1xuICBzLnN0cmF0ZWd5ID0gc3RyYXRlZ3k7XG4gIHMubWV0aG9kID0gbWV0aG9kO1xuXG4gIHJldHVybiBkZWZsYXRlUmVzZXQoc3RybSk7XG59XG5cbmZ1bmN0aW9uIGRlZmxhdGVJbml0KHN0cm0sIGxldmVsKSB7XG4gIHJldHVybiBkZWZsYXRlSW5pdDIoc3RybSwgbGV2ZWwsIFpfREVGTEFURUQsIE1BWF9XQklUUywgREVGX01FTV9MRVZFTCwgWl9ERUZBVUxUX1NUUkFURUdZKTtcbn1cblxuXG5mdW5jdGlvbiBkZWZsYXRlKHN0cm0sIGZsdXNoKSB7XG4gIHZhciBvbGRfZmx1c2gsIHM7XG4gIHZhciBiZWcsIHZhbDsgLy8gZm9yIGd6aXAgaGVhZGVyIHdyaXRlIG9ubHlcblxuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUgfHxcbiAgICBmbHVzaCA+IFpfQkxPQ0sgfHwgZmx1c2ggPCAwKSB7XG4gICAgcmV0dXJuIHN0cm0gPyBlcnIoc3RybSwgWl9TVFJFQU1fRVJST1IpIDogWl9TVFJFQU1fRVJST1I7XG4gIH1cblxuICBzID0gc3RybS5zdGF0ZTtcblxuICBpZiAoIXN0cm0ub3V0cHV0IHx8XG4gICAgICAoIXN0cm0uaW5wdXQgJiYgc3RybS5hdmFpbF9pbiAhPT0gMCkgfHxcbiAgICAgIChzLnN0YXR1cyA9PT0gRklOSVNIX1NUQVRFICYmIGZsdXNoICE9PSBaX0ZJTklTSCkpIHtcbiAgICByZXR1cm4gZXJyKHN0cm0sIChzdHJtLmF2YWlsX291dCA9PT0gMCkgPyBaX0JVRl9FUlJPUiA6IFpfU1RSRUFNX0VSUk9SKTtcbiAgfVxuXG4gIHMuc3RybSA9IHN0cm07IC8qIGp1c3QgaW4gY2FzZSAqL1xuICBvbGRfZmx1c2ggPSBzLmxhc3RfZmx1c2g7XG4gIHMubGFzdF9mbHVzaCA9IGZsdXNoO1xuXG4gIC8qIFdyaXRlIHRoZSBoZWFkZXIgKi9cbiAgaWYgKHMuc3RhdHVzID09PSBJTklUX1NUQVRFKSB7XG5cbiAgICBpZiAocy53cmFwID09PSAyKSB7IC8vIEdaSVAgaGVhZGVyXG4gICAgICBzdHJtLmFkbGVyID0gMDsgIC8vY3JjMzIoMEwsIFpfTlVMTCwgMCk7XG4gICAgICBwdXRfYnl0ZShzLCAzMSk7XG4gICAgICBwdXRfYnl0ZShzLCAxMzkpO1xuICAgICAgcHV0X2J5dGUocywgOCk7XG4gICAgICBpZiAoIXMuZ3poZWFkKSB7IC8vIHMtPmd6aGVhZCA9PSBaX05VTExcbiAgICAgICAgcHV0X2J5dGUocywgMCk7XG4gICAgICAgIHB1dF9ieXRlKHMsIDApO1xuICAgICAgICBwdXRfYnl0ZShzLCAwKTtcbiAgICAgICAgcHV0X2J5dGUocywgMCk7XG4gICAgICAgIHB1dF9ieXRlKHMsIDApO1xuICAgICAgICBwdXRfYnl0ZShzLCBzLmxldmVsID09PSA5ID8gMiA6XG4gICAgICAgICAgICAgICAgICAgIChzLnN0cmF0ZWd5ID49IFpfSFVGRk1BTl9PTkxZIHx8IHMubGV2ZWwgPCAyID9cbiAgICAgICAgICAgICAgICAgICAgIDQgOiAwKSk7XG4gICAgICAgIHB1dF9ieXRlKHMsIE9TX0NPREUpO1xuICAgICAgICBzLnN0YXR1cyA9IEJVU1lfU1RBVEU7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgcHV0X2J5dGUocywgKHMuZ3poZWFkLnRleHQgPyAxIDogMCkgK1xuICAgICAgICAgICAgICAgICAgICAocy5nemhlYWQuaGNyYyA/IDIgOiAwKSArXG4gICAgICAgICAgICAgICAgICAgICghcy5nemhlYWQuZXh0cmEgPyAwIDogNCkgK1xuICAgICAgICAgICAgICAgICAgICAoIXMuZ3poZWFkLm5hbWUgPyAwIDogOCkgK1xuICAgICAgICAgICAgICAgICAgICAoIXMuZ3poZWFkLmNvbW1lbnQgPyAwIDogMTYpXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgcHV0X2J5dGUocywgcy5nemhlYWQudGltZSAmIDB4ZmYpO1xuICAgICAgICBwdXRfYnl0ZShzLCAocy5nemhlYWQudGltZSA+PiA4KSAmIDB4ZmYpO1xuICAgICAgICBwdXRfYnl0ZShzLCAocy5nemhlYWQudGltZSA+PiAxNikgJiAweGZmKTtcbiAgICAgICAgcHV0X2J5dGUocywgKHMuZ3poZWFkLnRpbWUgPj4gMjQpICYgMHhmZik7XG4gICAgICAgIHB1dF9ieXRlKHMsIHMubGV2ZWwgPT09IDkgPyAyIDpcbiAgICAgICAgICAgICAgICAgICAgKHMuc3RyYXRlZ3kgPj0gWl9IVUZGTUFOX09OTFkgfHwgcy5sZXZlbCA8IDIgP1xuICAgICAgICAgICAgICAgICAgICAgNCA6IDApKTtcbiAgICAgICAgcHV0X2J5dGUocywgcy5nemhlYWQub3MgJiAweGZmKTtcbiAgICAgICAgaWYgKHMuZ3poZWFkLmV4dHJhICYmIHMuZ3poZWFkLmV4dHJhLmxlbmd0aCkge1xuICAgICAgICAgIHB1dF9ieXRlKHMsIHMuZ3poZWFkLmV4dHJhLmxlbmd0aCAmIDB4ZmYpO1xuICAgICAgICAgIHB1dF9ieXRlKHMsIChzLmd6aGVhZC5leHRyYS5sZW5ndGggPj4gOCkgJiAweGZmKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAocy5nemhlYWQuaGNyYykge1xuICAgICAgICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmcsIDApO1xuICAgICAgICB9XG4gICAgICAgIHMuZ3ppbmRleCA9IDA7XG4gICAgICAgIHMuc3RhdHVzID0gRVhUUkFfU1RBVEU7XG4gICAgICB9XG4gICAgfVxuICAgIGVsc2UgLy8gREVGTEFURSBoZWFkZXJcbiAgICB7XG4gICAgICB2YXIgaGVhZGVyID0gKFpfREVGTEFURUQgKyAoKHMud19iaXRzIC0gOCkgPDwgNCkpIDw8IDg7XG4gICAgICB2YXIgbGV2ZWxfZmxhZ3MgPSAtMTtcblxuICAgICAgaWYgKHMuc3RyYXRlZ3kgPj0gWl9IVUZGTUFOX09OTFkgfHwgcy5sZXZlbCA8IDIpIHtcbiAgICAgICAgbGV2ZWxfZmxhZ3MgPSAwO1xuICAgICAgfSBlbHNlIGlmIChzLmxldmVsIDwgNikge1xuICAgICAgICBsZXZlbF9mbGFncyA9IDE7XG4gICAgICB9IGVsc2UgaWYgKHMubGV2ZWwgPT09IDYpIHtcbiAgICAgICAgbGV2ZWxfZmxhZ3MgPSAyO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGV2ZWxfZmxhZ3MgPSAzO1xuICAgICAgfVxuICAgICAgaGVhZGVyIHw9IChsZXZlbF9mbGFncyA8PCA2KTtcbiAgICAgIGlmIChzLnN0cnN0YXJ0ICE9PSAwKSB7IGhlYWRlciB8PSBQUkVTRVRfRElDVDsgfVxuICAgICAgaGVhZGVyICs9IDMxIC0gKGhlYWRlciAlIDMxKTtcblxuICAgICAgcy5zdGF0dXMgPSBCVVNZX1NUQVRFO1xuICAgICAgcHV0U2hvcnRNU0IocywgaGVhZGVyKTtcblxuICAgICAgLyogU2F2ZSB0aGUgYWRsZXIzMiBvZiB0aGUgcHJlc2V0IGRpY3Rpb25hcnk6ICovXG4gICAgICBpZiAocy5zdHJzdGFydCAhPT0gMCkge1xuICAgICAgICBwdXRTaG9ydE1TQihzLCBzdHJtLmFkbGVyID4+PiAxNik7XG4gICAgICAgIHB1dFNob3J0TVNCKHMsIHN0cm0uYWRsZXIgJiAweGZmZmYpO1xuICAgICAgfVxuICAgICAgc3RybS5hZGxlciA9IDE7IC8vIGFkbGVyMzIoMEwsIFpfTlVMTCwgMCk7XG4gICAgfVxuICB9XG5cbi8vI2lmZGVmIEdaSVBcbiAgaWYgKHMuc3RhdHVzID09PSBFWFRSQV9TVEFURSkge1xuICAgIGlmIChzLmd6aGVhZC5leHRyYS8qICE9IFpfTlVMTCovKSB7XG4gICAgICBiZWcgPSBzLnBlbmRpbmc7ICAvKiBzdGFydCBvZiBieXRlcyB0byB1cGRhdGUgY3JjICovXG5cbiAgICAgIHdoaWxlIChzLmd6aW5kZXggPCAocy5nemhlYWQuZXh0cmEubGVuZ3RoICYgMHhmZmZmKSkge1xuICAgICAgICBpZiAocy5wZW5kaW5nID09PSBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgICBpZiAocy5nemhlYWQuaGNyYyAmJiBzLnBlbmRpbmcgPiBiZWcpIHtcbiAgICAgICAgICAgIHN0cm0uYWRsZXIgPSBjcmMzMihzdHJtLmFkbGVyLCBzLnBlbmRpbmdfYnVmLCBzLnBlbmRpbmcgLSBiZWcsIGJlZyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZsdXNoX3BlbmRpbmcoc3RybSk7XG4gICAgICAgICAgYmVnID0gcy5wZW5kaW5nO1xuICAgICAgICAgIGlmIChzLnBlbmRpbmcgPT09IHMucGVuZGluZ19idWZfc2l6ZSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHB1dF9ieXRlKHMsIHMuZ3poZWFkLmV4dHJhW3MuZ3ppbmRleF0gJiAweGZmKTtcbiAgICAgICAgcy5nemluZGV4Kys7XG4gICAgICB9XG4gICAgICBpZiAocy5nemhlYWQuaGNyYyAmJiBzLnBlbmRpbmcgPiBiZWcpIHtcbiAgICAgICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIHMucGVuZGluZ19idWYsIHMucGVuZGluZyAtIGJlZywgYmVnKTtcbiAgICAgIH1cbiAgICAgIGlmIChzLmd6aW5kZXggPT09IHMuZ3poZWFkLmV4dHJhLmxlbmd0aCkge1xuICAgICAgICBzLmd6aW5kZXggPSAwO1xuICAgICAgICBzLnN0YXR1cyA9IE5BTUVfU1RBVEU7XG4gICAgICB9XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgcy5zdGF0dXMgPSBOQU1FX1NUQVRFO1xuICAgIH1cbiAgfVxuICBpZiAocy5zdGF0dXMgPT09IE5BTUVfU1RBVEUpIHtcbiAgICBpZiAocy5nemhlYWQubmFtZS8qICE9IFpfTlVMTCovKSB7XG4gICAgICBiZWcgPSBzLnBlbmRpbmc7ICAvKiBzdGFydCBvZiBieXRlcyB0byB1cGRhdGUgY3JjICovXG4gICAgICAvL2ludCB2YWw7XG5cbiAgICAgIGRvIHtcbiAgICAgICAgaWYgKHMucGVuZGluZyA9PT0gcy5wZW5kaW5nX2J1Zl9zaXplKSB7XG4gICAgICAgICAgaWYgKHMuZ3poZWFkLmhjcmMgJiYgcy5wZW5kaW5nID4gYmVnKSB7XG4gICAgICAgICAgICBzdHJtLmFkbGVyID0gY3JjMzIoc3RybS5hZGxlciwgcy5wZW5kaW5nX2J1Ziwgcy5wZW5kaW5nIC0gYmVnLCBiZWcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBmbHVzaF9wZW5kaW5nKHN0cm0pO1xuICAgICAgICAgIGJlZyA9IHMucGVuZGluZztcbiAgICAgICAgICBpZiAocy5wZW5kaW5nID09PSBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgICAgIHZhbCA9IDE7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gSlMgc3BlY2lmaWM6IGxpdHRsZSBtYWdpYyB0byBhZGQgemVybyB0ZXJtaW5hdG9yIHRvIGVuZCBvZiBzdHJpbmdcbiAgICAgICAgaWYgKHMuZ3ppbmRleCA8IHMuZ3poZWFkLm5hbWUubGVuZ3RoKSB7XG4gICAgICAgICAgdmFsID0gcy5nemhlYWQubmFtZS5jaGFyQ29kZUF0KHMuZ3ppbmRleCsrKSAmIDB4ZmY7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdmFsID0gMDtcbiAgICAgICAgfVxuICAgICAgICBwdXRfYnl0ZShzLCB2YWwpO1xuICAgICAgfSB3aGlsZSAodmFsICE9PSAwKTtcblxuICAgICAgaWYgKHMuZ3poZWFkLmhjcmMgJiYgcy5wZW5kaW5nID4gYmVnKXtcbiAgICAgICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIHMucGVuZGluZ19idWYsIHMucGVuZGluZyAtIGJlZywgYmVnKTtcbiAgICAgIH1cbiAgICAgIGlmICh2YWwgPT09IDApIHtcbiAgICAgICAgcy5nemluZGV4ID0gMDtcbiAgICAgICAgcy5zdGF0dXMgPSBDT01NRU5UX1NUQVRFO1xuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHMuc3RhdHVzID0gQ09NTUVOVF9TVEFURTtcbiAgICB9XG4gIH1cbiAgaWYgKHMuc3RhdHVzID09PSBDT01NRU5UX1NUQVRFKSB7XG4gICAgaWYgKHMuZ3poZWFkLmNvbW1lbnQvKiAhPSBaX05VTEwqLykge1xuICAgICAgYmVnID0gcy5wZW5kaW5nOyAgLyogc3RhcnQgb2YgYnl0ZXMgdG8gdXBkYXRlIGNyYyAqL1xuICAgICAgLy9pbnQgdmFsO1xuXG4gICAgICBkbyB7XG4gICAgICAgIGlmIChzLnBlbmRpbmcgPT09IHMucGVuZGluZ19idWZfc2l6ZSkge1xuICAgICAgICAgIGlmIChzLmd6aGVhZC5oY3JjICYmIHMucGVuZGluZyA+IGJlZykge1xuICAgICAgICAgICAgc3RybS5hZGxlciA9IGNyYzMyKHN0cm0uYWRsZXIsIHMucGVuZGluZ19idWYsIHMucGVuZGluZyAtIGJlZywgYmVnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgICAgICAgICBiZWcgPSBzLnBlbmRpbmc7XG4gICAgICAgICAgaWYgKHMucGVuZGluZyA9PT0gcy5wZW5kaW5nX2J1Zl9zaXplKSB7XG4gICAgICAgICAgICB2YWwgPSAxO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEpTIHNwZWNpZmljOiBsaXR0bGUgbWFnaWMgdG8gYWRkIHplcm8gdGVybWluYXRvciB0byBlbmQgb2Ygc3RyaW5nXG4gICAgICAgIGlmIChzLmd6aW5kZXggPCBzLmd6aGVhZC5jb21tZW50Lmxlbmd0aCkge1xuICAgICAgICAgIHZhbCA9IHMuZ3poZWFkLmNvbW1lbnQuY2hhckNvZGVBdChzLmd6aW5kZXgrKykgJiAweGZmO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhbCA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgcHV0X2J5dGUocywgdmFsKTtcbiAgICAgIH0gd2hpbGUgKHZhbCAhPT0gMCk7XG5cbiAgICAgIGlmIChzLmd6aGVhZC5oY3JjICYmIHMucGVuZGluZyA+IGJlZykge1xuICAgICAgICBzdHJtLmFkbGVyID0gY3JjMzIoc3RybS5hZGxlciwgcy5wZW5kaW5nX2J1Ziwgcy5wZW5kaW5nIC0gYmVnLCBiZWcpO1xuICAgICAgfVxuICAgICAgaWYgKHZhbCA9PT0gMCkge1xuICAgICAgICBzLnN0YXR1cyA9IEhDUkNfU1RBVEU7XG4gICAgICB9XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgcy5zdGF0dXMgPSBIQ1JDX1NUQVRFO1xuICAgIH1cbiAgfVxuICBpZiAocy5zdGF0dXMgPT09IEhDUkNfU1RBVEUpIHtcbiAgICBpZiAocy5nemhlYWQuaGNyYykge1xuICAgICAgaWYgKHMucGVuZGluZyArIDIgPiBzLnBlbmRpbmdfYnVmX3NpemUpIHtcbiAgICAgICAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgICAgIH1cbiAgICAgIGlmIChzLnBlbmRpbmcgKyAyIDw9IHMucGVuZGluZ19idWZfc2l6ZSkge1xuICAgICAgICBwdXRfYnl0ZShzLCBzdHJtLmFkbGVyICYgMHhmZik7XG4gICAgICAgIHB1dF9ieXRlKHMsIChzdHJtLmFkbGVyID4+IDgpICYgMHhmZik7XG4gICAgICAgIHN0cm0uYWRsZXIgPSAwOyAvL2NyYzMyKDBMLCBaX05VTEwsIDApO1xuICAgICAgICBzLnN0YXR1cyA9IEJVU1lfU1RBVEU7XG4gICAgICB9XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgcy5zdGF0dXMgPSBCVVNZX1NUQVRFO1xuICAgIH1cbiAgfVxuLy8jZW5kaWZcblxuICAvKiBGbHVzaCBhcyBtdWNoIHBlbmRpbmcgb3V0cHV0IGFzIHBvc3NpYmxlICovXG4gIGlmIChzLnBlbmRpbmcgIT09IDApIHtcbiAgICBmbHVzaF9wZW5kaW5nKHN0cm0pO1xuICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgLyogU2luY2UgYXZhaWxfb3V0IGlzIDAsIGRlZmxhdGUgd2lsbCBiZSBjYWxsZWQgYWdhaW4gd2l0aFxuICAgICAgICogbW9yZSBvdXRwdXQgc3BhY2UsIGJ1dCBwb3NzaWJseSB3aXRoIGJvdGggcGVuZGluZyBhbmRcbiAgICAgICAqIGF2YWlsX2luIGVxdWFsIHRvIHplcm8uIFRoZXJlIHdvbid0IGJlIGFueXRoaW5nIHRvIGRvLFxuICAgICAgICogYnV0IHRoaXMgaXMgbm90IGFuIGVycm9yIHNpdHVhdGlvbiBzbyBtYWtlIHN1cmUgd2VcbiAgICAgICAqIHJldHVybiBPSyBpbnN0ZWFkIG9mIEJVRl9FUlJPUiBhdCBuZXh0IGNhbGwgb2YgZGVmbGF0ZTpcbiAgICAgICAqL1xuICAgICAgcy5sYXN0X2ZsdXNoID0gLTE7XG4gICAgICByZXR1cm4gWl9PSztcbiAgICB9XG5cbiAgICAvKiBNYWtlIHN1cmUgdGhlcmUgaXMgc29tZXRoaW5nIHRvIGRvIGFuZCBhdm9pZCBkdXBsaWNhdGUgY29uc2VjdXRpdmVcbiAgICAgKiBmbHVzaGVzLiBGb3IgcmVwZWF0ZWQgYW5kIHVzZWxlc3MgY2FsbHMgd2l0aCBaX0ZJTklTSCwgd2Uga2VlcFxuICAgICAqIHJldHVybmluZyBaX1NUUkVBTV9FTkQgaW5zdGVhZCBvZiBaX0JVRl9FUlJPUi5cbiAgICAgKi9cbiAgfSBlbHNlIGlmIChzdHJtLmF2YWlsX2luID09PSAwICYmIHJhbmsoZmx1c2gpIDw9IHJhbmsob2xkX2ZsdXNoKSAmJlxuICAgIGZsdXNoICE9PSBaX0ZJTklTSCkge1xuICAgIHJldHVybiBlcnIoc3RybSwgWl9CVUZfRVJST1IpO1xuICB9XG5cbiAgLyogVXNlciBtdXN0IG5vdCBwcm92aWRlIG1vcmUgaW5wdXQgYWZ0ZXIgdGhlIGZpcnN0IEZJTklTSDogKi9cbiAgaWYgKHMuc3RhdHVzID09PSBGSU5JU0hfU1RBVEUgJiYgc3RybS5hdmFpbF9pbiAhPT0gMCkge1xuICAgIHJldHVybiBlcnIoc3RybSwgWl9CVUZfRVJST1IpO1xuICB9XG5cbiAgLyogU3RhcnQgYSBuZXcgYmxvY2sgb3IgY29udGludWUgdGhlIGN1cnJlbnQgb25lLlxuICAgKi9cbiAgaWYgKHN0cm0uYXZhaWxfaW4gIT09IDAgfHwgcy5sb29rYWhlYWQgIT09IDAgfHxcbiAgICAoZmx1c2ggIT09IFpfTk9fRkxVU0ggJiYgcy5zdGF0dXMgIT09IEZJTklTSF9TVEFURSkpIHtcbiAgICB2YXIgYnN0YXRlID0gKHMuc3RyYXRlZ3kgPT09IFpfSFVGRk1BTl9PTkxZKSA/IGRlZmxhdGVfaHVmZihzLCBmbHVzaCkgOlxuICAgICAgKHMuc3RyYXRlZ3kgPT09IFpfUkxFID8gZGVmbGF0ZV9ybGUocywgZmx1c2gpIDpcbiAgICAgICAgY29uZmlndXJhdGlvbl90YWJsZVtzLmxldmVsXS5mdW5jKHMsIGZsdXNoKSk7XG5cbiAgICBpZiAoYnN0YXRlID09PSBCU19GSU5JU0hfU1RBUlRFRCB8fCBic3RhdGUgPT09IEJTX0ZJTklTSF9ET05FKSB7XG4gICAgICBzLnN0YXR1cyA9IEZJTklTSF9TVEFURTtcbiAgICB9XG4gICAgaWYgKGJzdGF0ZSA9PT0gQlNfTkVFRF9NT1JFIHx8IGJzdGF0ZSA9PT0gQlNfRklOSVNIX1NUQVJURUQpIHtcbiAgICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICBzLmxhc3RfZmx1c2ggPSAtMTtcbiAgICAgICAgLyogYXZvaWQgQlVGX0VSUk9SIG5leHQgY2FsbCwgc2VlIGFib3ZlICovXG4gICAgICB9XG4gICAgICByZXR1cm4gWl9PSztcbiAgICAgIC8qIElmIGZsdXNoICE9IFpfTk9fRkxVU0ggJiYgYXZhaWxfb3V0ID09IDAsIHRoZSBuZXh0IGNhbGxcbiAgICAgICAqIG9mIGRlZmxhdGUgc2hvdWxkIHVzZSB0aGUgc2FtZSBmbHVzaCBwYXJhbWV0ZXIgdG8gbWFrZSBzdXJlXG4gICAgICAgKiB0aGF0IHRoZSBmbHVzaCBpcyBjb21wbGV0ZS4gU28gd2UgZG9uJ3QgaGF2ZSB0byBvdXRwdXQgYW5cbiAgICAgICAqIGVtcHR5IGJsb2NrIGhlcmUsIHRoaXMgd2lsbCBiZSBkb25lIGF0IG5leHQgY2FsbC4gVGhpcyBhbHNvXG4gICAgICAgKiBlbnN1cmVzIHRoYXQgZm9yIGEgdmVyeSBzbWFsbCBvdXRwdXQgYnVmZmVyLCB3ZSBlbWl0IGF0IG1vc3RcbiAgICAgICAqIG9uZSBlbXB0eSBibG9jay5cbiAgICAgICAqL1xuICAgIH1cbiAgICBpZiAoYnN0YXRlID09PSBCU19CTE9DS19ET05FKSB7XG4gICAgICBpZiAoZmx1c2ggPT09IFpfUEFSVElBTF9GTFVTSCkge1xuICAgICAgICB0cmVlcy5fdHJfYWxpZ24ocyk7XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChmbHVzaCAhPT0gWl9CTE9DSykgeyAvKiBGVUxMX0ZMVVNIIG9yIFNZTkNfRkxVU0ggKi9cblxuICAgICAgICB0cmVlcy5fdHJfc3RvcmVkX2Jsb2NrKHMsIDAsIDAsIGZhbHNlKTtcbiAgICAgICAgLyogRm9yIGEgZnVsbCBmbHVzaCwgdGhpcyBlbXB0eSBibG9jayB3aWxsIGJlIHJlY29nbml6ZWRcbiAgICAgICAgICogYXMgYSBzcGVjaWFsIG1hcmtlciBieSBpbmZsYXRlX3N5bmMoKS5cbiAgICAgICAgICovXG4gICAgICAgIGlmIChmbHVzaCA9PT0gWl9GVUxMX0ZMVVNIKSB7XG4gICAgICAgICAgLyoqKiBDTEVBUl9IQVNIKHMpOyAqKiovICAgICAgICAgICAgIC8qIGZvcmdldCBoaXN0b3J5ICovXG4gICAgICAgICAgemVybyhzLmhlYWQpOyAvLyBGaWxsIHdpdGggTklMICg9IDApO1xuXG4gICAgICAgICAgaWYgKHMubG9va2FoZWFkID09PSAwKSB7XG4gICAgICAgICAgICBzLnN0cnN0YXJ0ID0gMDtcbiAgICAgICAgICAgIHMuYmxvY2tfc3RhcnQgPSAwO1xuICAgICAgICAgICAgcy5pbnNlcnQgPSAwO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZmx1c2hfcGVuZGluZyhzdHJtKTtcbiAgICAgIGlmIChzdHJtLmF2YWlsX291dCA9PT0gMCkge1xuICAgICAgICBzLmxhc3RfZmx1c2ggPSAtMTsgLyogYXZvaWQgQlVGX0VSUk9SIGF0IG5leHQgY2FsbCwgc2VlIGFib3ZlICovXG4gICAgICAgIHJldHVybiBaX09LO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICAvL0Fzc2VydChzdHJtLT5hdmFpbF9vdXQgPiAwLCBcImJ1ZzJcIik7XG4gIC8vaWYgKHN0cm0uYXZhaWxfb3V0IDw9IDApIHsgdGhyb3cgbmV3IEVycm9yKFwiYnVnMlwiKTt9XG5cbiAgaWYgKGZsdXNoICE9PSBaX0ZJTklTSCkgeyByZXR1cm4gWl9PSzsgfVxuICBpZiAocy53cmFwIDw9IDApIHsgcmV0dXJuIFpfU1RSRUFNX0VORDsgfVxuXG4gIC8qIFdyaXRlIHRoZSB0cmFpbGVyICovXG4gIGlmIChzLndyYXAgPT09IDIpIHtcbiAgICBwdXRfYnl0ZShzLCBzdHJtLmFkbGVyICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0uYWRsZXIgPj4gOCkgJiAweGZmKTtcbiAgICBwdXRfYnl0ZShzLCAoc3RybS5hZGxlciA+PiAxNikgJiAweGZmKTtcbiAgICBwdXRfYnl0ZShzLCAoc3RybS5hZGxlciA+PiAyNCkgJiAweGZmKTtcbiAgICBwdXRfYnl0ZShzLCBzdHJtLnRvdGFsX2luICYgMHhmZik7XG4gICAgcHV0X2J5dGUocywgKHN0cm0udG90YWxfaW4gPj4gOCkgJiAweGZmKTtcbiAgICBwdXRfYnl0ZShzLCAoc3RybS50b3RhbF9pbiA+PiAxNikgJiAweGZmKTtcbiAgICBwdXRfYnl0ZShzLCAoc3RybS50b3RhbF9pbiA+PiAyNCkgJiAweGZmKTtcbiAgfVxuICBlbHNlXG4gIHtcbiAgICBwdXRTaG9ydE1TQihzLCBzdHJtLmFkbGVyID4+PiAxNik7XG4gICAgcHV0U2hvcnRNU0Iocywgc3RybS5hZGxlciAmIDB4ZmZmZik7XG4gIH1cblxuICBmbHVzaF9wZW5kaW5nKHN0cm0pO1xuICAvKiBJZiBhdmFpbF9vdXQgaXMgemVybywgdGhlIGFwcGxpY2F0aW9uIHdpbGwgY2FsbCBkZWZsYXRlIGFnYWluXG4gICAqIHRvIGZsdXNoIHRoZSByZXN0LlxuICAgKi9cbiAgaWYgKHMud3JhcCA+IDApIHsgcy53cmFwID0gLXMud3JhcDsgfVxuICAvKiB3cml0ZSB0aGUgdHJhaWxlciBvbmx5IG9uY2UhICovXG4gIHJldHVybiBzLnBlbmRpbmcgIT09IDAgPyBaX09LIDogWl9TVFJFQU1fRU5EO1xufVxuXG5mdW5jdGlvbiBkZWZsYXRlRW5kKHN0cm0pIHtcbiAgdmFyIHN0YXR1cztcblxuICBpZiAoIXN0cm0vKj09IFpfTlVMTCovIHx8ICFzdHJtLnN0YXRlLyo9PSBaX05VTEwqLykge1xuICAgIHJldHVybiBaX1NUUkVBTV9FUlJPUjtcbiAgfVxuXG4gIHN0YXR1cyA9IHN0cm0uc3RhdGUuc3RhdHVzO1xuICBpZiAoc3RhdHVzICE9PSBJTklUX1NUQVRFICYmXG4gICAgc3RhdHVzICE9PSBFWFRSQV9TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gTkFNRV9TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gQ09NTUVOVF9TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gSENSQ19TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gQlVTWV9TVEFURSAmJlxuICAgIHN0YXR1cyAhPT0gRklOSVNIX1NUQVRFXG4gICkge1xuICAgIHJldHVybiBlcnIoc3RybSwgWl9TVFJFQU1fRVJST1IpO1xuICB9XG5cbiAgc3RybS5zdGF0ZSA9IG51bGw7XG5cbiAgcmV0dXJuIHN0YXR1cyA9PT0gQlVTWV9TVEFURSA/IGVycihzdHJtLCBaX0RBVEFfRVJST1IpIDogWl9PSztcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogQ29weSB0aGUgc291cmNlIHN0YXRlIHRvIHRoZSBkZXN0aW5hdGlvbiBzdGF0ZVxuICovXG4vL2Z1bmN0aW9uIGRlZmxhdGVDb3B5KGRlc3QsIHNvdXJjZSkge1xuLy9cbi8vfVxuXG5leHBvcnRzLmRlZmxhdGVJbml0ID0gZGVmbGF0ZUluaXQ7XG5leHBvcnRzLmRlZmxhdGVJbml0MiA9IGRlZmxhdGVJbml0MjtcbmV4cG9ydHMuZGVmbGF0ZVJlc2V0ID0gZGVmbGF0ZVJlc2V0O1xuZXhwb3J0cy5kZWZsYXRlUmVzZXRLZWVwID0gZGVmbGF0ZVJlc2V0S2VlcDtcbmV4cG9ydHMuZGVmbGF0ZVNldEhlYWRlciA9IGRlZmxhdGVTZXRIZWFkZXI7XG5leHBvcnRzLmRlZmxhdGUgPSBkZWZsYXRlO1xuZXhwb3J0cy5kZWZsYXRlRW5kID0gZGVmbGF0ZUVuZDtcbmV4cG9ydHMuZGVmbGF0ZUluZm8gPSAncGFrbyBkZWZsYXRlIChmcm9tIE5vZGVjYSBwcm9qZWN0KSc7XG5cbi8qIE5vdCBpbXBsZW1lbnRlZFxuZXhwb3J0cy5kZWZsYXRlQm91bmQgPSBkZWZsYXRlQm91bmQ7XG5leHBvcnRzLmRlZmxhdGVDb3B5ID0gZGVmbGF0ZUNvcHk7XG5leHBvcnRzLmRlZmxhdGVTZXREaWN0aW9uYXJ5ID0gZGVmbGF0ZVNldERpY3Rpb25hcnk7XG5leHBvcnRzLmRlZmxhdGVQYXJhbXMgPSBkZWZsYXRlUGFyYW1zO1xuZXhwb3J0cy5kZWZsYXRlUGVuZGluZyA9IGRlZmxhdGVQZW5kaW5nO1xuZXhwb3J0cy5kZWZsYXRlUHJpbWUgPSBkZWZsYXRlUHJpbWU7XG5leHBvcnRzLmRlZmxhdGVUdW5lID0gZGVmbGF0ZVR1bmU7XG4qLyIsIid1c2Ugc3RyaWN0JztcblxuXG5mdW5jdGlvbiBHWmhlYWRlcigpIHtcbiAgLyogdHJ1ZSBpZiBjb21wcmVzc2VkIGRhdGEgYmVsaWV2ZWQgdG8gYmUgdGV4dCAqL1xuICB0aGlzLnRleHQgICAgICAgPSAwO1xuICAvKiBtb2RpZmljYXRpb24gdGltZSAqL1xuICB0aGlzLnRpbWUgICAgICAgPSAwO1xuICAvKiBleHRyYSBmbGFncyAobm90IHVzZWQgd2hlbiB3cml0aW5nIGEgZ3ppcCBmaWxlKSAqL1xuICB0aGlzLnhmbGFncyAgICAgPSAwO1xuICAvKiBvcGVyYXRpbmcgc3lzdGVtICovXG4gIHRoaXMub3MgICAgICAgICA9IDA7XG4gIC8qIHBvaW50ZXIgdG8gZXh0cmEgZmllbGQgb3IgWl9OVUxMIGlmIG5vbmUgKi9cbiAgdGhpcy5leHRyYSAgICAgID0gbnVsbDtcbiAgLyogZXh0cmEgZmllbGQgbGVuZ3RoICh2YWxpZCBpZiBleHRyYSAhPSBaX05VTEwpICovXG4gIHRoaXMuZXh0cmFfbGVuICA9IDA7IC8vIEFjdHVhbGx5LCB3ZSBkb24ndCBuZWVkIGl0IGluIEpTLFxuICAgICAgICAgICAgICAgICAgICAgICAvLyBidXQgbGVhdmUgZm9yIGZldyBjb2RlIG1vZGlmaWNhdGlvbnNcblxuICAvL1xuICAvLyBTZXR1cCBsaW1pdHMgaXMgbm90IG5lY2Vzc2FyeSBiZWNhdXNlIGluIGpzIHdlIHNob3VsZCBub3QgcHJlYWxsb2NhdGUgbWVtb3J5IFxuICAvLyBmb3IgaW5mbGF0ZSB1c2UgY29uc3RhbnQgbGltaXQgaW4gNjU1MzYgYnl0ZXNcbiAgLy9cblxuICAvKiBzcGFjZSBhdCBleHRyYSAob25seSB3aGVuIHJlYWRpbmcgaGVhZGVyKSAqL1xuICAvLyB0aGlzLmV4dHJhX21heCAgPSAwO1xuICAvKiBwb2ludGVyIHRvIHplcm8tdGVybWluYXRlZCBmaWxlIG5hbWUgb3IgWl9OVUxMICovXG4gIHRoaXMubmFtZSAgICAgICA9ICcnO1xuICAvKiBzcGFjZSBhdCBuYW1lIChvbmx5IHdoZW4gcmVhZGluZyBoZWFkZXIpICovXG4gIC8vIHRoaXMubmFtZV9tYXggICA9IDA7XG4gIC8qIHBvaW50ZXIgdG8gemVyby10ZXJtaW5hdGVkIGNvbW1lbnQgb3IgWl9OVUxMICovXG4gIHRoaXMuY29tbWVudCAgICA9ICcnO1xuICAvKiBzcGFjZSBhdCBjb21tZW50IChvbmx5IHdoZW4gcmVhZGluZyBoZWFkZXIpICovXG4gIC8vIHRoaXMuY29tbV9tYXggICA9IDA7XG4gIC8qIHRydWUgaWYgdGhlcmUgd2FzIG9yIHdpbGwgYmUgYSBoZWFkZXIgY3JjICovXG4gIHRoaXMuaGNyYyAgICAgICA9IDA7XG4gIC8qIHRydWUgd2hlbiBkb25lIHJlYWRpbmcgZ3ppcCBoZWFkZXIgKG5vdCB1c2VkIHdoZW4gd3JpdGluZyBhIGd6aXAgZmlsZSkgKi9cbiAgdGhpcy5kb25lICAgICAgID0gZmFsc2U7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gR1poZWFkZXI7IiwiJ3VzZSBzdHJpY3QnO1xuXG4vLyBTZWUgc3RhdGUgZGVmcyBmcm9tIGluZmxhdGUuanNcbnZhciBCQUQgPSAzMDsgICAgICAgLyogZ290IGEgZGF0YSBlcnJvciAtLSByZW1haW4gaGVyZSB1bnRpbCByZXNldCAqL1xudmFyIFRZUEUgPSAxMjsgICAgICAvKiBpOiB3YWl0aW5nIGZvciB0eXBlIGJpdHMsIGluY2x1ZGluZyBsYXN0LWZsYWcgYml0ICovXG5cbi8qXG4gICBEZWNvZGUgbGl0ZXJhbCwgbGVuZ3RoLCBhbmQgZGlzdGFuY2UgY29kZXMgYW5kIHdyaXRlIG91dCB0aGUgcmVzdWx0aW5nXG4gICBsaXRlcmFsIGFuZCBtYXRjaCBieXRlcyB1bnRpbCBlaXRoZXIgbm90IGVub3VnaCBpbnB1dCBvciBvdXRwdXQgaXNcbiAgIGF2YWlsYWJsZSwgYW4gZW5kLW9mLWJsb2NrIGlzIGVuY291bnRlcmVkLCBvciBhIGRhdGEgZXJyb3IgaXMgZW5jb3VudGVyZWQuXG4gICBXaGVuIGxhcmdlIGVub3VnaCBpbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgYXJlIHN1cHBsaWVkIHRvIGluZmxhdGUoKSwgZm9yXG4gICBleGFtcGxlLCBhIDE2SyBpbnB1dCBidWZmZXIgYW5kIGEgNjRLIG91dHB1dCBidWZmZXIsIG1vcmUgdGhhbiA5NSUgb2YgdGhlXG4gICBpbmZsYXRlIGV4ZWN1dGlvbiB0aW1lIGlzIHNwZW50IGluIHRoaXMgcm91dGluZS5cblxuICAgRW50cnkgYXNzdW1wdGlvbnM6XG5cbiAgICAgICAgc3RhdGUubW9kZSA9PT0gTEVOXG4gICAgICAgIHN0cm0uYXZhaWxfaW4gPj0gNlxuICAgICAgICBzdHJtLmF2YWlsX291dCA+PSAyNThcbiAgICAgICAgc3RhcnQgPj0gc3RybS5hdmFpbF9vdXRcbiAgICAgICAgc3RhdGUuYml0cyA8IDhcblxuICAgT24gcmV0dXJuLCBzdGF0ZS5tb2RlIGlzIG9uZSBvZjpcblxuICAgICAgICBMRU4gLS0gcmFuIG91dCBvZiBlbm91Z2ggb3V0cHV0IHNwYWNlIG9yIGVub3VnaCBhdmFpbGFibGUgaW5wdXRcbiAgICAgICAgVFlQRSAtLSByZWFjaGVkIGVuZCBvZiBibG9jayBjb2RlLCBpbmZsYXRlKCkgdG8gaW50ZXJwcmV0IG5leHQgYmxvY2tcbiAgICAgICAgQkFEIC0tIGVycm9yIGluIGJsb2NrIGRhdGFcblxuICAgTm90ZXM6XG5cbiAgICAtIFRoZSBtYXhpbXVtIGlucHV0IGJpdHMgdXNlZCBieSBhIGxlbmd0aC9kaXN0YW5jZSBwYWlyIGlzIDE1IGJpdHMgZm9yIHRoZVxuICAgICAgbGVuZ3RoIGNvZGUsIDUgYml0cyBmb3IgdGhlIGxlbmd0aCBleHRyYSwgMTUgYml0cyBmb3IgdGhlIGRpc3RhbmNlIGNvZGUsXG4gICAgICBhbmQgMTMgYml0cyBmb3IgdGhlIGRpc3RhbmNlIGV4dHJhLiAgVGhpcyB0b3RhbHMgNDggYml0cywgb3Igc2l4IGJ5dGVzLlxuICAgICAgVGhlcmVmb3JlIGlmIHN0cm0uYXZhaWxfaW4gPj0gNiwgdGhlbiB0aGVyZSBpcyBlbm91Z2ggaW5wdXQgdG8gYXZvaWRcbiAgICAgIGNoZWNraW5nIGZvciBhdmFpbGFibGUgaW5wdXQgd2hpbGUgZGVjb2RpbmcuXG5cbiAgICAtIFRoZSBtYXhpbXVtIGJ5dGVzIHRoYXQgYSBzaW5nbGUgbGVuZ3RoL2Rpc3RhbmNlIHBhaXIgY2FuIG91dHB1dCBpcyAyNThcbiAgICAgIGJ5dGVzLCB3aGljaCBpcyB0aGUgbWF4aW11bSBsZW5ndGggdGhhdCBjYW4gYmUgY29kZWQuICBpbmZsYXRlX2Zhc3QoKVxuICAgICAgcmVxdWlyZXMgc3RybS5hdmFpbF9vdXQgPj0gMjU4IGZvciBlYWNoIGxvb3AgdG8gYXZvaWQgY2hlY2tpbmcgZm9yXG4gICAgICBvdXRwdXQgc3BhY2UuXG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gaW5mbGF0ZV9mYXN0KHN0cm0sIHN0YXJ0KSB7XG4gIHZhciBzdGF0ZTtcbiAgdmFyIF9pbjsgICAgICAgICAgICAgICAgICAgIC8qIGxvY2FsIHN0cm0uaW5wdXQgKi9cbiAgdmFyIGxhc3Q7ICAgICAgICAgICAgICAgICAgIC8qIGhhdmUgZW5vdWdoIGlucHV0IHdoaWxlIGluIDwgbGFzdCAqL1xuICB2YXIgX291dDsgICAgICAgICAgICAgICAgICAgLyogbG9jYWwgc3RybS5vdXRwdXQgKi9cbiAgdmFyIGJlZzsgICAgICAgICAgICAgICAgICAgIC8qIGluZmxhdGUoKSdzIGluaXRpYWwgc3RybS5vdXRwdXQgKi9cbiAgdmFyIGVuZDsgICAgICAgICAgICAgICAgICAgIC8qIHdoaWxlIG91dCA8IGVuZCwgZW5vdWdoIHNwYWNlIGF2YWlsYWJsZSAqL1xuLy8jaWZkZWYgSU5GTEFURV9TVFJJQ1RcbiAgdmFyIGRtYXg7ICAgICAgICAgICAgICAgICAgIC8qIG1heGltdW0gZGlzdGFuY2UgZnJvbSB6bGliIGhlYWRlciAqL1xuLy8jZW5kaWZcbiAgdmFyIHdzaXplOyAgICAgICAgICAgICAgICAgIC8qIHdpbmRvdyBzaXplIG9yIHplcm8gaWYgbm90IHVzaW5nIHdpbmRvdyAqL1xuICB2YXIgd2hhdmU7ICAgICAgICAgICAgICAgICAgLyogdmFsaWQgYnl0ZXMgaW4gdGhlIHdpbmRvdyAqL1xuICB2YXIgd25leHQ7ICAgICAgICAgICAgICAgICAgLyogd2luZG93IHdyaXRlIGluZGV4ICovXG4gIHZhciB3aW5kb3c7ICAgICAgICAgICAgICAgICAvKiBhbGxvY2F0ZWQgc2xpZGluZyB3aW5kb3csIGlmIHdzaXplICE9IDAgKi9cbiAgdmFyIGhvbGQ7ICAgICAgICAgICAgICAgICAgIC8qIGxvY2FsIHN0cm0uaG9sZCAqL1xuICB2YXIgYml0czsgICAgICAgICAgICAgICAgICAgLyogbG9jYWwgc3RybS5iaXRzICovXG4gIHZhciBsY29kZTsgICAgICAgICAgICAgICAgICAvKiBsb2NhbCBzdHJtLmxlbmNvZGUgKi9cbiAgdmFyIGRjb2RlOyAgICAgICAgICAgICAgICAgIC8qIGxvY2FsIHN0cm0uZGlzdGNvZGUgKi9cbiAgdmFyIGxtYXNrOyAgICAgICAgICAgICAgICAgIC8qIG1hc2sgZm9yIGZpcnN0IGxldmVsIG9mIGxlbmd0aCBjb2RlcyAqL1xuICB2YXIgZG1hc2s7ICAgICAgICAgICAgICAgICAgLyogbWFzayBmb3IgZmlyc3QgbGV2ZWwgb2YgZGlzdGFuY2UgY29kZXMgKi9cbiAgdmFyIGhlcmU7ICAgICAgICAgICAgICAgICAgIC8qIHJldHJpZXZlZCB0YWJsZSBlbnRyeSAqL1xuICB2YXIgb3A7ICAgICAgICAgICAgICAgICAgICAgLyogY29kZSBiaXRzLCBvcGVyYXRpb24sIGV4dHJhIGJpdHMsIG9yICovXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiAgd2luZG93IHBvc2l0aW9uLCB3aW5kb3cgYnl0ZXMgdG8gY29weSAqL1xuICB2YXIgbGVuOyAgICAgICAgICAgICAgICAgICAgLyogbWF0Y2ggbGVuZ3RoLCB1bnVzZWQgYnl0ZXMgKi9cbiAgdmFyIGRpc3Q7ICAgICAgICAgICAgICAgICAgIC8qIG1hdGNoIGRpc3RhbmNlICovXG4gIHZhciBmcm9tOyAgICAgICAgICAgICAgICAgICAvKiB3aGVyZSB0byBjb3B5IG1hdGNoIGZyb20gKi9cbiAgdmFyIGZyb21fc291cmNlO1xuXG5cbiAgdmFyIGlucHV0LCBvdXRwdXQ7IC8vIEpTIHNwZWNpZmljLCBiZWNhdXNlIHdlIGhhdmUgbm8gcG9pbnRlcnNcblxuICAvKiBjb3B5IHN0YXRlIHRvIGxvY2FsIHZhcmlhYmxlcyAqL1xuICBzdGF0ZSA9IHN0cm0uc3RhdGU7XG4gIC8vaGVyZSA9IHN0YXRlLmhlcmU7XG4gIF9pbiA9IHN0cm0ubmV4dF9pbjtcbiAgaW5wdXQgPSBzdHJtLmlucHV0O1xuICBsYXN0ID0gX2luICsgKHN0cm0uYXZhaWxfaW4gLSA1KTtcbiAgX291dCA9IHN0cm0ubmV4dF9vdXQ7XG4gIG91dHB1dCA9IHN0cm0ub3V0cHV0O1xuICBiZWcgPSBfb3V0IC0gKHN0YXJ0IC0gc3RybS5hdmFpbF9vdXQpO1xuICBlbmQgPSBfb3V0ICsgKHN0cm0uYXZhaWxfb3V0IC0gMjU3KTtcbi8vI2lmZGVmIElORkxBVEVfU1RSSUNUXG4gIGRtYXggPSBzdGF0ZS5kbWF4O1xuLy8jZW5kaWZcbiAgd3NpemUgPSBzdGF0ZS53c2l6ZTtcbiAgd2hhdmUgPSBzdGF0ZS53aGF2ZTtcbiAgd25leHQgPSBzdGF0ZS53bmV4dDtcbiAgd2luZG93ID0gc3RhdGUud2luZG93O1xuICBob2xkID0gc3RhdGUuaG9sZDtcbiAgYml0cyA9IHN0YXRlLmJpdHM7XG4gIGxjb2RlID0gc3RhdGUubGVuY29kZTtcbiAgZGNvZGUgPSBzdGF0ZS5kaXN0Y29kZTtcbiAgbG1hc2sgPSAoMSA8PCBzdGF0ZS5sZW5iaXRzKSAtIDE7XG4gIGRtYXNrID0gKDEgPDwgc3RhdGUuZGlzdGJpdHMpIC0gMTtcblxuXG4gIC8qIGRlY29kZSBsaXRlcmFscyBhbmQgbGVuZ3RoL2Rpc3RhbmNlcyB1bnRpbCBlbmQtb2YtYmxvY2sgb3Igbm90IGVub3VnaFxuICAgICBpbnB1dCBkYXRhIG9yIG91dHB1dCBzcGFjZSAqL1xuXG4gIHRvcDpcbiAgZG8ge1xuICAgIGlmIChiaXRzIDwgMTUpIHtcbiAgICAgIGhvbGQgKz0gaW5wdXRbX2luKytdIDw8IGJpdHM7XG4gICAgICBiaXRzICs9IDg7XG4gICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgYml0cyArPSA4O1xuICAgIH1cblxuICAgIGhlcmUgPSBsY29kZVtob2xkICYgbG1hc2tdO1xuXG4gICAgZG9sZW46XG4gICAgZm9yICg7OykgeyAvLyBHb3RvIGVtdWxhdGlvblxuICAgICAgb3AgPSBoZXJlID4+PiAyNC8qaGVyZS5iaXRzKi87XG4gICAgICBob2xkID4+Pj0gb3A7XG4gICAgICBiaXRzIC09IG9wO1xuICAgICAgb3AgPSAoaGVyZSA+Pj4gMTYpICYgMHhmZi8qaGVyZS5vcCovO1xuICAgICAgaWYgKG9wID09PSAwKSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBsaXRlcmFsICovXG4gICAgICAgIC8vVHJhY2V2digoc3RkZXJyLCBoZXJlLnZhbCA+PSAweDIwICYmIGhlcmUudmFsIDwgMHg3ZiA/XG4gICAgICAgIC8vICAgICAgICBcImluZmxhdGU6ICAgICAgICAgbGl0ZXJhbCAnJWMnXFxuXCIgOlxuICAgICAgICAvLyAgICAgICAgXCJpbmZsYXRlOiAgICAgICAgIGxpdGVyYWwgMHglMDJ4XFxuXCIsIGhlcmUudmFsKSk7XG4gICAgICAgIG91dHB1dFtfb3V0KytdID0gaGVyZSAmIDB4ZmZmZi8qaGVyZS52YWwqLztcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYgKG9wICYgMTYpIHsgICAgICAgICAgICAgICAgICAgICAvKiBsZW5ndGggYmFzZSAqL1xuICAgICAgICBsZW4gPSBoZXJlICYgMHhmZmZmLypoZXJlLnZhbCovO1xuICAgICAgICBvcCAmPSAxNTsgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBudW1iZXIgb2YgZXh0cmEgYml0cyAqL1xuICAgICAgICBpZiAob3ApIHtcbiAgICAgICAgICBpZiAoYml0cyA8IG9wKSB7XG4gICAgICAgICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgIH1cbiAgICAgICAgICBsZW4gKz0gaG9sZCAmICgoMSA8PCBvcCkgLSAxKTtcbiAgICAgICAgICBob2xkID4+Pj0gb3A7XG4gICAgICAgICAgYml0cyAtPSBvcDtcbiAgICAgICAgfVxuICAgICAgICAvL1RyYWNldnYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgICAgIGxlbmd0aCAldVxcblwiLCBsZW4pKTtcbiAgICAgICAgaWYgKGJpdHMgPCAxNSkge1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbX2luKytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbX2luKytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIGhlcmUgPSBkY29kZVtob2xkICYgZG1hc2tdO1xuXG4gICAgICAgIGRvZGlzdDpcbiAgICAgICAgZm9yICg7OykgeyAvLyBnb3RvIGVtdWxhdGlvblxuICAgICAgICAgIG9wID0gaGVyZSA+Pj4gMjQvKmhlcmUuYml0cyovO1xuICAgICAgICAgIGhvbGQgPj4+PSBvcDtcbiAgICAgICAgICBiaXRzIC09IG9wO1xuICAgICAgICAgIG9wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmYvKmhlcmUub3AqLztcblxuICAgICAgICAgIGlmIChvcCAmIDE2KSB7ICAgICAgICAgICAgICAgICAgICAgIC8qIGRpc3RhbmNlIGJhc2UgKi9cbiAgICAgICAgICAgIGRpc3QgPSBoZXJlICYgMHhmZmZmLypoZXJlLnZhbCovO1xuICAgICAgICAgICAgb3AgJj0gMTU7ICAgICAgICAgICAgICAgICAgICAgICAvKiBudW1iZXIgb2YgZXh0cmEgYml0cyAqL1xuICAgICAgICAgICAgaWYgKGJpdHMgPCBvcCkge1xuICAgICAgICAgICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgICAgICAgIGlmIChiaXRzIDwgb3ApIHtcbiAgICAgICAgICAgICAgICBob2xkICs9IGlucHV0W19pbisrXSA8PCBiaXRzO1xuICAgICAgICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZGlzdCArPSBob2xkICYgKCgxIDw8IG9wKSAtIDEpO1xuLy8jaWZkZWYgSU5GTEFURV9TVFJJQ1RcbiAgICAgICAgICAgIGlmIChkaXN0ID4gZG1heCkge1xuICAgICAgICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGRpc3RhbmNlIHRvbyBmYXIgYmFjayc7XG4gICAgICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgICAgIGJyZWFrIHRvcDtcbiAgICAgICAgICAgIH1cbi8vI2VuZGlmXG4gICAgICAgICAgICBob2xkID4+Pj0gb3A7XG4gICAgICAgICAgICBiaXRzIC09IG9wO1xuICAgICAgICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgICBkaXN0YW5jZSAldVxcblwiLCBkaXN0KSk7XG4gICAgICAgICAgICBvcCA9IF9vdXQgLSBiZWc7ICAgICAgICAgICAgICAgIC8qIG1heCBkaXN0YW5jZSBpbiBvdXRwdXQgKi9cbiAgICAgICAgICAgIGlmIChkaXN0ID4gb3ApIHsgICAgICAgICAgICAgICAgLyogc2VlIGlmIGNvcHkgZnJvbSB3aW5kb3cgKi9cbiAgICAgICAgICAgICAgb3AgPSBkaXN0IC0gb3A7ICAgICAgICAgICAgICAgLyogZGlzdGFuY2UgYmFjayBpbiB3aW5kb3cgKi9cbiAgICAgICAgICAgICAgaWYgKG9wID4gd2hhdmUpIHtcbiAgICAgICAgICAgICAgICBpZiAoc3RhdGUuc2FuZSkge1xuICAgICAgICAgICAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBkaXN0YW5jZSB0b28gZmFyIGJhY2snO1xuICAgICAgICAgICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICAgICAgICAgIGJyZWFrIHRvcDtcbiAgICAgICAgICAgICAgICB9XG5cbi8vICghKSBUaGlzIGJsb2NrIGlzIGRpc2FibGVkIGluIHpsaWIgZGVmYWlsdHMsXG4vLyBkb24ndCBlbmFibGUgaXQgZm9yIGJpbmFyeSBjb21wYXRpYmlsaXR5XG4vLyNpZmRlZiBJTkZMQVRFX0FMTE9XX0lOVkFMSURfRElTVEFOQ0VfVE9PRkFSX0FSUlJcbi8vICAgICAgICAgICAgICAgIGlmIChsZW4gPD0gb3AgLSB3aGF2ZSkge1xuLy8gICAgICAgICAgICAgICAgICBkbyB7XG4vLyAgICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSAwO1xuLy8gICAgICAgICAgICAgICAgICB9IHdoaWxlICgtLWxlbik7XG4vLyAgICAgICAgICAgICAgICAgIGNvbnRpbnVlIHRvcDtcbi8vICAgICAgICAgICAgICAgIH1cbi8vICAgICAgICAgICAgICAgIGxlbiAtPSBvcCAtIHdoYXZlO1xuLy8gICAgICAgICAgICAgICAgZG8ge1xuLy8gICAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IDA7XG4vLyAgICAgICAgICAgICAgICB9IHdoaWxlICgtLW9wID4gd2hhdmUpO1xuLy8gICAgICAgICAgICAgICAgaWYgKG9wID09PSAwKSB7XG4vLyAgICAgICAgICAgICAgICAgIGZyb20gPSBfb3V0IC0gZGlzdDtcbi8vICAgICAgICAgICAgICAgICAgZG8ge1xuLy8gICAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gb3V0cHV0W2Zyb20rK107XG4vLyAgICAgICAgICAgICAgICAgIH0gd2hpbGUgKC0tbGVuKTtcbi8vICAgICAgICAgICAgICAgICAgY29udGludWUgdG9wO1xuLy8gICAgICAgICAgICAgICAgfVxuLy8jZW5kaWZcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBmcm9tID0gMDsgLy8gd2luZG93IGluZGV4XG4gICAgICAgICAgICAgIGZyb21fc291cmNlID0gd2luZG93O1xuICAgICAgICAgICAgICBpZiAod25leHQgPT09IDApIHsgICAgICAgICAgIC8qIHZlcnkgY29tbW9uIGNhc2UgKi9cbiAgICAgICAgICAgICAgICBmcm9tICs9IHdzaXplIC0gb3A7XG4gICAgICAgICAgICAgICAgaWYgKG9wIDwgbGVuKSB7ICAgICAgICAgLyogc29tZSBmcm9tIHdpbmRvdyAqL1xuICAgICAgICAgICAgICAgICAgbGVuIC09IG9wO1xuICAgICAgICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IHdpbmRvd1tmcm9tKytdO1xuICAgICAgICAgICAgICAgICAgfSB3aGlsZSAoLS1vcCk7XG4gICAgICAgICAgICAgICAgICBmcm9tID0gX291dCAtIGRpc3Q7ICAvKiByZXN0IGZyb20gb3V0cHV0ICovXG4gICAgICAgICAgICAgICAgICBmcm9tX3NvdXJjZSA9IG91dHB1dDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZWxzZSBpZiAod25leHQgPCBvcCkgeyAgICAgIC8qIHdyYXAgYXJvdW5kIHdpbmRvdyAqL1xuICAgICAgICAgICAgICAgIGZyb20gKz0gd3NpemUgKyB3bmV4dCAtIG9wO1xuICAgICAgICAgICAgICAgIG9wIC09IHduZXh0O1xuICAgICAgICAgICAgICAgIGlmIChvcCA8IGxlbikgeyAgICAgICAgIC8qIHNvbWUgZnJvbSBlbmQgb2Ygd2luZG93ICovXG4gICAgICAgICAgICAgICAgICBsZW4gLT0gb3A7XG4gICAgICAgICAgICAgICAgICBkbyB7XG4gICAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gd2luZG93W2Zyb20rK107XG4gICAgICAgICAgICAgICAgICB9IHdoaWxlICgtLW9wKTtcbiAgICAgICAgICAgICAgICAgIGZyb20gPSAwO1xuICAgICAgICAgICAgICAgICAgaWYgKHduZXh0IDwgbGVuKSB7ICAvKiBzb21lIGZyb20gc3RhcnQgb2Ygd2luZG93ICovXG4gICAgICAgICAgICAgICAgICAgIG9wID0gd25leHQ7XG4gICAgICAgICAgICAgICAgICAgIGxlbiAtPSBvcDtcbiAgICAgICAgICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gd2luZG93W2Zyb20rK107XG4gICAgICAgICAgICAgICAgICAgIH0gd2hpbGUgKC0tb3ApO1xuICAgICAgICAgICAgICAgICAgICBmcm9tID0gX291dCAtIGRpc3Q7ICAgICAgLyogcmVzdCBmcm9tIG91dHB1dCAqL1xuICAgICAgICAgICAgICAgICAgICBmcm9tX3NvdXJjZSA9IG91dHB1dDtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZWxzZSB7ICAgICAgICAgICAgICAgICAgICAgIC8qIGNvbnRpZ3VvdXMgaW4gd2luZG93ICovXG4gICAgICAgICAgICAgICAgZnJvbSArPSB3bmV4dCAtIG9wO1xuICAgICAgICAgICAgICAgIGlmIChvcCA8IGxlbikgeyAgICAgICAgIC8qIHNvbWUgZnJvbSB3aW5kb3cgKi9cbiAgICAgICAgICAgICAgICAgIGxlbiAtPSBvcDtcbiAgICAgICAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSB3aW5kb3dbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICAgIH0gd2hpbGUgKC0tb3ApO1xuICAgICAgICAgICAgICAgICAgZnJvbSA9IF9vdXQgLSBkaXN0OyAgLyogcmVzdCBmcm9tIG91dHB1dCAqL1xuICAgICAgICAgICAgICAgICAgZnJvbV9zb3VyY2UgPSBvdXRwdXQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHdoaWxlIChsZW4gPiAyKSB7XG4gICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBmcm9tX3NvdXJjZVtmcm9tKytdO1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gZnJvbV9zb3VyY2VbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IGZyb21fc291cmNlW2Zyb20rK107XG4gICAgICAgICAgICAgICAgbGVuIC09IDM7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKGxlbikge1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gZnJvbV9zb3VyY2VbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBpZiAobGVuID4gMSkge1xuICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBmcm9tX3NvdXJjZVtmcm9tKytdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgIGZyb20gPSBfb3V0IC0gZGlzdDsgICAgICAgICAgLyogY29weSBkaXJlY3QgZnJvbSBvdXRwdXQgKi9cbiAgICAgICAgICAgICAgZG8geyAgICAgICAgICAgICAgICAgICAgICAgIC8qIG1pbmltdW0gbGVuZ3RoIGlzIHRocmVlICovXG4gICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBvdXRwdXRbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBvdXRwdXRbX291dCsrXSA9IG91dHB1dFtmcm9tKytdO1xuICAgICAgICAgICAgICAgIG91dHB1dFtfb3V0KytdID0gb3V0cHV0W2Zyb20rK107XG4gICAgICAgICAgICAgICAgbGVuIC09IDM7XG4gICAgICAgICAgICAgIH0gd2hpbGUgKGxlbiA+IDIpO1xuICAgICAgICAgICAgICBpZiAobGVuKSB7XG4gICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBvdXRwdXRbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICBpZiAobGVuID4gMSkge1xuICAgICAgICAgICAgICAgICAgb3V0cHV0W19vdXQrK10gPSBvdXRwdXRbZnJvbSsrXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgZWxzZSBpZiAoKG9wICYgNjQpID09PSAwKSB7ICAgICAgICAgIC8qIDJuZCBsZXZlbCBkaXN0YW5jZSBjb2RlICovXG4gICAgICAgICAgICBoZXJlID0gZGNvZGVbKGhlcmUgJiAweGZmZmYpLypoZXJlLnZhbCovICsgKGhvbGQgJiAoKDEgPDwgb3ApIC0gMSkpXTtcbiAgICAgICAgICAgIGNvbnRpbnVlIGRvZGlzdDtcbiAgICAgICAgICB9XG4gICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGRpc3RhbmNlIGNvZGUnO1xuICAgICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICAgIGJyZWFrIHRvcDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhazsgLy8gbmVlZCB0byBlbXVsYXRlIGdvdG8gdmlhIFwiY29udGludWVcIlxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBlbHNlIGlmICgob3AgJiA2NCkgPT09IDApIHsgICAgICAgICAgICAgIC8qIDJuZCBsZXZlbCBsZW5ndGggY29kZSAqL1xuICAgICAgICBoZXJlID0gbGNvZGVbKGhlcmUgJiAweGZmZmYpLypoZXJlLnZhbCovICsgKGhvbGQgJiAoKDEgPDwgb3ApIC0gMSkpXTtcbiAgICAgICAgY29udGludWUgZG9sZW47XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChvcCAmIDMyKSB7ICAgICAgICAgICAgICAgICAgICAgLyogZW5kLW9mLWJsb2NrICovXG4gICAgICAgIC8vVHJhY2V2digoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgICAgZW5kIG9mIGJsb2NrXFxuXCIpKTtcbiAgICAgICAgc3RhdGUubW9kZSA9IFRZUEU7XG4gICAgICAgIGJyZWFrIHRvcDtcbiAgICAgIH1cbiAgICAgIGVsc2Uge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGxpdGVyYWwvbGVuZ3RoIGNvZGUnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhayB0b3A7XG4gICAgICB9XG5cbiAgICAgIGJyZWFrOyAvLyBuZWVkIHRvIGVtdWxhdGUgZ290byB2aWEgXCJjb250aW51ZVwiXG4gICAgfVxuICB9IHdoaWxlIChfaW4gPCBsYXN0ICYmIF9vdXQgPCBlbmQpO1xuXG4gIC8qIHJldHVybiB1bnVzZWQgYnl0ZXMgKG9uIGVudHJ5LCBiaXRzIDwgOCwgc28gaW4gd29uJ3QgZ28gdG9vIGZhciBiYWNrKSAqL1xuICBsZW4gPSBiaXRzID4+IDM7XG4gIF9pbiAtPSBsZW47XG4gIGJpdHMgLT0gbGVuIDw8IDM7XG4gIGhvbGQgJj0gKDEgPDwgYml0cykgLSAxO1xuXG4gIC8qIHVwZGF0ZSBzdGF0ZSBhbmQgcmV0dXJuICovXG4gIHN0cm0ubmV4dF9pbiA9IF9pbjtcbiAgc3RybS5uZXh0X291dCA9IF9vdXQ7XG4gIHN0cm0uYXZhaWxfaW4gPSAoX2luIDwgbGFzdCA/IDUgKyAobGFzdCAtIF9pbikgOiA1IC0gKF9pbiAtIGxhc3QpKTtcbiAgc3RybS5hdmFpbF9vdXQgPSAoX291dCA8IGVuZCA/IDI1NyArIChlbmQgLSBfb3V0KSA6IDI1NyAtIChfb3V0IC0gZW5kKSk7XG4gIHN0YXRlLmhvbGQgPSBob2xkO1xuICBzdGF0ZS5iaXRzID0gYml0cztcbiAgcmV0dXJuO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy9jb21tb24nKTtcbnZhciBhZGxlcjMyID0gcmVxdWlyZSgnLi9hZGxlcjMyJyk7XG52YXIgY3JjMzIgICA9IHJlcXVpcmUoJy4vY3JjMzInKTtcbnZhciBpbmZsYXRlX2Zhc3QgPSByZXF1aXJlKCcuL2luZmZhc3QnKTtcbnZhciBpbmZsYXRlX3RhYmxlID0gcmVxdWlyZSgnLi9pbmZ0cmVlcycpO1xuXG52YXIgQ09ERVMgPSAwO1xudmFyIExFTlMgPSAxO1xudmFyIERJU1RTID0gMjtcblxuLyogUHVibGljIGNvbnN0YW50cyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxuLyogQWxsb3dlZCBmbHVzaCB2YWx1ZXM7IHNlZSBkZWZsYXRlKCkgYW5kIGluZmxhdGUoKSBiZWxvdyBmb3IgZGV0YWlscyAqL1xuLy92YXIgWl9OT19GTFVTSCAgICAgID0gMDtcbi8vdmFyIFpfUEFSVElBTF9GTFVTSCA9IDE7XG4vL3ZhciBaX1NZTkNfRkxVU0ggICAgPSAyO1xuLy92YXIgWl9GVUxMX0ZMVVNIICAgID0gMztcbnZhciBaX0ZJTklTSCAgICAgICAgPSA0O1xudmFyIFpfQkxPQ0sgICAgICAgICA9IDU7XG52YXIgWl9UUkVFUyAgICAgICAgID0gNjtcblxuXG4vKiBSZXR1cm4gY29kZXMgZm9yIHRoZSBjb21wcmVzc2lvbi9kZWNvbXByZXNzaW9uIGZ1bmN0aW9ucy4gTmVnYXRpdmUgdmFsdWVzXG4gKiBhcmUgZXJyb3JzLCBwb3NpdGl2ZSB2YWx1ZXMgYXJlIHVzZWQgZm9yIHNwZWNpYWwgYnV0IG5vcm1hbCBldmVudHMuXG4gKi9cbnZhciBaX09LICAgICAgICAgICAgPSAwO1xudmFyIFpfU1RSRUFNX0VORCAgICA9IDE7XG52YXIgWl9ORUVEX0RJQ1QgICAgID0gMjtcbi8vdmFyIFpfRVJSTk8gICAgICAgICA9IC0xO1xudmFyIFpfU1RSRUFNX0VSUk9SICA9IC0yO1xudmFyIFpfREFUQV9FUlJPUiAgICA9IC0zO1xudmFyIFpfTUVNX0VSUk9SICAgICA9IC00O1xudmFyIFpfQlVGX0VSUk9SICAgICA9IC01O1xuLy92YXIgWl9WRVJTSU9OX0VSUk9SID0gLTY7XG5cbi8qIFRoZSBkZWZsYXRlIGNvbXByZXNzaW9uIG1ldGhvZCAqL1xudmFyIFpfREVGTEFURUQgID0gODtcblxuXG4vKiBTVEFURVMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuXG52YXIgICAgSEVBRCA9IDE7ICAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIG1hZ2ljIGhlYWRlciAqL1xudmFyICAgIEZMQUdTID0gMjsgICAgICAvKiBpOiB3YWl0aW5nIGZvciBtZXRob2QgYW5kIGZsYWdzIChnemlwKSAqL1xudmFyICAgIFRJTUUgPSAzOyAgICAgICAvKiBpOiB3YWl0aW5nIGZvciBtb2RpZmljYXRpb24gdGltZSAoZ3ppcCkgKi9cbnZhciAgICBPUyA9IDQ7ICAgICAgICAgLyogaTogd2FpdGluZyBmb3IgZXh0cmEgZmxhZ3MgYW5kIG9wZXJhdGluZyBzeXN0ZW0gKGd6aXApICovXG52YXIgICAgRVhMRU4gPSA1OyAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGV4dHJhIGxlbmd0aCAoZ3ppcCkgKi9cbnZhciAgICBFWFRSQSA9IDY7ICAgICAgLyogaTogd2FpdGluZyBmb3IgZXh0cmEgYnl0ZXMgKGd6aXApICovXG52YXIgICAgTkFNRSA9IDc7ICAgICAgIC8qIGk6IHdhaXRpbmcgZm9yIGVuZCBvZiBmaWxlIG5hbWUgKGd6aXApICovXG52YXIgICAgQ09NTUVOVCA9IDg7ICAgIC8qIGk6IHdhaXRpbmcgZm9yIGVuZCBvZiBjb21tZW50IChnemlwKSAqL1xudmFyICAgIEhDUkMgPSA5OyAgICAgICAvKiBpOiB3YWl0aW5nIGZvciBoZWFkZXIgY3JjIChnemlwKSAqL1xudmFyICAgIERJQ1RJRCA9IDEwOyAgICAvKiBpOiB3YWl0aW5nIGZvciBkaWN0aW9uYXJ5IGNoZWNrIHZhbHVlICovXG52YXIgICAgRElDVCA9IDExOyAgICAgIC8qIHdhaXRpbmcgZm9yIGluZmxhdGVTZXREaWN0aW9uYXJ5KCkgY2FsbCAqL1xudmFyICAgICAgICBUWVBFID0gMTI7ICAgICAgLyogaTogd2FpdGluZyBmb3IgdHlwZSBiaXRzLCBpbmNsdWRpbmcgbGFzdC1mbGFnIGJpdCAqL1xudmFyICAgICAgICBUWVBFRE8gPSAxMzsgICAgLyogaTogc2FtZSwgYnV0IHNraXAgY2hlY2sgdG8gZXhpdCBpbmZsYXRlIG9uIG5ldyBibG9jayAqL1xudmFyICAgICAgICBTVE9SRUQgPSAxNDsgICAgLyogaTogd2FpdGluZyBmb3Igc3RvcmVkIHNpemUgKGxlbmd0aCBhbmQgY29tcGxlbWVudCkgKi9cbnZhciAgICAgICAgQ09QWV8gPSAxNTsgICAgIC8qIGkvbzogc2FtZSBhcyBDT1BZIGJlbG93LCBidXQgb25seSBmaXJzdCB0aW1lIGluICovXG52YXIgICAgICAgIENPUFkgPSAxNjsgICAgICAvKiBpL286IHdhaXRpbmcgZm9yIGlucHV0IG9yIG91dHB1dCB0byBjb3B5IHN0b3JlZCBibG9jayAqL1xudmFyICAgICAgICBUQUJMRSA9IDE3OyAgICAgLyogaTogd2FpdGluZyBmb3IgZHluYW1pYyBibG9jayB0YWJsZSBsZW5ndGhzICovXG52YXIgICAgICAgIExFTkxFTlMgPSAxODsgICAvKiBpOiB3YWl0aW5nIGZvciBjb2RlIGxlbmd0aCBjb2RlIGxlbmd0aHMgKi9cbnZhciAgICAgICAgQ09ERUxFTlMgPSAxOTsgIC8qIGk6IHdhaXRpbmcgZm9yIGxlbmd0aC9saXQgYW5kIGRpc3RhbmNlIGNvZGUgbGVuZ3RocyAqL1xudmFyICAgICAgICAgICAgTEVOXyA9IDIwOyAgICAgIC8qIGk6IHNhbWUgYXMgTEVOIGJlbG93LCBidXQgb25seSBmaXJzdCB0aW1lIGluICovXG52YXIgICAgICAgICAgICBMRU4gPSAyMTsgICAgICAgLyogaTogd2FpdGluZyBmb3IgbGVuZ3RoL2xpdC9lb2IgY29kZSAqL1xudmFyICAgICAgICAgICAgTEVORVhUID0gMjI7ICAgIC8qIGk6IHdhaXRpbmcgZm9yIGxlbmd0aCBleHRyYSBiaXRzICovXG52YXIgICAgICAgICAgICBESVNUID0gMjM7ICAgICAgLyogaTogd2FpdGluZyBmb3IgZGlzdGFuY2UgY29kZSAqL1xudmFyICAgICAgICAgICAgRElTVEVYVCA9IDI0OyAgIC8qIGk6IHdhaXRpbmcgZm9yIGRpc3RhbmNlIGV4dHJhIGJpdHMgKi9cbnZhciAgICAgICAgICAgIE1BVENIID0gMjU7ICAgICAvKiBvOiB3YWl0aW5nIGZvciBvdXRwdXQgc3BhY2UgdG8gY29weSBzdHJpbmcgKi9cbnZhciAgICAgICAgICAgIExJVCA9IDI2OyAgICAgICAvKiBvOiB3YWl0aW5nIGZvciBvdXRwdXQgc3BhY2UgdG8gd3JpdGUgbGl0ZXJhbCAqL1xudmFyICAgIENIRUNLID0gMjc7ICAgICAvKiBpOiB3YWl0aW5nIGZvciAzMi1iaXQgY2hlY2sgdmFsdWUgKi9cbnZhciAgICBMRU5HVEggPSAyODsgICAgLyogaTogd2FpdGluZyBmb3IgMzItYml0IGxlbmd0aCAoZ3ppcCkgKi9cbnZhciAgICBET05FID0gMjk7ICAgICAgLyogZmluaXNoZWQgY2hlY2ssIGRvbmUgLS0gcmVtYWluIGhlcmUgdW50aWwgcmVzZXQgKi9cbnZhciAgICBCQUQgPSAzMDsgICAgICAgLyogZ290IGEgZGF0YSBlcnJvciAtLSByZW1haW4gaGVyZSB1bnRpbCByZXNldCAqL1xudmFyICAgIE1FTSA9IDMxOyAgICAgICAvKiBnb3QgYW4gaW5mbGF0ZSgpIG1lbW9yeSBlcnJvciAtLSByZW1haW4gaGVyZSB1bnRpbCByZXNldCAqL1xudmFyICAgIFNZTkMgPSAzMjsgICAgICAvKiBsb29raW5nIGZvciBzeW5jaHJvbml6YXRpb24gYnl0ZXMgdG8gcmVzdGFydCBpbmZsYXRlKCkgKi9cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuXG5cbnZhciBFTk9VR0hfTEVOUyA9IDg1MjtcbnZhciBFTk9VR0hfRElTVFMgPSA1OTI7XG4vL3ZhciBFTk9VR0ggPSAgKEVOT1VHSF9MRU5TK0VOT1VHSF9ESVNUUyk7XG5cbnZhciBNQVhfV0JJVFMgPSAxNTtcbi8qIDMySyBMWjc3IHdpbmRvdyAqL1xudmFyIERFRl9XQklUUyA9IE1BWF9XQklUUztcblxuXG5mdW5jdGlvbiBaU1dBUDMyKHEpIHtcbiAgcmV0dXJuICAoKChxID4+PiAyNCkgJiAweGZmKSArXG4gICAgICAgICAgKChxID4+PiA4KSAmIDB4ZmYwMCkgK1xuICAgICAgICAgICgocSAmIDB4ZmYwMCkgPDwgOCkgK1xuICAgICAgICAgICgocSAmIDB4ZmYpIDw8IDI0KSk7XG59XG5cblxuZnVuY3Rpb24gSW5mbGF0ZVN0YXRlKCkge1xuICB0aGlzLm1vZGUgPSAwOyAgICAgICAgICAgICAvKiBjdXJyZW50IGluZmxhdGUgbW9kZSAqL1xuICB0aGlzLmxhc3QgPSBmYWxzZTsgICAgICAgICAgLyogdHJ1ZSBpZiBwcm9jZXNzaW5nIGxhc3QgYmxvY2sgKi9cbiAgdGhpcy53cmFwID0gMDsgICAgICAgICAgICAgIC8qIGJpdCAwIHRydWUgZm9yIHpsaWIsIGJpdCAxIHRydWUgZm9yIGd6aXAgKi9cbiAgdGhpcy5oYXZlZGljdCA9IGZhbHNlOyAgICAgIC8qIHRydWUgaWYgZGljdGlvbmFyeSBwcm92aWRlZCAqL1xuICB0aGlzLmZsYWdzID0gMDsgICAgICAgICAgICAgLyogZ3ppcCBoZWFkZXIgbWV0aG9kIGFuZCBmbGFncyAoMCBpZiB6bGliKSAqL1xuICB0aGlzLmRtYXggPSAwOyAgICAgICAgICAgICAgLyogemxpYiBoZWFkZXIgbWF4IGRpc3RhbmNlIChJTkZMQVRFX1NUUklDVCkgKi9cbiAgdGhpcy5jaGVjayA9IDA7ICAgICAgICAgICAgIC8qIHByb3RlY3RlZCBjb3B5IG9mIGNoZWNrIHZhbHVlICovXG4gIHRoaXMudG90YWwgPSAwOyAgICAgICAgICAgICAvKiBwcm90ZWN0ZWQgY29weSBvZiBvdXRwdXQgY291bnQgKi9cbiAgLy8gVE9ETzogbWF5IGJlIHt9XG4gIHRoaXMuaGVhZCA9IG51bGw7ICAgICAgICAgICAvKiB3aGVyZSB0byBzYXZlIGd6aXAgaGVhZGVyIGluZm9ybWF0aW9uICovXG5cbiAgLyogc2xpZGluZyB3aW5kb3cgKi9cbiAgdGhpcy53Yml0cyA9IDA7ICAgICAgICAgICAgIC8qIGxvZyBiYXNlIDIgb2YgcmVxdWVzdGVkIHdpbmRvdyBzaXplICovXG4gIHRoaXMud3NpemUgPSAwOyAgICAgICAgICAgICAvKiB3aW5kb3cgc2l6ZSBvciB6ZXJvIGlmIG5vdCB1c2luZyB3aW5kb3cgKi9cbiAgdGhpcy53aGF2ZSA9IDA7ICAgICAgICAgICAgIC8qIHZhbGlkIGJ5dGVzIGluIHRoZSB3aW5kb3cgKi9cbiAgdGhpcy53bmV4dCA9IDA7ICAgICAgICAgICAgIC8qIHdpbmRvdyB3cml0ZSBpbmRleCAqL1xuICB0aGlzLndpbmRvdyA9IG51bGw7ICAgICAgICAgLyogYWxsb2NhdGVkIHNsaWRpbmcgd2luZG93LCBpZiBuZWVkZWQgKi9cblxuICAvKiBiaXQgYWNjdW11bGF0b3IgKi9cbiAgdGhpcy5ob2xkID0gMDsgICAgICAgICAgICAgIC8qIGlucHV0IGJpdCBhY2N1bXVsYXRvciAqL1xuICB0aGlzLmJpdHMgPSAwOyAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGJpdHMgaW4gXCJpblwiICovXG5cbiAgLyogZm9yIHN0cmluZyBhbmQgc3RvcmVkIGJsb2NrIGNvcHlpbmcgKi9cbiAgdGhpcy5sZW5ndGggPSAwOyAgICAgICAgICAgIC8qIGxpdGVyYWwgb3IgbGVuZ3RoIG9mIGRhdGEgdG8gY29weSAqL1xuICB0aGlzLm9mZnNldCA9IDA7ICAgICAgICAgICAgLyogZGlzdGFuY2UgYmFjayB0byBjb3B5IHN0cmluZyBmcm9tICovXG5cbiAgLyogZm9yIHRhYmxlIGFuZCBjb2RlIGRlY29kaW5nICovXG4gIHRoaXMuZXh0cmEgPSAwOyAgICAgICAgICAgICAvKiBleHRyYSBiaXRzIG5lZWRlZCAqL1xuXG4gIC8qIGZpeGVkIGFuZCBkeW5hbWljIGNvZGUgdGFibGVzICovXG4gIHRoaXMubGVuY29kZSA9IG51bGw7ICAgICAgICAgIC8qIHN0YXJ0aW5nIHRhYmxlIGZvciBsZW5ndGgvbGl0ZXJhbCBjb2RlcyAqL1xuICB0aGlzLmRpc3Rjb2RlID0gbnVsbDsgICAgICAgICAvKiBzdGFydGluZyB0YWJsZSBmb3IgZGlzdGFuY2UgY29kZXMgKi9cbiAgdGhpcy5sZW5iaXRzID0gMDsgICAgICAgICAgIC8qIGluZGV4IGJpdHMgZm9yIGxlbmNvZGUgKi9cbiAgdGhpcy5kaXN0Yml0cyA9IDA7ICAgICAgICAgIC8qIGluZGV4IGJpdHMgZm9yIGRpc3Rjb2RlICovXG5cbiAgLyogZHluYW1pYyB0YWJsZSBidWlsZGluZyAqL1xuICB0aGlzLm5jb2RlID0gMDsgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGNvZGUgbGVuZ3RoIGNvZGUgbGVuZ3RocyAqL1xuICB0aGlzLm5sZW4gPSAwOyAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGxlbmd0aCBjb2RlIGxlbmd0aHMgKi9cbiAgdGhpcy5uZGlzdCA9IDA7ICAgICAgICAgICAgIC8qIG51bWJlciBvZiBkaXN0YW5jZSBjb2RlIGxlbmd0aHMgKi9cbiAgdGhpcy5oYXZlID0gMDsgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBjb2RlIGxlbmd0aHMgaW4gbGVuc1tdICovXG4gIHRoaXMubmV4dCA9IG51bGw7ICAgICAgICAgICAgICAvKiBuZXh0IGF2YWlsYWJsZSBzcGFjZSBpbiBjb2Rlc1tdICovXG5cbiAgdGhpcy5sZW5zID0gbmV3IHV0aWxzLkJ1ZjE2KDMyMCk7IC8qIHRlbXBvcmFyeSBzdG9yYWdlIGZvciBjb2RlIGxlbmd0aHMgKi9cbiAgdGhpcy53b3JrID0gbmV3IHV0aWxzLkJ1ZjE2KDI4OCk7IC8qIHdvcmsgYXJlYSBmb3IgY29kZSB0YWJsZSBidWlsZGluZyAqL1xuXG4gIC8qXG4gICBiZWNhdXNlIHdlIGRvbid0IGhhdmUgcG9pbnRlcnMgaW4ganMsIHdlIHVzZSBsZW5jb2RlIGFuZCBkaXN0Y29kZSBkaXJlY3RseVxuICAgYXMgYnVmZmVycyBzbyB3ZSBkb24ndCBuZWVkIGNvZGVzXG4gICovXG4gIC8vdGhpcy5jb2RlcyA9IG5ldyB1dGlscy5CdWYzMihFTk9VR0gpOyAgICAgICAvKiBzcGFjZSBmb3IgY29kZSB0YWJsZXMgKi9cbiAgdGhpcy5sZW5keW4gPSBudWxsOyAgICAgICAgICAgICAgLyogZHluYW1pYyB0YWJsZSBmb3IgbGVuZ3RoL2xpdGVyYWwgY29kZXMgKEpTIHNwZWNpZmljKSAqL1xuICB0aGlzLmRpc3RkeW4gPSBudWxsOyAgICAgICAgICAgICAvKiBkeW5hbWljIHRhYmxlIGZvciBkaXN0YW5jZSBjb2RlcyAoSlMgc3BlY2lmaWMpICovXG4gIHRoaXMuc2FuZSA9IDA7ICAgICAgICAgICAgICAgICAgIC8qIGlmIGZhbHNlLCBhbGxvdyBpbnZhbGlkIGRpc3RhbmNlIHRvbyBmYXIgKi9cbiAgdGhpcy5iYWNrID0gMDsgICAgICAgICAgICAgICAgICAgLyogYml0cyBiYWNrIG9mIGxhc3QgdW5wcm9jZXNzZWQgbGVuZ3RoL2xpdCAqL1xuICB0aGlzLndhcyA9IDA7ICAgICAgICAgICAgICAgICAgICAvKiBpbml0aWFsIGxlbmd0aCBvZiBtYXRjaCAqL1xufVxuXG5mdW5jdGlvbiBpbmZsYXRlUmVzZXRLZWVwKHN0cm0pIHtcbiAgdmFyIHN0YXRlO1xuXG4gIGlmICghc3RybSB8fCAhc3RybS5zdGF0ZSkgeyByZXR1cm4gWl9TVFJFQU1fRVJST1I7IH1cbiAgc3RhdGUgPSBzdHJtLnN0YXRlO1xuICBzdHJtLnRvdGFsX2luID0gc3RybS50b3RhbF9vdXQgPSBzdGF0ZS50b3RhbCA9IDA7XG4gIHN0cm0ubXNnID0gJyc7IC8qWl9OVUxMKi9cbiAgaWYgKHN0YXRlLndyYXApIHsgICAgICAgLyogdG8gc3VwcG9ydCBpbGwtY29uY2VpdmVkIEphdmEgdGVzdCBzdWl0ZSAqL1xuICAgIHN0cm0uYWRsZXIgPSBzdGF0ZS53cmFwICYgMTtcbiAgfVxuICBzdGF0ZS5tb2RlID0gSEVBRDtcbiAgc3RhdGUubGFzdCA9IDA7XG4gIHN0YXRlLmhhdmVkaWN0ID0gMDtcbiAgc3RhdGUuZG1heCA9IDMyNzY4O1xuICBzdGF0ZS5oZWFkID0gbnVsbC8qWl9OVUxMKi87XG4gIHN0YXRlLmhvbGQgPSAwO1xuICBzdGF0ZS5iaXRzID0gMDtcbiAgLy9zdGF0ZS5sZW5jb2RlID0gc3RhdGUuZGlzdGNvZGUgPSBzdGF0ZS5uZXh0ID0gc3RhdGUuY29kZXM7XG4gIHN0YXRlLmxlbmNvZGUgPSBzdGF0ZS5sZW5keW4gPSBuZXcgdXRpbHMuQnVmMzIoRU5PVUdIX0xFTlMpO1xuICBzdGF0ZS5kaXN0Y29kZSA9IHN0YXRlLmRpc3RkeW4gPSBuZXcgdXRpbHMuQnVmMzIoRU5PVUdIX0RJU1RTKTtcblxuICBzdGF0ZS5zYW5lID0gMTtcbiAgc3RhdGUuYmFjayA9IC0xO1xuICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6IHJlc2V0XFxuXCIpKTtcbiAgcmV0dXJuIFpfT0s7XG59XG5cbmZ1bmN0aW9uIGluZmxhdGVSZXNldChzdHJtKSB7XG4gIHZhciBzdGF0ZTtcblxuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUpIHsgcmV0dXJuIFpfU1RSRUFNX0VSUk9SOyB9XG4gIHN0YXRlID0gc3RybS5zdGF0ZTtcbiAgc3RhdGUud3NpemUgPSAwO1xuICBzdGF0ZS53aGF2ZSA9IDA7XG4gIHN0YXRlLnduZXh0ID0gMDtcbiAgcmV0dXJuIGluZmxhdGVSZXNldEtlZXAoc3RybSk7XG5cbn1cblxuZnVuY3Rpb24gaW5mbGF0ZVJlc2V0MihzdHJtLCB3aW5kb3dCaXRzKSB7XG4gIHZhciB3cmFwO1xuICB2YXIgc3RhdGU7XG5cbiAgLyogZ2V0IHRoZSBzdGF0ZSAqL1xuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUpIHsgcmV0dXJuIFpfU1RSRUFNX0VSUk9SOyB9XG4gIHN0YXRlID0gc3RybS5zdGF0ZTtcblxuICAvKiBleHRyYWN0IHdyYXAgcmVxdWVzdCBmcm9tIHdpbmRvd0JpdHMgcGFyYW1ldGVyICovXG4gIGlmICh3aW5kb3dCaXRzIDwgMCkge1xuICAgIHdyYXAgPSAwO1xuICAgIHdpbmRvd0JpdHMgPSAtd2luZG93Qml0cztcbiAgfVxuICBlbHNlIHtcbiAgICB3cmFwID0gKHdpbmRvd0JpdHMgPj4gNCkgKyAxO1xuICAgIGlmICh3aW5kb3dCaXRzIDwgNDgpIHtcbiAgICAgIHdpbmRvd0JpdHMgJj0gMTU7XG4gICAgfVxuICB9XG5cbiAgLyogc2V0IG51bWJlciBvZiB3aW5kb3cgYml0cywgZnJlZSB3aW5kb3cgaWYgZGlmZmVyZW50ICovXG4gIGlmICh3aW5kb3dCaXRzICYmICh3aW5kb3dCaXRzIDwgOCB8fCB3aW5kb3dCaXRzID4gMTUpKSB7XG4gICAgcmV0dXJuIFpfU1RSRUFNX0VSUk9SO1xuICB9XG4gIGlmIChzdGF0ZS53aW5kb3cgIT09IG51bGwgJiYgc3RhdGUud2JpdHMgIT09IHdpbmRvd0JpdHMpIHtcbiAgICBzdGF0ZS53aW5kb3cgPSBudWxsO1xuICB9XG5cbiAgLyogdXBkYXRlIHN0YXRlIGFuZCByZXNldCB0aGUgcmVzdCBvZiBpdCAqL1xuICBzdGF0ZS53cmFwID0gd3JhcDtcbiAgc3RhdGUud2JpdHMgPSB3aW5kb3dCaXRzO1xuICByZXR1cm4gaW5mbGF0ZVJlc2V0KHN0cm0pO1xufVxuXG5mdW5jdGlvbiBpbmZsYXRlSW5pdDIoc3RybSwgd2luZG93Qml0cykge1xuICB2YXIgcmV0O1xuICB2YXIgc3RhdGU7XG5cbiAgaWYgKCFzdHJtKSB7IHJldHVybiBaX1NUUkVBTV9FUlJPUjsgfVxuICAvL3N0cm0ubXNnID0gWl9OVUxMOyAgICAgICAgICAgICAgICAgLyogaW4gY2FzZSB3ZSByZXR1cm4gYW4gZXJyb3IgKi9cblxuICBzdGF0ZSA9IG5ldyBJbmZsYXRlU3RhdGUoKTtcblxuICAvL2lmIChzdGF0ZSA9PT0gWl9OVUxMKSByZXR1cm4gWl9NRU1fRVJST1I7XG4gIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogYWxsb2NhdGVkXFxuXCIpKTtcbiAgc3RybS5zdGF0ZSA9IHN0YXRlO1xuICBzdGF0ZS53aW5kb3cgPSBudWxsLypaX05VTEwqLztcbiAgcmV0ID0gaW5mbGF0ZVJlc2V0MihzdHJtLCB3aW5kb3dCaXRzKTtcbiAgaWYgKHJldCAhPT0gWl9PSykge1xuICAgIHN0cm0uc3RhdGUgPSBudWxsLypaX05VTEwqLztcbiAgfVxuICByZXR1cm4gcmV0O1xufVxuXG5mdW5jdGlvbiBpbmZsYXRlSW5pdChzdHJtKSB7XG4gIHJldHVybiBpbmZsYXRlSW5pdDIoc3RybSwgREVGX1dCSVRTKTtcbn1cblxuXG4vKlxuIFJldHVybiBzdGF0ZSB3aXRoIGxlbmd0aCBhbmQgZGlzdGFuY2UgZGVjb2RpbmcgdGFibGVzIGFuZCBpbmRleCBzaXplcyBzZXQgdG9cbiBmaXhlZCBjb2RlIGRlY29kaW5nLiAgTm9ybWFsbHkgdGhpcyByZXR1cm5zIGZpeGVkIHRhYmxlcyBmcm9tIGluZmZpeGVkLmguXG4gSWYgQlVJTERGSVhFRCBpcyBkZWZpbmVkLCB0aGVuIGluc3RlYWQgdGhpcyByb3V0aW5lIGJ1aWxkcyB0aGUgdGFibGVzIHRoZVxuIGZpcnN0IHRpbWUgaXQncyBjYWxsZWQsIGFuZCByZXR1cm5zIHRob3NlIHRhYmxlcyB0aGUgZmlyc3QgdGltZSBhbmRcbiB0aGVyZWFmdGVyLiAgVGhpcyByZWR1Y2VzIHRoZSBzaXplIG9mIHRoZSBjb2RlIGJ5IGFib3V0IDJLIGJ5dGVzLCBpblxuIGV4Y2hhbmdlIGZvciBhIGxpdHRsZSBleGVjdXRpb24gdGltZS4gIEhvd2V2ZXIsIEJVSUxERklYRUQgc2hvdWxkIG5vdCBiZVxuIHVzZWQgZm9yIHRocmVhZGVkIGFwcGxpY2F0aW9ucywgc2luY2UgdGhlIHJld3JpdGluZyBvZiB0aGUgdGFibGVzIGFuZCB2aXJnaW5cbiBtYXkgbm90IGJlIHRocmVhZC1zYWZlLlxuICovXG52YXIgdmlyZ2luID0gdHJ1ZTtcblxudmFyIGxlbmZpeCwgZGlzdGZpeDsgLy8gV2UgaGF2ZSBubyBwb2ludGVycyBpbiBKUywgc28ga2VlcCB0YWJsZXMgc2VwYXJhdGVcblxuZnVuY3Rpb24gZml4ZWR0YWJsZXMoc3RhdGUpIHtcbiAgLyogYnVpbGQgZml4ZWQgaHVmZm1hbiB0YWJsZXMgaWYgZmlyc3QgY2FsbCAobWF5IG5vdCBiZSB0aHJlYWQgc2FmZSkgKi9cbiAgaWYgKHZpcmdpbikge1xuICAgIHZhciBzeW07XG5cbiAgICBsZW5maXggPSBuZXcgdXRpbHMuQnVmMzIoNTEyKTtcbiAgICBkaXN0Zml4ID0gbmV3IHV0aWxzLkJ1ZjMyKDMyKTtcblxuICAgIC8qIGxpdGVyYWwvbGVuZ3RoIHRhYmxlICovXG4gICAgc3ltID0gMDtcbiAgICB3aGlsZSAoc3ltIDwgMTQ0KSB7IHN0YXRlLmxlbnNbc3ltKytdID0gODsgfVxuICAgIHdoaWxlIChzeW0gPCAyNTYpIHsgc3RhdGUubGVuc1tzeW0rK10gPSA5OyB9XG4gICAgd2hpbGUgKHN5bSA8IDI4MCkgeyBzdGF0ZS5sZW5zW3N5bSsrXSA9IDc7IH1cbiAgICB3aGlsZSAoc3ltIDwgMjg4KSB7IHN0YXRlLmxlbnNbc3ltKytdID0gODsgfVxuXG4gICAgaW5mbGF0ZV90YWJsZShMRU5TLCAgc3RhdGUubGVucywgMCwgMjg4LCBsZW5maXgsICAgMCwgc3RhdGUud29yaywge2JpdHM6IDl9KTtcblxuICAgIC8qIGRpc3RhbmNlIHRhYmxlICovXG4gICAgc3ltID0gMDtcbiAgICB3aGlsZSAoc3ltIDwgMzIpIHsgc3RhdGUubGVuc1tzeW0rK10gPSA1OyB9XG5cbiAgICBpbmZsYXRlX3RhYmxlKERJU1RTLCBzdGF0ZS5sZW5zLCAwLCAzMiwgICBkaXN0Zml4LCAwLCBzdGF0ZS53b3JrLCB7Yml0czogNX0pO1xuXG4gICAgLyogZG8gdGhpcyBqdXN0IG9uY2UgKi9cbiAgICB2aXJnaW4gPSBmYWxzZTtcbiAgfVxuXG4gIHN0YXRlLmxlbmNvZGUgPSBsZW5maXg7XG4gIHN0YXRlLmxlbmJpdHMgPSA5O1xuICBzdGF0ZS5kaXN0Y29kZSA9IGRpc3RmaXg7XG4gIHN0YXRlLmRpc3RiaXRzID0gNTtcbn1cblxuXG4vKlxuIFVwZGF0ZSB0aGUgd2luZG93IHdpdGggdGhlIGxhc3Qgd3NpemUgKG5vcm1hbGx5IDMySykgYnl0ZXMgd3JpdHRlbiBiZWZvcmVcbiByZXR1cm5pbmcuICBJZiB3aW5kb3cgZG9lcyBub3QgZXhpc3QgeWV0LCBjcmVhdGUgaXQuICBUaGlzIGlzIG9ubHkgY2FsbGVkXG4gd2hlbiBhIHdpbmRvdyBpcyBhbHJlYWR5IGluIHVzZSwgb3Igd2hlbiBvdXRwdXQgaGFzIGJlZW4gd3JpdHRlbiBkdXJpbmcgdGhpc1xuIGluZmxhdGUgY2FsbCwgYnV0IHRoZSBlbmQgb2YgdGhlIGRlZmxhdGUgc3RyZWFtIGhhcyBub3QgYmVlbiByZWFjaGVkIHlldC5cbiBJdCBpcyBhbHNvIGNhbGxlZCB0byBjcmVhdGUgYSB3aW5kb3cgZm9yIGRpY3Rpb25hcnkgZGF0YSB3aGVuIGEgZGljdGlvbmFyeVxuIGlzIGxvYWRlZC5cblxuIFByb3ZpZGluZyBvdXRwdXQgYnVmZmVycyBsYXJnZXIgdGhhbiAzMksgdG8gaW5mbGF0ZSgpIHNob3VsZCBwcm92aWRlIGEgc3BlZWRcbiBhZHZhbnRhZ2UsIHNpbmNlIG9ubHkgdGhlIGxhc3QgMzJLIG9mIG91dHB1dCBpcyBjb3BpZWQgdG8gdGhlIHNsaWRpbmcgd2luZG93XG4gdXBvbiByZXR1cm4gZnJvbSBpbmZsYXRlKCksIGFuZCBzaW5jZSBhbGwgZGlzdGFuY2VzIGFmdGVyIHRoZSBmaXJzdCAzMksgb2ZcbiBvdXRwdXQgd2lsbCBmYWxsIGluIHRoZSBvdXRwdXQgZGF0YSwgbWFraW5nIG1hdGNoIGNvcGllcyBzaW1wbGVyIGFuZCBmYXN0ZXIuXG4gVGhlIGFkdmFudGFnZSBtYXkgYmUgZGVwZW5kZW50IG9uIHRoZSBzaXplIG9mIHRoZSBwcm9jZXNzb3IncyBkYXRhIGNhY2hlcy5cbiAqL1xuZnVuY3Rpb24gdXBkYXRld2luZG93KHN0cm0sIHNyYywgZW5kLCBjb3B5KSB7XG4gIHZhciBkaXN0O1xuICB2YXIgc3RhdGUgPSBzdHJtLnN0YXRlO1xuXG4gIC8qIGlmIGl0IGhhc24ndCBiZWVuIGRvbmUgYWxyZWFkeSwgYWxsb2NhdGUgc3BhY2UgZm9yIHRoZSB3aW5kb3cgKi9cbiAgaWYgKHN0YXRlLndpbmRvdyA9PT0gbnVsbCkge1xuICAgIHN0YXRlLndzaXplID0gMSA8PCBzdGF0ZS53Yml0cztcbiAgICBzdGF0ZS53bmV4dCA9IDA7XG4gICAgc3RhdGUud2hhdmUgPSAwO1xuXG4gICAgc3RhdGUud2luZG93ID0gbmV3IHV0aWxzLkJ1Zjgoc3RhdGUud3NpemUpO1xuICB9XG5cbiAgLyogY29weSBzdGF0ZS0+d3NpemUgb3IgbGVzcyBvdXRwdXQgYnl0ZXMgaW50byB0aGUgY2lyY3VsYXIgd2luZG93ICovXG4gIGlmIChjb3B5ID49IHN0YXRlLndzaXplKSB7XG4gICAgdXRpbHMuYXJyYXlTZXQoc3RhdGUud2luZG93LHNyYywgZW5kIC0gc3RhdGUud3NpemUsIHN0YXRlLndzaXplLCAwKTtcbiAgICBzdGF0ZS53bmV4dCA9IDA7XG4gICAgc3RhdGUud2hhdmUgPSBzdGF0ZS53c2l6ZTtcbiAgfVxuICBlbHNlIHtcbiAgICBkaXN0ID0gc3RhdGUud3NpemUgLSBzdGF0ZS53bmV4dDtcbiAgICBpZiAoZGlzdCA+IGNvcHkpIHtcbiAgICAgIGRpc3QgPSBjb3B5O1xuICAgIH1cbiAgICAvL3ptZW1jcHkoc3RhdGUtPndpbmRvdyArIHN0YXRlLT53bmV4dCwgZW5kIC0gY29weSwgZGlzdCk7XG4gICAgdXRpbHMuYXJyYXlTZXQoc3RhdGUud2luZG93LHNyYywgZW5kIC0gY29weSwgZGlzdCwgc3RhdGUud25leHQpO1xuICAgIGNvcHkgLT0gZGlzdDtcbiAgICBpZiAoY29weSkge1xuICAgICAgLy96bWVtY3B5KHN0YXRlLT53aW5kb3csIGVuZCAtIGNvcHksIGNvcHkpO1xuICAgICAgdXRpbHMuYXJyYXlTZXQoc3RhdGUud2luZG93LHNyYywgZW5kIC0gY29weSwgY29weSwgMCk7XG4gICAgICBzdGF0ZS53bmV4dCA9IGNvcHk7XG4gICAgICBzdGF0ZS53aGF2ZSA9IHN0YXRlLndzaXplO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHN0YXRlLnduZXh0ICs9IGRpc3Q7XG4gICAgICBpZiAoc3RhdGUud25leHQgPT09IHN0YXRlLndzaXplKSB7IHN0YXRlLnduZXh0ID0gMDsgfVxuICAgICAgaWYgKHN0YXRlLndoYXZlIDwgc3RhdGUud3NpemUpIHsgc3RhdGUud2hhdmUgKz0gZGlzdDsgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gMDtcbn1cblxuZnVuY3Rpb24gaW5mbGF0ZShzdHJtLCBmbHVzaCkge1xuICB2YXIgc3RhdGU7XG4gIHZhciBpbnB1dCwgb3V0cHV0OyAgICAgICAgICAvLyBpbnB1dC9vdXRwdXQgYnVmZmVyc1xuICB2YXIgbmV4dDsgICAgICAgICAgICAgICAgICAgLyogbmV4dCBpbnB1dCBJTkRFWCAqL1xuICB2YXIgcHV0OyAgICAgICAgICAgICAgICAgICAgLyogbmV4dCBvdXRwdXQgSU5ERVggKi9cbiAgdmFyIGhhdmUsIGxlZnQ7ICAgICAgICAgICAgIC8qIGF2YWlsYWJsZSBpbnB1dCBhbmQgb3V0cHV0ICovXG4gIHZhciBob2xkOyAgICAgICAgICAgICAgICAgICAvKiBiaXQgYnVmZmVyICovXG4gIHZhciBiaXRzOyAgICAgICAgICAgICAgICAgICAvKiBiaXRzIGluIGJpdCBidWZmZXIgKi9cbiAgdmFyIF9pbiwgX291dDsgICAgICAgICAgICAgIC8qIHNhdmUgc3RhcnRpbmcgYXZhaWxhYmxlIGlucHV0IGFuZCBvdXRwdXQgKi9cbiAgdmFyIGNvcHk7ICAgICAgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBzdG9yZWQgb3IgbWF0Y2ggYnl0ZXMgdG8gY29weSAqL1xuICB2YXIgZnJvbTsgICAgICAgICAgICAgICAgICAgLyogd2hlcmUgdG8gY29weSBtYXRjaCBieXRlcyBmcm9tICovXG4gIHZhciBmcm9tX3NvdXJjZTtcbiAgdmFyIGhlcmUgPSAwOyAgICAgICAgICAgICAgIC8qIGN1cnJlbnQgZGVjb2RpbmcgdGFibGUgZW50cnkgKi9cbiAgdmFyIGhlcmVfYml0cywgaGVyZV9vcCwgaGVyZV92YWw7IC8vIHBha2VkIFwiaGVyZVwiIGRlbm9ybWFsaXplZCAoSlMgc3BlY2lmaWMpXG4gIC8vdmFyIGxhc3Q7ICAgICAgICAgICAgICAgICAgIC8qIHBhcmVudCB0YWJsZSBlbnRyeSAqL1xuICB2YXIgbGFzdF9iaXRzLCBsYXN0X29wLCBsYXN0X3ZhbDsgLy8gcGFrZWQgXCJsYXN0XCIgZGVub3JtYWxpemVkIChKUyBzcGVjaWZpYylcbiAgdmFyIGxlbjsgICAgICAgICAgICAgICAgICAgIC8qIGxlbmd0aCB0byBjb3B5IGZvciByZXBlYXRzLCBiaXRzIHRvIGRyb3AgKi9cbiAgdmFyIHJldDsgICAgICAgICAgICAgICAgICAgIC8qIHJldHVybiBjb2RlICovXG4gIHZhciBoYnVmID0gbmV3IHV0aWxzLkJ1ZjgoNCk7ICAgIC8qIGJ1ZmZlciBmb3IgZ3ppcCBoZWFkZXIgY3JjIGNhbGN1bGF0aW9uICovXG4gIHZhciBvcHRzO1xuXG4gIHZhciBuOyAvLyB0ZW1wb3JhcnkgdmFyIGZvciBORUVEX0JJVFNcblxuICB2YXIgb3JkZXIgPSAvKiBwZXJtdXRhdGlvbiBvZiBjb2RlIGxlbmd0aHMgKi9cbiAgICBbMTYsIDE3LCAxOCwgMCwgOCwgNywgOSwgNiwgMTAsIDUsIDExLCA0LCAxMiwgMywgMTMsIDIsIDE0LCAxLCAxNV07XG5cblxuICBpZiAoIXN0cm0gfHwgIXN0cm0uc3RhdGUgfHwgIXN0cm0ub3V0cHV0IHx8XG4gICAgICAoIXN0cm0uaW5wdXQgJiYgc3RybS5hdmFpbF9pbiAhPT0gMCkpIHtcbiAgICByZXR1cm4gWl9TVFJFQU1fRVJST1I7XG4gIH1cblxuICBzdGF0ZSA9IHN0cm0uc3RhdGU7XG4gIGlmIChzdGF0ZS5tb2RlID09PSBUWVBFKSB7IHN0YXRlLm1vZGUgPSBUWVBFRE87IH0gICAgLyogc2tpcCBjaGVjayAqL1xuXG5cbiAgLy8tLS0gTE9BRCgpIC0tLVxuICBwdXQgPSBzdHJtLm5leHRfb3V0O1xuICBvdXRwdXQgPSBzdHJtLm91dHB1dDtcbiAgbGVmdCA9IHN0cm0uYXZhaWxfb3V0O1xuICBuZXh0ID0gc3RybS5uZXh0X2luO1xuICBpbnB1dCA9IHN0cm0uaW5wdXQ7XG4gIGhhdmUgPSBzdHJtLmF2YWlsX2luO1xuICBob2xkID0gc3RhdGUuaG9sZDtcbiAgYml0cyA9IHN0YXRlLmJpdHM7XG4gIC8vLS0tXG5cbiAgX2luID0gaGF2ZTtcbiAgX291dCA9IGxlZnQ7XG4gIHJldCA9IFpfT0s7XG5cbiAgaW5mX2xlYXZlOiAvLyBnb3RvIGVtdWxhdGlvblxuICBmb3IgKDs7KSB7XG4gICAgc3dpdGNoIChzdGF0ZS5tb2RlKSB7XG4gICAgY2FzZSBIRUFEOlxuICAgICAgaWYgKHN0YXRlLndyYXAgPT09IDApIHtcbiAgICAgICAgc3RhdGUubW9kZSA9IFRZUEVETztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvLz09PSBORUVEQklUUygxNik7XG4gICAgICB3aGlsZSAoYml0cyA8IDE2KSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBpZiAoKHN0YXRlLndyYXAgJiAyKSAmJiBob2xkID09PSAweDhiMWYpIHsgIC8qIGd6aXAgaGVhZGVyICovXG4gICAgICAgIHN0YXRlLmNoZWNrID0gMC8qY3JjMzIoMEwsIFpfTlVMTCwgMCkqLztcbiAgICAgICAgLy89PT0gQ1JDMihzdGF0ZS5jaGVjaywgaG9sZCk7XG4gICAgICAgIGhidWZbMF0gPSBob2xkICYgMHhmZjtcbiAgICAgICAgaGJ1ZlsxXSA9IChob2xkID4+PiA4KSAmIDB4ZmY7XG4gICAgICAgIHN0YXRlLmNoZWNrID0gY3JjMzIoc3RhdGUuY2hlY2ssIGhidWYsIDIsIDApO1xuICAgICAgICAvLz09PS8vXG5cbiAgICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgICAgaG9sZCA9IDA7XG4gICAgICAgIGJpdHMgPSAwO1xuICAgICAgICAvLz09PS8vXG4gICAgICAgIHN0YXRlLm1vZGUgPSBGTEFHUztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBzdGF0ZS5mbGFncyA9IDA7ICAgICAgICAgICAvKiBleHBlY3QgemxpYiBoZWFkZXIgKi9cbiAgICAgIGlmIChzdGF0ZS5oZWFkKSB7XG4gICAgICAgIHN0YXRlLmhlYWQuZG9uZSA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgaWYgKCEoc3RhdGUud3JhcCAmIDEpIHx8ICAgLyogY2hlY2sgaWYgemxpYiBoZWFkZXIgYWxsb3dlZCAqL1xuICAgICAgICAoKChob2xkICYgMHhmZikvKkJJVFMoOCkqLyA8PCA4KSArIChob2xkID4+IDgpKSAlIDMxKSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2luY29ycmVjdCBoZWFkZXIgY2hlY2snO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmICgoaG9sZCAmIDB4MGYpLypCSVRTKDQpKi8gIT09IFpfREVGTEFURUQpIHtcbiAgICAgICAgc3RybS5tc2cgPSAndW5rbm93biBjb21wcmVzc2lvbiBtZXRob2QnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIC8vLS0tIERST1BCSVRTKDQpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gNDtcbiAgICAgIGJpdHMgLT0gNDtcbiAgICAgIC8vLS0tLy9cbiAgICAgIGxlbiA9IChob2xkICYgMHgwZikvKkJJVFMoNCkqLyArIDg7XG4gICAgICBpZiAoc3RhdGUud2JpdHMgPT09IDApIHtcbiAgICAgICAgc3RhdGUud2JpdHMgPSBsZW47XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChsZW4gPiBzdGF0ZS53Yml0cykge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIHdpbmRvdyBzaXplJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBzdGF0ZS5kbWF4ID0gMSA8PCBsZW47XG4gICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgemxpYiBoZWFkZXIgb2tcXG5cIikpO1xuICAgICAgc3RybS5hZGxlciA9IHN0YXRlLmNoZWNrID0gMS8qYWRsZXIzMigwTCwgWl9OVUxMLCAwKSovO1xuICAgICAgc3RhdGUubW9kZSA9IGhvbGQgJiAweDIwMCA/IERJQ1RJRCA6IFRZUEU7XG4gICAgICAvLz09PSBJTklUQklUUygpO1xuICAgICAgaG9sZCA9IDA7XG4gICAgICBiaXRzID0gMDtcbiAgICAgIC8vPT09Ly9cbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgRkxBR1M6XG4gICAgICAvLz09PSBORUVEQklUUygxNik7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDE2KSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBzdGF0ZS5mbGFncyA9IGhvbGQ7XG4gICAgICBpZiAoKHN0YXRlLmZsYWdzICYgMHhmZikgIT09IFpfREVGTEFURUQpIHtcbiAgICAgICAgc3RybS5tc2cgPSAndW5rbm93biBjb21wcmVzc2lvbiBtZXRob2QnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4ZTAwMCkge1xuICAgICAgICBzdHJtLm1zZyA9ICd1bmtub3duIGhlYWRlciBmbGFncyBzZXQnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZS5oZWFkKSB7XG4gICAgICAgIHN0YXRlLmhlYWQudGV4dCA9ICgoaG9sZCA+PiA4KSAmIDEpO1xuICAgICAgfVxuICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgIC8vPT09IENSQzIoc3RhdGUuY2hlY2ssIGhvbGQpO1xuICAgICAgICBoYnVmWzBdID0gaG9sZCAmIDB4ZmY7XG4gICAgICAgIGhidWZbMV0gPSAoaG9sZCA+Pj4gOCkgJiAweGZmO1xuICAgICAgICBzdGF0ZS5jaGVjayA9IGNyYzMyKHN0YXRlLmNoZWNrLCBoYnVmLCAyLCAwKTtcbiAgICAgICAgLy89PT0vL1xuICAgICAgfVxuICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgIGhvbGQgPSAwO1xuICAgICAgYml0cyA9IDA7XG4gICAgICAvLz09PS8vXG4gICAgICBzdGF0ZS5tb2RlID0gVElNRTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIFRJTUU6XG4gICAgICAvLz09PSBORUVEQklUUygzMik7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDMyKSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLnRpbWUgPSBob2xkO1xuICAgICAgfVxuICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgIC8vPT09IENSQzQoc3RhdGUuY2hlY2ssIGhvbGQpXG4gICAgICAgIGhidWZbMF0gPSBob2xkICYgMHhmZjtcbiAgICAgICAgaGJ1ZlsxXSA9IChob2xkID4+PiA4KSAmIDB4ZmY7XG4gICAgICAgIGhidWZbMl0gPSAoaG9sZCA+Pj4gMTYpICYgMHhmZjtcbiAgICAgICAgaGJ1ZlszXSA9IChob2xkID4+PiAyNCkgJiAweGZmO1xuICAgICAgICBzdGF0ZS5jaGVjayA9IGNyYzMyKHN0YXRlLmNoZWNrLCBoYnVmLCA0LCAwKTtcbiAgICAgICAgLy89PT1cbiAgICAgIH1cbiAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICBob2xkID0gMDtcbiAgICAgIGJpdHMgPSAwO1xuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubW9kZSA9IE9TO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgT1M6XG4gICAgICAvLz09PSBORUVEQklUUygxNik7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDE2KSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLnhmbGFncyA9IChob2xkICYgMHhmZik7XG4gICAgICAgIHN0YXRlLmhlYWQub3MgPSAoaG9sZCA+PiA4KTtcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAvLz09PSBDUkMyKHN0YXRlLmNoZWNrLCBob2xkKTtcbiAgICAgICAgaGJ1ZlswXSA9IGhvbGQgJiAweGZmO1xuICAgICAgICBoYnVmWzFdID0gKGhvbGQgPj4+IDgpICYgMHhmZjtcbiAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaGJ1ZiwgMiwgMCk7XG4gICAgICAgIC8vPT09Ly9cbiAgICAgIH1cbiAgICAgIC8vPT09IElOSVRCSVRTKCk7XG4gICAgICBob2xkID0gMDtcbiAgICAgIGJpdHMgPSAwO1xuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubW9kZSA9IEVYTEVOO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgRVhMRU46XG4gICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDA0MDApIHtcbiAgICAgICAgLy89PT0gTkVFREJJVFMoMTYpOyAqL1xuICAgICAgICB3aGlsZSAoYml0cyA8IDE2KSB7XG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgfVxuICAgICAgICAvLz09PS8vXG4gICAgICAgIHN0YXRlLmxlbmd0aCA9IGhvbGQ7XG4gICAgICAgIGlmIChzdGF0ZS5oZWFkKSB7XG4gICAgICAgICAgc3RhdGUuaGVhZC5leHRyYV9sZW4gPSBob2xkO1xuICAgICAgICB9XG4gICAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDIwMCkge1xuICAgICAgICAgIC8vPT09IENSQzIoc3RhdGUuY2hlY2ssIGhvbGQpO1xuICAgICAgICAgIGhidWZbMF0gPSBob2xkICYgMHhmZjtcbiAgICAgICAgICBoYnVmWzFdID0gKGhvbGQgPj4+IDgpICYgMHhmZjtcbiAgICAgICAgICBzdGF0ZS5jaGVjayA9IGNyYzMyKHN0YXRlLmNoZWNrLCBoYnVmLCAyLCAwKTtcbiAgICAgICAgICAvLz09PS8vXG4gICAgICAgIH1cbiAgICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgICAgaG9sZCA9IDA7XG4gICAgICAgIGJpdHMgPSAwO1xuICAgICAgICAvLz09PS8vXG4gICAgICB9XG4gICAgICBlbHNlIGlmIChzdGF0ZS5oZWFkKSB7XG4gICAgICAgIHN0YXRlLmhlYWQuZXh0cmEgPSBudWxsLypaX05VTEwqLztcbiAgICAgIH1cbiAgICAgIHN0YXRlLm1vZGUgPSBFWFRSQTtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIEVYVFJBOlxuICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwNDAwKSB7XG4gICAgICAgIGNvcHkgPSBzdGF0ZS5sZW5ndGg7XG4gICAgICAgIGlmIChjb3B5ID4gaGF2ZSkgeyBjb3B5ID0gaGF2ZTsgfVxuICAgICAgICBpZiAoY29weSkge1xuICAgICAgICAgIGlmIChzdGF0ZS5oZWFkKSB7XG4gICAgICAgICAgICBsZW4gPSBzdGF0ZS5oZWFkLmV4dHJhX2xlbiAtIHN0YXRlLmxlbmd0aDtcbiAgICAgICAgICAgIGlmICghc3RhdGUuaGVhZC5leHRyYSkge1xuICAgICAgICAgICAgICAvLyBVc2UgdW50eXBlZCBhcnJheSBmb3IgbW9yZSBjb252ZW5pZW5kIHByb2Nlc3NpbmcgbGF0ZXJcbiAgICAgICAgICAgICAgc3RhdGUuaGVhZC5leHRyYSA9IG5ldyBBcnJheShzdGF0ZS5oZWFkLmV4dHJhX2xlbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB1dGlscy5hcnJheVNldChcbiAgICAgICAgICAgICAgc3RhdGUuaGVhZC5leHRyYSxcbiAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAgIG5leHQsXG4gICAgICAgICAgICAgIC8vIGV4dHJhIGZpZWxkIGlzIGxpbWl0ZWQgdG8gNjU1MzYgYnl0ZXNcbiAgICAgICAgICAgICAgLy8gLSBubyBuZWVkIGZvciBhZGRpdGlvbmFsIHNpemUgY2hlY2tcbiAgICAgICAgICAgICAgY29weSxcbiAgICAgICAgICAgICAgLypsZW4gKyBjb3B5ID4gc3RhdGUuaGVhZC5leHRyYV9tYXggLSBsZW4gPyBzdGF0ZS5oZWFkLmV4dHJhX21heCA6IGNvcHksKi9cbiAgICAgICAgICAgICAgbGVuXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgLy96bWVtY3B5KHN0YXRlLmhlYWQuZXh0cmEgKyBsZW4sIG5leHQsXG4gICAgICAgICAgICAvLyAgICAgICAgbGVuICsgY29weSA+IHN0YXRlLmhlYWQuZXh0cmFfbWF4ID9cbiAgICAgICAgICAgIC8vICAgICAgICBzdGF0ZS5oZWFkLmV4dHJhX21heCAtIGxlbiA6IGNvcHkpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUuZmxhZ3MgJiAweDAyMDApIHtcbiAgICAgICAgICAgIHN0YXRlLmNoZWNrID0gY3JjMzIoc3RhdGUuY2hlY2ssIGlucHV0LCBjb3B5LCBuZXh0KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaGF2ZSAtPSBjb3B5O1xuICAgICAgICAgIG5leHQgKz0gY29weTtcbiAgICAgICAgICBzdGF0ZS5sZW5ndGggLT0gY29weTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc3RhdGUubGVuZ3RoKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgfVxuICAgICAgc3RhdGUubGVuZ3RoID0gMDtcbiAgICAgIHN0YXRlLm1vZGUgPSBOQU1FO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgTkFNRTpcbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MDgwMCkge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgY29weSA9IDA7XG4gICAgICAgIGRvIHtcbiAgICAgICAgICAvLyBUT0RPOiAyIG9yIDEgYnl0ZXM/XG4gICAgICAgICAgbGVuID0gaW5wdXRbbmV4dCArIGNvcHkrK107XG4gICAgICAgICAgLyogdXNlIGNvbnN0YW50IGxpbWl0IGJlY2F1c2UgaW4ganMgd2Ugc2hvdWxkIG5vdCBwcmVhbGxvY2F0ZSBtZW1vcnkgKi9cbiAgICAgICAgICBpZiAoc3RhdGUuaGVhZCAmJiBsZW4gJiZcbiAgICAgICAgICAgICAgKHN0YXRlLmxlbmd0aCA8IDY1NTM2IC8qc3RhdGUuaGVhZC5uYW1lX21heCovKSkge1xuICAgICAgICAgICAgc3RhdGUuaGVhZC5uYW1lICs9IFN0cmluZy5mcm9tQ2hhckNvZGUobGVuKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gd2hpbGUgKGxlbiAmJiBjb3B5IDwgaGF2ZSk7XG5cbiAgICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaW5wdXQsIGNvcHksIG5leHQpO1xuICAgICAgICB9XG4gICAgICAgIGhhdmUgLT0gY29weTtcbiAgICAgICAgbmV4dCArPSBjb3B5O1xuICAgICAgICBpZiAobGVuKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgfVxuICAgICAgZWxzZSBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLm5hbWUgPSBudWxsO1xuICAgICAgfVxuICAgICAgc3RhdGUubGVuZ3RoID0gMDtcbiAgICAgIHN0YXRlLm1vZGUgPSBDT01NRU5UO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgQ09NTUVOVDpcbiAgICAgIGlmIChzdGF0ZS5mbGFncyAmIDB4MTAwMCkge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgY29weSA9IDA7XG4gICAgICAgIGRvIHtcbiAgICAgICAgICBsZW4gPSBpbnB1dFtuZXh0ICsgY29weSsrXTtcbiAgICAgICAgICAvKiB1c2UgY29uc3RhbnQgbGltaXQgYmVjYXVzZSBpbiBqcyB3ZSBzaG91bGQgbm90IHByZWFsbG9jYXRlIG1lbW9yeSAqL1xuICAgICAgICAgIGlmIChzdGF0ZS5oZWFkICYmIGxlbiAmJlxuICAgICAgICAgICAgICAoc3RhdGUubGVuZ3RoIDwgNjU1MzYgLypzdGF0ZS5oZWFkLmNvbW1fbWF4Ki8pKSB7XG4gICAgICAgICAgICBzdGF0ZS5oZWFkLmNvbW1lbnQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShsZW4pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSB3aGlsZSAobGVuICYmIGNvcHkgPCBoYXZlKTtcbiAgICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgICAgc3RhdGUuY2hlY2sgPSBjcmMzMihzdGF0ZS5jaGVjaywgaW5wdXQsIGNvcHksIG5leHQpO1xuICAgICAgICB9XG4gICAgICAgIGhhdmUgLT0gY29weTtcbiAgICAgICAgbmV4dCArPSBjb3B5O1xuICAgICAgICBpZiAobGVuKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgfVxuICAgICAgZWxzZSBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLmNvbW1lbnQgPSBudWxsO1xuICAgICAgfVxuICAgICAgc3RhdGUubW9kZSA9IEhDUkM7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBIQ1JDOlxuICAgICAgaWYgKHN0YXRlLmZsYWdzICYgMHgwMjAwKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKDE2KTsgKi9cbiAgICAgICAgd2hpbGUgKGJpdHMgPCAxNikge1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0vL1xuICAgICAgICBpZiAoaG9sZCAhPT0gKHN0YXRlLmNoZWNrICYgMHhmZmZmKSkge1xuICAgICAgICAgIHN0cm0ubXNnID0gJ2hlYWRlciBjcmMgbWlzbWF0Y2gnO1xuICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgICAgaG9sZCA9IDA7XG4gICAgICAgIGJpdHMgPSAwO1xuICAgICAgICAvLz09PS8vXG4gICAgICB9XG4gICAgICBpZiAoc3RhdGUuaGVhZCkge1xuICAgICAgICBzdGF0ZS5oZWFkLmhjcmMgPSAoKHN0YXRlLmZsYWdzID4+IDkpICYgMSk7XG4gICAgICAgIHN0YXRlLmhlYWQuZG9uZSA9IHRydWU7XG4gICAgICB9XG4gICAgICBzdHJtLmFkbGVyID0gc3RhdGUuY2hlY2sgPSAwIC8qY3JjMzIoMEwsIFpfTlVMTCwgMCkqLztcbiAgICAgIHN0YXRlLm1vZGUgPSBUWVBFO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBESUNUSUQ6XG4gICAgICAvLz09PSBORUVEQklUUygzMik7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDMyKSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBzdHJtLmFkbGVyID0gc3RhdGUuY2hlY2sgPSBaU1dBUDMyKGhvbGQpO1xuICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgIGhvbGQgPSAwO1xuICAgICAgYml0cyA9IDA7XG4gICAgICAvLz09PS8vXG4gICAgICBzdGF0ZS5tb2RlID0gRElDVDtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIERJQ1Q6XG4gICAgICBpZiAoc3RhdGUuaGF2ZWRpY3QgPT09IDApIHtcbiAgICAgICAgLy8tLS0gUkVTVE9SRSgpIC0tLVxuICAgICAgICBzdHJtLm5leHRfb3V0ID0gcHV0O1xuICAgICAgICBzdHJtLmF2YWlsX291dCA9IGxlZnQ7XG4gICAgICAgIHN0cm0ubmV4dF9pbiA9IG5leHQ7XG4gICAgICAgIHN0cm0uYXZhaWxfaW4gPSBoYXZlO1xuICAgICAgICBzdGF0ZS5ob2xkID0gaG9sZDtcbiAgICAgICAgc3RhdGUuYml0cyA9IGJpdHM7XG4gICAgICAgIC8vLS0tXG4gICAgICAgIHJldHVybiBaX05FRURfRElDVDtcbiAgICAgIH1cbiAgICAgIHN0cm0uYWRsZXIgPSBzdGF0ZS5jaGVjayA9IDEvKmFkbGVyMzIoMEwsIFpfTlVMTCwgMCkqLztcbiAgICAgIHN0YXRlLm1vZGUgPSBUWVBFO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgVFlQRTpcbiAgICAgIGlmIChmbHVzaCA9PT0gWl9CTE9DSyB8fCBmbHVzaCA9PT0gWl9UUkVFUykgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIFRZUEVETzpcbiAgICAgIGlmIChzdGF0ZS5sYXN0KSB7XG4gICAgICAgIC8vLS0tIEJZVEVCSVRTKCkgLS0tLy9cbiAgICAgICAgaG9sZCA+Pj49IGJpdHMgJiA3O1xuICAgICAgICBiaXRzIC09IGJpdHMgJiA3O1xuICAgICAgICAvLy0tLS8vXG4gICAgICAgIHN0YXRlLm1vZGUgPSBDSEVDSztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvLz09PSBORUVEQklUUygzKTsgKi9cbiAgICAgIHdoaWxlIChiaXRzIDwgMykge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubGFzdCA9IChob2xkICYgMHgwMSkvKkJJVFMoMSkqLztcbiAgICAgIC8vLS0tIERST1BCSVRTKDEpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gMTtcbiAgICAgIGJpdHMgLT0gMTtcbiAgICAgIC8vLS0tLy9cblxuICAgICAgc3dpdGNoICgoaG9sZCAmIDB4MDMpLypCSVRTKDIpKi8pIHtcbiAgICAgIGNhc2UgMDogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIHN0b3JlZCBibG9jayAqL1xuICAgICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICBzdG9yZWQgYmxvY2slc1xcblwiLFxuICAgICAgICAvLyAgICAgICAgc3RhdGUubGFzdCA/IFwiIChsYXN0KVwiIDogXCJcIikpO1xuICAgICAgICBzdGF0ZS5tb2RlID0gU1RPUkVEO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMTogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIGZpeGVkIGJsb2NrICovXG4gICAgICAgIGZpeGVkdGFibGVzKHN0YXRlKTtcbiAgICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgZml4ZWQgY29kZXMgYmxvY2slc1xcblwiLFxuICAgICAgICAvLyAgICAgICAgc3RhdGUubGFzdCA/IFwiIChsYXN0KVwiIDogXCJcIikpO1xuICAgICAgICBzdGF0ZS5tb2RlID0gTEVOXzsgICAgICAgICAgICAgLyogZGVjb2RlIGNvZGVzICovXG4gICAgICAgIGlmIChmbHVzaCA9PT0gWl9UUkVFUykge1xuICAgICAgICAgIC8vLS0tIERST1BCSVRTKDIpIC0tLS8vXG4gICAgICAgICAgaG9sZCA+Pj49IDI7XG4gICAgICAgICAgYml0cyAtPSAyO1xuICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgICBicmVhayBpbmZfbGVhdmU7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDI6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBkeW5hbWljIGJsb2NrICovXG4gICAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgIGR5bmFtaWMgY29kZXMgYmxvY2slc1xcblwiLFxuICAgICAgICAvLyAgICAgICAgc3RhdGUubGFzdCA/IFwiIChsYXN0KVwiIDogXCJcIikpO1xuICAgICAgICBzdGF0ZS5tb2RlID0gVEFCTEU7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGJsb2NrIHR5cGUnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgfVxuICAgICAgLy8tLS0gRFJPUEJJVFMoMikgLS0tLy9cbiAgICAgIGhvbGQgPj4+PSAyO1xuICAgICAgYml0cyAtPSAyO1xuICAgICAgLy8tLS0vL1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBTVE9SRUQ6XG4gICAgICAvLy0tLSBCWVRFQklUUygpIC0tLS8vIC8qIGdvIHRvIGJ5dGUgYm91bmRhcnkgKi9cbiAgICAgIGhvbGQgPj4+PSBiaXRzICYgNztcbiAgICAgIGJpdHMgLT0gYml0cyAmIDc7XG4gICAgICAvLy0tLS8vXG4gICAgICAvLz09PSBORUVEQklUUygzMik7ICovXG4gICAgICB3aGlsZSAoYml0cyA8IDMyKSB7XG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICB9XG4gICAgICAvLz09PS8vXG4gICAgICBpZiAoKGhvbGQgJiAweGZmZmYpICE9PSAoKGhvbGQgPj4+IDE2KSBeIDB4ZmZmZikpIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBzdG9yZWQgYmxvY2sgbGVuZ3Rocyc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUubGVuZ3RoID0gaG9sZCAmIDB4ZmZmZjtcbiAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgc3RvcmVkIGxlbmd0aCAldVxcblwiLFxuICAgICAgLy8gICAgICAgIHN0YXRlLmxlbmd0aCkpO1xuICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgIGhvbGQgPSAwO1xuICAgICAgYml0cyA9IDA7XG4gICAgICAvLz09PS8vXG4gICAgICBzdGF0ZS5tb2RlID0gQ09QWV87XG4gICAgICBpZiAoZmx1c2ggPT09IFpfVFJFRVMpIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBDT1BZXzpcbiAgICAgIHN0YXRlLm1vZGUgPSBDT1BZO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgQ09QWTpcbiAgICAgIGNvcHkgPSBzdGF0ZS5sZW5ndGg7XG4gICAgICBpZiAoY29weSkge1xuICAgICAgICBpZiAoY29weSA+IGhhdmUpIHsgY29weSA9IGhhdmU7IH1cbiAgICAgICAgaWYgKGNvcHkgPiBsZWZ0KSB7IGNvcHkgPSBsZWZ0OyB9XG4gICAgICAgIGlmIChjb3B5ID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAvLy0tLSB6bWVtY3B5KHB1dCwgbmV4dCwgY29weSk7IC0tLVxuICAgICAgICB1dGlscy5hcnJheVNldChvdXRwdXQsIGlucHV0LCBuZXh0LCBjb3B5LCBwdXQpO1xuICAgICAgICAvLy0tLS8vXG4gICAgICAgIGhhdmUgLT0gY29weTtcbiAgICAgICAgbmV4dCArPSBjb3B5O1xuICAgICAgICBsZWZ0IC09IGNvcHk7XG4gICAgICAgIHB1dCArPSBjb3B5O1xuICAgICAgICBzdGF0ZS5sZW5ndGggLT0gY29weTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICAvL1RyYWNldigoc3RkZXJyLCBcImluZmxhdGU6ICAgICAgIHN0b3JlZCBlbmRcXG5cIikpO1xuICAgICAgc3RhdGUubW9kZSA9IFRZUEU7XG4gICAgICBicmVhaztcbiAgICBjYXNlIFRBQkxFOlxuICAgICAgLy89PT0gTkVFREJJVFMoMTQpOyAqL1xuICAgICAgd2hpbGUgKGJpdHMgPCAxNCkge1xuICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgaGF2ZS0tO1xuICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgYml0cyArPSA4O1xuICAgICAgfVxuICAgICAgLy89PT0vL1xuICAgICAgc3RhdGUubmxlbiA9IChob2xkICYgMHgxZikvKkJJVFMoNSkqLyArIDI1NztcbiAgICAgIC8vLS0tIERST1BCSVRTKDUpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gNTtcbiAgICAgIGJpdHMgLT0gNTtcbiAgICAgIC8vLS0tLy9cbiAgICAgIHN0YXRlLm5kaXN0ID0gKGhvbGQgJiAweDFmKS8qQklUUyg1KSovICsgMTtcbiAgICAgIC8vLS0tIERST1BCSVRTKDUpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gNTtcbiAgICAgIGJpdHMgLT0gNTtcbiAgICAgIC8vLS0tLy9cbiAgICAgIHN0YXRlLm5jb2RlID0gKGhvbGQgJiAweDBmKS8qQklUUyg0KSovICsgNDtcbiAgICAgIC8vLS0tIERST1BCSVRTKDQpIC0tLS8vXG4gICAgICBob2xkID4+Pj0gNDtcbiAgICAgIGJpdHMgLT0gNDtcbiAgICAgIC8vLS0tLy9cbi8vI2lmbmRlZiBQS1pJUF9CVUdfV09SS0FST1VORFxuICAgICAgaWYgKHN0YXRlLm5sZW4gPiAyODYgfHwgc3RhdGUubmRpc3QgPiAzMCkge1xuICAgICAgICBzdHJtLm1zZyA9ICd0b28gbWFueSBsZW5ndGggb3IgZGlzdGFuY2Ugc3ltYm9scyc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuLy8jZW5kaWZcbiAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgdGFibGUgc2l6ZXMgb2tcXG5cIikpO1xuICAgICAgc3RhdGUuaGF2ZSA9IDA7XG4gICAgICBzdGF0ZS5tb2RlID0gTEVOTEVOUztcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIExFTkxFTlM6XG4gICAgICB3aGlsZSAoc3RhdGUuaGF2ZSA8IHN0YXRlLm5jb2RlKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKDMpO1xuICAgICAgICB3aGlsZSAoYml0cyA8IDMpIHtcbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgc3RhdGUubGVuc1tvcmRlcltzdGF0ZS5oYXZlKytdXSA9IChob2xkICYgMHgwNyk7Ly9CSVRTKDMpO1xuICAgICAgICAvLy0tLSBEUk9QQklUUygzKSAtLS0vL1xuICAgICAgICBob2xkID4+Pj0gMztcbiAgICAgICAgYml0cyAtPSAzO1xuICAgICAgICAvLy0tLS8vXG4gICAgICB9XG4gICAgICB3aGlsZSAoc3RhdGUuaGF2ZSA8IDE5KSB7XG4gICAgICAgIHN0YXRlLmxlbnNbb3JkZXJbc3RhdGUuaGF2ZSsrXV0gPSAwO1xuICAgICAgfVxuICAgICAgLy8gV2UgaGF2ZSBzZXBhcmF0ZSB0YWJsZXMgJiBubyBwb2ludGVycy4gMiBjb21tZW50ZWQgbGluZXMgYmVsb3cgbm90IG5lZWRlZC5cbiAgICAgIC8vc3RhdGUubmV4dCA9IHN0YXRlLmNvZGVzO1xuICAgICAgLy9zdGF0ZS5sZW5jb2RlID0gc3RhdGUubmV4dDtcbiAgICAgIC8vIFN3aXRjaCB0byB1c2UgZHluYW1pYyB0YWJsZVxuICAgICAgc3RhdGUubGVuY29kZSA9IHN0YXRlLmxlbmR5bjtcbiAgICAgIHN0YXRlLmxlbmJpdHMgPSA3O1xuXG4gICAgICBvcHRzID0ge2JpdHM6IHN0YXRlLmxlbmJpdHN9O1xuICAgICAgcmV0ID0gaW5mbGF0ZV90YWJsZShDT0RFUywgc3RhdGUubGVucywgMCwgMTksIHN0YXRlLmxlbmNvZGUsIDAsIHN0YXRlLndvcmssIG9wdHMpO1xuICAgICAgc3RhdGUubGVuYml0cyA9IG9wdHMuYml0cztcblxuICAgICAgaWYgKHJldCkge1xuICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGNvZGUgbGVuZ3RocyBzZXQnO1xuICAgICAgICBzdGF0ZS5tb2RlID0gQkFEO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgY29kZSBsZW5ndGhzIG9rXFxuXCIpKTtcbiAgICAgIHN0YXRlLmhhdmUgPSAwO1xuICAgICAgc3RhdGUubW9kZSA9IENPREVMRU5TO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgQ09ERUxFTlM6XG4gICAgICB3aGlsZSAoc3RhdGUuaGF2ZSA8IHN0YXRlLm5sZW4gKyBzdGF0ZS5uZGlzdCkge1xuICAgICAgICBmb3IgKDs7KSB7XG4gICAgICAgICAgaGVyZSA9IHN0YXRlLmxlbmNvZGVbaG9sZCAmICgoMSA8PCBzdGF0ZS5sZW5iaXRzKSAtIDEpXTsvKkJJVFMoc3RhdGUubGVuYml0cykqL1xuICAgICAgICAgIGhlcmVfYml0cyA9IGhlcmUgPj4+IDI0O1xuICAgICAgICAgIGhlcmVfb3AgPSAoaGVyZSA+Pj4gMTYpICYgMHhmZjtcbiAgICAgICAgICBoZXJlX3ZhbCA9IGhlcmUgJiAweGZmZmY7XG5cbiAgICAgICAgICBpZiAoKGhlcmVfYml0cykgPD0gYml0cykgeyBicmVhazsgfVxuICAgICAgICAgIC8vLS0tIFBVTExCWVRFKCkgLS0tLy9cbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgfVxuICAgICAgICBpZiAoaGVyZV92YWwgPCAxNikge1xuICAgICAgICAgIC8vLS0tIERST1BCSVRTKGhlcmUuYml0cykgLS0tLy9cbiAgICAgICAgICBob2xkID4+Pj0gaGVyZV9iaXRzO1xuICAgICAgICAgIGJpdHMgLT0gaGVyZV9iaXRzO1xuICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgICBzdGF0ZS5sZW5zW3N0YXRlLmhhdmUrK10gPSBoZXJlX3ZhbDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICBpZiAoaGVyZV92YWwgPT09IDE2KSB7XG4gICAgICAgICAgICAvLz09PSBORUVEQklUUyhoZXJlLmJpdHMgKyAyKTtcbiAgICAgICAgICAgIG4gPSBoZXJlX2JpdHMgKyAyO1xuICAgICAgICAgICAgd2hpbGUgKGJpdHMgPCBuKSB7XG4gICAgICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLz09PS8vXG4gICAgICAgICAgICAvLy0tLSBEUk9QQklUUyhoZXJlLmJpdHMpIC0tLS8vXG4gICAgICAgICAgICBob2xkID4+Pj0gaGVyZV9iaXRzO1xuICAgICAgICAgICAgYml0cyAtPSBoZXJlX2JpdHM7XG4gICAgICAgICAgICAvLy0tLS8vXG4gICAgICAgICAgICBpZiAoc3RhdGUuaGF2ZSA9PT0gMCkge1xuICAgICAgICAgICAgICBzdHJtLm1zZyA9ICdpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0JztcbiAgICAgICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZW4gPSBzdGF0ZS5sZW5zW3N0YXRlLmhhdmUgLSAxXTtcbiAgICAgICAgICAgIGNvcHkgPSAzICsgKGhvbGQgJiAweDAzKTsvL0JJVFMoMik7XG4gICAgICAgICAgICAvLy0tLSBEUk9QQklUUygyKSAtLS0vL1xuICAgICAgICAgICAgaG9sZCA+Pj49IDI7XG4gICAgICAgICAgICBiaXRzIC09IDI7XG4gICAgICAgICAgICAvLy0tLS8vXG4gICAgICAgICAgfVxuICAgICAgICAgIGVsc2UgaWYgKGhlcmVfdmFsID09PSAxNykge1xuICAgICAgICAgICAgLy89PT0gTkVFREJJVFMoaGVyZS5iaXRzICsgMyk7XG4gICAgICAgICAgICBuID0gaGVyZV9iaXRzICsgMztcbiAgICAgICAgICAgIHdoaWxlIChiaXRzIDwgbikge1xuICAgICAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy89PT0vL1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoaGVyZS5iaXRzKSAtLS0vL1xuICAgICAgICAgICAgaG9sZCA+Pj49IGhlcmVfYml0cztcbiAgICAgICAgICAgIGJpdHMgLT0gaGVyZV9iaXRzO1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgICAgbGVuID0gMDtcbiAgICAgICAgICAgIGNvcHkgPSAzICsgKGhvbGQgJiAweDA3KTsvL0JJVFMoMyk7XG4gICAgICAgICAgICAvLy0tLSBEUk9QQklUUygzKSAtLS0vL1xuICAgICAgICAgICAgaG9sZCA+Pj49IDM7XG4gICAgICAgICAgICBiaXRzIC09IDM7XG4gICAgICAgICAgICAvLy0tLS8vXG4gICAgICAgICAgfVxuICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy89PT0gTkVFREJJVFMoaGVyZS5iaXRzICsgNyk7XG4gICAgICAgICAgICBuID0gaGVyZV9iaXRzICsgNztcbiAgICAgICAgICAgIHdoaWxlIChiaXRzIDwgbikge1xuICAgICAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy89PT0vL1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoaGVyZS5iaXRzKSAtLS0vL1xuICAgICAgICAgICAgaG9sZCA+Pj49IGhlcmVfYml0cztcbiAgICAgICAgICAgIGJpdHMgLT0gaGVyZV9iaXRzO1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgICAgbGVuID0gMDtcbiAgICAgICAgICAgIGNvcHkgPSAxMSArIChob2xkICYgMHg3Zik7Ly9CSVRTKDcpO1xuICAgICAgICAgICAgLy8tLS0gRFJPUEJJVFMoNykgLS0tLy9cbiAgICAgICAgICAgIGhvbGQgPj4+PSA3O1xuICAgICAgICAgICAgYml0cyAtPSA3O1xuICAgICAgICAgICAgLy8tLS0vL1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUuaGF2ZSArIGNvcHkgPiBzdGF0ZS5ubGVuICsgc3RhdGUubmRpc3QpIHtcbiAgICAgICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgYml0IGxlbmd0aCByZXBlYXQnO1xuICAgICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgICB3aGlsZSAoY29weS0tKSB7XG4gICAgICAgICAgICBzdGF0ZS5sZW5zW3N0YXRlLmhhdmUrK10gPSBsZW47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8qIGhhbmRsZSBlcnJvciBicmVha3MgaW4gd2hpbGUgKi9cbiAgICAgIGlmIChzdGF0ZS5tb2RlID09PSBCQUQpIHsgYnJlYWs7IH1cblxuICAgICAgLyogY2hlY2sgZm9yIGVuZC1vZi1ibG9jayBjb2RlIChiZXR0ZXIgaGF2ZSBvbmUpICovXG4gICAgICBpZiAoc3RhdGUubGVuc1syNTZdID09PSAwKSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgY29kZSAtLSBtaXNzaW5nIGVuZC1vZi1ibG9jayc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICAvKiBidWlsZCBjb2RlIHRhYmxlcyAtLSBub3RlOiBkbyBub3QgY2hhbmdlIHRoZSBsZW5iaXRzIG9yIGRpc3RiaXRzXG4gICAgICAgICB2YWx1ZXMgaGVyZSAoOSBhbmQgNikgd2l0aG91dCByZWFkaW5nIHRoZSBjb21tZW50cyBpbiBpbmZ0cmVlcy5oXG4gICAgICAgICBjb25jZXJuaW5nIHRoZSBFTk9VR0ggY29uc3RhbnRzLCB3aGljaCBkZXBlbmQgb24gdGhvc2UgdmFsdWVzICovXG4gICAgICBzdGF0ZS5sZW5iaXRzID0gOTtcblxuICAgICAgb3B0cyA9IHtiaXRzOiBzdGF0ZS5sZW5iaXRzfTtcbiAgICAgIHJldCA9IGluZmxhdGVfdGFibGUoTEVOUywgc3RhdGUubGVucywgMCwgc3RhdGUubmxlbiwgc3RhdGUubGVuY29kZSwgMCwgc3RhdGUud29yaywgb3B0cyk7XG4gICAgICAvLyBXZSBoYXZlIHNlcGFyYXRlIHRhYmxlcyAmIG5vIHBvaW50ZXJzLiAyIGNvbW1lbnRlZCBsaW5lcyBiZWxvdyBub3QgbmVlZGVkLlxuICAgICAgLy8gc3RhdGUubmV4dF9pbmRleCA9IG9wdHMudGFibGVfaW5kZXg7XG4gICAgICBzdGF0ZS5sZW5iaXRzID0gb3B0cy5iaXRzO1xuICAgICAgLy8gc3RhdGUubGVuY29kZSA9IHN0YXRlLm5leHQ7XG5cbiAgICAgIGlmIChyZXQpIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBsaXRlcmFsL2xlbmd0aHMgc2V0JztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIHN0YXRlLmRpc3RiaXRzID0gNjtcbiAgICAgIC8vc3RhdGUuZGlzdGNvZGUuY29weShzdGF0ZS5jb2Rlcyk7XG4gICAgICAvLyBTd2l0Y2ggdG8gdXNlIGR5bmFtaWMgdGFibGVcbiAgICAgIHN0YXRlLmRpc3Rjb2RlID0gc3RhdGUuZGlzdGR5bjtcbiAgICAgIG9wdHMgPSB7Yml0czogc3RhdGUuZGlzdGJpdHN9O1xuICAgICAgcmV0ID0gaW5mbGF0ZV90YWJsZShESVNUUywgc3RhdGUubGVucywgc3RhdGUubmxlbiwgc3RhdGUubmRpc3QsIHN0YXRlLmRpc3Rjb2RlLCAwLCBzdGF0ZS53b3JrLCBvcHRzKTtcbiAgICAgIC8vIFdlIGhhdmUgc2VwYXJhdGUgdGFibGVzICYgbm8gcG9pbnRlcnMuIDIgY29tbWVudGVkIGxpbmVzIGJlbG93IG5vdCBuZWVkZWQuXG4gICAgICAvLyBzdGF0ZS5uZXh0X2luZGV4ID0gb3B0cy50YWJsZV9pbmRleDtcbiAgICAgIHN0YXRlLmRpc3RiaXRzID0gb3B0cy5iaXRzO1xuICAgICAgLy8gc3RhdGUuZGlzdGNvZGUgPSBzdGF0ZS5uZXh0O1xuXG4gICAgICBpZiAocmV0KSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgZGlzdGFuY2VzIHNldCc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgJ2luZmxhdGU6ICAgICAgIGNvZGVzIG9rXFxuJykpO1xuICAgICAgc3RhdGUubW9kZSA9IExFTl87XG4gICAgICBpZiAoZmx1c2ggPT09IFpfVFJFRVMpIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBMRU5fOlxuICAgICAgc3RhdGUubW9kZSA9IExFTjtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIExFTjpcbiAgICAgIGlmIChoYXZlID49IDYgJiYgbGVmdCA+PSAyNTgpIHtcbiAgICAgICAgLy8tLS0gUkVTVE9SRSgpIC0tLVxuICAgICAgICBzdHJtLm5leHRfb3V0ID0gcHV0O1xuICAgICAgICBzdHJtLmF2YWlsX291dCA9IGxlZnQ7XG4gICAgICAgIHN0cm0ubmV4dF9pbiA9IG5leHQ7XG4gICAgICAgIHN0cm0uYXZhaWxfaW4gPSBoYXZlO1xuICAgICAgICBzdGF0ZS5ob2xkID0gaG9sZDtcbiAgICAgICAgc3RhdGUuYml0cyA9IGJpdHM7XG4gICAgICAgIC8vLS0tXG4gICAgICAgIGluZmxhdGVfZmFzdChzdHJtLCBfb3V0KTtcbiAgICAgICAgLy8tLS0gTE9BRCgpIC0tLVxuICAgICAgICBwdXQgPSBzdHJtLm5leHRfb3V0O1xuICAgICAgICBvdXRwdXQgPSBzdHJtLm91dHB1dDtcbiAgICAgICAgbGVmdCA9IHN0cm0uYXZhaWxfb3V0O1xuICAgICAgICBuZXh0ID0gc3RybS5uZXh0X2luO1xuICAgICAgICBpbnB1dCA9IHN0cm0uaW5wdXQ7XG4gICAgICAgIGhhdmUgPSBzdHJtLmF2YWlsX2luO1xuICAgICAgICBob2xkID0gc3RhdGUuaG9sZDtcbiAgICAgICAgYml0cyA9IHN0YXRlLmJpdHM7XG4gICAgICAgIC8vLS0tXG5cbiAgICAgICAgaWYgKHN0YXRlLm1vZGUgPT09IFRZUEUpIHtcbiAgICAgICAgICBzdGF0ZS5iYWNrID0gLTE7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBzdGF0ZS5iYWNrID0gMDtcbiAgICAgIGZvciAoOzspIHtcbiAgICAgICAgaGVyZSA9IHN0YXRlLmxlbmNvZGVbaG9sZCAmICgoMSA8PCBzdGF0ZS5sZW5iaXRzKSAtMSldOyAgLypCSVRTKHN0YXRlLmxlbmJpdHMpKi9cbiAgICAgICAgaGVyZV9iaXRzID0gaGVyZSA+Pj4gMjQ7XG4gICAgICAgIGhlcmVfb3AgPSAoaGVyZSA+Pj4gMTYpICYgMHhmZjtcbiAgICAgICAgaGVyZV92YWwgPSBoZXJlICYgMHhmZmZmO1xuXG4gICAgICAgIGlmIChoZXJlX2JpdHMgPD0gYml0cykgeyBicmVhazsgfVxuICAgICAgICAvLy0tLSBQVUxMQllURSgpIC0tLS8vXG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIC8vLS0tLy9cbiAgICAgIH1cbiAgICAgIGlmIChoZXJlX29wICYmIChoZXJlX29wICYgMHhmMCkgPT09IDApIHtcbiAgICAgICAgbGFzdF9iaXRzID0gaGVyZV9iaXRzO1xuICAgICAgICBsYXN0X29wID0gaGVyZV9vcDtcbiAgICAgICAgbGFzdF92YWwgPSBoZXJlX3ZhbDtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGhlcmUgPSBzdGF0ZS5sZW5jb2RlW2xhc3RfdmFsICtcbiAgICAgICAgICAgICAgICAgICgoaG9sZCAmICgoMSA8PCAobGFzdF9iaXRzICsgbGFzdF9vcCkpIC0xKSkvKkJJVFMobGFzdC5iaXRzICsgbGFzdC5vcCkqLyA+PiBsYXN0X2JpdHMpXTtcbiAgICAgICAgICBoZXJlX2JpdHMgPSBoZXJlID4+PiAyNDtcbiAgICAgICAgICBoZXJlX29wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgICAgaGVyZV92YWwgPSBoZXJlICYgMHhmZmZmO1xuXG4gICAgICAgICAgaWYgKChsYXN0X2JpdHMgKyBoZXJlX2JpdHMpIDw9IGJpdHMpIHsgYnJlYWs7IH1cbiAgICAgICAgICAvLy0tLSBQVUxMQllURSgpIC0tLS8vXG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgICAvLy0tLS8vXG4gICAgICAgIH1cbiAgICAgICAgLy8tLS0gRFJPUEJJVFMobGFzdC5iaXRzKSAtLS0vL1xuICAgICAgICBob2xkID4+Pj0gbGFzdF9iaXRzO1xuICAgICAgICBiaXRzIC09IGxhc3RfYml0cztcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBzdGF0ZS5iYWNrICs9IGxhc3RfYml0cztcbiAgICAgIH1cbiAgICAgIC8vLS0tIERST1BCSVRTKGhlcmUuYml0cykgLS0tLy9cbiAgICAgIGhvbGQgPj4+PSBoZXJlX2JpdHM7XG4gICAgICBiaXRzIC09IGhlcmVfYml0cztcbiAgICAgIC8vLS0tLy9cbiAgICAgIHN0YXRlLmJhY2sgKz0gaGVyZV9iaXRzO1xuICAgICAgc3RhdGUubGVuZ3RoID0gaGVyZV92YWw7XG4gICAgICBpZiAoaGVyZV9vcCA9PT0gMCkge1xuICAgICAgICAvL1RyYWNldnYoKHN0ZGVyciwgaGVyZS52YWwgPj0gMHgyMCAmJiBoZXJlLnZhbCA8IDB4N2YgP1xuICAgICAgICAvLyAgICAgICAgXCJpbmZsYXRlOiAgICAgICAgIGxpdGVyYWwgJyVjJ1xcblwiIDpcbiAgICAgICAgLy8gICAgICAgIFwiaW5mbGF0ZTogICAgICAgICBsaXRlcmFsIDB4JTAyeFxcblwiLCBoZXJlLnZhbCkpO1xuICAgICAgICBzdGF0ZS5tb2RlID0gTElUO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmIChoZXJlX29wICYgMzIpIHtcbiAgICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgICBlbmQgb2YgYmxvY2tcXG5cIikpO1xuICAgICAgICBzdGF0ZS5iYWNrID0gLTE7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBUWVBFO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmIChoZXJlX29wICYgNjQpIHtcbiAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBsaXRlcmFsL2xlbmd0aCBjb2RlJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBzdGF0ZS5leHRyYSA9IGhlcmVfb3AgJiAxNTtcbiAgICAgIHN0YXRlLm1vZGUgPSBMRU5FWFQ7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBMRU5FWFQ6XG4gICAgICBpZiAoc3RhdGUuZXh0cmEpIHtcbiAgICAgICAgLy89PT0gTkVFREJJVFMoc3RhdGUuZXh0cmEpO1xuICAgICAgICBuID0gc3RhdGUuZXh0cmE7XG4gICAgICAgIHdoaWxlIChiaXRzIDwgbikge1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICBob2xkICs9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0vL1xuICAgICAgICBzdGF0ZS5sZW5ndGggKz0gaG9sZCAmICgoMSA8PCBzdGF0ZS5leHRyYSkgLTEpLypCSVRTKHN0YXRlLmV4dHJhKSovO1xuICAgICAgICAvLy0tLSBEUk9QQklUUyhzdGF0ZS5leHRyYSkgLS0tLy9cbiAgICAgICAgaG9sZCA+Pj49IHN0YXRlLmV4dHJhO1xuICAgICAgICBiaXRzIC09IHN0YXRlLmV4dHJhO1xuICAgICAgICAvLy0tLS8vXG4gICAgICAgIHN0YXRlLmJhY2sgKz0gc3RhdGUuZXh0cmE7XG4gICAgICB9XG4gICAgICAvL1RyYWNldnYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgICAgICAgIGxlbmd0aCAldVxcblwiLCBzdGF0ZS5sZW5ndGgpKTtcbiAgICAgIHN0YXRlLndhcyA9IHN0YXRlLmxlbmd0aDtcbiAgICAgIHN0YXRlLm1vZGUgPSBESVNUO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgRElTVDpcbiAgICAgIGZvciAoOzspIHtcbiAgICAgICAgaGVyZSA9IHN0YXRlLmRpc3Rjb2RlW2hvbGQgJiAoKDEgPDwgc3RhdGUuZGlzdGJpdHMpIC0xKV07LypCSVRTKHN0YXRlLmRpc3RiaXRzKSovXG4gICAgICAgIGhlcmVfYml0cyA9IGhlcmUgPj4+IDI0O1xuICAgICAgICBoZXJlX29wID0gKGhlcmUgPj4+IDE2KSAmIDB4ZmY7XG4gICAgICAgIGhlcmVfdmFsID0gaGVyZSAmIDB4ZmZmZjtcblxuICAgICAgICBpZiAoKGhlcmVfYml0cykgPD0gYml0cykgeyBicmVhazsgfVxuICAgICAgICAvLy0tLSBQVUxMQllURSgpIC0tLS8vXG4gICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICBoYXZlLS07XG4gICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIC8vLS0tLy9cbiAgICAgIH1cbiAgICAgIGlmICgoaGVyZV9vcCAmIDB4ZjApID09PSAwKSB7XG4gICAgICAgIGxhc3RfYml0cyA9IGhlcmVfYml0cztcbiAgICAgICAgbGFzdF9vcCA9IGhlcmVfb3A7XG4gICAgICAgIGxhc3RfdmFsID0gaGVyZV92YWw7XG4gICAgICAgIGZvciAoOzspIHtcbiAgICAgICAgICBoZXJlID0gc3RhdGUuZGlzdGNvZGVbbGFzdF92YWwgK1xuICAgICAgICAgICAgICAgICAgKChob2xkICYgKCgxIDw8IChsYXN0X2JpdHMgKyBsYXN0X29wKSkgLTEpKS8qQklUUyhsYXN0LmJpdHMgKyBsYXN0Lm9wKSovID4+IGxhc3RfYml0cyldO1xuICAgICAgICAgIGhlcmVfYml0cyA9IGhlcmUgPj4+IDI0O1xuICAgICAgICAgIGhlcmVfb3AgPSAoaGVyZSA+Pj4gMTYpICYgMHhmZjtcbiAgICAgICAgICBoZXJlX3ZhbCA9IGhlcmUgJiAweGZmZmY7XG5cbiAgICAgICAgICBpZiAoKGxhc3RfYml0cyArIGhlcmVfYml0cykgPD0gYml0cykgeyBicmVhazsgfVxuICAgICAgICAgIC8vLS0tIFBVTExCWVRFKCkgLS0tLy9cbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICAgIC8vLS0tLy9cbiAgICAgICAgfVxuICAgICAgICAvLy0tLSBEUk9QQklUUyhsYXN0LmJpdHMpIC0tLS8vXG4gICAgICAgIGhvbGQgPj4+PSBsYXN0X2JpdHM7XG4gICAgICAgIGJpdHMgLT0gbGFzdF9iaXRzO1xuICAgICAgICAvLy0tLS8vXG4gICAgICAgIHN0YXRlLmJhY2sgKz0gbGFzdF9iaXRzO1xuICAgICAgfVxuICAgICAgLy8tLS0gRFJPUEJJVFMoaGVyZS5iaXRzKSAtLS0vL1xuICAgICAgaG9sZCA+Pj49IGhlcmVfYml0cztcbiAgICAgIGJpdHMgLT0gaGVyZV9iaXRzO1xuICAgICAgLy8tLS0vL1xuICAgICAgc3RhdGUuYmFjayArPSBoZXJlX2JpdHM7XG4gICAgICBpZiAoaGVyZV9vcCAmIDY0KSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgZGlzdGFuY2UgY29kZSc7XG4gICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgc3RhdGUub2Zmc2V0ID0gaGVyZV92YWw7XG4gICAgICBzdGF0ZS5leHRyYSA9IChoZXJlX29wKSAmIDE1O1xuICAgICAgc3RhdGUubW9kZSA9IERJU1RFWFQ7XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgY2FzZSBESVNURVhUOlxuICAgICAgaWYgKHN0YXRlLmV4dHJhKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKHN0YXRlLmV4dHJhKTtcbiAgICAgICAgbiA9IHN0YXRlLmV4dHJhO1xuICAgICAgICB3aGlsZSAoYml0cyA8IG4pIHtcbiAgICAgICAgICBpZiAoaGF2ZSA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgICAgICBoYXZlLS07XG4gICAgICAgICAgaG9sZCArPSBpbnB1dFtuZXh0KytdIDw8IGJpdHM7XG4gICAgICAgICAgYml0cyArPSA4O1xuICAgICAgICB9XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgc3RhdGUub2Zmc2V0ICs9IGhvbGQgJiAoKDEgPDwgc3RhdGUuZXh0cmEpIC0xKS8qQklUUyhzdGF0ZS5leHRyYSkqLztcbiAgICAgICAgLy8tLS0gRFJPUEJJVFMoc3RhdGUuZXh0cmEpIC0tLS8vXG4gICAgICAgIGhvbGQgPj4+PSBzdGF0ZS5leHRyYTtcbiAgICAgICAgYml0cyAtPSBzdGF0ZS5leHRyYTtcbiAgICAgICAgLy8tLS0vL1xuICAgICAgICBzdGF0ZS5iYWNrICs9IHN0YXRlLmV4dHJhO1xuICAgICAgfVxuLy8jaWZkZWYgSU5GTEFURV9TVFJJQ1RcbiAgICAgIGlmIChzdGF0ZS5vZmZzZXQgPiBzdGF0ZS5kbWF4KSB7XG4gICAgICAgIHN0cm0ubXNnID0gJ2ludmFsaWQgZGlzdGFuY2UgdG9vIGZhciBiYWNrJztcbiAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4vLyNlbmRpZlxuICAgICAgLy9UcmFjZXZ2KChzdGRlcnIsIFwiaW5mbGF0ZTogICAgICAgICBkaXN0YW5jZSAldVxcblwiLCBzdGF0ZS5vZmZzZXQpKTtcbiAgICAgIHN0YXRlLm1vZGUgPSBNQVRDSDtcbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICBjYXNlIE1BVENIOlxuICAgICAgaWYgKGxlZnQgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICBjb3B5ID0gX291dCAtIGxlZnQ7XG4gICAgICBpZiAoc3RhdGUub2Zmc2V0ID4gY29weSkgeyAgICAgICAgIC8qIGNvcHkgZnJvbSB3aW5kb3cgKi9cbiAgICAgICAgY29weSA9IHN0YXRlLm9mZnNldCAtIGNvcHk7XG4gICAgICAgIGlmIChjb3B5ID4gc3RhdGUud2hhdmUpIHtcbiAgICAgICAgICBpZiAoc3RhdGUuc2FuZSkge1xuICAgICAgICAgICAgc3RybS5tc2cgPSAnaW52YWxpZCBkaXN0YW5jZSB0b28gZmFyIGJhY2snO1xuICAgICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbi8vICghKSBUaGlzIGJsb2NrIGlzIGRpc2FibGVkIGluIHpsaWIgZGVmYWlsdHMsXG4vLyBkb24ndCBlbmFibGUgaXQgZm9yIGJpbmFyeSBjb21wYXRpYmlsaXR5XG4vLyNpZmRlZiBJTkZMQVRFX0FMTE9XX0lOVkFMSURfRElTVEFOQ0VfVE9PRkFSX0FSUlJcbi8vICAgICAgICAgIFRyYWNlKChzdGRlcnIsIFwiaW5mbGF0ZS5jIHRvbyBmYXJcXG5cIikpO1xuLy8gICAgICAgICAgY29weSAtPSBzdGF0ZS53aGF2ZTtcbi8vICAgICAgICAgIGlmIChjb3B5ID4gc3RhdGUubGVuZ3RoKSB7IGNvcHkgPSBzdGF0ZS5sZW5ndGg7IH1cbi8vICAgICAgICAgIGlmIChjb3B5ID4gbGVmdCkgeyBjb3B5ID0gbGVmdDsgfVxuLy8gICAgICAgICAgbGVmdCAtPSBjb3B5O1xuLy8gICAgICAgICAgc3RhdGUubGVuZ3RoIC09IGNvcHk7XG4vLyAgICAgICAgICBkbyB7XG4vLyAgICAgICAgICAgIG91dHB1dFtwdXQrK10gPSAwO1xuLy8gICAgICAgICAgfSB3aGlsZSAoLS1jb3B5KTtcbi8vICAgICAgICAgIGlmIChzdGF0ZS5sZW5ndGggPT09IDApIHsgc3RhdGUubW9kZSA9IExFTjsgfVxuLy8gICAgICAgICAgYnJlYWs7XG4vLyNlbmRpZlxuICAgICAgICB9XG4gICAgICAgIGlmIChjb3B5ID4gc3RhdGUud25leHQpIHtcbiAgICAgICAgICBjb3B5IC09IHN0YXRlLnduZXh0O1xuICAgICAgICAgIGZyb20gPSBzdGF0ZS53c2l6ZSAtIGNvcHk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgZnJvbSA9IHN0YXRlLnduZXh0IC0gY29weTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29weSA+IHN0YXRlLmxlbmd0aCkgeyBjb3B5ID0gc3RhdGUubGVuZ3RoOyB9XG4gICAgICAgIGZyb21fc291cmNlID0gc3RhdGUud2luZG93O1xuICAgICAgfVxuICAgICAgZWxzZSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLyogY29weSBmcm9tIG91dHB1dCAqL1xuICAgICAgICBmcm9tX3NvdXJjZSA9IG91dHB1dDtcbiAgICAgICAgZnJvbSA9IHB1dCAtIHN0YXRlLm9mZnNldDtcbiAgICAgICAgY29weSA9IHN0YXRlLmxlbmd0aDtcbiAgICAgIH1cbiAgICAgIGlmIChjb3B5ID4gbGVmdCkgeyBjb3B5ID0gbGVmdDsgfVxuICAgICAgbGVmdCAtPSBjb3B5O1xuICAgICAgc3RhdGUubGVuZ3RoIC09IGNvcHk7XG4gICAgICBkbyB7XG4gICAgICAgIG91dHB1dFtwdXQrK10gPSBmcm9tX3NvdXJjZVtmcm9tKytdO1xuICAgICAgfSB3aGlsZSAoLS1jb3B5KTtcbiAgICAgIGlmIChzdGF0ZS5sZW5ndGggPT09IDApIHsgc3RhdGUubW9kZSA9IExFTjsgfVxuICAgICAgYnJlYWs7XG4gICAgY2FzZSBMSVQ6XG4gICAgICBpZiAobGVmdCA9PT0gMCkgeyBicmVhayBpbmZfbGVhdmU7IH1cbiAgICAgIG91dHB1dFtwdXQrK10gPSBzdGF0ZS5sZW5ndGg7XG4gICAgICBsZWZ0LS07XG4gICAgICBzdGF0ZS5tb2RlID0gTEVOO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBDSEVDSzpcbiAgICAgIGlmIChzdGF0ZS53cmFwKSB7XG4gICAgICAgIC8vPT09IE5FRURCSVRTKDMyKTtcbiAgICAgICAgd2hpbGUgKGJpdHMgPCAzMikge1xuICAgICAgICAgIGlmIChoYXZlID09PSAwKSB7IGJyZWFrIGluZl9sZWF2ZTsgfVxuICAgICAgICAgIGhhdmUtLTtcbiAgICAgICAgICAvLyBVc2UgJ3wnIGluc2RlYWQgb2YgJysnIHRvIG1ha2Ugc3VyZSB0aGF0IHJlc3VsdCBpcyBzaWduZWRcbiAgICAgICAgICBob2xkIHw9IGlucHV0W25leHQrK10gPDwgYml0cztcbiAgICAgICAgICBiaXRzICs9IDg7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0vL1xuICAgICAgICBfb3V0IC09IGxlZnQ7XG4gICAgICAgIHN0cm0udG90YWxfb3V0ICs9IF9vdXQ7XG4gICAgICAgIHN0YXRlLnRvdGFsICs9IF9vdXQ7XG4gICAgICAgIGlmIChfb3V0KSB7XG4gICAgICAgICAgc3RybS5hZGxlciA9IHN0YXRlLmNoZWNrID1cbiAgICAgICAgICAgICAgLypVUERBVEUoc3RhdGUuY2hlY2ssIHB1dCAtIF9vdXQsIF9vdXQpOyovXG4gICAgICAgICAgICAgIChzdGF0ZS5mbGFncyA/IGNyYzMyKHN0YXRlLmNoZWNrLCBvdXRwdXQsIF9vdXQsIHB1dCAtIF9vdXQpIDogYWRsZXIzMihzdGF0ZS5jaGVjaywgb3V0cHV0LCBfb3V0LCBwdXQgLSBfb3V0KSk7XG5cbiAgICAgICAgfVxuICAgICAgICBfb3V0ID0gbGVmdDtcbiAgICAgICAgLy8gTkI6IGNyYzMyIHN0b3JlZCBhcyBzaWduZWQgMzItYml0IGludCwgWlNXQVAzMiByZXR1cm5zIHNpZ25lZCB0b29cbiAgICAgICAgaWYgKChzdGF0ZS5mbGFncyA/IGhvbGQgOiBaU1dBUDMyKGhvbGQpKSAhPT0gc3RhdGUuY2hlY2spIHtcbiAgICAgICAgICBzdHJtLm1zZyA9ICdpbmNvcnJlY3QgZGF0YSBjaGVjayc7XG4gICAgICAgICAgc3RhdGUubW9kZSA9IEJBRDtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICAvLz09PSBJTklUQklUUygpO1xuICAgICAgICBob2xkID0gMDtcbiAgICAgICAgYml0cyA9IDA7XG4gICAgICAgIC8vPT09Ly9cbiAgICAgICAgLy9UcmFjZXYoKHN0ZGVyciwgXCJpbmZsYXRlOiAgIGNoZWNrIG1hdGNoZXMgdHJhaWxlclxcblwiKSk7XG4gICAgICB9XG4gICAgICBzdGF0ZS5tb2RlID0gTEVOR1RIO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgTEVOR1RIOlxuICAgICAgaWYgKHN0YXRlLndyYXAgJiYgc3RhdGUuZmxhZ3MpIHtcbiAgICAgICAgLy89PT0gTkVFREJJVFMoMzIpO1xuICAgICAgICB3aGlsZSAoYml0cyA8IDMyKSB7XG4gICAgICAgICAgaWYgKGhhdmUgPT09IDApIHsgYnJlYWsgaW5mX2xlYXZlOyB9XG4gICAgICAgICAgaGF2ZS0tO1xuICAgICAgICAgIGhvbGQgKz0gaW5wdXRbbmV4dCsrXSA8PCBiaXRzO1xuICAgICAgICAgIGJpdHMgKz0gODtcbiAgICAgICAgfVxuICAgICAgICAvLz09PS8vXG4gICAgICAgIGlmIChob2xkICE9PSAoc3RhdGUudG90YWwgJiAweGZmZmZmZmZmKSkge1xuICAgICAgICAgIHN0cm0ubXNnID0gJ2luY29ycmVjdCBsZW5ndGggY2hlY2snO1xuICAgICAgICAgIHN0YXRlLm1vZGUgPSBCQUQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgLy89PT0gSU5JVEJJVFMoKTtcbiAgICAgICAgaG9sZCA9IDA7XG4gICAgICAgIGJpdHMgPSAwO1xuICAgICAgICAvLz09PS8vXG4gICAgICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiaW5mbGF0ZTogICBsZW5ndGggbWF0Y2hlcyB0cmFpbGVyXFxuXCIpKTtcbiAgICAgIH1cbiAgICAgIHN0YXRlLm1vZGUgPSBET05FO1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgIGNhc2UgRE9ORTpcbiAgICAgIHJldCA9IFpfU1RSRUFNX0VORDtcbiAgICAgIGJyZWFrIGluZl9sZWF2ZTtcbiAgICBjYXNlIEJBRDpcbiAgICAgIHJldCA9IFpfREFUQV9FUlJPUjtcbiAgICAgIGJyZWFrIGluZl9sZWF2ZTtcbiAgICBjYXNlIE1FTTpcbiAgICAgIHJldHVybiBaX01FTV9FUlJPUjtcbiAgICBjYXNlIFNZTkM6XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiBaX1NUUkVBTV9FUlJPUjtcbiAgICB9XG4gIH1cblxuICAvLyBpbmZfbGVhdmUgPC0gaGVyZSBpcyByZWFsIHBsYWNlIGZvciBcImdvdG8gaW5mX2xlYXZlXCIsIGVtdWxhdGVkIHZpYSBcImJyZWFrIGluZl9sZWF2ZVwiXG5cbiAgLypcbiAgICAgUmV0dXJuIGZyb20gaW5mbGF0ZSgpLCB1cGRhdGluZyB0aGUgdG90YWwgY291bnRzIGFuZCB0aGUgY2hlY2sgdmFsdWUuXG4gICAgIElmIHRoZXJlIHdhcyBubyBwcm9ncmVzcyBkdXJpbmcgdGhlIGluZmxhdGUoKSBjYWxsLCByZXR1cm4gYSBidWZmZXJcbiAgICAgZXJyb3IuICBDYWxsIHVwZGF0ZXdpbmRvdygpIHRvIGNyZWF0ZSBhbmQvb3IgdXBkYXRlIHRoZSB3aW5kb3cgc3RhdGUuXG4gICAgIE5vdGU6IGEgbWVtb3J5IGVycm9yIGZyb20gaW5mbGF0ZSgpIGlzIG5vbi1yZWNvdmVyYWJsZS5cbiAgICovXG5cbiAgLy8tLS0gUkVTVE9SRSgpIC0tLVxuICBzdHJtLm5leHRfb3V0ID0gcHV0O1xuICBzdHJtLmF2YWlsX291dCA9IGxlZnQ7XG4gIHN0cm0ubmV4dF9pbiA9IG5leHQ7XG4gIHN0cm0uYXZhaWxfaW4gPSBoYXZlO1xuICBzdGF0ZS5ob2xkID0gaG9sZDtcbiAgc3RhdGUuYml0cyA9IGJpdHM7XG4gIC8vLS0tXG5cbiAgaWYgKHN0YXRlLndzaXplIHx8IChfb3V0ICE9PSBzdHJtLmF2YWlsX291dCAmJiBzdGF0ZS5tb2RlIDwgQkFEICYmXG4gICAgICAgICAgICAgICAgICAgICAgKHN0YXRlLm1vZGUgPCBDSEVDSyB8fCBmbHVzaCAhPT0gWl9GSU5JU0gpKSkge1xuICAgIGlmICh1cGRhdGV3aW5kb3coc3RybSwgc3RybS5vdXRwdXQsIHN0cm0ubmV4dF9vdXQsIF9vdXQgLSBzdHJtLmF2YWlsX291dCkpIHtcbiAgICAgIHN0YXRlLm1vZGUgPSBNRU07XG4gICAgICByZXR1cm4gWl9NRU1fRVJST1I7XG4gICAgfVxuICB9XG4gIF9pbiAtPSBzdHJtLmF2YWlsX2luO1xuICBfb3V0IC09IHN0cm0uYXZhaWxfb3V0O1xuICBzdHJtLnRvdGFsX2luICs9IF9pbjtcbiAgc3RybS50b3RhbF9vdXQgKz0gX291dDtcbiAgc3RhdGUudG90YWwgKz0gX291dDtcbiAgaWYgKHN0YXRlLndyYXAgJiYgX291dCkge1xuICAgIHN0cm0uYWRsZXIgPSBzdGF0ZS5jaGVjayA9IC8qVVBEQVRFKHN0YXRlLmNoZWNrLCBzdHJtLm5leHRfb3V0IC0gX291dCwgX291dCk7Ki9cbiAgICAgIChzdGF0ZS5mbGFncyA/IGNyYzMyKHN0YXRlLmNoZWNrLCBvdXRwdXQsIF9vdXQsIHN0cm0ubmV4dF9vdXQgLSBfb3V0KSA6IGFkbGVyMzIoc3RhdGUuY2hlY2ssIG91dHB1dCwgX291dCwgc3RybS5uZXh0X291dCAtIF9vdXQpKTtcbiAgfVxuICBzdHJtLmRhdGFfdHlwZSA9IHN0YXRlLmJpdHMgKyAoc3RhdGUubGFzdCA/IDY0IDogMCkgK1xuICAgICAgICAgICAgICAgICAgICAoc3RhdGUubW9kZSA9PT0gVFlQRSA/IDEyOCA6IDApICtcbiAgICAgICAgICAgICAgICAgICAgKHN0YXRlLm1vZGUgPT09IExFTl8gfHwgc3RhdGUubW9kZSA9PT0gQ09QWV8gPyAyNTYgOiAwKTtcbiAgaWYgKCgoX2luID09PSAwICYmIF9vdXQgPT09IDApIHx8IGZsdXNoID09PSBaX0ZJTklTSCkgJiYgcmV0ID09PSBaX09LKSB7XG4gICAgcmV0ID0gWl9CVUZfRVJST1I7XG4gIH1cbiAgcmV0dXJuIHJldDtcbn1cblxuZnVuY3Rpb24gaW5mbGF0ZUVuZChzdHJtKSB7XG5cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlIC8qfHwgc3RybS0+emZyZWUgPT0gKGZyZWVfZnVuYykwKi8pIHtcbiAgICByZXR1cm4gWl9TVFJFQU1fRVJST1I7XG4gIH1cblxuICB2YXIgc3RhdGUgPSBzdHJtLnN0YXRlO1xuICBpZiAoc3RhdGUud2luZG93KSB7XG4gICAgc3RhdGUud2luZG93ID0gbnVsbDtcbiAgfVxuICBzdHJtLnN0YXRlID0gbnVsbDtcbiAgcmV0dXJuIFpfT0s7XG59XG5cbmZ1bmN0aW9uIGluZmxhdGVHZXRIZWFkZXIoc3RybSwgaGVhZCkge1xuICB2YXIgc3RhdGU7XG5cbiAgLyogY2hlY2sgc3RhdGUgKi9cbiAgaWYgKCFzdHJtIHx8ICFzdHJtLnN0YXRlKSB7IHJldHVybiBaX1NUUkVBTV9FUlJPUjsgfVxuICBzdGF0ZSA9IHN0cm0uc3RhdGU7XG4gIGlmICgoc3RhdGUud3JhcCAmIDIpID09PSAwKSB7IHJldHVybiBaX1NUUkVBTV9FUlJPUjsgfVxuXG4gIC8qIHNhdmUgaGVhZGVyIHN0cnVjdHVyZSAqL1xuICBzdGF0ZS5oZWFkID0gaGVhZDtcbiAgaGVhZC5kb25lID0gZmFsc2U7XG4gIHJldHVybiBaX09LO1xufVxuXG5cbmV4cG9ydHMuaW5mbGF0ZVJlc2V0ID0gaW5mbGF0ZVJlc2V0O1xuZXhwb3J0cy5pbmZsYXRlUmVzZXQyID0gaW5mbGF0ZVJlc2V0MjtcbmV4cG9ydHMuaW5mbGF0ZVJlc2V0S2VlcCA9IGluZmxhdGVSZXNldEtlZXA7XG5leHBvcnRzLmluZmxhdGVJbml0ID0gaW5mbGF0ZUluaXQ7XG5leHBvcnRzLmluZmxhdGVJbml0MiA9IGluZmxhdGVJbml0MjtcbmV4cG9ydHMuaW5mbGF0ZSA9IGluZmxhdGU7XG5leHBvcnRzLmluZmxhdGVFbmQgPSBpbmZsYXRlRW5kO1xuZXhwb3J0cy5pbmZsYXRlR2V0SGVhZGVyID0gaW5mbGF0ZUdldEhlYWRlcjtcbmV4cG9ydHMuaW5mbGF0ZUluZm8gPSAncGFrbyBpbmZsYXRlIChmcm9tIE5vZGVjYSBwcm9qZWN0KSc7XG5cbi8qIE5vdCBpbXBsZW1lbnRlZFxuZXhwb3J0cy5pbmZsYXRlQ29weSA9IGluZmxhdGVDb3B5O1xuZXhwb3J0cy5pbmZsYXRlR2V0RGljdGlvbmFyeSA9IGluZmxhdGVHZXREaWN0aW9uYXJ5O1xuZXhwb3J0cy5pbmZsYXRlTWFyayA9IGluZmxhdGVNYXJrO1xuZXhwb3J0cy5pbmZsYXRlUHJpbWUgPSBpbmZsYXRlUHJpbWU7XG5leHBvcnRzLmluZmxhdGVTZXREaWN0aW9uYXJ5ID0gaW5mbGF0ZVNldERpY3Rpb25hcnk7XG5leHBvcnRzLmluZmxhdGVTeW5jID0gaW5mbGF0ZVN5bmM7XG5leHBvcnRzLmluZmxhdGVTeW5jUG9pbnQgPSBpbmZsYXRlU3luY1BvaW50O1xuZXhwb3J0cy5pbmZsYXRlVW5kZXJtaW5lID0gaW5mbGF0ZVVuZGVybWluZTtcbiovIiwiJ3VzZSBzdHJpY3QnO1xuXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbW1vbicpO1xuXG52YXIgTUFYQklUUyA9IDE1O1xudmFyIEVOT1VHSF9MRU5TID0gODUyO1xudmFyIEVOT1VHSF9ESVNUUyA9IDU5Mjtcbi8vdmFyIEVOT1VHSCA9IChFTk9VR0hfTEVOUytFTk9VR0hfRElTVFMpO1xuXG52YXIgQ09ERVMgPSAwO1xudmFyIExFTlMgPSAxO1xudmFyIERJU1RTID0gMjtcblxudmFyIGxiYXNlID0gWyAvKiBMZW5ndGggY29kZXMgMjU3Li4yODUgYmFzZSAqL1xuICAzLCA0LCA1LCA2LCA3LCA4LCA5LCAxMCwgMTEsIDEzLCAxNSwgMTcsIDE5LCAyMywgMjcsIDMxLFxuICAzNSwgNDMsIDUxLCA1OSwgNjcsIDgzLCA5OSwgMTE1LCAxMzEsIDE2MywgMTk1LCAyMjcsIDI1OCwgMCwgMFxuXTtcblxudmFyIGxleHQgPSBbIC8qIExlbmd0aCBjb2RlcyAyNTcuLjI4NSBleHRyYSAqL1xuICAxNiwgMTYsIDE2LCAxNiwgMTYsIDE2LCAxNiwgMTYsIDE3LCAxNywgMTcsIDE3LCAxOCwgMTgsIDE4LCAxOCxcbiAgMTksIDE5LCAxOSwgMTksIDIwLCAyMCwgMjAsIDIwLCAyMSwgMjEsIDIxLCAyMSwgMTYsIDcyLCA3OFxuXTtcblxudmFyIGRiYXNlID0gWyAvKiBEaXN0YW5jZSBjb2RlcyAwLi4yOSBiYXNlICovXG4gIDEsIDIsIDMsIDQsIDUsIDcsIDksIDEzLCAxNywgMjUsIDMzLCA0OSwgNjUsIDk3LCAxMjksIDE5MyxcbiAgMjU3LCAzODUsIDUxMywgNzY5LCAxMDI1LCAxNTM3LCAyMDQ5LCAzMDczLCA0MDk3LCA2MTQ1LFxuICA4MTkzLCAxMjI4OSwgMTYzODUsIDI0NTc3LCAwLCAwXG5dO1xuXG52YXIgZGV4dCA9IFsgLyogRGlzdGFuY2UgY29kZXMgMC4uMjkgZXh0cmEgKi9cbiAgMTYsIDE2LCAxNiwgMTYsIDE3LCAxNywgMTgsIDE4LCAxOSwgMTksIDIwLCAyMCwgMjEsIDIxLCAyMiwgMjIsXG4gIDIzLCAyMywgMjQsIDI0LCAyNSwgMjUsIDI2LCAyNiwgMjcsIDI3LFxuICAyOCwgMjgsIDI5LCAyOSwgNjQsIDY0XG5dO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGluZmxhdGVfdGFibGUodHlwZSwgbGVucywgbGVuc19pbmRleCwgY29kZXMsIHRhYmxlLCB0YWJsZV9pbmRleCwgd29yaywgb3B0cylcbntcbiAgdmFyIGJpdHMgPSBvcHRzLmJpdHM7XG4gICAgICAvL2hlcmUgPSBvcHRzLmhlcmU7IC8qIHRhYmxlIGVudHJ5IGZvciBkdXBsaWNhdGlvbiAqL1xuXG4gIHZhciBsZW4gPSAwOyAgICAgICAgICAgICAgIC8qIGEgY29kZSdzIGxlbmd0aCBpbiBiaXRzICovXG4gIHZhciBzeW0gPSAwOyAgICAgICAgICAgICAgIC8qIGluZGV4IG9mIGNvZGUgc3ltYm9scyAqL1xuICB2YXIgbWluID0gMCwgbWF4ID0gMDsgICAgICAgICAgLyogbWluaW11bSBhbmQgbWF4aW11bSBjb2RlIGxlbmd0aHMgKi9cbiAgdmFyIHJvb3QgPSAwOyAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGluZGV4IGJpdHMgZm9yIHJvb3QgdGFibGUgKi9cbiAgdmFyIGN1cnIgPSAwOyAgICAgICAgICAgICAgLyogbnVtYmVyIG9mIGluZGV4IGJpdHMgZm9yIGN1cnJlbnQgdGFibGUgKi9cbiAgdmFyIGRyb3AgPSAwOyAgICAgICAgICAgICAgLyogY29kZSBiaXRzIHRvIGRyb3AgZm9yIHN1Yi10YWJsZSAqL1xuICB2YXIgbGVmdCA9IDA7ICAgICAgICAgICAgICAgICAgIC8qIG51bWJlciBvZiBwcmVmaXggY29kZXMgYXZhaWxhYmxlICovXG4gIHZhciB1c2VkID0gMDsgICAgICAgICAgICAgIC8qIGNvZGUgZW50cmllcyBpbiB0YWJsZSB1c2VkICovXG4gIHZhciBodWZmID0gMDsgICAgICAgICAgICAgIC8qIEh1ZmZtYW4gY29kZSAqL1xuICB2YXIgaW5jcjsgICAgICAgICAgICAgIC8qIGZvciBpbmNyZW1lbnRpbmcgY29kZSwgaW5kZXggKi9cbiAgdmFyIGZpbGw7ICAgICAgICAgICAgICAvKiBpbmRleCBmb3IgcmVwbGljYXRpbmcgZW50cmllcyAqL1xuICB2YXIgbG93OyAgICAgICAgICAgICAgIC8qIGxvdyBiaXRzIGZvciBjdXJyZW50IHJvb3QgZW50cnkgKi9cbiAgdmFyIG1hc2s7ICAgICAgICAgICAgICAvKiBtYXNrIGZvciBsb3cgcm9vdCBiaXRzICovXG4gIHZhciBuZXh0OyAgICAgICAgICAgICAvKiBuZXh0IGF2YWlsYWJsZSBzcGFjZSBpbiB0YWJsZSAqL1xuICB2YXIgYmFzZSA9IG51bGw7ICAgICAvKiBiYXNlIHZhbHVlIHRhYmxlIHRvIHVzZSAqL1xuICB2YXIgYmFzZV9pbmRleCA9IDA7XG4vLyAgdmFyIHNob2V4dHJhOyAgICAvKiBleHRyYSBiaXRzIHRhYmxlIHRvIHVzZSAqL1xuICB2YXIgZW5kOyAgICAgICAgICAgICAgICAgICAgLyogdXNlIGJhc2UgYW5kIGV4dHJhIGZvciBzeW1ib2wgPiBlbmQgKi9cbiAgdmFyIGNvdW50ID0gbmV3IHV0aWxzLkJ1ZjE2KE1BWEJJVFMrMSk7IC8vW01BWEJJVFMrMV07ICAgIC8qIG51bWJlciBvZiBjb2RlcyBvZiBlYWNoIGxlbmd0aCAqL1xuICB2YXIgb2ZmcyA9IG5ldyB1dGlscy5CdWYxNihNQVhCSVRTKzEpOyAvL1tNQVhCSVRTKzFdOyAgICAgLyogb2Zmc2V0cyBpbiB0YWJsZSBmb3IgZWFjaCBsZW5ndGggKi9cbiAgdmFyIGV4dHJhID0gbnVsbDtcbiAgdmFyIGV4dHJhX2luZGV4ID0gMDtcblxuICB2YXIgaGVyZV9iaXRzLCBoZXJlX29wLCBoZXJlX3ZhbDtcblxuICAvKlxuICAgUHJvY2VzcyBhIHNldCBvZiBjb2RlIGxlbmd0aHMgdG8gY3JlYXRlIGEgY2Fub25pY2FsIEh1ZmZtYW4gY29kZS4gIFRoZVxuICAgY29kZSBsZW5ndGhzIGFyZSBsZW5zWzAuLmNvZGVzLTFdLiAgRWFjaCBsZW5ndGggY29ycmVzcG9uZHMgdG8gdGhlXG4gICBzeW1ib2xzIDAuLmNvZGVzLTEuICBUaGUgSHVmZm1hbiBjb2RlIGlzIGdlbmVyYXRlZCBieSBmaXJzdCBzb3J0aW5nIHRoZVxuICAgc3ltYm9scyBieSBsZW5ndGggZnJvbSBzaG9ydCB0byBsb25nLCBhbmQgcmV0YWluaW5nIHRoZSBzeW1ib2wgb3JkZXJcbiAgIGZvciBjb2RlcyB3aXRoIGVxdWFsIGxlbmd0aHMuICBUaGVuIHRoZSBjb2RlIHN0YXJ0cyB3aXRoIGFsbCB6ZXJvIGJpdHNcbiAgIGZvciB0aGUgZmlyc3QgY29kZSBvZiB0aGUgc2hvcnRlc3QgbGVuZ3RoLCBhbmQgdGhlIGNvZGVzIGFyZSBpbnRlZ2VyXG4gICBpbmNyZW1lbnRzIGZvciB0aGUgc2FtZSBsZW5ndGgsIGFuZCB6ZXJvcyBhcmUgYXBwZW5kZWQgYXMgdGhlIGxlbmd0aFxuICAgaW5jcmVhc2VzLiAgRm9yIHRoZSBkZWZsYXRlIGZvcm1hdCwgdGhlc2UgYml0cyBhcmUgc3RvcmVkIGJhY2t3YXJkc1xuICAgZnJvbSB0aGVpciBtb3JlIG5hdHVyYWwgaW50ZWdlciBpbmNyZW1lbnQgb3JkZXJpbmcsIGFuZCBzbyB3aGVuIHRoZVxuICAgZGVjb2RpbmcgdGFibGVzIGFyZSBidWlsdCBpbiB0aGUgbGFyZ2UgbG9vcCBiZWxvdywgdGhlIGludGVnZXIgY29kZXNcbiAgIGFyZSBpbmNyZW1lbnRlZCBiYWNrd2FyZHMuXG5cbiAgIFRoaXMgcm91dGluZSBhc3N1bWVzLCBidXQgZG9lcyBub3QgY2hlY2ssIHRoYXQgYWxsIG9mIHRoZSBlbnRyaWVzIGluXG4gICBsZW5zW10gYXJlIGluIHRoZSByYW5nZSAwLi5NQVhCSVRTLiAgVGhlIGNhbGxlciBtdXN0IGFzc3VyZSB0aGlzLlxuICAgMS4uTUFYQklUUyBpcyBpbnRlcnByZXRlZCBhcyB0aGF0IGNvZGUgbGVuZ3RoLiAgemVybyBtZWFucyB0aGF0IHRoYXRcbiAgIHN5bWJvbCBkb2VzIG5vdCBvY2N1ciBpbiB0aGlzIGNvZGUuXG5cbiAgIFRoZSBjb2RlcyBhcmUgc29ydGVkIGJ5IGNvbXB1dGluZyBhIGNvdW50IG9mIGNvZGVzIGZvciBlYWNoIGxlbmd0aCxcbiAgIGNyZWF0aW5nIGZyb20gdGhhdCBhIHRhYmxlIG9mIHN0YXJ0aW5nIGluZGljZXMgZm9yIGVhY2ggbGVuZ3RoIGluIHRoZVxuICAgc29ydGVkIHRhYmxlLCBhbmQgdGhlbiBlbnRlcmluZyB0aGUgc3ltYm9scyBpbiBvcmRlciBpbiB0aGUgc29ydGVkXG4gICB0YWJsZS4gIFRoZSBzb3J0ZWQgdGFibGUgaXMgd29ya1tdLCB3aXRoIHRoYXQgc3BhY2UgYmVpbmcgcHJvdmlkZWQgYnlcbiAgIHRoZSBjYWxsZXIuXG5cbiAgIFRoZSBsZW5ndGggY291bnRzIGFyZSB1c2VkIGZvciBvdGhlciBwdXJwb3NlcyBhcyB3ZWxsLCBpLmUuIGZpbmRpbmdcbiAgIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCBjb2RlcywgZGV0ZXJtaW5pbmcgaWYgdGhlcmUgYXJlIGFueVxuICAgY29kZXMgYXQgYWxsLCBjaGVja2luZyBmb3IgYSB2YWxpZCBzZXQgb2YgbGVuZ3RocywgYW5kIGxvb2tpbmcgYWhlYWRcbiAgIGF0IGxlbmd0aCBjb3VudHMgdG8gZGV0ZXJtaW5lIHN1Yi10YWJsZSBzaXplcyB3aGVuIGJ1aWxkaW5nIHRoZVxuICAgZGVjb2RpbmcgdGFibGVzLlxuICAgKi9cblxuICAvKiBhY2N1bXVsYXRlIGxlbmd0aHMgZm9yIGNvZGVzIChhc3N1bWVzIGxlbnNbXSBhbGwgaW4gMC4uTUFYQklUUykgKi9cbiAgZm9yIChsZW4gPSAwOyBsZW4gPD0gTUFYQklUUzsgbGVuKyspIHtcbiAgICBjb3VudFtsZW5dID0gMDtcbiAgfVxuICBmb3IgKHN5bSA9IDA7IHN5bSA8IGNvZGVzOyBzeW0rKykge1xuICAgIGNvdW50W2xlbnNbbGVuc19pbmRleCArIHN5bV1dKys7XG4gIH1cblxuICAvKiBib3VuZCBjb2RlIGxlbmd0aHMsIGZvcmNlIHJvb3QgdG8gYmUgd2l0aGluIGNvZGUgbGVuZ3RocyAqL1xuICByb290ID0gYml0cztcbiAgZm9yIChtYXggPSBNQVhCSVRTOyBtYXggPj0gMTsgbWF4LS0pIHtcbiAgICBpZiAoY291bnRbbWF4XSAhPT0gMCkgeyBicmVhazsgfVxuICB9XG4gIGlmIChyb290ID4gbWF4KSB7XG4gICAgcm9vdCA9IG1heDtcbiAgfVxuICBpZiAobWF4ID09PSAwKSB7ICAgICAgICAgICAgICAgICAgICAgLyogbm8gc3ltYm9scyB0byBjb2RlIGF0IGFsbCAqL1xuICAgIC8vdGFibGUub3Bbb3B0cy50YWJsZV9pbmRleF0gPSA2NDsgIC8vaGVyZS5vcCA9ICh2YXIgY2hhcik2NDsgICAgLyogaW52YWxpZCBjb2RlIG1hcmtlciAqL1xuICAgIC8vdGFibGUuYml0c1tvcHRzLnRhYmxlX2luZGV4XSA9IDE7ICAgLy9oZXJlLmJpdHMgPSAodmFyIGNoYXIpMTtcbiAgICAvL3RhYmxlLnZhbFtvcHRzLnRhYmxlX2luZGV4KytdID0gMDsgICAvL2hlcmUudmFsID0gKHZhciBzaG9ydCkwO1xuICAgIHRhYmxlW3RhYmxlX2luZGV4KytdID0gKDEgPDwgMjQpIHwgKDY0IDw8IDE2KSB8IDA7XG5cblxuICAgIC8vdGFibGUub3Bbb3B0cy50YWJsZV9pbmRleF0gPSA2NDtcbiAgICAvL3RhYmxlLmJpdHNbb3B0cy50YWJsZV9pbmRleF0gPSAxO1xuICAgIC8vdGFibGUudmFsW29wdHMudGFibGVfaW5kZXgrK10gPSAwO1xuICAgIHRhYmxlW3RhYmxlX2luZGV4KytdID0gKDEgPDwgMjQpIHwgKDY0IDw8IDE2KSB8IDA7XG5cbiAgICBvcHRzLmJpdHMgPSAxO1xuICAgIHJldHVybiAwOyAgICAgLyogbm8gc3ltYm9scywgYnV0IHdhaXQgZm9yIGRlY29kaW5nIHRvIHJlcG9ydCBlcnJvciAqL1xuICB9XG4gIGZvciAobWluID0gMTsgbWluIDwgbWF4OyBtaW4rKykge1xuICAgIGlmIChjb3VudFttaW5dICE9PSAwKSB7IGJyZWFrOyB9XG4gIH1cbiAgaWYgKHJvb3QgPCBtaW4pIHtcbiAgICByb290ID0gbWluO1xuICB9XG5cbiAgLyogY2hlY2sgZm9yIGFuIG92ZXItc3Vic2NyaWJlZCBvciBpbmNvbXBsZXRlIHNldCBvZiBsZW5ndGhzICovXG4gIGxlZnQgPSAxO1xuICBmb3IgKGxlbiA9IDE7IGxlbiA8PSBNQVhCSVRTOyBsZW4rKykge1xuICAgIGxlZnQgPDw9IDE7XG4gICAgbGVmdCAtPSBjb3VudFtsZW5dO1xuICAgIGlmIChsZWZ0IDwgMCkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH0gICAgICAgIC8qIG92ZXItc3Vic2NyaWJlZCAqL1xuICB9XG4gIGlmIChsZWZ0ID4gMCAmJiAodHlwZSA9PT0gQ09ERVMgfHwgbWF4ICE9PSAxKSkge1xuICAgIHJldHVybiAtMTsgICAgICAgICAgICAgICAgICAgICAgLyogaW5jb21wbGV0ZSBzZXQgKi9cbiAgfVxuXG4gIC8qIGdlbmVyYXRlIG9mZnNldHMgaW50byBzeW1ib2wgdGFibGUgZm9yIGVhY2ggbGVuZ3RoIGZvciBzb3J0aW5nICovXG4gIG9mZnNbMV0gPSAwO1xuICBmb3IgKGxlbiA9IDE7IGxlbiA8IE1BWEJJVFM7IGxlbisrKSB7XG4gICAgb2Zmc1tsZW4gKyAxXSA9IG9mZnNbbGVuXSArIGNvdW50W2xlbl07XG4gIH1cblxuICAvKiBzb3J0IHN5bWJvbHMgYnkgbGVuZ3RoLCBieSBzeW1ib2wgb3JkZXIgd2l0aGluIGVhY2ggbGVuZ3RoICovXG4gIGZvciAoc3ltID0gMDsgc3ltIDwgY29kZXM7IHN5bSsrKSB7XG4gICAgaWYgKGxlbnNbbGVuc19pbmRleCArIHN5bV0gIT09IDApIHtcbiAgICAgIHdvcmtbb2Zmc1tsZW5zW2xlbnNfaW5kZXggKyBzeW1dXSsrXSA9IHN5bTtcbiAgICB9XG4gIH1cblxuICAvKlxuICAgQ3JlYXRlIGFuZCBmaWxsIGluIGRlY29kaW5nIHRhYmxlcy4gIEluIHRoaXMgbG9vcCwgdGhlIHRhYmxlIGJlaW5nXG4gICBmaWxsZWQgaXMgYXQgbmV4dCBhbmQgaGFzIGN1cnIgaW5kZXggYml0cy4gIFRoZSBjb2RlIGJlaW5nIHVzZWQgaXMgaHVmZlxuICAgd2l0aCBsZW5ndGggbGVuLiAgVGhhdCBjb2RlIGlzIGNvbnZlcnRlZCB0byBhbiBpbmRleCBieSBkcm9wcGluZyBkcm9wXG4gICBiaXRzIG9mZiBvZiB0aGUgYm90dG9tLiAgRm9yIGNvZGVzIHdoZXJlIGxlbiBpcyBsZXNzIHRoYW4gZHJvcCArIGN1cnIsXG4gICB0aG9zZSB0b3AgZHJvcCArIGN1cnIgLSBsZW4gYml0cyBhcmUgaW5jcmVtZW50ZWQgdGhyb3VnaCBhbGwgdmFsdWVzIHRvXG4gICBmaWxsIHRoZSB0YWJsZSB3aXRoIHJlcGxpY2F0ZWQgZW50cmllcy5cblxuICAgcm9vdCBpcyB0aGUgbnVtYmVyIG9mIGluZGV4IGJpdHMgZm9yIHRoZSByb290IHRhYmxlLiAgV2hlbiBsZW4gZXhjZWVkc1xuICAgcm9vdCwgc3ViLXRhYmxlcyBhcmUgY3JlYXRlZCBwb2ludGVkIHRvIGJ5IHRoZSByb290IGVudHJ5IHdpdGggYW4gaW5kZXhcbiAgIG9mIHRoZSBsb3cgcm9vdCBiaXRzIG9mIGh1ZmYuICBUaGlzIGlzIHNhdmVkIGluIGxvdyB0byBjaGVjayBmb3Igd2hlbiBhXG4gICBuZXcgc3ViLXRhYmxlIHNob3VsZCBiZSBzdGFydGVkLiAgZHJvcCBpcyB6ZXJvIHdoZW4gdGhlIHJvb3QgdGFibGUgaXNcbiAgIGJlaW5nIGZpbGxlZCwgYW5kIGRyb3AgaXMgcm9vdCB3aGVuIHN1Yi10YWJsZXMgYXJlIGJlaW5nIGZpbGxlZC5cblxuICAgV2hlbiBhIG5ldyBzdWItdGFibGUgaXMgbmVlZGVkLCBpdCBpcyBuZWNlc3NhcnkgdG8gbG9vayBhaGVhZCBpbiB0aGVcbiAgIGNvZGUgbGVuZ3RocyB0byBkZXRlcm1pbmUgd2hhdCBzaXplIHN1Yi10YWJsZSBpcyBuZWVkZWQuICBUaGUgbGVuZ3RoXG4gICBjb3VudHMgYXJlIHVzZWQgZm9yIHRoaXMsIGFuZCBzbyBjb3VudFtdIGlzIGRlY3JlbWVudGVkIGFzIGNvZGVzIGFyZVxuICAgZW50ZXJlZCBpbiB0aGUgdGFibGVzLlxuXG4gICB1c2VkIGtlZXBzIHRyYWNrIG9mIGhvdyBtYW55IHRhYmxlIGVudHJpZXMgaGF2ZSBiZWVuIGFsbG9jYXRlZCBmcm9tIHRoZVxuICAgcHJvdmlkZWQgKnRhYmxlIHNwYWNlLiAgSXQgaXMgY2hlY2tlZCBmb3IgTEVOUyBhbmQgRElTVCB0YWJsZXMgYWdhaW5zdFxuICAgdGhlIGNvbnN0YW50cyBFTk9VR0hfTEVOUyBhbmQgRU5PVUdIX0RJU1RTIHRvIGd1YXJkIGFnYWluc3QgY2hhbmdlcyBpblxuICAgdGhlIGluaXRpYWwgcm9vdCB0YWJsZSBzaXplIGNvbnN0YW50cy4gIFNlZSB0aGUgY29tbWVudHMgaW4gaW5mdHJlZXMuaFxuICAgZm9yIG1vcmUgaW5mb3JtYXRpb24uXG5cbiAgIHN5bSBpbmNyZW1lbnRzIHRocm91Z2ggYWxsIHN5bWJvbHMsIGFuZCB0aGUgbG9vcCB0ZXJtaW5hdGVzIHdoZW5cbiAgIGFsbCBjb2RlcyBvZiBsZW5ndGggbWF4LCBpLmUuIGFsbCBjb2RlcywgaGF2ZSBiZWVuIHByb2Nlc3NlZC4gIFRoaXNcbiAgIHJvdXRpbmUgcGVybWl0cyBpbmNvbXBsZXRlIGNvZGVzLCBzbyBhbm90aGVyIGxvb3AgYWZ0ZXIgdGhpcyBvbmUgZmlsbHNcbiAgIGluIHRoZSByZXN0IG9mIHRoZSBkZWNvZGluZyB0YWJsZXMgd2l0aCBpbnZhbGlkIGNvZGUgbWFya2Vycy5cbiAgICovXG5cbiAgLyogc2V0IHVwIGZvciBjb2RlIHR5cGUgKi9cbiAgLy8gcG9vciBtYW4gb3B0aW1pemF0aW9uIC0gdXNlIGlmLWVsc2UgaW5zdGVhZCBvZiBzd2l0Y2gsXG4gIC8vIHRvIGF2b2lkIGRlb3B0cyBpbiBvbGQgdjhcbiAgaWYgKHR5cGUgPT09IENPREVTKSB7XG4gICAgICBiYXNlID0gZXh0cmEgPSB3b3JrOyAgICAvKiBkdW1teSB2YWx1ZS0tbm90IHVzZWQgKi9cbiAgICAgIGVuZCA9IDE5O1xuICB9IGVsc2UgaWYgKHR5cGUgPT09IExFTlMpIHtcbiAgICAgIGJhc2UgPSBsYmFzZTtcbiAgICAgIGJhc2VfaW5kZXggLT0gMjU3O1xuICAgICAgZXh0cmEgPSBsZXh0O1xuICAgICAgZXh0cmFfaW5kZXggLT0gMjU3O1xuICAgICAgZW5kID0gMjU2O1xuICB9IGVsc2UgeyAgICAgICAgICAgICAgICAgICAgLyogRElTVFMgKi9cbiAgICAgIGJhc2UgPSBkYmFzZTtcbiAgICAgIGV4dHJhID0gZGV4dDtcbiAgICAgIGVuZCA9IC0xO1xuICB9XG5cbiAgLyogaW5pdGlhbGl6ZSBvcHRzIGZvciBsb29wICovXG4gIGh1ZmYgPSAwOyAgICAgICAgICAgICAgICAgICAvKiBzdGFydGluZyBjb2RlICovXG4gIHN5bSA9IDA7ICAgICAgICAgICAgICAgICAgICAvKiBzdGFydGluZyBjb2RlIHN5bWJvbCAqL1xuICBsZW4gPSBtaW47ICAgICAgICAgICAgICAgICAgLyogc3RhcnRpbmcgY29kZSBsZW5ndGggKi9cbiAgbmV4dCA9IHRhYmxlX2luZGV4OyAgICAgICAgICAgICAgLyogY3VycmVudCB0YWJsZSB0byBmaWxsIGluICovXG4gIGN1cnIgPSByb290OyAgICAgICAgICAgICAgICAvKiBjdXJyZW50IHRhYmxlIGluZGV4IGJpdHMgKi9cbiAgZHJvcCA9IDA7ICAgICAgICAgICAgICAgICAgIC8qIGN1cnJlbnQgYml0cyB0byBkcm9wIGZyb20gY29kZSBmb3IgaW5kZXggKi9cbiAgbG93ID0gLTE7ICAgICAgICAgICAgICAgICAgIC8qIHRyaWdnZXIgbmV3IHN1Yi10YWJsZSB3aGVuIGxlbiA+IHJvb3QgKi9cbiAgdXNlZCA9IDEgPDwgcm9vdDsgICAgICAgICAgLyogdXNlIHJvb3QgdGFibGUgZW50cmllcyAqL1xuICBtYXNrID0gdXNlZCAtIDE7ICAgICAgICAgICAgLyogbWFzayBmb3IgY29tcGFyaW5nIGxvdyAqL1xuXG4gIC8qIGNoZWNrIGF2YWlsYWJsZSB0YWJsZSBzcGFjZSAqL1xuICBpZiAoKHR5cGUgPT09IExFTlMgJiYgdXNlZCA+IEVOT1VHSF9MRU5TKSB8fFxuICAgICh0eXBlID09PSBESVNUUyAmJiB1c2VkID4gRU5PVUdIX0RJU1RTKSkge1xuICAgIHJldHVybiAxO1xuICB9XG5cbiAgdmFyIGk9MDtcbiAgLyogcHJvY2VzcyBhbGwgY29kZXMgYW5kIG1ha2UgdGFibGUgZW50cmllcyAqL1xuICBmb3IgKDs7KSB7XG4gICAgaSsrO1xuICAgIC8qIGNyZWF0ZSB0YWJsZSBlbnRyeSAqL1xuICAgIGhlcmVfYml0cyA9IGxlbiAtIGRyb3A7XG4gICAgaWYgKHdvcmtbc3ltXSA8IGVuZCkge1xuICAgICAgaGVyZV9vcCA9IDA7XG4gICAgICBoZXJlX3ZhbCA9IHdvcmtbc3ltXTtcbiAgICB9XG4gICAgZWxzZSBpZiAod29ya1tzeW1dID4gZW5kKSB7XG4gICAgICBoZXJlX29wID0gZXh0cmFbZXh0cmFfaW5kZXggKyB3b3JrW3N5bV1dO1xuICAgICAgaGVyZV92YWwgPSBiYXNlW2Jhc2VfaW5kZXggKyB3b3JrW3N5bV1dO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIGhlcmVfb3AgPSAzMiArIDY0OyAgICAgICAgIC8qIGVuZCBvZiBibG9jayAqL1xuICAgICAgaGVyZV92YWwgPSAwO1xuICAgIH1cblxuICAgIC8qIHJlcGxpY2F0ZSBmb3IgdGhvc2UgaW5kaWNlcyB3aXRoIGxvdyBsZW4gYml0cyBlcXVhbCB0byBodWZmICovXG4gICAgaW5jciA9IDEgPDwgKGxlbiAtIGRyb3ApO1xuICAgIGZpbGwgPSAxIDw8IGN1cnI7XG4gICAgbWluID0gZmlsbDsgICAgICAgICAgICAgICAgIC8qIHNhdmUgb2Zmc2V0IHRvIG5leHQgdGFibGUgKi9cbiAgICBkbyB7XG4gICAgICBmaWxsIC09IGluY3I7XG4gICAgICB0YWJsZVtuZXh0ICsgKGh1ZmYgPj4gZHJvcCkgKyBmaWxsXSA9IChoZXJlX2JpdHMgPDwgMjQpIHwgKGhlcmVfb3AgPDwgMTYpIHwgaGVyZV92YWwgfDA7XG4gICAgfSB3aGlsZSAoZmlsbCAhPT0gMCk7XG5cbiAgICAvKiBiYWNrd2FyZHMgaW5jcmVtZW50IHRoZSBsZW4tYml0IGNvZGUgaHVmZiAqL1xuICAgIGluY3IgPSAxIDw8IChsZW4gLSAxKTtcbiAgICB3aGlsZSAoaHVmZiAmIGluY3IpIHtcbiAgICAgIGluY3IgPj49IDE7XG4gICAgfVxuICAgIGlmIChpbmNyICE9PSAwKSB7XG4gICAgICBodWZmICY9IGluY3IgLSAxO1xuICAgICAgaHVmZiArPSBpbmNyO1xuICAgIH0gZWxzZSB7XG4gICAgICBodWZmID0gMDtcbiAgICB9XG5cbiAgICAvKiBnbyB0byBuZXh0IHN5bWJvbCwgdXBkYXRlIGNvdW50LCBsZW4gKi9cbiAgICBzeW0rKztcbiAgICBpZiAoLS1jb3VudFtsZW5dID09PSAwKSB7XG4gICAgICBpZiAobGVuID09PSBtYXgpIHsgYnJlYWs7IH1cbiAgICAgIGxlbiA9IGxlbnNbbGVuc19pbmRleCArIHdvcmtbc3ltXV07XG4gICAgfVxuXG4gICAgLyogY3JlYXRlIG5ldyBzdWItdGFibGUgaWYgbmVlZGVkICovXG4gICAgaWYgKGxlbiA+IHJvb3QgJiYgKGh1ZmYgJiBtYXNrKSAhPT0gbG93KSB7XG4gICAgICAvKiBpZiBmaXJzdCB0aW1lLCB0cmFuc2l0aW9uIHRvIHN1Yi10YWJsZXMgKi9cbiAgICAgIGlmIChkcm9wID09PSAwKSB7XG4gICAgICAgIGRyb3AgPSByb290O1xuICAgICAgfVxuXG4gICAgICAvKiBpbmNyZW1lbnQgcGFzdCBsYXN0IHRhYmxlICovXG4gICAgICBuZXh0ICs9IG1pbjsgICAgICAgICAgICAvKiBoZXJlIG1pbiBpcyAxIDw8IGN1cnIgKi9cblxuICAgICAgLyogZGV0ZXJtaW5lIGxlbmd0aCBvZiBuZXh0IHRhYmxlICovXG4gICAgICBjdXJyID0gbGVuIC0gZHJvcDtcbiAgICAgIGxlZnQgPSAxIDw8IGN1cnI7XG4gICAgICB3aGlsZSAoY3VyciArIGRyb3AgPCBtYXgpIHtcbiAgICAgICAgbGVmdCAtPSBjb3VudFtjdXJyICsgZHJvcF07XG4gICAgICAgIGlmIChsZWZ0IDw9IDApIHsgYnJlYWs7IH1cbiAgICAgICAgY3VycisrO1xuICAgICAgICBsZWZ0IDw8PSAxO1xuICAgICAgfVxuXG4gICAgICAvKiBjaGVjayBmb3IgZW5vdWdoIHNwYWNlICovXG4gICAgICB1c2VkICs9IDEgPDwgY3VycjtcbiAgICAgIGlmICgodHlwZSA9PT0gTEVOUyAmJiB1c2VkID4gRU5PVUdIX0xFTlMpIHx8XG4gICAgICAgICh0eXBlID09PSBESVNUUyAmJiB1c2VkID4gRU5PVUdIX0RJU1RTKSkge1xuICAgICAgICByZXR1cm4gMTtcbiAgICAgIH1cblxuICAgICAgLyogcG9pbnQgZW50cnkgaW4gcm9vdCB0YWJsZSB0byBzdWItdGFibGUgKi9cbiAgICAgIGxvdyA9IGh1ZmYgJiBtYXNrO1xuICAgICAgLyp0YWJsZS5vcFtsb3ddID0gY3VycjtcbiAgICAgIHRhYmxlLmJpdHNbbG93XSA9IHJvb3Q7XG4gICAgICB0YWJsZS52YWxbbG93XSA9IG5leHQgLSBvcHRzLnRhYmxlX2luZGV4OyovXG4gICAgICB0YWJsZVtsb3ddID0gKHJvb3QgPDwgMjQpIHwgKGN1cnIgPDwgMTYpIHwgKG5leHQgLSB0YWJsZV9pbmRleCkgfDA7XG4gICAgfVxuICB9XG5cbiAgLyogZmlsbCBpbiByZW1haW5pbmcgdGFibGUgZW50cnkgaWYgY29kZSBpcyBpbmNvbXBsZXRlIChndWFyYW50ZWVkIHRvIGhhdmVcbiAgIGF0IG1vc3Qgb25lIHJlbWFpbmluZyBlbnRyeSwgc2luY2UgaWYgdGhlIGNvZGUgaXMgaW5jb21wbGV0ZSwgdGhlXG4gICBtYXhpbXVtIGNvZGUgbGVuZ3RoIHRoYXQgd2FzIGFsbG93ZWQgdG8gZ2V0IHRoaXMgZmFyIGlzIG9uZSBiaXQpICovXG4gIGlmIChodWZmICE9PSAwKSB7XG4gICAgLy90YWJsZS5vcFtuZXh0ICsgaHVmZl0gPSA2NDsgICAgICAgICAgICAvKiBpbnZhbGlkIGNvZGUgbWFya2VyICovXG4gICAgLy90YWJsZS5iaXRzW25leHQgKyBodWZmXSA9IGxlbiAtIGRyb3A7XG4gICAgLy90YWJsZS52YWxbbmV4dCArIGh1ZmZdID0gMDtcbiAgICB0YWJsZVtuZXh0ICsgaHVmZl0gPSAoKGxlbiAtIGRyb3ApIDw8IDI0KSB8ICg2NCA8PCAxNikgfDA7XG4gIH1cblxuICAvKiBzZXQgcmV0dXJuIHBhcmFtZXRlcnMgKi9cbiAgLy9vcHRzLnRhYmxlX2luZGV4ICs9IHVzZWQ7XG4gIG9wdHMuYml0cyA9IHJvb3Q7XG4gIHJldHVybiAwO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICcyJzogICAgJ25lZWQgZGljdGlvbmFyeScsICAgICAvKiBaX05FRURfRElDVCAgICAgICAyICAqL1xuICAnMSc6ICAgICdzdHJlYW0gZW5kJywgICAgICAgICAgLyogWl9TVFJFQU1fRU5EICAgICAgMSAgKi9cbiAgJzAnOiAgICAnJywgICAgICAgICAgICAgICAgICAgIC8qIFpfT0sgICAgICAgICAgICAgIDAgICovXG4gICctMSc6ICAgJ2ZpbGUgZXJyb3InLCAgICAgICAgICAvKiBaX0VSUk5PICAgICAgICAgKC0xKSAqL1xuICAnLTInOiAgICdzdHJlYW0gZXJyb3InLCAgICAgICAgLyogWl9TVFJFQU1fRVJST1IgICgtMikgKi9cbiAgJy0zJzogICAnZGF0YSBlcnJvcicsICAgICAgICAgIC8qIFpfREFUQV9FUlJPUiAgICAoLTMpICovXG4gICctNCc6ICAgJ2luc3VmZmljaWVudCBtZW1vcnknLCAvKiBaX01FTV9FUlJPUiAgICAgKC00KSAqL1xuICAnLTUnOiAgICdidWZmZXIgZXJyb3InLCAgICAgICAgLyogWl9CVUZfRVJST1IgICAgICgtNSkgKi9cbiAgJy02JzogICAnaW5jb21wYXRpYmxlIHZlcnNpb24nIC8qIFpfVkVSU0lPTl9FUlJPUiAoLTYpICovXG59OyIsIid1c2Ugc3RyaWN0JztcblxuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy9jb21tb24nKTtcblxuLyogUHVibGljIGNvbnN0YW50cyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxuLy92YXIgWl9GSUxURVJFRCAgICAgICAgICA9IDE7XG4vL3ZhciBaX0hVRkZNQU5fT05MWSAgICAgID0gMjtcbi8vdmFyIFpfUkxFICAgICAgICAgICAgICAgPSAzO1xudmFyIFpfRklYRUQgICAgICAgICAgICAgICA9IDQ7XG4vL3ZhciBaX0RFRkFVTFRfU1RSQVRFR1kgID0gMDtcblxuLyogUG9zc2libGUgdmFsdWVzIG9mIHRoZSBkYXRhX3R5cGUgZmllbGQgKHRob3VnaCBzZWUgaW5mbGF0ZSgpKSAqL1xudmFyIFpfQklOQVJZICAgICAgICAgICAgICA9IDA7XG52YXIgWl9URVhUICAgICAgICAgICAgICAgID0gMTtcbi8vdmFyIFpfQVNDSUkgICAgICAgICAgICAgPSAxOyAvLyA9IFpfVEVYVFxudmFyIFpfVU5LTk9XTiAgICAgICAgICAgICA9IDI7XG5cbi8qPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cblxuZnVuY3Rpb24gemVybyhidWYpIHsgdmFyIGxlbiA9IGJ1Zi5sZW5ndGg7IHdoaWxlICgtLWxlbiA+PSAwKSB7IGJ1ZltsZW5dID0gMDsgfSB9XG5cbi8vIEZyb20genV0aWwuaFxuXG52YXIgU1RPUkVEX0JMT0NLID0gMDtcbnZhciBTVEFUSUNfVFJFRVMgPSAxO1xudmFyIERZTl9UUkVFUyAgICA9IDI7XG4vKiBUaGUgdGhyZWUga2luZHMgb2YgYmxvY2sgdHlwZSAqL1xuXG52YXIgTUlOX01BVENIICAgID0gMztcbnZhciBNQVhfTUFUQ0ggICAgPSAyNTg7XG4vKiBUaGUgbWluaW11bSBhbmQgbWF4aW11bSBtYXRjaCBsZW5ndGhzICovXG5cbi8vIEZyb20gZGVmbGF0ZS5oXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEludGVybmFsIGNvbXByZXNzaW9uIHN0YXRlLlxuICovXG5cbnZhciBMRU5HVEhfQ09ERVMgID0gMjk7XG4vKiBudW1iZXIgb2YgbGVuZ3RoIGNvZGVzLCBub3QgY291bnRpbmcgdGhlIHNwZWNpYWwgRU5EX0JMT0NLIGNvZGUgKi9cblxudmFyIExJVEVSQUxTICAgICAgPSAyNTY7XG4vKiBudW1iZXIgb2YgbGl0ZXJhbCBieXRlcyAwLi4yNTUgKi9cblxudmFyIExfQ09ERVMgICAgICAgPSBMSVRFUkFMUyArIDEgKyBMRU5HVEhfQ09ERVM7XG4vKiBudW1iZXIgb2YgTGl0ZXJhbCBvciBMZW5ndGggY29kZXMsIGluY2x1ZGluZyB0aGUgRU5EX0JMT0NLIGNvZGUgKi9cblxudmFyIERfQ09ERVMgICAgICAgPSAzMDtcbi8qIG51bWJlciBvZiBkaXN0YW5jZSBjb2RlcyAqL1xuXG52YXIgQkxfQ09ERVMgICAgICA9IDE5O1xuLyogbnVtYmVyIG9mIGNvZGVzIHVzZWQgdG8gdHJhbnNmZXIgdGhlIGJpdCBsZW5ndGhzICovXG5cbnZhciBIRUFQX1NJWkUgICAgID0gMipMX0NPREVTICsgMTtcbi8qIG1heGltdW0gaGVhcCBzaXplICovXG5cbnZhciBNQVhfQklUUyAgICAgID0gMTU7XG4vKiBBbGwgY29kZXMgbXVzdCBub3QgZXhjZWVkIE1BWF9CSVRTIGJpdHMgKi9cblxudmFyIEJ1Zl9zaXplICAgICAgPSAxNjtcbi8qIHNpemUgb2YgYml0IGJ1ZmZlciBpbiBiaV9idWYgKi9cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvbnN0YW50c1xuICovXG5cbnZhciBNQVhfQkxfQklUUyA9IDc7XG4vKiBCaXQgbGVuZ3RoIGNvZGVzIG11c3Qgbm90IGV4Y2VlZCBNQVhfQkxfQklUUyBiaXRzICovXG5cbnZhciBFTkRfQkxPQ0sgICA9IDI1Njtcbi8qIGVuZCBvZiBibG9jayBsaXRlcmFsIGNvZGUgKi9cblxudmFyIFJFUF8zXzYgICAgID0gMTY7XG4vKiByZXBlYXQgcHJldmlvdXMgYml0IGxlbmd0aCAzLTYgdGltZXMgKDIgYml0cyBvZiByZXBlYXQgY291bnQpICovXG5cbnZhciBSRVBaXzNfMTAgICA9IDE3O1xuLyogcmVwZWF0IGEgemVybyBsZW5ndGggMy0xMCB0aW1lcyAgKDMgYml0cyBvZiByZXBlYXQgY291bnQpICovXG5cbnZhciBSRVBaXzExXzEzOCA9IDE4O1xuLyogcmVwZWF0IGEgemVybyBsZW5ndGggMTEtMTM4IHRpbWVzICAoNyBiaXRzIG9mIHJlcGVhdCBjb3VudCkgKi9cblxudmFyIGV4dHJhX2xiaXRzID0gICAvKiBleHRyYSBiaXRzIGZvciBlYWNoIGxlbmd0aCBjb2RlICovXG4gIFswLDAsMCwwLDAsMCwwLDAsMSwxLDEsMSwyLDIsMiwyLDMsMywzLDMsNCw0LDQsNCw1LDUsNSw1LDBdO1xuXG52YXIgZXh0cmFfZGJpdHMgPSAgIC8qIGV4dHJhIGJpdHMgZm9yIGVhY2ggZGlzdGFuY2UgY29kZSAqL1xuICBbMCwwLDAsMCwxLDEsMiwyLDMsMyw0LDQsNSw1LDYsNiw3LDcsOCw4LDksOSwxMCwxMCwxMSwxMSwxMiwxMiwxMywxM107XG5cbnZhciBleHRyYV9ibGJpdHMgPSAgLyogZXh0cmEgYml0cyBmb3IgZWFjaCBiaXQgbGVuZ3RoIGNvZGUgKi9cbiAgWzAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMiwzLDddO1xuXG52YXIgYmxfb3JkZXIgPVxuICBbMTYsMTcsMTgsMCw4LDcsOSw2LDEwLDUsMTEsNCwxMiwzLDEzLDIsMTQsMSwxNV07XG4vKiBUaGUgbGVuZ3RocyBvZiB0aGUgYml0IGxlbmd0aCBjb2RlcyBhcmUgc2VudCBpbiBvcmRlciBvZiBkZWNyZWFzaW5nXG4gKiBwcm9iYWJpbGl0eSwgdG8gYXZvaWQgdHJhbnNtaXR0aW5nIHRoZSBsZW5ndGhzIGZvciB1bnVzZWQgYml0IGxlbmd0aCBjb2Rlcy5cbiAqL1xuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIExvY2FsIGRhdGEuIFRoZXNlIGFyZSBpbml0aWFsaXplZCBvbmx5IG9uY2UuXG4gKi9cblxuLy8gV2UgcHJlLWZpbGwgYXJyYXlzIHdpdGggMCB0byBhdm9pZCB1bmluaXRpYWxpemVkIGdhcHNcblxudmFyIERJU1RfQ09ERV9MRU4gPSA1MTI7IC8qIHNlZSBkZWZpbml0aW9uIG9mIGFycmF5IGRpc3RfY29kZSBiZWxvdyAqL1xuXG4vLyAhISEhIFVzZSBmbGF0IGFycmF5IGluc2RlYWQgb2Ygc3RydWN0dXJlLCBGcmVxID0gaSoyLCBMZW4gPSBpKjIrMVxudmFyIHN0YXRpY19sdHJlZSAgPSBuZXcgQXJyYXkoKExfQ09ERVMrMikgKiAyKTtcbnplcm8oc3RhdGljX2x0cmVlKTtcbi8qIFRoZSBzdGF0aWMgbGl0ZXJhbCB0cmVlLiBTaW5jZSB0aGUgYml0IGxlbmd0aHMgYXJlIGltcG9zZWQsIHRoZXJlIGlzIG5vXG4gKiBuZWVkIGZvciB0aGUgTF9DT0RFUyBleHRyYSBjb2RlcyB1c2VkIGR1cmluZyBoZWFwIGNvbnN0cnVjdGlvbi4gSG93ZXZlclxuICogVGhlIGNvZGVzIDI4NiBhbmQgMjg3IGFyZSBuZWVkZWQgdG8gYnVpbGQgYSBjYW5vbmljYWwgdHJlZSAoc2VlIF90cl9pbml0XG4gKiBiZWxvdykuXG4gKi9cblxudmFyIHN0YXRpY19kdHJlZSAgPSBuZXcgQXJyYXkoRF9DT0RFUyAqIDIpO1xuemVybyhzdGF0aWNfZHRyZWUpO1xuLyogVGhlIHN0YXRpYyBkaXN0YW5jZSB0cmVlLiAoQWN0dWFsbHkgYSB0cml2aWFsIHRyZWUgc2luY2UgYWxsIGNvZGVzIHVzZVxuICogNSBiaXRzLilcbiAqL1xuXG52YXIgX2Rpc3RfY29kZSAgICA9IG5ldyBBcnJheShESVNUX0NPREVfTEVOKTtcbnplcm8oX2Rpc3RfY29kZSk7XG4vKiBEaXN0YW5jZSBjb2Rlcy4gVGhlIGZpcnN0IDI1NiB2YWx1ZXMgY29ycmVzcG9uZCB0byB0aGUgZGlzdGFuY2VzXG4gKiAzIC4uIDI1OCwgdGhlIGxhc3QgMjU2IHZhbHVlcyBjb3JyZXNwb25kIHRvIHRoZSB0b3AgOCBiaXRzIG9mXG4gKiB0aGUgMTUgYml0IGRpc3RhbmNlcy5cbiAqL1xuXG52YXIgX2xlbmd0aF9jb2RlICA9IG5ldyBBcnJheShNQVhfTUFUQ0gtTUlOX01BVENIKzEpO1xuemVybyhfbGVuZ3RoX2NvZGUpO1xuLyogbGVuZ3RoIGNvZGUgZm9yIGVhY2ggbm9ybWFsaXplZCBtYXRjaCBsZW5ndGggKDAgPT0gTUlOX01BVENIKSAqL1xuXG52YXIgYmFzZV9sZW5ndGggICA9IG5ldyBBcnJheShMRU5HVEhfQ09ERVMpO1xuemVybyhiYXNlX2xlbmd0aCk7XG4vKiBGaXJzdCBub3JtYWxpemVkIGxlbmd0aCBmb3IgZWFjaCBjb2RlICgwID0gTUlOX01BVENIKSAqL1xuXG52YXIgYmFzZV9kaXN0ICAgICA9IG5ldyBBcnJheShEX0NPREVTKTtcbnplcm8oYmFzZV9kaXN0KTtcbi8qIEZpcnN0IG5vcm1hbGl6ZWQgZGlzdGFuY2UgZm9yIGVhY2ggY29kZSAoMCA9IGRpc3RhbmNlIG9mIDEpICovXG5cblxudmFyIFN0YXRpY1RyZWVEZXNjID0gZnVuY3Rpb24gKHN0YXRpY190cmVlLCBleHRyYV9iaXRzLCBleHRyYV9iYXNlLCBlbGVtcywgbWF4X2xlbmd0aCkge1xuXG4gIHRoaXMuc3RhdGljX3RyZWUgID0gc3RhdGljX3RyZWU7ICAvKiBzdGF0aWMgdHJlZSBvciBOVUxMICovXG4gIHRoaXMuZXh0cmFfYml0cyAgID0gZXh0cmFfYml0czsgICAvKiBleHRyYSBiaXRzIGZvciBlYWNoIGNvZGUgb3IgTlVMTCAqL1xuICB0aGlzLmV4dHJhX2Jhc2UgICA9IGV4dHJhX2Jhc2U7ICAgLyogYmFzZSBpbmRleCBmb3IgZXh0cmFfYml0cyAqL1xuICB0aGlzLmVsZW1zICAgICAgICA9IGVsZW1zOyAgICAgICAgLyogbWF4IG51bWJlciBvZiBlbGVtZW50cyBpbiB0aGUgdHJlZSAqL1xuICB0aGlzLm1heF9sZW5ndGggICA9IG1heF9sZW5ndGg7ICAgLyogbWF4IGJpdCBsZW5ndGggZm9yIHRoZSBjb2RlcyAqL1xuXG4gIC8vIHNob3cgaWYgYHN0YXRpY190cmVlYCBoYXMgZGF0YSBvciBkdW1teSAtIG5lZWRlZCBmb3IgbW9ub21vcnBoaWMgb2JqZWN0c1xuICB0aGlzLmhhc19zdHJlZSAgICA9IHN0YXRpY190cmVlICYmIHN0YXRpY190cmVlLmxlbmd0aDtcbn07XG5cblxudmFyIHN0YXRpY19sX2Rlc2M7XG52YXIgc3RhdGljX2RfZGVzYztcbnZhciBzdGF0aWNfYmxfZGVzYztcblxuXG52YXIgVHJlZURlc2MgPSBmdW5jdGlvbihkeW5fdHJlZSwgc3RhdF9kZXNjKSB7XG4gIHRoaXMuZHluX3RyZWUgPSBkeW5fdHJlZTsgICAgIC8qIHRoZSBkeW5hbWljIHRyZWUgKi9cbiAgdGhpcy5tYXhfY29kZSA9IDA7ICAgICAgICAgICAgLyogbGFyZ2VzdCBjb2RlIHdpdGggbm9uIHplcm8gZnJlcXVlbmN5ICovXG4gIHRoaXMuc3RhdF9kZXNjID0gc3RhdF9kZXNjOyAgIC8qIHRoZSBjb3JyZXNwb25kaW5nIHN0YXRpYyB0cmVlICovXG59O1xuXG5cblxuZnVuY3Rpb24gZF9jb2RlKGRpc3QpIHtcbiAgcmV0dXJuIGRpc3QgPCAyNTYgPyBfZGlzdF9jb2RlW2Rpc3RdIDogX2Rpc3RfY29kZVsyNTYgKyAoZGlzdCA+Pj4gNyldO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogT3V0cHV0IGEgc2hvcnQgTFNCIGZpcnN0IG9uIHRoZSBzdHJlYW0uXG4gKiBJTiBhc3NlcnRpb246IHRoZXJlIGlzIGVub3VnaCByb29tIGluIHBlbmRpbmdCdWYuXG4gKi9cbmZ1bmN0aW9uIHB1dF9zaG9ydCAocywgdykge1xuLy8gICAgcHV0X2J5dGUocywgKHVjaCkoKHcpICYgMHhmZikpO1xuLy8gICAgcHV0X2J5dGUocywgKHVjaCkoKHVzaCkodykgPj4gOCkpO1xuICBzLnBlbmRpbmdfYnVmW3MucGVuZGluZysrXSA9ICh3KSAmIDB4ZmY7XG4gIHMucGVuZGluZ19idWZbcy5wZW5kaW5nKytdID0gKHcgPj4+IDgpICYgMHhmZjtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNlbmQgYSB2YWx1ZSBvbiBhIGdpdmVuIG51bWJlciBvZiBiaXRzLlxuICogSU4gYXNzZXJ0aW9uOiBsZW5ndGggPD0gMTYgYW5kIHZhbHVlIGZpdHMgaW4gbGVuZ3RoIGJpdHMuXG4gKi9cbmZ1bmN0aW9uIHNlbmRfYml0cyhzLCB2YWx1ZSwgbGVuZ3RoKSB7XG4gIGlmIChzLmJpX3ZhbGlkID4gKEJ1Zl9zaXplIC0gbGVuZ3RoKSkge1xuICAgIHMuYmlfYnVmIHw9ICh2YWx1ZSA8PCBzLmJpX3ZhbGlkKSAmIDB4ZmZmZjtcbiAgICBwdXRfc2hvcnQocywgcy5iaV9idWYpO1xuICAgIHMuYmlfYnVmID0gdmFsdWUgPj4gKEJ1Zl9zaXplIC0gcy5iaV92YWxpZCk7XG4gICAgcy5iaV92YWxpZCArPSBsZW5ndGggLSBCdWZfc2l6ZTtcbiAgfSBlbHNlIHtcbiAgICBzLmJpX2J1ZiB8PSAodmFsdWUgPDwgcy5iaV92YWxpZCkgJiAweGZmZmY7XG4gICAgcy5iaV92YWxpZCArPSBsZW5ndGg7XG4gIH1cbn1cblxuXG5mdW5jdGlvbiBzZW5kX2NvZGUocywgYywgdHJlZSkge1xuICBzZW5kX2JpdHMocywgdHJlZVtjKjJdLyouQ29kZSovLCB0cmVlW2MqMiArIDFdLyouTGVuKi8pO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogUmV2ZXJzZSB0aGUgZmlyc3QgbGVuIGJpdHMgb2YgYSBjb2RlLCB1c2luZyBzdHJhaWdodGZvcndhcmQgY29kZSAoYSBmYXN0ZXJcbiAqIG1ldGhvZCB3b3VsZCB1c2UgYSB0YWJsZSlcbiAqIElOIGFzc2VydGlvbjogMSA8PSBsZW4gPD0gMTVcbiAqL1xuZnVuY3Rpb24gYmlfcmV2ZXJzZShjb2RlLCBsZW4pIHtcbiAgdmFyIHJlcyA9IDA7XG4gIGRvIHtcbiAgICByZXMgfD0gY29kZSAmIDE7XG4gICAgY29kZSA+Pj49IDE7XG4gICAgcmVzIDw8PSAxO1xuICB9IHdoaWxlICgtLWxlbiA+IDApO1xuICByZXR1cm4gcmVzID4+PiAxO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogRmx1c2ggdGhlIGJpdCBidWZmZXIsIGtlZXBpbmcgYXQgbW9zdCA3IGJpdHMgaW4gaXQuXG4gKi9cbmZ1bmN0aW9uIGJpX2ZsdXNoKHMpIHtcbiAgaWYgKHMuYmlfdmFsaWQgPT09IDE2KSB7XG4gICAgcHV0X3Nob3J0KHMsIHMuYmlfYnVmKTtcbiAgICBzLmJpX2J1ZiA9IDA7XG4gICAgcy5iaV92YWxpZCA9IDA7XG5cbiAgfSBlbHNlIGlmIChzLmJpX3ZhbGlkID49IDgpIHtcbiAgICBzLnBlbmRpbmdfYnVmW3MucGVuZGluZysrXSA9IHMuYmlfYnVmICYgMHhmZjtcbiAgICBzLmJpX2J1ZiA+Pj0gODtcbiAgICBzLmJpX3ZhbGlkIC09IDg7XG4gIH1cbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvbXB1dGUgdGhlIG9wdGltYWwgYml0IGxlbmd0aHMgZm9yIGEgdHJlZSBhbmQgdXBkYXRlIHRoZSB0b3RhbCBiaXQgbGVuZ3RoXG4gKiBmb3IgdGhlIGN1cnJlbnQgYmxvY2suXG4gKiBJTiBhc3NlcnRpb246IHRoZSBmaWVsZHMgZnJlcSBhbmQgZGFkIGFyZSBzZXQsIGhlYXBbaGVhcF9tYXhdIGFuZFxuICogICAgYWJvdmUgYXJlIHRoZSB0cmVlIG5vZGVzIHNvcnRlZCBieSBpbmNyZWFzaW5nIGZyZXF1ZW5jeS5cbiAqIE9VVCBhc3NlcnRpb25zOiB0aGUgZmllbGQgbGVuIGlzIHNldCB0byB0aGUgb3B0aW1hbCBiaXQgbGVuZ3RoLCB0aGVcbiAqICAgICBhcnJheSBibF9jb3VudCBjb250YWlucyB0aGUgZnJlcXVlbmNpZXMgZm9yIGVhY2ggYml0IGxlbmd0aC5cbiAqICAgICBUaGUgbGVuZ3RoIG9wdF9sZW4gaXMgdXBkYXRlZDsgc3RhdGljX2xlbiBpcyBhbHNvIHVwZGF0ZWQgaWYgc3RyZWUgaXNcbiAqICAgICBub3QgbnVsbC5cbiAqL1xuZnVuY3Rpb24gZ2VuX2JpdGxlbihzLCBkZXNjKVxuLy8gICAgZGVmbGF0ZV9zdGF0ZSAqcztcbi8vICAgIHRyZWVfZGVzYyAqZGVzYzsgICAgLyogdGhlIHRyZWUgZGVzY3JpcHRvciAqL1xue1xuICB2YXIgdHJlZSAgICAgICAgICAgID0gZGVzYy5keW5fdHJlZTtcbiAgdmFyIG1heF9jb2RlICAgICAgICA9IGRlc2MubWF4X2NvZGU7XG4gIHZhciBzdHJlZSAgICAgICAgICAgPSBkZXNjLnN0YXRfZGVzYy5zdGF0aWNfdHJlZTtcbiAgdmFyIGhhc19zdHJlZSAgICAgICA9IGRlc2Muc3RhdF9kZXNjLmhhc19zdHJlZTtcbiAgdmFyIGV4dHJhICAgICAgICAgICA9IGRlc2Muc3RhdF9kZXNjLmV4dHJhX2JpdHM7XG4gIHZhciBiYXNlICAgICAgICAgICAgPSBkZXNjLnN0YXRfZGVzYy5leHRyYV9iYXNlO1xuICB2YXIgbWF4X2xlbmd0aCAgICAgID0gZGVzYy5zdGF0X2Rlc2MubWF4X2xlbmd0aDtcbiAgdmFyIGg7ICAgICAgICAgICAgICAvKiBoZWFwIGluZGV4ICovXG4gIHZhciBuLCBtOyAgICAgICAgICAgLyogaXRlcmF0ZSBvdmVyIHRoZSB0cmVlIGVsZW1lbnRzICovXG4gIHZhciBiaXRzOyAgICAgICAgICAgLyogYml0IGxlbmd0aCAqL1xuICB2YXIgeGJpdHM7ICAgICAgICAgIC8qIGV4dHJhIGJpdHMgKi9cbiAgdmFyIGY7ICAgICAgICAgICAgICAvKiBmcmVxdWVuY3kgKi9cbiAgdmFyIG92ZXJmbG93ID0gMDsgICAvKiBudW1iZXIgb2YgZWxlbWVudHMgd2l0aCBiaXQgbGVuZ3RoIHRvbyBsYXJnZSAqL1xuXG4gIGZvciAoYml0cyA9IDA7IGJpdHMgPD0gTUFYX0JJVFM7IGJpdHMrKykge1xuICAgIHMuYmxfY291bnRbYml0c10gPSAwO1xuICB9XG5cbiAgLyogSW4gYSBmaXJzdCBwYXNzLCBjb21wdXRlIHRoZSBvcHRpbWFsIGJpdCBsZW5ndGhzICh3aGljaCBtYXlcbiAgICogb3ZlcmZsb3cgaW4gdGhlIGNhc2Ugb2YgdGhlIGJpdCBsZW5ndGggdHJlZSkuXG4gICAqL1xuICB0cmVlW3MuaGVhcFtzLmhlYXBfbWF4XSoyICsgMV0vKi5MZW4qLyA9IDA7IC8qIHJvb3Qgb2YgdGhlIGhlYXAgKi9cblxuICBmb3IgKGggPSBzLmhlYXBfbWF4KzE7IGggPCBIRUFQX1NJWkU7IGgrKykge1xuICAgIG4gPSBzLmhlYXBbaF07XG4gICAgYml0cyA9IHRyZWVbdHJlZVtuKjIgKzFdLyouRGFkKi8gKiAyICsgMV0vKi5MZW4qLyArIDE7XG4gICAgaWYgKGJpdHMgPiBtYXhfbGVuZ3RoKSB7XG4gICAgICBiaXRzID0gbWF4X2xlbmd0aDtcbiAgICAgIG92ZXJmbG93Kys7XG4gICAgfVxuICAgIHRyZWVbbioyICsgMV0vKi5MZW4qLyA9IGJpdHM7XG4gICAgLyogV2Ugb3ZlcndyaXRlIHRyZWVbbl0uRGFkIHdoaWNoIGlzIG5vIGxvbmdlciBuZWVkZWQgKi9cblxuICAgIGlmIChuID4gbWF4X2NvZGUpIHsgY29udGludWU7IH0gLyogbm90IGEgbGVhZiBub2RlICovXG5cbiAgICBzLmJsX2NvdW50W2JpdHNdKys7XG4gICAgeGJpdHMgPSAwO1xuICAgIGlmIChuID49IGJhc2UpIHtcbiAgICAgIHhiaXRzID0gZXh0cmFbbi1iYXNlXTtcbiAgICB9XG4gICAgZiA9IHRyZWVbbiAqIDJdLyouRnJlcSovO1xuICAgIHMub3B0X2xlbiArPSBmICogKGJpdHMgKyB4Yml0cyk7XG4gICAgaWYgKGhhc19zdHJlZSkge1xuICAgICAgcy5zdGF0aWNfbGVuICs9IGYgKiAoc3RyZWVbbioyICsgMV0vKi5MZW4qLyArIHhiaXRzKTtcbiAgICB9XG4gIH1cbiAgaWYgKG92ZXJmbG93ID09PSAwKSB7IHJldHVybjsgfVxuXG4gIC8vIFRyYWNlKChzdGRlcnIsXCJcXG5iaXQgbGVuZ3RoIG92ZXJmbG93XFxuXCIpKTtcbiAgLyogVGhpcyBoYXBwZW5zIGZvciBleGFtcGxlIG9uIG9iajIgYW5kIHBpYyBvZiB0aGUgQ2FsZ2FyeSBjb3JwdXMgKi9cblxuICAvKiBGaW5kIHRoZSBmaXJzdCBiaXQgbGVuZ3RoIHdoaWNoIGNvdWxkIGluY3JlYXNlOiAqL1xuICBkbyB7XG4gICAgYml0cyA9IG1heF9sZW5ndGgtMTtcbiAgICB3aGlsZSAocy5ibF9jb3VudFtiaXRzXSA9PT0gMCkgeyBiaXRzLS07IH1cbiAgICBzLmJsX2NvdW50W2JpdHNdLS07ICAgICAgLyogbW92ZSBvbmUgbGVhZiBkb3duIHRoZSB0cmVlICovXG4gICAgcy5ibF9jb3VudFtiaXRzKzFdICs9IDI7IC8qIG1vdmUgb25lIG92ZXJmbG93IGl0ZW0gYXMgaXRzIGJyb3RoZXIgKi9cbiAgICBzLmJsX2NvdW50W21heF9sZW5ndGhdLS07XG4gICAgLyogVGhlIGJyb3RoZXIgb2YgdGhlIG92ZXJmbG93IGl0ZW0gYWxzbyBtb3ZlcyBvbmUgc3RlcCB1cCxcbiAgICAgKiBidXQgdGhpcyBkb2VzIG5vdCBhZmZlY3QgYmxfY291bnRbbWF4X2xlbmd0aF1cbiAgICAgKi9cbiAgICBvdmVyZmxvdyAtPSAyO1xuICB9IHdoaWxlIChvdmVyZmxvdyA+IDApO1xuXG4gIC8qIE5vdyByZWNvbXB1dGUgYWxsIGJpdCBsZW5ndGhzLCBzY2FubmluZyBpbiBpbmNyZWFzaW5nIGZyZXF1ZW5jeS5cbiAgICogaCBpcyBzdGlsbCBlcXVhbCB0byBIRUFQX1NJWkUuIChJdCBpcyBzaW1wbGVyIHRvIHJlY29uc3RydWN0IGFsbFxuICAgKiBsZW5ndGhzIGluc3RlYWQgb2YgZml4aW5nIG9ubHkgdGhlIHdyb25nIG9uZXMuIFRoaXMgaWRlYSBpcyB0YWtlblxuICAgKiBmcm9tICdhcicgd3JpdHRlbiBieSBIYXJ1aGlrbyBPa3VtdXJhLilcbiAgICovXG4gIGZvciAoYml0cyA9IG1heF9sZW5ndGg7IGJpdHMgIT09IDA7IGJpdHMtLSkge1xuICAgIG4gPSBzLmJsX2NvdW50W2JpdHNdO1xuICAgIHdoaWxlIChuICE9PSAwKSB7XG4gICAgICBtID0gcy5oZWFwWy0taF07XG4gICAgICBpZiAobSA+IG1heF9jb2RlKSB7IGNvbnRpbnVlOyB9XG4gICAgICBpZiAodHJlZVttKjIgKyAxXS8qLkxlbiovICE9PSBiaXRzKSB7XG4gICAgICAgIC8vIFRyYWNlKChzdGRlcnIsXCJjb2RlICVkIGJpdHMgJWQtPiVkXFxuXCIsIG0sIHRyZWVbbV0uTGVuLCBiaXRzKSk7XG4gICAgICAgIHMub3B0X2xlbiArPSAoYml0cyAtIHRyZWVbbSoyICsgMV0vKi5MZW4qLykqdHJlZVttKjJdLyouRnJlcSovO1xuICAgICAgICB0cmVlW20qMiArIDFdLyouTGVuKi8gPSBiaXRzO1xuICAgICAgfVxuICAgICAgbi0tO1xuICAgIH1cbiAgfVxufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogR2VuZXJhdGUgdGhlIGNvZGVzIGZvciBhIGdpdmVuIHRyZWUgYW5kIGJpdCBjb3VudHMgKHdoaWNoIG5lZWQgbm90IGJlXG4gKiBvcHRpbWFsKS5cbiAqIElOIGFzc2VydGlvbjogdGhlIGFycmF5IGJsX2NvdW50IGNvbnRhaW5zIHRoZSBiaXQgbGVuZ3RoIHN0YXRpc3RpY3MgZm9yXG4gKiB0aGUgZ2l2ZW4gdHJlZSBhbmQgdGhlIGZpZWxkIGxlbiBpcyBzZXQgZm9yIGFsbCB0cmVlIGVsZW1lbnRzLlxuICogT1VUIGFzc2VydGlvbjogdGhlIGZpZWxkIGNvZGUgaXMgc2V0IGZvciBhbGwgdHJlZSBlbGVtZW50cyBvZiBub25cbiAqICAgICB6ZXJvIGNvZGUgbGVuZ3RoLlxuICovXG5mdW5jdGlvbiBnZW5fY29kZXModHJlZSwgbWF4X2NvZGUsIGJsX2NvdW50KVxuLy8gICAgY3RfZGF0YSAqdHJlZTsgICAgICAgICAgICAgLyogdGhlIHRyZWUgdG8gZGVjb3JhdGUgKi9cbi8vICAgIGludCBtYXhfY29kZTsgICAgICAgICAgICAgIC8qIGxhcmdlc3QgY29kZSB3aXRoIG5vbiB6ZXJvIGZyZXF1ZW5jeSAqL1xuLy8gICAgdXNoZiAqYmxfY291bnQ7ICAgICAgICAgICAgLyogbnVtYmVyIG9mIGNvZGVzIGF0IGVhY2ggYml0IGxlbmd0aCAqL1xue1xuICB2YXIgbmV4dF9jb2RlID0gbmV3IEFycmF5KE1BWF9CSVRTKzEpOyAvKiBuZXh0IGNvZGUgdmFsdWUgZm9yIGVhY2ggYml0IGxlbmd0aCAqL1xuICB2YXIgY29kZSA9IDA7ICAgICAgICAgICAgICAvKiBydW5uaW5nIGNvZGUgdmFsdWUgKi9cbiAgdmFyIGJpdHM7ICAgICAgICAgICAgICAgICAgLyogYml0IGluZGV4ICovXG4gIHZhciBuOyAgICAgICAgICAgICAgICAgICAgIC8qIGNvZGUgaW5kZXggKi9cblxuICAvKiBUaGUgZGlzdHJpYnV0aW9uIGNvdW50cyBhcmUgZmlyc3QgdXNlZCB0byBnZW5lcmF0ZSB0aGUgY29kZSB2YWx1ZXNcbiAgICogd2l0aG91dCBiaXQgcmV2ZXJzYWwuXG4gICAqL1xuICBmb3IgKGJpdHMgPSAxOyBiaXRzIDw9IE1BWF9CSVRTOyBiaXRzKyspIHtcbiAgICBuZXh0X2NvZGVbYml0c10gPSBjb2RlID0gKGNvZGUgKyBibF9jb3VudFtiaXRzLTFdKSA8PCAxO1xuICB9XG4gIC8qIENoZWNrIHRoYXQgdGhlIGJpdCBjb3VudHMgaW4gYmxfY291bnQgYXJlIGNvbnNpc3RlbnQuIFRoZSBsYXN0IGNvZGVcbiAgICogbXVzdCBiZSBhbGwgb25lcy5cbiAgICovXG4gIC8vQXNzZXJ0IChjb2RlICsgYmxfY291bnRbTUFYX0JJVFNdLTEgPT0gKDE8PE1BWF9CSVRTKS0xLFxuICAvLyAgICAgICAgXCJpbmNvbnNpc3RlbnQgYml0IGNvdW50c1wiKTtcbiAgLy9UcmFjZXYoKHN0ZGVycixcIlxcbmdlbl9jb2RlczogbWF4X2NvZGUgJWQgXCIsIG1heF9jb2RlKSk7XG5cbiAgZm9yIChuID0gMDsgIG4gPD0gbWF4X2NvZGU7IG4rKykge1xuICAgIHZhciBsZW4gPSB0cmVlW24qMiArIDFdLyouTGVuKi87XG4gICAgaWYgKGxlbiA9PT0gMCkgeyBjb250aW51ZTsgfVxuICAgIC8qIE5vdyByZXZlcnNlIHRoZSBiaXRzICovXG4gICAgdHJlZVtuKjJdLyouQ29kZSovID0gYmlfcmV2ZXJzZShuZXh0X2NvZGVbbGVuXSsrLCBsZW4pO1xuXG4gICAgLy9UcmFjZWN2KHRyZWUgIT0gc3RhdGljX2x0cmVlLCAoc3RkZXJyLFwiXFxubiAlM2QgJWMgbCAlMmQgYyAlNHggKCV4KSBcIixcbiAgICAvLyAgICAgbiwgKGlzZ3JhcGgobikgPyBuIDogJyAnKSwgbGVuLCB0cmVlW25dLkNvZGUsIG5leHRfY29kZVtsZW5dLTEpKTtcbiAgfVxufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogSW5pdGlhbGl6ZSB0aGUgdmFyaW91cyAnY29uc3RhbnQnIHRhYmxlcy5cbiAqL1xuZnVuY3Rpb24gdHJfc3RhdGljX2luaXQoKSB7XG4gIHZhciBuOyAgICAgICAgLyogaXRlcmF0ZXMgb3ZlciB0cmVlIGVsZW1lbnRzICovXG4gIHZhciBiaXRzOyAgICAgLyogYml0IGNvdW50ZXIgKi9cbiAgdmFyIGxlbmd0aDsgICAvKiBsZW5ndGggdmFsdWUgKi9cbiAgdmFyIGNvZGU7ICAgICAvKiBjb2RlIHZhbHVlICovXG4gIHZhciBkaXN0OyAgICAgLyogZGlzdGFuY2UgaW5kZXggKi9cbiAgdmFyIGJsX2NvdW50ID0gbmV3IEFycmF5KE1BWF9CSVRTKzEpO1xuICAvKiBudW1iZXIgb2YgY29kZXMgYXQgZWFjaCBiaXQgbGVuZ3RoIGZvciBhbiBvcHRpbWFsIHRyZWUgKi9cblxuICAvLyBkbyBjaGVjayBpbiBfdHJfaW5pdCgpXG4gIC8vaWYgKHN0YXRpY19pbml0X2RvbmUpIHJldHVybjtcblxuICAvKiBGb3Igc29tZSBlbWJlZGRlZCB0YXJnZXRzLCBnbG9iYWwgdmFyaWFibGVzIGFyZSBub3QgaW5pdGlhbGl6ZWQ6ICovXG4vKiNpZmRlZiBOT19JTklUX0dMT0JBTF9QT0lOVEVSU1xuICBzdGF0aWNfbF9kZXNjLnN0YXRpY190cmVlID0gc3RhdGljX2x0cmVlO1xuICBzdGF0aWNfbF9kZXNjLmV4dHJhX2JpdHMgPSBleHRyYV9sYml0cztcbiAgc3RhdGljX2RfZGVzYy5zdGF0aWNfdHJlZSA9IHN0YXRpY19kdHJlZTtcbiAgc3RhdGljX2RfZGVzYy5leHRyYV9iaXRzID0gZXh0cmFfZGJpdHM7XG4gIHN0YXRpY19ibF9kZXNjLmV4dHJhX2JpdHMgPSBleHRyYV9ibGJpdHM7XG4jZW5kaWYqL1xuXG4gIC8qIEluaXRpYWxpemUgdGhlIG1hcHBpbmcgbGVuZ3RoICgwLi4yNTUpIC0+IGxlbmd0aCBjb2RlICgwLi4yOCkgKi9cbiAgbGVuZ3RoID0gMDtcbiAgZm9yIChjb2RlID0gMDsgY29kZSA8IExFTkdUSF9DT0RFUy0xOyBjb2RlKyspIHtcbiAgICBiYXNlX2xlbmd0aFtjb2RlXSA9IGxlbmd0aDtcbiAgICBmb3IgKG4gPSAwOyBuIDwgKDE8PGV4dHJhX2xiaXRzW2NvZGVdKTsgbisrKSB7XG4gICAgICBfbGVuZ3RoX2NvZGVbbGVuZ3RoKytdID0gY29kZTtcbiAgICB9XG4gIH1cbiAgLy9Bc3NlcnQgKGxlbmd0aCA9PSAyNTYsIFwidHJfc3RhdGljX2luaXQ6IGxlbmd0aCAhPSAyNTZcIik7XG4gIC8qIE5vdGUgdGhhdCB0aGUgbGVuZ3RoIDI1NSAobWF0Y2ggbGVuZ3RoIDI1OCkgY2FuIGJlIHJlcHJlc2VudGVkXG4gICAqIGluIHR3byBkaWZmZXJlbnQgd2F5czogY29kZSAyODQgKyA1IGJpdHMgb3IgY29kZSAyODUsIHNvIHdlXG4gICAqIG92ZXJ3cml0ZSBsZW5ndGhfY29kZVsyNTVdIHRvIHVzZSB0aGUgYmVzdCBlbmNvZGluZzpcbiAgICovXG4gIF9sZW5ndGhfY29kZVtsZW5ndGgtMV0gPSBjb2RlO1xuXG4gIC8qIEluaXRpYWxpemUgdGhlIG1hcHBpbmcgZGlzdCAoMC4uMzJLKSAtPiBkaXN0IGNvZGUgKDAuLjI5KSAqL1xuICBkaXN0ID0gMDtcbiAgZm9yIChjb2RlID0gMCA7IGNvZGUgPCAxNjsgY29kZSsrKSB7XG4gICAgYmFzZV9kaXN0W2NvZGVdID0gZGlzdDtcbiAgICBmb3IgKG4gPSAwOyBuIDwgKDE8PGV4dHJhX2RiaXRzW2NvZGVdKTsgbisrKSB7XG4gICAgICBfZGlzdF9jb2RlW2Rpc3QrK10gPSBjb2RlO1xuICAgIH1cbiAgfVxuICAvL0Fzc2VydCAoZGlzdCA9PSAyNTYsIFwidHJfc3RhdGljX2luaXQ6IGRpc3QgIT0gMjU2XCIpO1xuICBkaXN0ID4+PSA3OyAvKiBmcm9tIG5vdyBvbiwgYWxsIGRpc3RhbmNlcyBhcmUgZGl2aWRlZCBieSAxMjggKi9cbiAgZm9yICggOyBjb2RlIDwgRF9DT0RFUzsgY29kZSsrKSB7XG4gICAgYmFzZV9kaXN0W2NvZGVdID0gZGlzdCA8PCA3O1xuICAgIGZvciAobiA9IDA7IG4gPCAoMTw8KGV4dHJhX2RiaXRzW2NvZGVdLTcpKTsgbisrKSB7XG4gICAgICBfZGlzdF9jb2RlWzI1NiArIGRpc3QrK10gPSBjb2RlO1xuICAgIH1cbiAgfVxuICAvL0Fzc2VydCAoZGlzdCA9PSAyNTYsIFwidHJfc3RhdGljX2luaXQ6IDI1NitkaXN0ICE9IDUxMlwiKTtcblxuICAvKiBDb25zdHJ1Y3QgdGhlIGNvZGVzIG9mIHRoZSBzdGF0aWMgbGl0ZXJhbCB0cmVlICovXG4gIGZvciAoYml0cyA9IDA7IGJpdHMgPD0gTUFYX0JJVFM7IGJpdHMrKykge1xuICAgIGJsX2NvdW50W2JpdHNdID0gMDtcbiAgfVxuXG4gIG4gPSAwO1xuICB3aGlsZSAobiA8PSAxNDMpIHtcbiAgICBzdGF0aWNfbHRyZWVbbioyICsgMV0vKi5MZW4qLyA9IDg7XG4gICAgbisrO1xuICAgIGJsX2NvdW50WzhdKys7XG4gIH1cbiAgd2hpbGUgKG4gPD0gMjU1KSB7XG4gICAgc3RhdGljX2x0cmVlW24qMiArIDFdLyouTGVuKi8gPSA5O1xuICAgIG4rKztcbiAgICBibF9jb3VudFs5XSsrO1xuICB9XG4gIHdoaWxlIChuIDw9IDI3OSkge1xuICAgIHN0YXRpY19sdHJlZVtuKjIgKyAxXS8qLkxlbiovID0gNztcbiAgICBuKys7XG4gICAgYmxfY291bnRbN10rKztcbiAgfVxuICB3aGlsZSAobiA8PSAyODcpIHtcbiAgICBzdGF0aWNfbHRyZWVbbioyICsgMV0vKi5MZW4qLyA9IDg7XG4gICAgbisrO1xuICAgIGJsX2NvdW50WzhdKys7XG4gIH1cbiAgLyogQ29kZXMgMjg2IGFuZCAyODcgZG8gbm90IGV4aXN0LCBidXQgd2UgbXVzdCBpbmNsdWRlIHRoZW0gaW4gdGhlXG4gICAqIHRyZWUgY29uc3RydWN0aW9uIHRvIGdldCBhIGNhbm9uaWNhbCBIdWZmbWFuIHRyZWUgKGxvbmdlc3QgY29kZVxuICAgKiBhbGwgb25lcylcbiAgICovXG4gIGdlbl9jb2RlcyhzdGF0aWNfbHRyZWUsIExfQ09ERVMrMSwgYmxfY291bnQpO1xuXG4gIC8qIFRoZSBzdGF0aWMgZGlzdGFuY2UgdHJlZSBpcyB0cml2aWFsOiAqL1xuICBmb3IgKG4gPSAwOyBuIDwgRF9DT0RFUzsgbisrKSB7XG4gICAgc3RhdGljX2R0cmVlW24qMiArIDFdLyouTGVuKi8gPSA1O1xuICAgIHN0YXRpY19kdHJlZVtuKjJdLyouQ29kZSovID0gYmlfcmV2ZXJzZShuLCA1KTtcbiAgfVxuXG4gIC8vIE5vdyBkYXRhIHJlYWR5IGFuZCB3ZSBjYW4gaW5pdCBzdGF0aWMgdHJlZXNcbiAgc3RhdGljX2xfZGVzYyA9IG5ldyBTdGF0aWNUcmVlRGVzYyhzdGF0aWNfbHRyZWUsIGV4dHJhX2xiaXRzLCBMSVRFUkFMUysxLCBMX0NPREVTLCBNQVhfQklUUyk7XG4gIHN0YXRpY19kX2Rlc2MgPSBuZXcgU3RhdGljVHJlZURlc2Moc3RhdGljX2R0cmVlLCBleHRyYV9kYml0cywgMCwgICAgICAgICAgRF9DT0RFUywgTUFYX0JJVFMpO1xuICBzdGF0aWNfYmxfZGVzYyA9bmV3IFN0YXRpY1RyZWVEZXNjKG5ldyBBcnJheSgwKSwgZXh0cmFfYmxiaXRzLCAwLCAgICAgICAgIEJMX0NPREVTLCBNQVhfQkxfQklUUyk7XG5cbiAgLy9zdGF0aWNfaW5pdF9kb25lID0gdHJ1ZTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEluaXRpYWxpemUgYSBuZXcgYmxvY2suXG4gKi9cbmZ1bmN0aW9uIGluaXRfYmxvY2socykge1xuICB2YXIgbjsgLyogaXRlcmF0ZXMgb3ZlciB0cmVlIGVsZW1lbnRzICovXG5cbiAgLyogSW5pdGlhbGl6ZSB0aGUgdHJlZXMuICovXG4gIGZvciAobiA9IDA7IG4gPCBMX0NPREVTOyAgbisrKSB7IHMuZHluX2x0cmVlW24qMl0vKi5GcmVxKi8gPSAwOyB9XG4gIGZvciAobiA9IDA7IG4gPCBEX0NPREVTOyAgbisrKSB7IHMuZHluX2R0cmVlW24qMl0vKi5GcmVxKi8gPSAwOyB9XG4gIGZvciAobiA9IDA7IG4gPCBCTF9DT0RFUzsgbisrKSB7IHMuYmxfdHJlZVtuKjJdLyouRnJlcSovID0gMDsgfVxuXG4gIHMuZHluX2x0cmVlW0VORF9CTE9DSyoyXS8qLkZyZXEqLyA9IDE7XG4gIHMub3B0X2xlbiA9IHMuc3RhdGljX2xlbiA9IDA7XG4gIHMubGFzdF9saXQgPSBzLm1hdGNoZXMgPSAwO1xufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogRmx1c2ggdGhlIGJpdCBidWZmZXIgYW5kIGFsaWduIHRoZSBvdXRwdXQgb24gYSBieXRlIGJvdW5kYXJ5XG4gKi9cbmZ1bmN0aW9uIGJpX3dpbmR1cChzKVxue1xuICBpZiAocy5iaV92YWxpZCA+IDgpIHtcbiAgICBwdXRfc2hvcnQocywgcy5iaV9idWYpO1xuICB9IGVsc2UgaWYgKHMuYmlfdmFsaWQgPiAwKSB7XG4gICAgLy9wdXRfYnl0ZShzLCAoQnl0ZSlzLT5iaV9idWYpO1xuICAgIHMucGVuZGluZ19idWZbcy5wZW5kaW5nKytdID0gcy5iaV9idWY7XG4gIH1cbiAgcy5iaV9idWYgPSAwO1xuICBzLmJpX3ZhbGlkID0gMDtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb3B5IGEgc3RvcmVkIGJsb2NrLCBzdG9yaW5nIGZpcnN0IHRoZSBsZW5ndGggYW5kIGl0c1xuICogb25lJ3MgY29tcGxlbWVudCBpZiByZXF1ZXN0ZWQuXG4gKi9cbmZ1bmN0aW9uIGNvcHlfYmxvY2socywgYnVmLCBsZW4sIGhlYWRlcilcbi8vRGVmbGF0ZVN0YXRlICpzO1xuLy9jaGFyZiAgICAqYnVmOyAgICAvKiB0aGUgaW5wdXQgZGF0YSAqL1xuLy91bnNpZ25lZCBsZW47ICAgICAvKiBpdHMgbGVuZ3RoICovXG4vL2ludCAgICAgIGhlYWRlcjsgIC8qIHRydWUgaWYgYmxvY2sgaGVhZGVyIG11c3QgYmUgd3JpdHRlbiAqL1xue1xuICBiaV93aW5kdXAocyk7ICAgICAgICAvKiBhbGlnbiBvbiBieXRlIGJvdW5kYXJ5ICovXG5cbiAgaWYgKGhlYWRlcikge1xuICAgIHB1dF9zaG9ydChzLCBsZW4pO1xuICAgIHB1dF9zaG9ydChzLCB+bGVuKTtcbiAgfVxuLy8gIHdoaWxlIChsZW4tLSkge1xuLy8gICAgcHV0X2J5dGUocywgKmJ1ZisrKTtcbi8vICB9XG4gIHV0aWxzLmFycmF5U2V0KHMucGVuZGluZ19idWYsIHMud2luZG93LCBidWYsIGxlbiwgcy5wZW5kaW5nKTtcbiAgcy5wZW5kaW5nICs9IGxlbjtcbn1cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb21wYXJlcyB0byBzdWJ0cmVlcywgdXNpbmcgdGhlIHRyZWUgZGVwdGggYXMgdGllIGJyZWFrZXIgd2hlblxuICogdGhlIHN1YnRyZWVzIGhhdmUgZXF1YWwgZnJlcXVlbmN5LiBUaGlzIG1pbmltaXplcyB0aGUgd29yc3QgY2FzZSBsZW5ndGguXG4gKi9cbmZ1bmN0aW9uIHNtYWxsZXIodHJlZSwgbiwgbSwgZGVwdGgpIHtcbiAgdmFyIF9uMiA9IG4qMjtcbiAgdmFyIF9tMiA9IG0qMjtcbiAgcmV0dXJuICh0cmVlW19uMl0vKi5GcmVxKi8gPCB0cmVlW19tMl0vKi5GcmVxKi8gfHxcbiAgICAgICAgICh0cmVlW19uMl0vKi5GcmVxKi8gPT09IHRyZWVbX20yXS8qLkZyZXEqLyAmJiBkZXB0aFtuXSA8PSBkZXB0aFttXSkpO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFJlc3RvcmUgdGhlIGhlYXAgcHJvcGVydHkgYnkgbW92aW5nIGRvd24gdGhlIHRyZWUgc3RhcnRpbmcgYXQgbm9kZSBrLFxuICogZXhjaGFuZ2luZyBhIG5vZGUgd2l0aCB0aGUgc21hbGxlc3Qgb2YgaXRzIHR3byBzb25zIGlmIG5lY2Vzc2FyeSwgc3RvcHBpbmdcbiAqIHdoZW4gdGhlIGhlYXAgcHJvcGVydHkgaXMgcmUtZXN0YWJsaXNoZWQgKGVhY2ggZmF0aGVyIHNtYWxsZXIgdGhhbiBpdHNcbiAqIHR3byBzb25zKS5cbiAqL1xuZnVuY3Rpb24gcHFkb3duaGVhcChzLCB0cmVlLCBrKVxuLy8gICAgZGVmbGF0ZV9zdGF0ZSAqcztcbi8vICAgIGN0X2RhdGEgKnRyZWU7ICAvKiB0aGUgdHJlZSB0byByZXN0b3JlICovXG4vLyAgICBpbnQgazsgICAgICAgICAgICAgICAvKiBub2RlIHRvIG1vdmUgZG93biAqL1xue1xuICB2YXIgdiA9IHMuaGVhcFtrXTtcbiAgdmFyIGogPSBrIDw8IDE7ICAvKiBsZWZ0IHNvbiBvZiBrICovXG4gIHdoaWxlIChqIDw9IHMuaGVhcF9sZW4pIHtcbiAgICAvKiBTZXQgaiB0byB0aGUgc21hbGxlc3Qgb2YgdGhlIHR3byBzb25zOiAqL1xuICAgIGlmIChqIDwgcy5oZWFwX2xlbiAmJlxuICAgICAgc21hbGxlcih0cmVlLCBzLmhlYXBbaisxXSwgcy5oZWFwW2pdLCBzLmRlcHRoKSkge1xuICAgICAgaisrO1xuICAgIH1cbiAgICAvKiBFeGl0IGlmIHYgaXMgc21hbGxlciB0aGFuIGJvdGggc29ucyAqL1xuICAgIGlmIChzbWFsbGVyKHRyZWUsIHYsIHMuaGVhcFtqXSwgcy5kZXB0aCkpIHsgYnJlYWs7IH1cblxuICAgIC8qIEV4Y2hhbmdlIHYgd2l0aCB0aGUgc21hbGxlc3Qgc29uICovXG4gICAgcy5oZWFwW2tdID0gcy5oZWFwW2pdO1xuICAgIGsgPSBqO1xuXG4gICAgLyogQW5kIGNvbnRpbnVlIGRvd24gdGhlIHRyZWUsIHNldHRpbmcgaiB0byB0aGUgbGVmdCBzb24gb2YgayAqL1xuICAgIGogPDw9IDE7XG4gIH1cbiAgcy5oZWFwW2tdID0gdjtcbn1cblxuXG4vLyBpbmxpbmVkIG1hbnVhbGx5XG4vLyB2YXIgU01BTExFU1QgPSAxO1xuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNlbmQgdGhlIGJsb2NrIGRhdGEgY29tcHJlc3NlZCB1c2luZyB0aGUgZ2l2ZW4gSHVmZm1hbiB0cmVlc1xuICovXG5mdW5jdGlvbiBjb21wcmVzc19ibG9jayhzLCBsdHJlZSwgZHRyZWUpXG4vLyAgICBkZWZsYXRlX3N0YXRlICpzO1xuLy8gICAgY29uc3QgY3RfZGF0YSAqbHRyZWU7IC8qIGxpdGVyYWwgdHJlZSAqL1xuLy8gICAgY29uc3QgY3RfZGF0YSAqZHRyZWU7IC8qIGRpc3RhbmNlIHRyZWUgKi9cbntcbiAgdmFyIGRpc3Q7ICAgICAgICAgICAvKiBkaXN0YW5jZSBvZiBtYXRjaGVkIHN0cmluZyAqL1xuICB2YXIgbGM7ICAgICAgICAgICAgIC8qIG1hdGNoIGxlbmd0aCBvciB1bm1hdGNoZWQgY2hhciAoaWYgZGlzdCA9PSAwKSAqL1xuICB2YXIgbHggPSAwOyAgICAgICAgIC8qIHJ1bm5pbmcgaW5kZXggaW4gbF9idWYgKi9cbiAgdmFyIGNvZGU7ICAgICAgICAgICAvKiB0aGUgY29kZSB0byBzZW5kICovXG4gIHZhciBleHRyYTsgICAgICAgICAgLyogbnVtYmVyIG9mIGV4dHJhIGJpdHMgdG8gc2VuZCAqL1xuXG4gIGlmIChzLmxhc3RfbGl0ICE9PSAwKSB7XG4gICAgZG8ge1xuICAgICAgZGlzdCA9IChzLnBlbmRpbmdfYnVmW3MuZF9idWYgKyBseCoyXSA8PCA4KSB8IChzLnBlbmRpbmdfYnVmW3MuZF9idWYgKyBseCoyICsgMV0pO1xuICAgICAgbGMgPSBzLnBlbmRpbmdfYnVmW3MubF9idWYgKyBseF07XG4gICAgICBseCsrO1xuXG4gICAgICBpZiAoZGlzdCA9PT0gMCkge1xuICAgICAgICBzZW5kX2NvZGUocywgbGMsIGx0cmVlKTsgLyogc2VuZCBhIGxpdGVyYWwgYnl0ZSAqL1xuICAgICAgICAvL1RyYWNlY3YoaXNncmFwaChsYyksIChzdGRlcnIsXCIgJyVjJyBcIiwgbGMpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8qIEhlcmUsIGxjIGlzIHRoZSBtYXRjaCBsZW5ndGggLSBNSU5fTUFUQ0ggKi9cbiAgICAgICAgY29kZSA9IF9sZW5ndGhfY29kZVtsY107XG4gICAgICAgIHNlbmRfY29kZShzLCBjb2RlK0xJVEVSQUxTKzEsIGx0cmVlKTsgLyogc2VuZCB0aGUgbGVuZ3RoIGNvZGUgKi9cbiAgICAgICAgZXh0cmEgPSBleHRyYV9sYml0c1tjb2RlXTtcbiAgICAgICAgaWYgKGV4dHJhICE9PSAwKSB7XG4gICAgICAgICAgbGMgLT0gYmFzZV9sZW5ndGhbY29kZV07XG4gICAgICAgICAgc2VuZF9iaXRzKHMsIGxjLCBleHRyYSk7ICAgICAgIC8qIHNlbmQgdGhlIGV4dHJhIGxlbmd0aCBiaXRzICovXG4gICAgICAgIH1cbiAgICAgICAgZGlzdC0tOyAvKiBkaXN0IGlzIG5vdyB0aGUgbWF0Y2ggZGlzdGFuY2UgLSAxICovXG4gICAgICAgIGNvZGUgPSBkX2NvZGUoZGlzdCk7XG4gICAgICAgIC8vQXNzZXJ0IChjb2RlIDwgRF9DT0RFUywgXCJiYWQgZF9jb2RlXCIpO1xuXG4gICAgICAgIHNlbmRfY29kZShzLCBjb2RlLCBkdHJlZSk7ICAgICAgIC8qIHNlbmQgdGhlIGRpc3RhbmNlIGNvZGUgKi9cbiAgICAgICAgZXh0cmEgPSBleHRyYV9kYml0c1tjb2RlXTtcbiAgICAgICAgaWYgKGV4dHJhICE9PSAwKSB7XG4gICAgICAgICAgZGlzdCAtPSBiYXNlX2Rpc3RbY29kZV07XG4gICAgICAgICAgc2VuZF9iaXRzKHMsIGRpc3QsIGV4dHJhKTsgICAvKiBzZW5kIHRoZSBleHRyYSBkaXN0YW5jZSBiaXRzICovXG4gICAgICAgIH1cbiAgICAgIH0gLyogbGl0ZXJhbCBvciBtYXRjaCBwYWlyID8gKi9cblxuICAgICAgLyogQ2hlY2sgdGhhdCB0aGUgb3ZlcmxheSBiZXR3ZWVuIHBlbmRpbmdfYnVmIGFuZCBkX2J1ZitsX2J1ZiBpcyBvazogKi9cbiAgICAgIC8vQXNzZXJ0KCh1SW50KShzLT5wZW5kaW5nKSA8IHMtPmxpdF9idWZzaXplICsgMipseCxcbiAgICAgIC8vICAgICAgIFwicGVuZGluZ0J1ZiBvdmVyZmxvd1wiKTtcblxuICAgIH0gd2hpbGUgKGx4IDwgcy5sYXN0X2xpdCk7XG4gIH1cblxuICBzZW5kX2NvZGUocywgRU5EX0JMT0NLLCBsdHJlZSk7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBDb25zdHJ1Y3Qgb25lIEh1ZmZtYW4gdHJlZSBhbmQgYXNzaWducyB0aGUgY29kZSBiaXQgc3RyaW5ncyBhbmQgbGVuZ3Rocy5cbiAqIFVwZGF0ZSB0aGUgdG90YWwgYml0IGxlbmd0aCBmb3IgdGhlIGN1cnJlbnQgYmxvY2suXG4gKiBJTiBhc3NlcnRpb246IHRoZSBmaWVsZCBmcmVxIGlzIHNldCBmb3IgYWxsIHRyZWUgZWxlbWVudHMuXG4gKiBPVVQgYXNzZXJ0aW9uczogdGhlIGZpZWxkcyBsZW4gYW5kIGNvZGUgYXJlIHNldCB0byB0aGUgb3B0aW1hbCBiaXQgbGVuZ3RoXG4gKiAgICAgYW5kIGNvcnJlc3BvbmRpbmcgY29kZS4gVGhlIGxlbmd0aCBvcHRfbGVuIGlzIHVwZGF0ZWQ7IHN0YXRpY19sZW4gaXNcbiAqICAgICBhbHNvIHVwZGF0ZWQgaWYgc3RyZWUgaXMgbm90IG51bGwuIFRoZSBmaWVsZCBtYXhfY29kZSBpcyBzZXQuXG4gKi9cbmZ1bmN0aW9uIGJ1aWxkX3RyZWUocywgZGVzYylcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICB0cmVlX2Rlc2MgKmRlc2M7IC8qIHRoZSB0cmVlIGRlc2NyaXB0b3IgKi9cbntcbiAgdmFyIHRyZWUgICAgID0gZGVzYy5keW5fdHJlZTtcbiAgdmFyIHN0cmVlICAgID0gZGVzYy5zdGF0X2Rlc2Muc3RhdGljX3RyZWU7XG4gIHZhciBoYXNfc3RyZWUgPSBkZXNjLnN0YXRfZGVzYy5oYXNfc3RyZWU7XG4gIHZhciBlbGVtcyAgICA9IGRlc2Muc3RhdF9kZXNjLmVsZW1zO1xuICB2YXIgbiwgbTsgICAgICAgICAgLyogaXRlcmF0ZSBvdmVyIGhlYXAgZWxlbWVudHMgKi9cbiAgdmFyIG1heF9jb2RlID0gLTE7IC8qIGxhcmdlc3QgY29kZSB3aXRoIG5vbiB6ZXJvIGZyZXF1ZW5jeSAqL1xuICB2YXIgbm9kZTsgICAgICAgICAgLyogbmV3IG5vZGUgYmVpbmcgY3JlYXRlZCAqL1xuXG4gIC8qIENvbnN0cnVjdCB0aGUgaW5pdGlhbCBoZWFwLCB3aXRoIGxlYXN0IGZyZXF1ZW50IGVsZW1lbnQgaW5cbiAgICogaGVhcFtTTUFMTEVTVF0uIFRoZSBzb25zIG9mIGhlYXBbbl0gYXJlIGhlYXBbMipuXSBhbmQgaGVhcFsyKm4rMV0uXG4gICAqIGhlYXBbMF0gaXMgbm90IHVzZWQuXG4gICAqL1xuICBzLmhlYXBfbGVuID0gMDtcbiAgcy5oZWFwX21heCA9IEhFQVBfU0laRTtcblxuICBmb3IgKG4gPSAwOyBuIDwgZWxlbXM7IG4rKykge1xuICAgIGlmICh0cmVlW24gKiAyXS8qLkZyZXEqLyAhPT0gMCkge1xuICAgICAgcy5oZWFwWysrcy5oZWFwX2xlbl0gPSBtYXhfY29kZSA9IG47XG4gICAgICBzLmRlcHRoW25dID0gMDtcblxuICAgIH0gZWxzZSB7XG4gICAgICB0cmVlW24qMiArIDFdLyouTGVuKi8gPSAwO1xuICAgIH1cbiAgfVxuXG4gIC8qIFRoZSBwa3ppcCBmb3JtYXQgcmVxdWlyZXMgdGhhdCBhdCBsZWFzdCBvbmUgZGlzdGFuY2UgY29kZSBleGlzdHMsXG4gICAqIGFuZCB0aGF0IGF0IGxlYXN0IG9uZSBiaXQgc2hvdWxkIGJlIHNlbnQgZXZlbiBpZiB0aGVyZSBpcyBvbmx5IG9uZVxuICAgKiBwb3NzaWJsZSBjb2RlLiBTbyB0byBhdm9pZCBzcGVjaWFsIGNoZWNrcyBsYXRlciBvbiB3ZSBmb3JjZSBhdCBsZWFzdFxuICAgKiB0d28gY29kZXMgb2Ygbm9uIHplcm8gZnJlcXVlbmN5LlxuICAgKi9cbiAgd2hpbGUgKHMuaGVhcF9sZW4gPCAyKSB7XG4gICAgbm9kZSA9IHMuaGVhcFsrK3MuaGVhcF9sZW5dID0gKG1heF9jb2RlIDwgMiA/ICsrbWF4X2NvZGUgOiAwKTtcbiAgICB0cmVlW25vZGUgKiAyXS8qLkZyZXEqLyA9IDE7XG4gICAgcy5kZXB0aFtub2RlXSA9IDA7XG4gICAgcy5vcHRfbGVuLS07XG5cbiAgICBpZiAoaGFzX3N0cmVlKSB7XG4gICAgICBzLnN0YXRpY19sZW4gLT0gc3RyZWVbbm9kZSoyICsgMV0vKi5MZW4qLztcbiAgICB9XG4gICAgLyogbm9kZSBpcyAwIG9yIDEgc28gaXQgZG9lcyBub3QgaGF2ZSBleHRyYSBiaXRzICovXG4gIH1cbiAgZGVzYy5tYXhfY29kZSA9IG1heF9jb2RlO1xuXG4gIC8qIFRoZSBlbGVtZW50cyBoZWFwW2hlYXBfbGVuLzIrMSAuLiBoZWFwX2xlbl0gYXJlIGxlYXZlcyBvZiB0aGUgdHJlZSxcbiAgICogZXN0YWJsaXNoIHN1Yi1oZWFwcyBvZiBpbmNyZWFzaW5nIGxlbmd0aHM6XG4gICAqL1xuICBmb3IgKG4gPSAocy5oZWFwX2xlbiA+PiAxLyppbnQgLzIqLyk7IG4gPj0gMTsgbi0tKSB7IHBxZG93bmhlYXAocywgdHJlZSwgbik7IH1cblxuICAvKiBDb25zdHJ1Y3QgdGhlIEh1ZmZtYW4gdHJlZSBieSByZXBlYXRlZGx5IGNvbWJpbmluZyB0aGUgbGVhc3QgdHdvXG4gICAqIGZyZXF1ZW50IG5vZGVzLlxuICAgKi9cbiAgbm9kZSA9IGVsZW1zOyAgICAgICAgICAgICAgLyogbmV4dCBpbnRlcm5hbCBub2RlIG9mIHRoZSB0cmVlICovXG4gIGRvIHtcbiAgICAvL3BxcmVtb3ZlKHMsIHRyZWUsIG4pOyAgLyogbiA9IG5vZGUgb2YgbGVhc3QgZnJlcXVlbmN5ICovXG4gICAgLyoqKiBwcXJlbW92ZSAqKiovXG4gICAgbiA9IHMuaGVhcFsxLypTTUFMTEVTVCovXTtcbiAgICBzLmhlYXBbMS8qU01BTExFU1QqL10gPSBzLmhlYXBbcy5oZWFwX2xlbi0tXTtcbiAgICBwcWRvd25oZWFwKHMsIHRyZWUsIDEvKlNNQUxMRVNUKi8pO1xuICAgIC8qKiovXG5cbiAgICBtID0gcy5oZWFwWzEvKlNNQUxMRVNUKi9dOyAvKiBtID0gbm9kZSBvZiBuZXh0IGxlYXN0IGZyZXF1ZW5jeSAqL1xuXG4gICAgcy5oZWFwWy0tcy5oZWFwX21heF0gPSBuOyAvKiBrZWVwIHRoZSBub2RlcyBzb3J0ZWQgYnkgZnJlcXVlbmN5ICovXG4gICAgcy5oZWFwWy0tcy5oZWFwX21heF0gPSBtO1xuXG4gICAgLyogQ3JlYXRlIGEgbmV3IG5vZGUgZmF0aGVyIG9mIG4gYW5kIG0gKi9cbiAgICB0cmVlW25vZGUgKiAyXS8qLkZyZXEqLyA9IHRyZWVbbiAqIDJdLyouRnJlcSovICsgdHJlZVttICogMl0vKi5GcmVxKi87XG4gICAgcy5kZXB0aFtub2RlXSA9IChzLmRlcHRoW25dID49IHMuZGVwdGhbbV0gPyBzLmRlcHRoW25dIDogcy5kZXB0aFttXSkgKyAxO1xuICAgIHRyZWVbbioyICsgMV0vKi5EYWQqLyA9IHRyZWVbbSoyICsgMV0vKi5EYWQqLyA9IG5vZGU7XG5cbiAgICAvKiBhbmQgaW5zZXJ0IHRoZSBuZXcgbm9kZSBpbiB0aGUgaGVhcCAqL1xuICAgIHMuaGVhcFsxLypTTUFMTEVTVCovXSA9IG5vZGUrKztcbiAgICBwcWRvd25oZWFwKHMsIHRyZWUsIDEvKlNNQUxMRVNUKi8pO1xuXG4gIH0gd2hpbGUgKHMuaGVhcF9sZW4gPj0gMik7XG5cbiAgcy5oZWFwWy0tcy5oZWFwX21heF0gPSBzLmhlYXBbMS8qU01BTExFU1QqL107XG5cbiAgLyogQXQgdGhpcyBwb2ludCwgdGhlIGZpZWxkcyBmcmVxIGFuZCBkYWQgYXJlIHNldC4gV2UgY2FuIG5vd1xuICAgKiBnZW5lcmF0ZSB0aGUgYml0IGxlbmd0aHMuXG4gICAqL1xuICBnZW5fYml0bGVuKHMsIGRlc2MpO1xuXG4gIC8qIFRoZSBmaWVsZCBsZW4gaXMgbm93IHNldCwgd2UgY2FuIGdlbmVyYXRlIHRoZSBiaXQgY29kZXMgKi9cbiAgZ2VuX2NvZGVzKHRyZWUsIG1heF9jb2RlLCBzLmJsX2NvdW50KTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIFNjYW4gYSBsaXRlcmFsIG9yIGRpc3RhbmNlIHRyZWUgdG8gZGV0ZXJtaW5lIHRoZSBmcmVxdWVuY2llcyBvZiB0aGUgY29kZXNcbiAqIGluIHRoZSBiaXQgbGVuZ3RoIHRyZWUuXG4gKi9cbmZ1bmN0aW9uIHNjYW5fdHJlZShzLCB0cmVlLCBtYXhfY29kZSlcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICBjdF9kYXRhICp0cmVlOyAgIC8qIHRoZSB0cmVlIHRvIGJlIHNjYW5uZWQgKi9cbi8vICAgIGludCBtYXhfY29kZTsgICAgLyogYW5kIGl0cyBsYXJnZXN0IGNvZGUgb2Ygbm9uIHplcm8gZnJlcXVlbmN5ICovXG57XG4gIHZhciBuOyAgICAgICAgICAgICAgICAgICAgIC8qIGl0ZXJhdGVzIG92ZXIgYWxsIHRyZWUgZWxlbWVudHMgKi9cbiAgdmFyIHByZXZsZW4gPSAtMTsgICAgICAgICAgLyogbGFzdCBlbWl0dGVkIGxlbmd0aCAqL1xuICB2YXIgY3VybGVuOyAgICAgICAgICAgICAgICAvKiBsZW5ndGggb2YgY3VycmVudCBjb2RlICovXG5cbiAgdmFyIG5leHRsZW4gPSB0cmVlWzAqMiArIDFdLyouTGVuKi87IC8qIGxlbmd0aCBvZiBuZXh0IGNvZGUgKi9cblxuICB2YXIgY291bnQgPSAwOyAgICAgICAgICAgICAvKiByZXBlYXQgY291bnQgb2YgdGhlIGN1cnJlbnQgY29kZSAqL1xuICB2YXIgbWF4X2NvdW50ID0gNzsgICAgICAgICAvKiBtYXggcmVwZWF0IGNvdW50ICovXG4gIHZhciBtaW5fY291bnQgPSA0OyAgICAgICAgIC8qIG1pbiByZXBlYXQgY291bnQgKi9cblxuICBpZiAobmV4dGxlbiA9PT0gMCkge1xuICAgIG1heF9jb3VudCA9IDEzODtcbiAgICBtaW5fY291bnQgPSAzO1xuICB9XG4gIHRyZWVbKG1heF9jb2RlKzEpKjIgKyAxXS8qLkxlbiovID0gMHhmZmZmOyAvKiBndWFyZCAqL1xuXG4gIGZvciAobiA9IDA7IG4gPD0gbWF4X2NvZGU7IG4rKykge1xuICAgIGN1cmxlbiA9IG5leHRsZW47XG4gICAgbmV4dGxlbiA9IHRyZWVbKG4rMSkqMiArIDFdLyouTGVuKi87XG5cbiAgICBpZiAoKytjb3VudCA8IG1heF9jb3VudCAmJiBjdXJsZW4gPT09IG5leHRsZW4pIHtcbiAgICAgIGNvbnRpbnVlO1xuXG4gICAgfSBlbHNlIGlmIChjb3VudCA8IG1pbl9jb3VudCkge1xuICAgICAgcy5ibF90cmVlW2N1cmxlbiAqIDJdLyouRnJlcSovICs9IGNvdW50O1xuXG4gICAgfSBlbHNlIGlmIChjdXJsZW4gIT09IDApIHtcblxuICAgICAgaWYgKGN1cmxlbiAhPT0gcHJldmxlbikgeyBzLmJsX3RyZWVbY3VybGVuICogMl0vKi5GcmVxKi8rKzsgfVxuICAgICAgcy5ibF90cmVlW1JFUF8zXzYqMl0vKi5GcmVxKi8rKztcblxuICAgIH0gZWxzZSBpZiAoY291bnQgPD0gMTApIHtcbiAgICAgIHMuYmxfdHJlZVtSRVBaXzNfMTAqMl0vKi5GcmVxKi8rKztcblxuICAgIH0gZWxzZSB7XG4gICAgICBzLmJsX3RyZWVbUkVQWl8xMV8xMzgqMl0vKi5GcmVxKi8rKztcbiAgICB9XG5cbiAgICBjb3VudCA9IDA7XG4gICAgcHJldmxlbiA9IGN1cmxlbjtcblxuICAgIGlmIChuZXh0bGVuID09PSAwKSB7XG4gICAgICBtYXhfY291bnQgPSAxMzg7XG4gICAgICBtaW5fY291bnQgPSAzO1xuXG4gICAgfSBlbHNlIGlmIChjdXJsZW4gPT09IG5leHRsZW4pIHtcbiAgICAgIG1heF9jb3VudCA9IDY7XG4gICAgICBtaW5fY291bnQgPSAzO1xuXG4gICAgfSBlbHNlIHtcbiAgICAgIG1heF9jb3VudCA9IDc7XG4gICAgICBtaW5fY291bnQgPSA0O1xuICAgIH1cbiAgfVxufVxuXG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2VuZCBhIGxpdGVyYWwgb3IgZGlzdGFuY2UgdHJlZSBpbiBjb21wcmVzc2VkIGZvcm0sIHVzaW5nIHRoZSBjb2RlcyBpblxuICogYmxfdHJlZS5cbiAqL1xuZnVuY3Rpb24gc2VuZF90cmVlKHMsIHRyZWUsIG1heF9jb2RlKVxuLy8gICAgZGVmbGF0ZV9zdGF0ZSAqcztcbi8vICAgIGN0X2RhdGEgKnRyZWU7IC8qIHRoZSB0cmVlIHRvIGJlIHNjYW5uZWQgKi9cbi8vICAgIGludCBtYXhfY29kZTsgICAgICAgLyogYW5kIGl0cyBsYXJnZXN0IGNvZGUgb2Ygbm9uIHplcm8gZnJlcXVlbmN5ICovXG57XG4gIHZhciBuOyAgICAgICAgICAgICAgICAgICAgIC8qIGl0ZXJhdGVzIG92ZXIgYWxsIHRyZWUgZWxlbWVudHMgKi9cbiAgdmFyIHByZXZsZW4gPSAtMTsgICAgICAgICAgLyogbGFzdCBlbWl0dGVkIGxlbmd0aCAqL1xuICB2YXIgY3VybGVuOyAgICAgICAgICAgICAgICAvKiBsZW5ndGggb2YgY3VycmVudCBjb2RlICovXG5cbiAgdmFyIG5leHRsZW4gPSB0cmVlWzAqMiArIDFdLyouTGVuKi87IC8qIGxlbmd0aCBvZiBuZXh0IGNvZGUgKi9cblxuICB2YXIgY291bnQgPSAwOyAgICAgICAgICAgICAvKiByZXBlYXQgY291bnQgb2YgdGhlIGN1cnJlbnQgY29kZSAqL1xuICB2YXIgbWF4X2NvdW50ID0gNzsgICAgICAgICAvKiBtYXggcmVwZWF0IGNvdW50ICovXG4gIHZhciBtaW5fY291bnQgPSA0OyAgICAgICAgIC8qIG1pbiByZXBlYXQgY291bnQgKi9cblxuICAvKiB0cmVlW21heF9jb2RlKzFdLkxlbiA9IC0xOyAqLyAgLyogZ3VhcmQgYWxyZWFkeSBzZXQgKi9cbiAgaWYgKG5leHRsZW4gPT09IDApIHtcbiAgICBtYXhfY291bnQgPSAxMzg7XG4gICAgbWluX2NvdW50ID0gMztcbiAgfVxuXG4gIGZvciAobiA9IDA7IG4gPD0gbWF4X2NvZGU7IG4rKykge1xuICAgIGN1cmxlbiA9IG5leHRsZW47XG4gICAgbmV4dGxlbiA9IHRyZWVbKG4rMSkqMiArIDFdLyouTGVuKi87XG5cbiAgICBpZiAoKytjb3VudCA8IG1heF9jb3VudCAmJiBjdXJsZW4gPT09IG5leHRsZW4pIHtcbiAgICAgIGNvbnRpbnVlO1xuXG4gICAgfSBlbHNlIGlmIChjb3VudCA8IG1pbl9jb3VudCkge1xuICAgICAgZG8geyBzZW5kX2NvZGUocywgY3VybGVuLCBzLmJsX3RyZWUpOyB9IHdoaWxlICgtLWNvdW50ICE9PSAwKTtcblxuICAgIH0gZWxzZSBpZiAoY3VybGVuICE9PSAwKSB7XG4gICAgICBpZiAoY3VybGVuICE9PSBwcmV2bGVuKSB7XG4gICAgICAgIHNlbmRfY29kZShzLCBjdXJsZW4sIHMuYmxfdHJlZSk7XG4gICAgICAgIGNvdW50LS07XG4gICAgICB9XG4gICAgICAvL0Fzc2VydChjb3VudCA+PSAzICYmIGNvdW50IDw9IDYsIFwiIDNfNj9cIik7XG4gICAgICBzZW5kX2NvZGUocywgUkVQXzNfNiwgcy5ibF90cmVlKTtcbiAgICAgIHNlbmRfYml0cyhzLCBjb3VudC0zLCAyKTtcblxuICAgIH0gZWxzZSBpZiAoY291bnQgPD0gMTApIHtcbiAgICAgIHNlbmRfY29kZShzLCBSRVBaXzNfMTAsIHMuYmxfdHJlZSk7XG4gICAgICBzZW5kX2JpdHMocywgY291bnQtMywgMyk7XG5cbiAgICB9IGVsc2Uge1xuICAgICAgc2VuZF9jb2RlKHMsIFJFUFpfMTFfMTM4LCBzLmJsX3RyZWUpO1xuICAgICAgc2VuZF9iaXRzKHMsIGNvdW50LTExLCA3KTtcbiAgICB9XG5cbiAgICBjb3VudCA9IDA7XG4gICAgcHJldmxlbiA9IGN1cmxlbjtcbiAgICBpZiAobmV4dGxlbiA9PT0gMCkge1xuICAgICAgbWF4X2NvdW50ID0gMTM4O1xuICAgICAgbWluX2NvdW50ID0gMztcblxuICAgIH0gZWxzZSBpZiAoY3VybGVuID09PSBuZXh0bGVuKSB7XG4gICAgICBtYXhfY291bnQgPSA2O1xuICAgICAgbWluX2NvdW50ID0gMztcblxuICAgIH0gZWxzZSB7XG4gICAgICBtYXhfY291bnQgPSA3O1xuICAgICAgbWluX2NvdW50ID0gNDtcbiAgICB9XG4gIH1cbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENvbnN0cnVjdCB0aGUgSHVmZm1hbiB0cmVlIGZvciB0aGUgYml0IGxlbmd0aHMgYW5kIHJldHVybiB0aGUgaW5kZXggaW5cbiAqIGJsX29yZGVyIG9mIHRoZSBsYXN0IGJpdCBsZW5ndGggY29kZSB0byBzZW5kLlxuICovXG5mdW5jdGlvbiBidWlsZF9ibF90cmVlKHMpIHtcbiAgdmFyIG1heF9ibGluZGV4OyAgLyogaW5kZXggb2YgbGFzdCBiaXQgbGVuZ3RoIGNvZGUgb2Ygbm9uIHplcm8gZnJlcSAqL1xuXG4gIC8qIERldGVybWluZSB0aGUgYml0IGxlbmd0aCBmcmVxdWVuY2llcyBmb3IgbGl0ZXJhbCBhbmQgZGlzdGFuY2UgdHJlZXMgKi9cbiAgc2Nhbl90cmVlKHMsIHMuZHluX2x0cmVlLCBzLmxfZGVzYy5tYXhfY29kZSk7XG4gIHNjYW5fdHJlZShzLCBzLmR5bl9kdHJlZSwgcy5kX2Rlc2MubWF4X2NvZGUpO1xuXG4gIC8qIEJ1aWxkIHRoZSBiaXQgbGVuZ3RoIHRyZWU6ICovXG4gIGJ1aWxkX3RyZWUocywgcy5ibF9kZXNjKTtcbiAgLyogb3B0X2xlbiBub3cgaW5jbHVkZXMgdGhlIGxlbmd0aCBvZiB0aGUgdHJlZSByZXByZXNlbnRhdGlvbnMsIGV4Y2VwdFxuICAgKiB0aGUgbGVuZ3RocyBvZiB0aGUgYml0IGxlbmd0aHMgY29kZXMgYW5kIHRoZSA1KzUrNCBiaXRzIGZvciB0aGUgY291bnRzLlxuICAgKi9cblxuICAvKiBEZXRlcm1pbmUgdGhlIG51bWJlciBvZiBiaXQgbGVuZ3RoIGNvZGVzIHRvIHNlbmQuIFRoZSBwa3ppcCBmb3JtYXRcbiAgICogcmVxdWlyZXMgdGhhdCBhdCBsZWFzdCA0IGJpdCBsZW5ndGggY29kZXMgYmUgc2VudC4gKGFwcG5vdGUudHh0IHNheXNcbiAgICogMyBidXQgdGhlIGFjdHVhbCB2YWx1ZSB1c2VkIGlzIDQuKVxuICAgKi9cbiAgZm9yIChtYXhfYmxpbmRleCA9IEJMX0NPREVTLTE7IG1heF9ibGluZGV4ID49IDM7IG1heF9ibGluZGV4LS0pIHtcbiAgICBpZiAocy5ibF90cmVlW2JsX29yZGVyW21heF9ibGluZGV4XSoyICsgMV0vKi5MZW4qLyAhPT0gMCkge1xuICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIC8qIFVwZGF0ZSBvcHRfbGVuIHRvIGluY2x1ZGUgdGhlIGJpdCBsZW5ndGggdHJlZSBhbmQgY291bnRzICovXG4gIHMub3B0X2xlbiArPSAzKihtYXhfYmxpbmRleCsxKSArIDUrNSs0O1xuICAvL1RyYWNldigoc3RkZXJyLCBcIlxcbmR5biB0cmVlczogZHluICVsZCwgc3RhdCAlbGRcIixcbiAgLy8gICAgICAgIHMtPm9wdF9sZW4sIHMtPnN0YXRpY19sZW4pKTtcblxuICByZXR1cm4gbWF4X2JsaW5kZXg7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTZW5kIHRoZSBoZWFkZXIgZm9yIGEgYmxvY2sgdXNpbmcgZHluYW1pYyBIdWZmbWFuIHRyZWVzOiB0aGUgY291bnRzLCB0aGVcbiAqIGxlbmd0aHMgb2YgdGhlIGJpdCBsZW5ndGggY29kZXMsIHRoZSBsaXRlcmFsIHRyZWUgYW5kIHRoZSBkaXN0YW5jZSB0cmVlLlxuICogSU4gYXNzZXJ0aW9uOiBsY29kZXMgPj0gMjU3LCBkY29kZXMgPj0gMSwgYmxjb2RlcyA+PSA0LlxuICovXG5mdW5jdGlvbiBzZW5kX2FsbF90cmVlcyhzLCBsY29kZXMsIGRjb2RlcywgYmxjb2Rlcylcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICBpbnQgbGNvZGVzLCBkY29kZXMsIGJsY29kZXM7IC8qIG51bWJlciBvZiBjb2RlcyBmb3IgZWFjaCB0cmVlICovXG57XG4gIHZhciByYW5rOyAgICAgICAgICAgICAgICAgICAgLyogaW5kZXggaW4gYmxfb3JkZXIgKi9cblxuICAvL0Fzc2VydCAobGNvZGVzID49IDI1NyAmJiBkY29kZXMgPj0gMSAmJiBibGNvZGVzID49IDQsIFwibm90IGVub3VnaCBjb2Rlc1wiKTtcbiAgLy9Bc3NlcnQgKGxjb2RlcyA8PSBMX0NPREVTICYmIGRjb2RlcyA8PSBEX0NPREVTICYmIGJsY29kZXMgPD0gQkxfQ09ERVMsXG4gIC8vICAgICAgICBcInRvbyBtYW55IGNvZGVzXCIpO1xuICAvL1RyYWNldigoc3RkZXJyLCBcIlxcbmJsIGNvdW50czogXCIpKTtcbiAgc2VuZF9iaXRzKHMsIGxjb2Rlcy0yNTcsIDUpOyAvKiBub3QgKzI1NSBhcyBzdGF0ZWQgaW4gYXBwbm90ZS50eHQgKi9cbiAgc2VuZF9iaXRzKHMsIGRjb2Rlcy0xLCAgIDUpO1xuICBzZW5kX2JpdHMocywgYmxjb2Rlcy00LCAgNCk7IC8qIG5vdCAtMyBhcyBzdGF0ZWQgaW4gYXBwbm90ZS50eHQgKi9cbiAgZm9yIChyYW5rID0gMDsgcmFuayA8IGJsY29kZXM7IHJhbmsrKykge1xuICAgIC8vVHJhY2V2KChzdGRlcnIsIFwiXFxuYmwgY29kZSAlMmQgXCIsIGJsX29yZGVyW3JhbmtdKSk7XG4gICAgc2VuZF9iaXRzKHMsIHMuYmxfdHJlZVtibF9vcmRlcltyYW5rXSoyICsgMV0vKi5MZW4qLywgMyk7XG4gIH1cbiAgLy9UcmFjZXYoKHN0ZGVyciwgXCJcXG5ibCB0cmVlOiBzZW50ICVsZFwiLCBzLT5iaXRzX3NlbnQpKTtcblxuICBzZW5kX3RyZWUocywgcy5keW5fbHRyZWUsIGxjb2Rlcy0xKTsgLyogbGl0ZXJhbCB0cmVlICovXG4gIC8vVHJhY2V2KChzdGRlcnIsIFwiXFxubGl0IHRyZWU6IHNlbnQgJWxkXCIsIHMtPmJpdHNfc2VudCkpO1xuXG4gIHNlbmRfdHJlZShzLCBzLmR5bl9kdHJlZSwgZGNvZGVzLTEpOyAvKiBkaXN0YW5jZSB0cmVlICovXG4gIC8vVHJhY2V2KChzdGRlcnIsIFwiXFxuZGlzdCB0cmVlOiBzZW50ICVsZFwiLCBzLT5iaXRzX3NlbnQpKTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIENoZWNrIGlmIHRoZSBkYXRhIHR5cGUgaXMgVEVYVCBvciBCSU5BUlksIHVzaW5nIHRoZSBmb2xsb3dpbmcgYWxnb3JpdGhtOlxuICogLSBURVhUIGlmIHRoZSB0d28gY29uZGl0aW9ucyBiZWxvdyBhcmUgc2F0aXNmaWVkOlxuICogICAgYSkgVGhlcmUgYXJlIG5vIG5vbi1wb3J0YWJsZSBjb250cm9sIGNoYXJhY3RlcnMgYmVsb25naW5nIHRvIHRoZVxuICogICAgICAgXCJibGFjayBsaXN0XCIgKDAuLjYsIDE0Li4yNSwgMjguLjMxKS5cbiAqICAgIGIpIFRoZXJlIGlzIGF0IGxlYXN0IG9uZSBwcmludGFibGUgY2hhcmFjdGVyIGJlbG9uZ2luZyB0byB0aGVcbiAqICAgICAgIFwid2hpdGUgbGlzdFwiICg5IHtUQUJ9LCAxMCB7TEZ9LCAxMyB7Q1J9LCAzMi4uMjU1KS5cbiAqIC0gQklOQVJZIG90aGVyd2lzZS5cbiAqIC0gVGhlIGZvbGxvd2luZyBwYXJ0aWFsbHktcG9ydGFibGUgY29udHJvbCBjaGFyYWN0ZXJzIGZvcm0gYVxuICogICBcImdyYXkgbGlzdFwiIHRoYXQgaXMgaWdub3JlZCBpbiB0aGlzIGRldGVjdGlvbiBhbGdvcml0aG06XG4gKiAgICg3IHtCRUx9LCA4IHtCU30sIDExIHtWVH0sIDEyIHtGRn0sIDI2IHtTVUJ9LCAyNyB7RVNDfSkuXG4gKiBJTiBhc3NlcnRpb246IHRoZSBmaWVsZHMgRnJlcSBvZiBkeW5fbHRyZWUgYXJlIHNldC5cbiAqL1xuZnVuY3Rpb24gZGV0ZWN0X2RhdGFfdHlwZShzKSB7XG4gIC8qIGJsYWNrX21hc2sgaXMgdGhlIGJpdCBtYXNrIG9mIGJsYWNrLWxpc3RlZCBieXRlc1xuICAgKiBzZXQgYml0cyAwLi42LCAxNC4uMjUsIGFuZCAyOC4uMzFcbiAgICogMHhmM2ZmYzA3ZiA9IGJpbmFyeSAxMTExMDAxMTExMTExMTExMTEwMDAwMDAwMTExMTExMVxuICAgKi9cbiAgdmFyIGJsYWNrX21hc2sgPSAweGYzZmZjMDdmO1xuICB2YXIgbjtcblxuICAvKiBDaGVjayBmb3Igbm9uLXRleHR1YWwgKFwiYmxhY2stbGlzdGVkXCIpIGJ5dGVzLiAqL1xuICBmb3IgKG4gPSAwOyBuIDw9IDMxOyBuKyssIGJsYWNrX21hc2sgPj4+PSAxKSB7XG4gICAgaWYgKChibGFja19tYXNrICYgMSkgJiYgKHMuZHluX2x0cmVlW24qMl0vKi5GcmVxKi8gIT09IDApKSB7XG4gICAgICByZXR1cm4gWl9CSU5BUlk7XG4gICAgfVxuICB9XG5cbiAgLyogQ2hlY2sgZm9yIHRleHR1YWwgKFwid2hpdGUtbGlzdGVkXCIpIGJ5dGVzLiAqL1xuICBpZiAocy5keW5fbHRyZWVbOSAqIDJdLyouRnJlcSovICE9PSAwIHx8IHMuZHluX2x0cmVlWzEwICogMl0vKi5GcmVxKi8gIT09IDAgfHxcbiAgICAgIHMuZHluX2x0cmVlWzEzICogMl0vKi5GcmVxKi8gIT09IDApIHtcbiAgICByZXR1cm4gWl9URVhUO1xuICB9XG4gIGZvciAobiA9IDMyOyBuIDwgTElURVJBTFM7IG4rKykge1xuICAgIGlmIChzLmR5bl9sdHJlZVtuICogMl0vKi5GcmVxKi8gIT09IDApIHtcbiAgICAgIHJldHVybiBaX1RFWFQ7XG4gICAgfVxuICB9XG5cbiAgLyogVGhlcmUgYXJlIG5vIFwiYmxhY2stbGlzdGVkXCIgb3IgXCJ3aGl0ZS1saXN0ZWRcIiBieXRlczpcbiAgICogdGhpcyBzdHJlYW0gZWl0aGVyIGlzIGVtcHR5IG9yIGhhcyB0b2xlcmF0ZWQgKFwiZ3JheS1saXN0ZWRcIikgYnl0ZXMgb25seS5cbiAgICovXG4gIHJldHVybiBaX0JJTkFSWTtcbn1cblxuXG52YXIgc3RhdGljX2luaXRfZG9uZSA9IGZhbHNlO1xuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIEluaXRpYWxpemUgdGhlIHRyZWUgZGF0YSBzdHJ1Y3R1cmVzIGZvciBhIG5ldyB6bGliIHN0cmVhbS5cbiAqL1xuZnVuY3Rpb24gX3RyX2luaXQocylcbntcblxuICBpZiAoIXN0YXRpY19pbml0X2RvbmUpIHtcbiAgICB0cl9zdGF0aWNfaW5pdCgpO1xuICAgIHN0YXRpY19pbml0X2RvbmUgPSB0cnVlO1xuICB9XG5cbiAgcy5sX2Rlc2MgID0gbmV3IFRyZWVEZXNjKHMuZHluX2x0cmVlLCBzdGF0aWNfbF9kZXNjKTtcbiAgcy5kX2Rlc2MgID0gbmV3IFRyZWVEZXNjKHMuZHluX2R0cmVlLCBzdGF0aWNfZF9kZXNjKTtcbiAgcy5ibF9kZXNjID0gbmV3IFRyZWVEZXNjKHMuYmxfdHJlZSwgc3RhdGljX2JsX2Rlc2MpO1xuXG4gIHMuYmlfYnVmID0gMDtcbiAgcy5iaV92YWxpZCA9IDA7XG5cbiAgLyogSW5pdGlhbGl6ZSB0aGUgZmlyc3QgYmxvY2sgb2YgdGhlIGZpcnN0IGZpbGU6ICovXG4gIGluaXRfYmxvY2socyk7XG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTZW5kIGEgc3RvcmVkIGJsb2NrXG4gKi9cbmZ1bmN0aW9uIF90cl9zdG9yZWRfYmxvY2socywgYnVmLCBzdG9yZWRfbGVuLCBsYXN0KVxuLy9EZWZsYXRlU3RhdGUgKnM7XG4vL2NoYXJmICpidWY7ICAgICAgIC8qIGlucHV0IGJsb2NrICovXG4vL3VsZyBzdG9yZWRfbGVuOyAgIC8qIGxlbmd0aCBvZiBpbnB1dCBibG9jayAqL1xuLy9pbnQgbGFzdDsgICAgICAgICAvKiBvbmUgaWYgdGhpcyBpcyB0aGUgbGFzdCBibG9jayBmb3IgYSBmaWxlICovXG57XG4gIHNlbmRfYml0cyhzLCAoU1RPUkVEX0JMT0NLPDwxKSsobGFzdCA/IDEgOiAwKSwgMyk7ICAgIC8qIHNlbmQgYmxvY2sgdHlwZSAqL1xuICBjb3B5X2Jsb2NrKHMsIGJ1Ziwgc3RvcmVkX2xlbiwgdHJ1ZSk7IC8qIHdpdGggaGVhZGVyICovXG59XG5cblxuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBTZW5kIG9uZSBlbXB0eSBzdGF0aWMgYmxvY2sgdG8gZ2l2ZSBlbm91Z2ggbG9va2FoZWFkIGZvciBpbmZsYXRlLlxuICogVGhpcyB0YWtlcyAxMCBiaXRzLCBvZiB3aGljaCA3IG1heSByZW1haW4gaW4gdGhlIGJpdCBidWZmZXIuXG4gKi9cbmZ1bmN0aW9uIF90cl9hbGlnbihzKSB7XG4gIHNlbmRfYml0cyhzLCBTVEFUSUNfVFJFRVM8PDEsIDMpO1xuICBzZW5kX2NvZGUocywgRU5EX0JMT0NLLCBzdGF0aWNfbHRyZWUpO1xuICBiaV9mbHVzaChzKTtcbn1cblxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqIERldGVybWluZSB0aGUgYmVzdCBlbmNvZGluZyBmb3IgdGhlIGN1cnJlbnQgYmxvY2s6IGR5bmFtaWMgdHJlZXMsIHN0YXRpY1xuICogdHJlZXMgb3Igc3RvcmUsIGFuZCBvdXRwdXQgdGhlIGVuY29kZWQgYmxvY2sgdG8gdGhlIHppcCBmaWxlLlxuICovXG5mdW5jdGlvbiBfdHJfZmx1c2hfYmxvY2socywgYnVmLCBzdG9yZWRfbGVuLCBsYXN0KVxuLy9EZWZsYXRlU3RhdGUgKnM7XG4vL2NoYXJmICpidWY7ICAgICAgIC8qIGlucHV0IGJsb2NrLCBvciBOVUxMIGlmIHRvbyBvbGQgKi9cbi8vdWxnIHN0b3JlZF9sZW47ICAgLyogbGVuZ3RoIG9mIGlucHV0IGJsb2NrICovXG4vL2ludCBsYXN0OyAgICAgICAgIC8qIG9uZSBpZiB0aGlzIGlzIHRoZSBsYXN0IGJsb2NrIGZvciBhIGZpbGUgKi9cbntcbiAgdmFyIG9wdF9sZW5iLCBzdGF0aWNfbGVuYjsgIC8qIG9wdF9sZW4gYW5kIHN0YXRpY19sZW4gaW4gYnl0ZXMgKi9cbiAgdmFyIG1heF9ibGluZGV4ID0gMDsgICAgICAgIC8qIGluZGV4IG9mIGxhc3QgYml0IGxlbmd0aCBjb2RlIG9mIG5vbiB6ZXJvIGZyZXEgKi9cblxuICAvKiBCdWlsZCB0aGUgSHVmZm1hbiB0cmVlcyB1bmxlc3MgYSBzdG9yZWQgYmxvY2sgaXMgZm9yY2VkICovXG4gIGlmIChzLmxldmVsID4gMCkge1xuXG4gICAgLyogQ2hlY2sgaWYgdGhlIGZpbGUgaXMgYmluYXJ5IG9yIHRleHQgKi9cbiAgICBpZiAocy5zdHJtLmRhdGFfdHlwZSA9PT0gWl9VTktOT1dOKSB7XG4gICAgICBzLnN0cm0uZGF0YV90eXBlID0gZGV0ZWN0X2RhdGFfdHlwZShzKTtcbiAgICB9XG5cbiAgICAvKiBDb25zdHJ1Y3QgdGhlIGxpdGVyYWwgYW5kIGRpc3RhbmNlIHRyZWVzICovXG4gICAgYnVpbGRfdHJlZShzLCBzLmxfZGVzYyk7XG4gICAgLy8gVHJhY2V2KChzdGRlcnIsIFwiXFxubGl0IGRhdGE6IGR5biAlbGQsIHN0YXQgJWxkXCIsIHMtPm9wdF9sZW4sXG4gICAgLy8gICAgICAgIHMtPnN0YXRpY19sZW4pKTtcblxuICAgIGJ1aWxkX3RyZWUocywgcy5kX2Rlc2MpO1xuICAgIC8vIFRyYWNldigoc3RkZXJyLCBcIlxcbmRpc3QgZGF0YTogZHluICVsZCwgc3RhdCAlbGRcIiwgcy0+b3B0X2xlbixcbiAgICAvLyAgICAgICAgcy0+c3RhdGljX2xlbikpO1xuICAgIC8qIEF0IHRoaXMgcG9pbnQsIG9wdF9sZW4gYW5kIHN0YXRpY19sZW4gYXJlIHRoZSB0b3RhbCBiaXQgbGVuZ3RocyBvZlxuICAgICAqIHRoZSBjb21wcmVzc2VkIGJsb2NrIGRhdGEsIGV4Y2x1ZGluZyB0aGUgdHJlZSByZXByZXNlbnRhdGlvbnMuXG4gICAgICovXG5cbiAgICAvKiBCdWlsZCB0aGUgYml0IGxlbmd0aCB0cmVlIGZvciB0aGUgYWJvdmUgdHdvIHRyZWVzLCBhbmQgZ2V0IHRoZSBpbmRleFxuICAgICAqIGluIGJsX29yZGVyIG9mIHRoZSBsYXN0IGJpdCBsZW5ndGggY29kZSB0byBzZW5kLlxuICAgICAqL1xuICAgIG1heF9ibGluZGV4ID0gYnVpbGRfYmxfdHJlZShzKTtcblxuICAgIC8qIERldGVybWluZSB0aGUgYmVzdCBlbmNvZGluZy4gQ29tcHV0ZSB0aGUgYmxvY2sgbGVuZ3RocyBpbiBieXRlcy4gKi9cbiAgICBvcHRfbGVuYiA9IChzLm9wdF9sZW4rMys3KSA+Pj4gMztcbiAgICBzdGF0aWNfbGVuYiA9IChzLnN0YXRpY19sZW4rMys3KSA+Pj4gMztcblxuICAgIC8vIFRyYWNldigoc3RkZXJyLCBcIlxcbm9wdCAlbHUoJWx1KSBzdGF0ICVsdSglbHUpIHN0b3JlZCAlbHUgbGl0ICV1IFwiLFxuICAgIC8vICAgICAgICBvcHRfbGVuYiwgcy0+b3B0X2xlbiwgc3RhdGljX2xlbmIsIHMtPnN0YXRpY19sZW4sIHN0b3JlZF9sZW4sXG4gICAgLy8gICAgICAgIHMtPmxhc3RfbGl0KSk7XG5cbiAgICBpZiAoc3RhdGljX2xlbmIgPD0gb3B0X2xlbmIpIHsgb3B0X2xlbmIgPSBzdGF0aWNfbGVuYjsgfVxuXG4gIH0gZWxzZSB7XG4gICAgLy8gQXNzZXJ0KGJ1ZiAhPSAoY2hhciopMCwgXCJsb3N0IGJ1ZlwiKTtcbiAgICBvcHRfbGVuYiA9IHN0YXRpY19sZW5iID0gc3RvcmVkX2xlbiArIDU7IC8qIGZvcmNlIGEgc3RvcmVkIGJsb2NrICovXG4gIH1cblxuICBpZiAoKHN0b3JlZF9sZW4rNCA8PSBvcHRfbGVuYikgJiYgKGJ1ZiAhPT0gLTEpKSB7XG4gICAgLyogNDogdHdvIHdvcmRzIGZvciB0aGUgbGVuZ3RocyAqL1xuXG4gICAgLyogVGhlIHRlc3QgYnVmICE9IE5VTEwgaXMgb25seSBuZWNlc3NhcnkgaWYgTElUX0JVRlNJWkUgPiBXU0laRS5cbiAgICAgKiBPdGhlcndpc2Ugd2UgY2FuJ3QgaGF2ZSBwcm9jZXNzZWQgbW9yZSB0aGFuIFdTSVpFIGlucHV0IGJ5dGVzIHNpbmNlXG4gICAgICogdGhlIGxhc3QgYmxvY2sgZmx1c2gsIGJlY2F1c2UgY29tcHJlc3Npb24gd291bGQgaGF2ZSBiZWVuXG4gICAgICogc3VjY2Vzc2Z1bC4gSWYgTElUX0JVRlNJWkUgPD0gV1NJWkUsIGl0IGlzIG5ldmVyIHRvbyBsYXRlIHRvXG4gICAgICogdHJhbnNmb3JtIGEgYmxvY2sgaW50byBhIHN0b3JlZCBibG9jay5cbiAgICAgKi9cbiAgICBfdHJfc3RvcmVkX2Jsb2NrKHMsIGJ1Ziwgc3RvcmVkX2xlbiwgbGFzdCk7XG5cbiAgfSBlbHNlIGlmIChzLnN0cmF0ZWd5ID09PSBaX0ZJWEVEIHx8IHN0YXRpY19sZW5iID09PSBvcHRfbGVuYikge1xuXG4gICAgc2VuZF9iaXRzKHMsIChTVEFUSUNfVFJFRVM8PDEpICsgKGxhc3QgPyAxIDogMCksIDMpO1xuICAgIGNvbXByZXNzX2Jsb2NrKHMsIHN0YXRpY19sdHJlZSwgc3RhdGljX2R0cmVlKTtcblxuICB9IGVsc2Uge1xuICAgIHNlbmRfYml0cyhzLCAoRFlOX1RSRUVTPDwxKSArIChsYXN0ID8gMSA6IDApLCAzKTtcbiAgICBzZW5kX2FsbF90cmVlcyhzLCBzLmxfZGVzYy5tYXhfY29kZSsxLCBzLmRfZGVzYy5tYXhfY29kZSsxLCBtYXhfYmxpbmRleCsxKTtcbiAgICBjb21wcmVzc19ibG9jayhzLCBzLmR5bl9sdHJlZSwgcy5keW5fZHRyZWUpO1xuICB9XG4gIC8vIEFzc2VydCAocy0+Y29tcHJlc3NlZF9sZW4gPT0gcy0+Yml0c19zZW50LCBcImJhZCBjb21wcmVzc2VkIHNpemVcIik7XG4gIC8qIFRoZSBhYm92ZSBjaGVjayBpcyBtYWRlIG1vZCAyXjMyLCBmb3IgZmlsZXMgbGFyZ2VyIHRoYW4gNTEyIE1CXG4gICAqIGFuZCB1TG9uZyBpbXBsZW1lbnRlZCBvbiAzMiBiaXRzLlxuICAgKi9cbiAgaW5pdF9ibG9jayhzKTtcblxuICBpZiAobGFzdCkge1xuICAgIGJpX3dpbmR1cChzKTtcbiAgfVxuICAvLyBUcmFjZXYoKHN0ZGVycixcIlxcbmNvbXBybGVuICVsdSglbHUpIFwiLCBzLT5jb21wcmVzc2VkX2xlbj4+MyxcbiAgLy8gICAgICAgcy0+Y29tcHJlc3NlZF9sZW4tNypsYXN0KSk7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogU2F2ZSB0aGUgbWF0Y2ggaW5mbyBhbmQgdGFsbHkgdGhlIGZyZXF1ZW5jeSBjb3VudHMuIFJldHVybiB0cnVlIGlmXG4gKiB0aGUgY3VycmVudCBibG9jayBtdXN0IGJlIGZsdXNoZWQuXG4gKi9cbmZ1bmN0aW9uIF90cl90YWxseShzLCBkaXN0LCBsYylcbi8vICAgIGRlZmxhdGVfc3RhdGUgKnM7XG4vLyAgICB1bnNpZ25lZCBkaXN0OyAgLyogZGlzdGFuY2Ugb2YgbWF0Y2hlZCBzdHJpbmcgKi9cbi8vICAgIHVuc2lnbmVkIGxjOyAgICAvKiBtYXRjaCBsZW5ndGgtTUlOX01BVENIIG9yIHVubWF0Y2hlZCBjaGFyIChpZiBkaXN0PT0wKSAqL1xue1xuICAvL3ZhciBvdXRfbGVuZ3RoLCBpbl9sZW5ndGgsIGRjb2RlO1xuXG4gIHMucGVuZGluZ19idWZbcy5kX2J1ZiArIHMubGFzdF9saXQgKiAyXSAgICAgPSAoZGlzdCA+Pj4gOCkgJiAweGZmO1xuICBzLnBlbmRpbmdfYnVmW3MuZF9idWYgKyBzLmxhc3RfbGl0ICogMiArIDFdID0gZGlzdCAmIDB4ZmY7XG5cbiAgcy5wZW5kaW5nX2J1ZltzLmxfYnVmICsgcy5sYXN0X2xpdF0gPSBsYyAmIDB4ZmY7XG4gIHMubGFzdF9saXQrKztcblxuICBpZiAoZGlzdCA9PT0gMCkge1xuICAgIC8qIGxjIGlzIHRoZSB1bm1hdGNoZWQgY2hhciAqL1xuICAgIHMuZHluX2x0cmVlW2xjKjJdLyouRnJlcSovKys7XG4gIH0gZWxzZSB7XG4gICAgcy5tYXRjaGVzKys7XG4gICAgLyogSGVyZSwgbGMgaXMgdGhlIG1hdGNoIGxlbmd0aCAtIE1JTl9NQVRDSCAqL1xuICAgIGRpc3QtLTsgICAgICAgICAgICAgLyogZGlzdCA9IG1hdGNoIGRpc3RhbmNlIC0gMSAqL1xuICAgIC8vQXNzZXJ0KCh1c2gpZGlzdCA8ICh1c2gpTUFYX0RJU1QocykgJiZcbiAgICAvLyAgICAgICAodXNoKWxjIDw9ICh1c2gpKE1BWF9NQVRDSC1NSU5fTUFUQ0gpICYmXG4gICAgLy8gICAgICAgKHVzaClkX2NvZGUoZGlzdCkgPCAodXNoKURfQ09ERVMsICBcIl90cl90YWxseTogYmFkIG1hdGNoXCIpO1xuXG4gICAgcy5keW5fbHRyZWVbKF9sZW5ndGhfY29kZVtsY10rTElURVJBTFMrMSkgKiAyXS8qLkZyZXEqLysrO1xuICAgIHMuZHluX2R0cmVlW2RfY29kZShkaXN0KSAqIDJdLyouRnJlcSovKys7XG4gIH1cblxuLy8gKCEpIFRoaXMgYmxvY2sgaXMgZGlzYWJsZWQgaW4gemxpYiBkZWZhaWx0cyxcbi8vIGRvbid0IGVuYWJsZSBpdCBmb3IgYmluYXJ5IGNvbXBhdGliaWxpdHlcblxuLy8jaWZkZWYgVFJVTkNBVEVfQkxPQ0tcbi8vICAvKiBUcnkgdG8gZ3Vlc3MgaWYgaXQgaXMgcHJvZml0YWJsZSB0byBzdG9wIHRoZSBjdXJyZW50IGJsb2NrIGhlcmUgKi9cbi8vICBpZiAoKHMubGFzdF9saXQgJiAweDFmZmYpID09PSAwICYmIHMubGV2ZWwgPiAyKSB7XG4vLyAgICAvKiBDb21wdXRlIGFuIHVwcGVyIGJvdW5kIGZvciB0aGUgY29tcHJlc3NlZCBsZW5ndGggKi9cbi8vICAgIG91dF9sZW5ndGggPSBzLmxhc3RfbGl0Kjg7XG4vLyAgICBpbl9sZW5ndGggPSBzLnN0cnN0YXJ0IC0gcy5ibG9ja19zdGFydDtcbi8vXG4vLyAgICBmb3IgKGRjb2RlID0gMDsgZGNvZGUgPCBEX0NPREVTOyBkY29kZSsrKSB7XG4vLyAgICAgIG91dF9sZW5ndGggKz0gcy5keW5fZHRyZWVbZGNvZGUqMl0vKi5GcmVxKi8gKiAoNSArIGV4dHJhX2RiaXRzW2Rjb2RlXSk7XG4vLyAgICB9XG4vLyAgICBvdXRfbGVuZ3RoID4+Pj0gMztcbi8vICAgIC8vVHJhY2V2KChzdGRlcnIsXCJcXG5sYXN0X2xpdCAldSwgaW4gJWxkLCBvdXQgfiVsZCglbGQlJSkgXCIsXG4vLyAgICAvLyAgICAgICBzLT5sYXN0X2xpdCwgaW5fbGVuZ3RoLCBvdXRfbGVuZ3RoLFxuLy8gICAgLy8gICAgICAgMTAwTCAtIG91dF9sZW5ndGgqMTAwTC9pbl9sZW5ndGgpKTtcbi8vICAgIGlmIChzLm1hdGNoZXMgPCAocy5sYXN0X2xpdD4+MSkvKmludCAvMiovICYmIG91dF9sZW5ndGggPCAoaW5fbGVuZ3RoPj4xKS8qaW50IC8yKi8pIHtcbi8vICAgICAgcmV0dXJuIHRydWU7XG4vLyAgICB9XG4vLyAgfVxuLy8jZW5kaWZcblxuICByZXR1cm4gKHMubGFzdF9saXQgPT09IHMubGl0X2J1ZnNpemUtMSk7XG4gIC8qIFdlIGF2b2lkIGVxdWFsaXR5IHdpdGggbGl0X2J1ZnNpemUgYmVjYXVzZSBvZiB3cmFwYXJvdW5kIGF0IDY0S1xuICAgKiBvbiAxNiBiaXQgbWFjaGluZXMgYW5kIGJlY2F1c2Ugc3RvcmVkIGJsb2NrcyBhcmUgcmVzdHJpY3RlZCB0b1xuICAgKiA2NEstMSBieXRlcy5cbiAgICovXG59XG5cbmV4cG9ydHMuX3RyX2luaXQgID0gX3RyX2luaXQ7XG5leHBvcnRzLl90cl9zdG9yZWRfYmxvY2sgPSBfdHJfc3RvcmVkX2Jsb2NrO1xuZXhwb3J0cy5fdHJfZmx1c2hfYmxvY2sgID0gX3RyX2ZsdXNoX2Jsb2NrO1xuZXhwb3J0cy5fdHJfdGFsbHkgPSBfdHJfdGFsbHk7XG5leHBvcnRzLl90cl9hbGlnbiA9IF90cl9hbGlnbjsiLCIndXNlIHN0cmljdCc7XG5cblxuZnVuY3Rpb24gWlN0cmVhbSgpIHtcbiAgLyogbmV4dCBpbnB1dCBieXRlICovXG4gIHRoaXMuaW5wdXQgPSBudWxsOyAvLyBKUyBzcGVjaWZpYywgYmVjYXVzZSB3ZSBoYXZlIG5vIHBvaW50ZXJzXG4gIHRoaXMubmV4dF9pbiA9IDA7XG4gIC8qIG51bWJlciBvZiBieXRlcyBhdmFpbGFibGUgYXQgaW5wdXQgKi9cbiAgdGhpcy5hdmFpbF9pbiA9IDA7XG4gIC8qIHRvdGFsIG51bWJlciBvZiBpbnB1dCBieXRlcyByZWFkIHNvIGZhciAqL1xuICB0aGlzLnRvdGFsX2luID0gMDtcbiAgLyogbmV4dCBvdXRwdXQgYnl0ZSBzaG91bGQgYmUgcHV0IHRoZXJlICovXG4gIHRoaXMub3V0cHV0ID0gbnVsbDsgLy8gSlMgc3BlY2lmaWMsIGJlY2F1c2Ugd2UgaGF2ZSBubyBwb2ludGVyc1xuICB0aGlzLm5leHRfb3V0ID0gMDtcbiAgLyogcmVtYWluaW5nIGZyZWUgc3BhY2UgYXQgb3V0cHV0ICovXG4gIHRoaXMuYXZhaWxfb3V0ID0gMDtcbiAgLyogdG90YWwgbnVtYmVyIG9mIGJ5dGVzIG91dHB1dCBzbyBmYXIgKi9cbiAgdGhpcy50b3RhbF9vdXQgPSAwO1xuICAvKiBsYXN0IGVycm9yIG1lc3NhZ2UsIE5VTEwgaWYgbm8gZXJyb3IgKi9cbiAgdGhpcy5tc2cgPSAnJy8qWl9OVUxMKi87XG4gIC8qIG5vdCB2aXNpYmxlIGJ5IGFwcGxpY2F0aW9ucyAqL1xuICB0aGlzLnN0YXRlID0gbnVsbDtcbiAgLyogYmVzdCBndWVzcyBhYm91dCB0aGUgZGF0YSB0eXBlOiBiaW5hcnkgb3IgdGV4dCAqL1xuICB0aGlzLmRhdGFfdHlwZSA9IDIvKlpfVU5LTk9XTiovO1xuICAvKiBhZGxlcjMyIHZhbHVlIG9mIHRoZSB1bmNvbXByZXNzZWQgZGF0YSAqL1xuICB0aGlzLmFkbGVyID0gMDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBaU3RyZWFtOyIsInZhciBNZWRpYVN0cmVhbVR5cGUgPSB7XG4gICAgVklERU9fVFlQRTogXCJWaWRlb1wiLFxuXG4gICAgQVVESU9fVFlQRTogXCJBdWRpb1wiXG59O1xubW9kdWxlLmV4cG9ydHMgPSBNZWRpYVN0cmVhbVR5cGU7IiwidmFyIFJUQ0Jyb3dzZXJUeXBlID0ge1xuICAgIFJUQ19CUk9XU0VSX0NIUk9NRTogXCJydGNfYnJvd3Nlci5jaHJvbWVcIixcblxuICAgIFJUQ19CUk9XU0VSX0ZJUkVGT1g6IFwicnRjX2Jyb3dzZXIuZmlyZWZveFwiXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJUQ0Jyb3dzZXJUeXBlOyIsInZhciBSVENFdmVudHMgPSB7XG4gICAgTEFTVE5fQ0hBTkdFRDogXCJydGMubGFzdG5fY2hhbmdlZFwiLFxuICAgIERPTUlOQU5UU1BFQUtFUl9DSEFOR0VEOiBcInJ0Yy5kb21pbmFudHNwZWFrZXJfY2hhbmdlZFwiLFxuICAgIExBU1ROX0VORFBPSU5UX0NIQU5HRUQ6IFwicnRjLmxhc3RuX2VuZHBvaW50X2NoYW5nZWRcIixcbiAgICBTSU1VTENBU1RfTEFZRVJfQ0hBTkdFRDogXCJydGMuc2ltdWxjYXN0X2xheWVyX2NoYW5nZWRcIixcbiAgICBTSU1VTENBU1RfTEFZRVJfQ0hBTkdJTkc6IFwicnRjLnNpbXVsY2FzdF9sYXllcl9jaGFuZ2luZ1wiLFxuICAgIFNJTVVMQ0FTVF9TVEFSVDogXCJydGMuc2ltbGNhc3Rfc3RhcnRcIixcbiAgICBTSU1VTENBU1RfU1RPUDogXCJydGMuc2ltbGNhc3Rfc3RvcFwiXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJUQ0V2ZW50czsiLCJ2YXIgU3RyZWFtRXZlbnRUeXBlcyA9IHtcbiAgICBFVkVOVF9UWVBFX0xPQ0FMX0NSRUFURUQ6IFwic3RyZWFtLmxvY2FsX2NyZWF0ZWRcIixcblxuICAgIEVWRU5UX1RZUEVfTE9DQUxfQ0hBTkdFRDogXCJzdHJlYW0ubG9jYWxfY2hhbmdlZFwiLFxuXG4gICAgRVZFTlRfVFlQRV9MT0NBTF9FTkRFRDogXCJzdHJlYW0ubG9jYWxfZW5kZWRcIixcblxuICAgIEVWRU5UX1RZUEVfUkVNT1RFX0NSRUFURUQ6IFwic3RyZWFtLnJlbW90ZV9jcmVhdGVkXCIsXG5cbiAgICBFVkVOVF9UWVBFX1JFTU9URV9FTkRFRDogXCJzdHJlYW0ucmVtb3RlX2VuZGVkXCJcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU3RyZWFtRXZlbnRUeXBlczsiLCJ2YXIgVUlFdmVudHMgPSB7XG4gICAgTklDS05BTUVfQ0hBTkdFRDogXCJVSS5uaWNrbmFtZV9jaGFuZ2VkXCIsXG4gICAgU0VMRUNURURfRU5EUE9JTlQ6IFwiVUkuc2VsZWN0ZWRfZW5kcG9pbnRcIixcbiAgICBQSU5ORURfRU5EUE9JTlQ6IFwiVUkucGlubmVkX2VuZHBvaW50XCJcbn07XG5tb2R1bGUuZXhwb3J0cyA9IFVJRXZlbnRzOyIsInZhciBDUUV2ZW50cyA9IHtcbiAgICBMT0NBTFNUQVRTX1VQREFURUQ6IFwiY3EubG9jYWxzdGF0c191cGRhdGVkXCIsXG4gICAgUkVNT1RFU1RBVFNfVVBEQVRFRDogXCJjcS5yZW1vdGVzdGF0c191cGRhdGVkXCIsXG4gICAgU1RPUDogXCJjcS5zdG9wXCJcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ1FFdmVudHM7IiwidmFyIERlc2t0b3BTaGFyaW5nRXZlbnRUeXBlcyA9IHtcbiAgICBJTklUOiBcImRzLmluaXRcIixcblxuICAgIFNXSVRDSElOR19ET05FOiBcImRzLnN3aXRjaGluZ19kb25lXCIsXG5cbiAgICBORVdfU1RSRUFNX0NSRUFURUQ6IFwiZHMubmV3X3N0cmVhbV9jcmVhdGVkXCJcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gRGVza3RvcFNoYXJpbmdFdmVudFR5cGVzOyIsIm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGdldExhbmd1YWdlcyA6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGxhbmd1YWdlcyA9IFtdO1xuICAgICAgICBmb3IodmFyIGxhbmcgaW4gdGhpcylcbiAgICAgICAge1xuICAgICAgICAgICAgaWYodHlwZW9mIHRoaXNbbGFuZ10gPT09IFwic3RyaW5nXCIpXG4gICAgICAgICAgICAgICAgbGFuZ3VhZ2VzLnB1c2godGhpc1tsYW5nXSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxhbmd1YWdlcztcbiAgICB9LFxuICAgIEVOOiBcImVuXCJcbn0iLCJ2YXIgWE1QUEV2ZW50cyA9IHtcbiAgICBDT05GRVJFTkNFX0NFUkFURUQ6IFwieG1wcC5jb25mZXJlbmNlQ3JlYXRlZC5qaW5nbGVcIixcbiAgICBDQUxMX1RFUk1JTkFURUQ6IFwieG1wcC5jYWxsdGVybWluYXRlZC5qaW5nbGVcIixcbiAgICBDQUxMX0lOQ09NSU5HOiBcInhtcHAuY2FsbGluY29taW5nLmppbmdsZVwiLFxuICAgIERJU1BPU0VfQ09ORkVSRU5DRTogXCJ4bXBwLmRpc3BvY2VfY29uZmVybmNlXCIsXG4gICAgS0lDS0VEOiBcInhtcHAua2lja2VkXCIsXG4gICAgQlJJREdFX0RPV046IFwieG1wcC5icmlkZ2VfZG93blwiLFxuICAgIFVTRVJfSURfQ0hBTkdFRDogXCJ4bXBwLnVzZXJfaWRfY2hhbmdlZFwiLFxuICAgIENIQU5HRURfU1RSRUFNUzogXCJ4bXBwLmNoYW5nZWRfc3RyZWFtc1wiLFxuICAgIE1VQ19KT0lORUQ6IFwieG1wcC5tdWNfam9pbmVkXCIsXG4gICAgTVVDX0VOVEVSOiBcInhtcHAubXVjX2VudGVyXCIsXG4gICAgTVVDX1JPTEVfQ0hBTkdFRDogXCJ4bXBwLm11Y19yb2xlX2NoYW5nZWRcIixcbiAgICBNVUNfTEVGVDogXCJ4bXBwLm11Y19sZWZ0XCIsXG4gICAgRElTUExBWV9OQU1FX0NIQU5HRUQ6IFwieG1wcC5kaXNwbGF5X25hbWVfY2hhbmdlZFwiLFxuICAgIFJFTU9URV9TVEFUUzogXCJ4bXBwLnJlbW90ZV9zdGF0c1wiLFxuICAgIExPQ0FMUk9MRV9DSEFOR0VEOiBcInhtcHAubG9jYWxyb2xlX2NoYW5nZWRcIixcbiAgICBQUkVTRU5DRV9TVEFUVVM6IFwieG1wcC5wcmVzZW5jZV9zdGF0dXNcIixcbiAgICBTVUJKRUNUX0NIQU5HRUQ6IFwieG1wcC5zdWJqZWN0X2NoYW5nZWRcIixcbiAgICBNRVNTQUdFX1JFQ0VJVkVEOiBcInhtcHAubWVzc2FnZV9yZWNlaXZlZFwiLFxuICAgIFNFTkRJTkdfQ0hBVF9NRVNTQUdFOiBcInhtcHAuc2VuZGluZ19jaGF0X21lc3NhZ2VcIixcbiAgICBQQVNTV09SRF9SRVFVSVJFRDogXCJ4bXBwLnBhc3N3b3JkX3JlcXVpcmVkXCIsXG4gICAgQVVUSEVOVElDQVRJT05fUkVRVUlSRUQ6IFwieG1wcC5hdXRoZW50aWNhdGlvbl9yZXF1aXJlZFwiLFxuICAgIENIQVRfRVJST1JfUkVDRUlWRUQ6IFwieG1wcC5jaGF0X2Vycm9yX3JlY2VpdmVkXCIsXG4gICAgRVRIRVJQQUQ6IFwieG1wcC5ldGhlcnBhZFwiXG59O1xubW9kdWxlLmV4cG9ydHMgPSBYTVBQRXZlbnRzOyIsIi8vIENvcHlyaWdodCBKb3llbnQsIEluYy4gYW5kIG90aGVyIE5vZGUgY29udHJpYnV0b3JzLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhXG4vLyBjb3B5IG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlXG4vLyBcIlNvZnR3YXJlXCIpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmdcbi8vIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCxcbi8vIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXRcbi8vIHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZVxuLy8gZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4vL1xuLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWRcbi8vIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1Ncbi8vIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0Zcbi8vIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU5cbi8vIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLFxuLy8gREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SXG4vLyBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFXG4vLyBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLlxuXG5mdW5jdGlvbiBFdmVudEVtaXR0ZXIoKSB7XG4gIHRoaXMuX2V2ZW50cyA9IHRoaXMuX2V2ZW50cyB8fCB7fTtcbiAgdGhpcy5fbWF4TGlzdGVuZXJzID0gdGhpcy5fbWF4TGlzdGVuZXJzIHx8IHVuZGVmaW5lZDtcbn1cbm1vZHVsZS5leHBvcnRzID0gRXZlbnRFbWl0dGVyO1xuXG4vLyBCYWNrd2FyZHMtY29tcGF0IHdpdGggbm9kZSAwLjEwLnhcbkV2ZW50RW1pdHRlci5FdmVudEVtaXR0ZXIgPSBFdmVudEVtaXR0ZXI7XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuX2V2ZW50cyA9IHVuZGVmaW5lZDtcbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuX21heExpc3RlbmVycyA9IHVuZGVmaW5lZDtcblxuLy8gQnkgZGVmYXVsdCBFdmVudEVtaXR0ZXJzIHdpbGwgcHJpbnQgYSB3YXJuaW5nIGlmIG1vcmUgdGhhbiAxMCBsaXN0ZW5lcnMgYXJlXG4vLyBhZGRlZCB0byBpdC4gVGhpcyBpcyBhIHVzZWZ1bCBkZWZhdWx0IHdoaWNoIGhlbHBzIGZpbmRpbmcgbWVtb3J5IGxlYWtzLlxuRXZlbnRFbWl0dGVyLmRlZmF1bHRNYXhMaXN0ZW5lcnMgPSAxMDtcblxuLy8gT2J2aW91c2x5IG5vdCBhbGwgRW1pdHRlcnMgc2hvdWxkIGJlIGxpbWl0ZWQgdG8gMTAuIFRoaXMgZnVuY3Rpb24gYWxsb3dzXG4vLyB0aGF0IHRvIGJlIGluY3JlYXNlZC4gU2V0IHRvIHplcm8gZm9yIHVubGltaXRlZC5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuc2V0TWF4TGlzdGVuZXJzID0gZnVuY3Rpb24obikge1xuICBpZiAoIWlzTnVtYmVyKG4pIHx8IG4gPCAwIHx8IGlzTmFOKG4pKVxuICAgIHRocm93IFR5cGVFcnJvcignbiBtdXN0IGJlIGEgcG9zaXRpdmUgbnVtYmVyJyk7XG4gIHRoaXMuX21heExpc3RlbmVycyA9IG47XG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24odHlwZSkge1xuICB2YXIgZXIsIGhhbmRsZXIsIGxlbiwgYXJncywgaSwgbGlzdGVuZXJzO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzKVxuICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuXG4gIC8vIElmIHRoZXJlIGlzIG5vICdlcnJvcicgZXZlbnQgbGlzdGVuZXIgdGhlbiB0aHJvdy5cbiAgaWYgKHR5cGUgPT09ICdlcnJvcicpIHtcbiAgICBpZiAoIXRoaXMuX2V2ZW50cy5lcnJvciB8fFxuICAgICAgICAoaXNPYmplY3QodGhpcy5fZXZlbnRzLmVycm9yKSAmJiAhdGhpcy5fZXZlbnRzLmVycm9yLmxlbmd0aCkpIHtcbiAgICAgIGVyID0gYXJndW1lbnRzWzFdO1xuICAgICAgaWYgKGVyIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgdGhyb3cgZXI7IC8vIFVuaGFuZGxlZCAnZXJyb3InIGV2ZW50XG4gICAgICB9XG4gICAgICB0aHJvdyBUeXBlRXJyb3IoJ1VuY2F1Z2h0LCB1bnNwZWNpZmllZCBcImVycm9yXCIgZXZlbnQuJyk7XG4gICAgfVxuICB9XG5cbiAgaGFuZGxlciA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcblxuICBpZiAoaXNVbmRlZmluZWQoaGFuZGxlcikpXG4gICAgcmV0dXJuIGZhbHNlO1xuXG4gIGlmIChpc0Z1bmN0aW9uKGhhbmRsZXIpKSB7XG4gICAgc3dpdGNoIChhcmd1bWVudHMubGVuZ3RoKSB7XG4gICAgICAvLyBmYXN0IGNhc2VzXG4gICAgICBjYXNlIDE6XG4gICAgICAgIGhhbmRsZXIuY2FsbCh0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIGhhbmRsZXIuY2FsbCh0aGlzLCBhcmd1bWVudHNbMV0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMsIGFyZ3VtZW50c1sxXSwgYXJndW1lbnRzWzJdKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICAvLyBzbG93ZXJcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgICAgIGFyZ3MgPSBuZXcgQXJyYXkobGVuIC0gMSk7XG4gICAgICAgIGZvciAoaSA9IDE7IGkgPCBsZW47IGkrKylcbiAgICAgICAgICBhcmdzW2kgLSAxXSA9IGFyZ3VtZW50c1tpXTtcbiAgICAgICAgaGFuZGxlci5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoaXNPYmplY3QoaGFuZGxlcikpIHtcbiAgICBsZW4gPSBhcmd1bWVudHMubGVuZ3RoO1xuICAgIGFyZ3MgPSBuZXcgQXJyYXkobGVuIC0gMSk7XG4gICAgZm9yIChpID0gMTsgaSA8IGxlbjsgaSsrKVxuICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG5cbiAgICBsaXN0ZW5lcnMgPSBoYW5kbGVyLnNsaWNlKCk7XG4gICAgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDtcbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspXG4gICAgICBsaXN0ZW5lcnNbaV0uYXBwbHkodGhpcywgYXJncyk7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUuYWRkTGlzdGVuZXIgPSBmdW5jdGlvbih0eXBlLCBsaXN0ZW5lcikge1xuICB2YXIgbTtcblxuICBpZiAoIWlzRnVuY3Rpb24obGlzdGVuZXIpKVxuICAgIHRocm93IFR5cGVFcnJvcignbGlzdGVuZXIgbXVzdCBiZSBhIGZ1bmN0aW9uJyk7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMpXG4gICAgdGhpcy5fZXZlbnRzID0ge307XG5cbiAgLy8gVG8gYXZvaWQgcmVjdXJzaW9uIGluIHRoZSBjYXNlIHRoYXQgdHlwZSA9PT0gXCJuZXdMaXN0ZW5lclwiISBCZWZvcmVcbiAgLy8gYWRkaW5nIGl0IHRvIHRoZSBsaXN0ZW5lcnMsIGZpcnN0IGVtaXQgXCJuZXdMaXN0ZW5lclwiLlxuICBpZiAodGhpcy5fZXZlbnRzLm5ld0xpc3RlbmVyKVxuICAgIHRoaXMuZW1pdCgnbmV3TGlzdGVuZXInLCB0eXBlLFxuICAgICAgICAgICAgICBpc0Z1bmN0aW9uKGxpc3RlbmVyLmxpc3RlbmVyKSA/XG4gICAgICAgICAgICAgIGxpc3RlbmVyLmxpc3RlbmVyIDogbGlzdGVuZXIpO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzW3R5cGVdKVxuICAgIC8vIE9wdGltaXplIHRoZSBjYXNlIG9mIG9uZSBsaXN0ZW5lci4gRG9uJ3QgbmVlZCB0aGUgZXh0cmEgYXJyYXkgb2JqZWN0LlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IGxpc3RlbmVyO1xuICBlbHNlIGlmIChpc09iamVjdCh0aGlzLl9ldmVudHNbdHlwZV0pKVxuICAgIC8vIElmIHdlJ3ZlIGFscmVhZHkgZ290IGFuIGFycmF5LCBqdXN0IGFwcGVuZC5cbiAgICB0aGlzLl9ldmVudHNbdHlwZV0ucHVzaChsaXN0ZW5lcik7XG4gIGVsc2VcbiAgICAvLyBBZGRpbmcgdGhlIHNlY29uZCBlbGVtZW50LCBuZWVkIHRvIGNoYW5nZSB0byBhcnJheS5cbiAgICB0aGlzLl9ldmVudHNbdHlwZV0gPSBbdGhpcy5fZXZlbnRzW3R5cGVdLCBsaXN0ZW5lcl07XG5cbiAgLy8gQ2hlY2sgZm9yIGxpc3RlbmVyIGxlYWtcbiAgaWYgKGlzT2JqZWN0KHRoaXMuX2V2ZW50c1t0eXBlXSkgJiYgIXRoaXMuX2V2ZW50c1t0eXBlXS53YXJuZWQpIHtcbiAgICB2YXIgbTtcbiAgICBpZiAoIWlzVW5kZWZpbmVkKHRoaXMuX21heExpc3RlbmVycykpIHtcbiAgICAgIG0gPSB0aGlzLl9tYXhMaXN0ZW5lcnM7XG4gICAgfSBlbHNlIHtcbiAgICAgIG0gPSBFdmVudEVtaXR0ZXIuZGVmYXVsdE1heExpc3RlbmVycztcbiAgICB9XG5cbiAgICBpZiAobSAmJiBtID4gMCAmJiB0aGlzLl9ldmVudHNbdHlwZV0ubGVuZ3RoID4gbSkge1xuICAgICAgdGhpcy5fZXZlbnRzW3R5cGVdLndhcm5lZCA9IHRydWU7XG4gICAgICBjb25zb2xlLmVycm9yKCcobm9kZSkgd2FybmluZzogcG9zc2libGUgRXZlbnRFbWl0dGVyIG1lbW9yeSAnICtcbiAgICAgICAgICAgICAgICAgICAgJ2xlYWsgZGV0ZWN0ZWQuICVkIGxpc3RlbmVycyBhZGRlZC4gJyArXG4gICAgICAgICAgICAgICAgICAgICdVc2UgZW1pdHRlci5zZXRNYXhMaXN0ZW5lcnMoKSB0byBpbmNyZWFzZSBsaW1pdC4nLFxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9ldmVudHNbdHlwZV0ubGVuZ3RoKTtcbiAgICAgIGlmICh0eXBlb2YgY29uc29sZS50cmFjZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAvLyBub3Qgc3VwcG9ydGVkIGluIElFIDEwXG4gICAgICAgIGNvbnNvbGUudHJhY2UoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub24gPSBFdmVudEVtaXR0ZXIucHJvdG90eXBlLmFkZExpc3RlbmVyO1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLm9uY2UgPSBmdW5jdGlvbih0eXBlLCBsaXN0ZW5lcikge1xuICBpZiAoIWlzRnVuY3Rpb24obGlzdGVuZXIpKVxuICAgIHRocm93IFR5cGVFcnJvcignbGlzdGVuZXIgbXVzdCBiZSBhIGZ1bmN0aW9uJyk7XG5cbiAgdmFyIGZpcmVkID0gZmFsc2U7XG5cbiAgZnVuY3Rpb24gZygpIHtcbiAgICB0aGlzLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGcpO1xuXG4gICAgaWYgKCFmaXJlZCkge1xuICAgICAgZmlyZWQgPSB0cnVlO1xuICAgICAgbGlzdGVuZXIuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9XG4gIH1cblxuICBnLmxpc3RlbmVyID0gbGlzdGVuZXI7XG4gIHRoaXMub24odHlwZSwgZyk7XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vLyBlbWl0cyBhICdyZW1vdmVMaXN0ZW5lcicgZXZlbnQgaWZmIHRoZSBsaXN0ZW5lciB3YXMgcmVtb3ZlZFxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIHZhciBsaXN0LCBwb3NpdGlvbiwgbGVuZ3RoLCBpO1xuXG4gIGlmICghaXNGdW5jdGlvbihsaXN0ZW5lcikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCdsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcblxuICBpZiAoIXRoaXMuX2V2ZW50cyB8fCAhdGhpcy5fZXZlbnRzW3R5cGVdKVxuICAgIHJldHVybiB0aGlzO1xuXG4gIGxpc3QgPSB0aGlzLl9ldmVudHNbdHlwZV07XG4gIGxlbmd0aCA9IGxpc3QubGVuZ3RoO1xuICBwb3NpdGlvbiA9IC0xO1xuXG4gIGlmIChsaXN0ID09PSBsaXN0ZW5lciB8fFxuICAgICAgKGlzRnVuY3Rpb24obGlzdC5saXN0ZW5lcikgJiYgbGlzdC5saXN0ZW5lciA9PT0gbGlzdGVuZXIpKSB7XG4gICAgZGVsZXRlIHRoaXMuX2V2ZW50c1t0eXBlXTtcbiAgICBpZiAodGhpcy5fZXZlbnRzLnJlbW92ZUxpc3RlbmVyKVxuICAgICAgdGhpcy5lbWl0KCdyZW1vdmVMaXN0ZW5lcicsIHR5cGUsIGxpc3RlbmVyKTtcblxuICB9IGVsc2UgaWYgKGlzT2JqZWN0KGxpc3QpKSB7XG4gICAgZm9yIChpID0gbGVuZ3RoOyBpLS0gPiAwOykge1xuICAgICAgaWYgKGxpc3RbaV0gPT09IGxpc3RlbmVyIHx8XG4gICAgICAgICAgKGxpc3RbaV0ubGlzdGVuZXIgJiYgbGlzdFtpXS5saXN0ZW5lciA9PT0gbGlzdGVuZXIpKSB7XG4gICAgICAgIHBvc2l0aW9uID0gaTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHBvc2l0aW9uIDwgMClcbiAgICAgIHJldHVybiB0aGlzO1xuXG4gICAgaWYgKGxpc3QubGVuZ3RoID09PSAxKSB7XG4gICAgICBsaXN0Lmxlbmd0aCA9IDA7XG4gICAgICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuICAgIH0gZWxzZSB7XG4gICAgICBsaXN0LnNwbGljZShwb3NpdGlvbiwgMSk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX2V2ZW50cy5yZW1vdmVMaXN0ZW5lcilcbiAgICAgIHRoaXMuZW1pdCgncmVtb3ZlTGlzdGVuZXInLCB0eXBlLCBsaXN0ZW5lcik7XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlQWxsTGlzdGVuZXJzID0gZnVuY3Rpb24odHlwZSkge1xuICB2YXIga2V5LCBsaXN0ZW5lcnM7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMpXG4gICAgcmV0dXJuIHRoaXM7XG5cbiAgLy8gbm90IGxpc3RlbmluZyBmb3IgcmVtb3ZlTGlzdGVuZXIsIG5vIG5lZWQgdG8gZW1pdFxuICBpZiAoIXRoaXMuX2V2ZW50cy5yZW1vdmVMaXN0ZW5lcikge1xuICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKVxuICAgICAgdGhpcy5fZXZlbnRzID0ge307XG4gICAgZWxzZSBpZiAodGhpcy5fZXZlbnRzW3R5cGVdKVxuICAgICAgZGVsZXRlIHRoaXMuX2V2ZW50c1t0eXBlXTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8vIGVtaXQgcmVtb3ZlTGlzdGVuZXIgZm9yIGFsbCBsaXN0ZW5lcnMgb24gYWxsIGV2ZW50c1xuICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgIGZvciAoa2V5IGluIHRoaXMuX2V2ZW50cykge1xuICAgICAgaWYgKGtleSA9PT0gJ3JlbW92ZUxpc3RlbmVyJykgY29udGludWU7XG4gICAgICB0aGlzLnJlbW92ZUFsbExpc3RlbmVycyhrZXkpO1xuICAgIH1cbiAgICB0aGlzLnJlbW92ZUFsbExpc3RlbmVycygncmVtb3ZlTGlzdGVuZXInKTtcbiAgICB0aGlzLl9ldmVudHMgPSB7fTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcblxuICBpZiAoaXNGdW5jdGlvbihsaXN0ZW5lcnMpKSB7XG4gICAgdGhpcy5yZW1vdmVMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lcnMpO1xuICB9IGVsc2Uge1xuICAgIC8vIExJRk8gb3JkZXJcbiAgICB3aGlsZSAobGlzdGVuZXJzLmxlbmd0aClcbiAgICAgIHRoaXMucmVtb3ZlTGlzdGVuZXIodHlwZSwgbGlzdGVuZXJzW2xpc3RlbmVycy5sZW5ndGggLSAxXSk7XG4gIH1cbiAgZGVsZXRlIHRoaXMuX2V2ZW50c1t0eXBlXTtcblxuICByZXR1cm4gdGhpcztcbn07XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUubGlzdGVuZXJzID0gZnVuY3Rpb24odHlwZSkge1xuICB2YXIgcmV0O1xuICBpZiAoIXRoaXMuX2V2ZW50cyB8fCAhdGhpcy5fZXZlbnRzW3R5cGVdKVxuICAgIHJldCA9IFtdO1xuICBlbHNlIGlmIChpc0Z1bmN0aW9uKHRoaXMuX2V2ZW50c1t0eXBlXSkpXG4gICAgcmV0ID0gW3RoaXMuX2V2ZW50c1t0eXBlXV07XG4gIGVsc2VcbiAgICByZXQgPSB0aGlzLl9ldmVudHNbdHlwZV0uc2xpY2UoKTtcbiAgcmV0dXJuIHJldDtcbn07XG5cbkV2ZW50RW1pdHRlci5saXN0ZW5lckNvdW50ID0gZnVuY3Rpb24oZW1pdHRlciwgdHlwZSkge1xuICB2YXIgcmV0O1xuICBpZiAoIWVtaXR0ZXIuX2V2ZW50cyB8fCAhZW1pdHRlci5fZXZlbnRzW3R5cGVdKVxuICAgIHJldCA9IDA7XG4gIGVsc2UgaWYgKGlzRnVuY3Rpb24oZW1pdHRlci5fZXZlbnRzW3R5cGVdKSlcbiAgICByZXQgPSAxO1xuICBlbHNlXG4gICAgcmV0ID0gZW1pdHRlci5fZXZlbnRzW3R5cGVdLmxlbmd0aDtcbiAgcmV0dXJuIHJldDtcbn07XG5cbmZ1bmN0aW9uIGlzRnVuY3Rpb24oYXJnKSB7XG4gIHJldHVybiB0eXBlb2YgYXJnID09PSAnZnVuY3Rpb24nO1xufVxuXG5mdW5jdGlvbiBpc051bWJlcihhcmcpIHtcbiAgcmV0dXJuIHR5cGVvZiBhcmcgPT09ICdudW1iZXInO1xufVxuXG5mdW5jdGlvbiBpc09iamVjdChhcmcpIHtcbiAgcmV0dXJuIHR5cGVvZiBhcmcgPT09ICdvYmplY3QnICYmIGFyZyAhPT0gbnVsbDtcbn1cblxuZnVuY3Rpb24gaXNVbmRlZmluZWQoYXJnKSB7XG4gIHJldHVybiBhcmcgPT09IHZvaWQgMDtcbn1cbiJdfQ==