/*! Hammer.JS - v2.0.8 - 2016-04-23
 * http://hammerjs.github.io/
 *
 * Copyright (c) 2016 Jorik Tangelder;
 * Licensed under the MIT license */
!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e<a.length;)b.call(c,a[e],e,a),e++;else for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)}function h(b,c,d){var e="DEPRECATED METHOD: "+c+"\n"+d+" AT \n";return function(){var c=new Error("get-stack-trace"),d=c&&c.stack?c.stack.replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;d<a.length;){if(c&&a[d][c]==b||!c&&a[d]===b)return d;d++}return-1}function s(a){return Array.prototype.slice.call(a,0)}function t(a,b,c){for(var d=[],e=[],f=0;f<a.length;){var g=b?a[f][b]:a[f];r(e,g)<0&&d.push(a[f]),e[f]=g,f++}return c&&(d=b?d.sort(function(a,c){return a[b]>c[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g<ma.length;){if(c=ma[g],e=c?c+f:b,e in a)return e;g++}return d}function v(){return ua++}function w(b){var c=b.ownerDocument||b;return c.defaultView||c.parentWindow||a}function x(a,b){var c=this;this.manager=a,this.callback=b,this.element=a.element,this.target=a.options.inputTarget,this.domHandler=function(b){k(a.options.enable,[a])&&c.handler(b)},this.init()}function y(a){var b,c=a.options.inputClass;return new(b=c?c:xa?M:ya?P:wa?R:L)(a,z)}function z(a,b,c){var d=c.pointers.length,e=c.changedPointers.length,f=b&Ea&&d-e===0,g=b&(Ga|Ha)&&d-e===0;c.isFirst=!!f,c.isFinal=!!g,f&&(a.session={}),c.eventType=b,A(a,c),a.emit("hammer.input",c),a.recognize(c),a.session.prevInput=c}function A(a,b){var c=a.session,d=b.pointers,e=d.length;c.firstInput||(c.firstInput=D(b)),e>1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;c<a.pointers.length;)b[c]={clientX:pa(a.pointers[c].clientX),clientY:pa(a.pointers[c].clientY)},c++;return{timeStamp:ra(),pointers:b,center:E(b),deltaX:a.deltaX,deltaY:a.deltaY}}function E(a){var b=a.length;if(1===b)return{x:pa(a[0].clientX),y:pa(a[0].clientY)};for(var c=0,d=0,e=0;b>e;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e<f.length;)d[f[e].identifier]=!0,e++;for(e=0;e<g.length;)d[g[e].identifier]&&h.push(g[e]),b&(Ga|Ha)&&delete d[g[e].identifier],e++;return h.length?[t(f.concat(h),"identifier",!0),h]:void 0}function R(){x.apply(this,arguments);var a=j(this.handler,this);this.touch=new P(this.manager,a),this.mouse=new L(this.manager,a),this.primaryTouch=null,this.lastTouches=[]}function S(a,b){a&Ea?(this.primaryTouch=b.changedPointers[0].identifier,T.call(this,b)):a&(Ga|Ha)&&T.call(this,b)}function T(a){var b=a.changedPointers[0];if(b.identifier===this.primaryTouch){var c={x:b.clientX,y:b.clientY};this.lastTouches.push(c);var d=this.lastTouches,e=function(){var a=d.indexOf(c);a>-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d<this.lastTouches.length;d++){var e=this.lastTouches[d],f=Math.abs(b-e.x),g=Math.abs(c-e.y);if(db>=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;c<arguments.length;c++){var e=arguments[c];if(e!==d&&null!==e)for(var f in e)e.hasOwnProperty(f)&&(b[f]=e[f])}return b}:Object.assign;var sa=h(function(a,b,c){for(var e=Object.keys(b),f=0;f<e.length;)(!c||c&&a[e[f]]===d)&&(a[e[f]]=b[e[f]]),f++;return a},"extend","Use `assign`."),ta=h(function(a,b){return sa(a,b,!0)},"merge","Use `assign`."),ua=1,va=/mobile|tablet|ip(ad|hone|od)|android/i,wa="ontouchstart"in a,xa=u(a,"PointerEvent")!==d,ya=wa&&va.test(navigator.userAgent),za="touch",Aa="pen",Ba="mouse",Ca="kinect",Da=25,Ea=1,Fa=2,Ga=4,Ha=8,Ia=1,Ja=2,Ka=4,La=8,Ma=16,Na=Ja|Ka,Oa=La|Ma,Pa=Na|Oa,Qa=["x","y"],Ra=["clientX","clientY"];x.prototype={handler:function(){},init:function(){this.evEl&&m(this.element,this.evEl,this.domHandler),this.evTarget&&m(this.target,this.evTarget,this.domHandler),this.evWin&&m(w(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&n(this.element,this.evEl,this.domHandler),this.evTarget&&n(this.target,this.evTarget,this.domHandler),this.evWin&&n(w(this.element),this.evWin,this.domHandler)}};var Sa={mousedown:Ea,mousemove:Fa,mouseup:Ga},Ta="mousedown",Ua="mousemove mouseup";i(L,x,{handler:function(a){var b=Sa[a.type];b&Ea&&0===a.button&&(this.pressed=!0),b&Fa&&1!==a.which&&(b=Ga),this.pressed&&(b&Ga&&(this.pressed=!1),this.callback(this.manager,b,{pointers:[a],changedPointers:[a],pointerType:Ba,srcEvent:a}))}});var Va={pointerdown:Ea,pointermove:Fa,pointerup:Ga,pointercancel:Ha,pointerout:Ha},Wa={2:za,3:Aa,4:Ba,5:Ca},Xa="pointerdown",Ya="pointermove pointerup pointercancel";a.MSPointerEvent&&!a.PointerEvent&&(Xa="MSPointerDown",Ya="MSPointerMove MSPointerUp MSPointerCancel"),i(M,x,{handler:function(a){var b=this.store,c=!1,d=a.type.toLowerCase().replace("ms",""),e=Va[d],f=Wa[a.pointerType]||a.pointerType,g=f==za,h=r(b,a.pointerId,"pointerId");e&Ea&&(0===a.button||g)?0>h&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;a<this.requireFail.length;){if(!(this.requireFail[a].state&(tb|nb)))return!1;a++}return!0},recognize:function(a){var b=la({},a);return k(this.options.enable,[this,b])?(this.state&(rb|sb|tb)&&(this.state=nb),this.state=this.process(b),void(this.state&(ob|pb|qb|sb)&&this.tryEmit(b))):(this.reset(),void(this.state=tb))},process:function(a){},getTouchAction:function(){},reset:function(){}},i(aa,Y,{defaults:{pointers:1},attrTest:function(a){var b=this.options.pointers;return 0===b||a.pointers.length===b},process:function(a){var b=this.state,c=a.eventType,d=b&(ob|pb),e=this.attrTest(a);return d&&(c&Ha||!e)?b|sb:d||e?c&Ga?b|qb:b&ob?b|pb:ob:tb}}),i(ba,aa,{defaults:{event:"pan",threshold:10,pointers:1,direction:Pa},getTouchAction:function(){var a=this.options.direction,b=[];return a&Na&&b.push(lb),a&Oa&&b.push(kb),b},directionTest:function(a){var b=this.options,c=!0,d=a.distance,e=a.direction,f=a.deltaX,g=a.deltaY;return e&b.direction||(b.direction&Na?(e=0===f?Ia:0>f?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime>b.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime<b.time;if(this.reset(),a.eventType&Ea&&0===this.count)return this.failTimeout();if(d&&f&&c){if(a.eventType!=Ga)return this.failTimeout();var g=this.pTime?a.timeStamp-this.pTime<b.interval:!0,h=!this.pCenter||H(this.pCenter,a.center)<b.posThreshold;this.pTime=a.timeStamp,this.pCenter=a.center,h&&g?this.count+=1:this.count=1,this._input=a;var i=this.count%b.taps;if(0===i)return this.hasRequireFailures()?(this._timer=e(function(){this.state=rb,this.tryEmit()},b.interval,this),ob):rb}return tb},failTimeout:function(){return this._timer=e(function(){this.state=tb},this.options.interval,this),tb},reset:function(){clearTimeout(this._timer)},emit:function(){this.state==rb&&(this._input.tapCount=this.count,this.manager.emit(this.options.event,this._input))}}),ha.VERSION="2.0.8",ha.defaults={domEvents:!1,touchAction:gb,enable:!0,inputTarget:null,inputClass:null,preset:[[ea,{enable:!1}],[ca,{enable:!1},["rotate"]],[fa,{direction:Na}],[ba,{direction:Na},["swipe"]],[ga],[ga,{event:"doubletap",taps:2},["tap"]],[da]],cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}};var ub=1,vb=2;ia.prototype={set:function(a){return la(this.options,a),a.touchAction&&this.touchAction.update(),a.inputTarget&&(this.input.destroy(),this.input.target=a.inputTarget,this.input.init()),this},stop:function(a){this.session.stopped=a?vb:ub},recognize:function(a){var b=this.session;if(!b.stopped){this.touchAction.preventDefaults(a);var c,d=this.recognizers,e=b.curRecognizer;(!e||e&&e.state&rb)&&(e=b.curRecognizer=null);for(var f=0;f<d.length;)c=d[f],b.stopped===vb||e&&c!=e&&!c.canRecognizeWith(e)?c.reset():c.recognize(a),!e&&c.state&(ob|pb|qb)&&(e=b.curRecognizer=c),f++}},get:function(a){if(a instanceof Y)return a;for(var b=this.recognizers,c=0;c<b.length;c++)if(b[c].options.event==a)return b[c];return null},add:function(a){if(f(a,"add",this))return this;var b=this.get(a.options.event);return b&&this.remove(b),this.recognizers.push(a),a.manager=this,this.touchAction.update(),a},remove:function(a){if(f(a,"remove",this))return this;if(a=this.get(a)){var b=this.recognizers,c=r(b,a);-1!==c&&(b.splice(c,1),this.touchAction.update())}return this},on:function(a,b){if(a!==d&&b!==d){var c=this.handlers;return g(q(a),function(a){c[a]=c[a]||[],c[a].push(b)}),this}},off:function(a,b){if(a!==d){var c=this.handlers;return g(q(a),function(a){b?c[a]&&c[a].splice(r(c[a],b),1):delete c[a]}),this}},emit:function(a,b){this.options.domEvents&&ka(a,b);var c=this.handlers[a]&&this.handlers[a].slice();if(c&&c.length){b.type=a,b.preventDefault=function(){b.srcEvent.preventDefault()};for(var d=0;d<c.length;)c[d](b),d++}},destroy:function(){this.element&&ja(this,!1),this.handlers={},this.session={},this.input.destroy(),this.element=null}},la(ha,{INPUT_START:Ea,INPUT_MOVE:Fa,INPUT_END:Ga,INPUT_CANCEL:Ha,STATE_POSSIBLE:nb,STATE_BEGAN:ob,STATE_CHANGED:pb,STATE_ENDED:qb,STATE_RECOGNIZED:rb,STATE_CANCELLED:sb,STATE_FAILED:tb,DIRECTION_NONE:Ia,DIRECTION_LEFT:Ja,DIRECTION_RIGHT:Ka,DIRECTION_UP:La,DIRECTION_DOWN:Ma,DIRECTION_HORIZONTAL:Na,DIRECTION_VERTICAL:Oa,DIRECTION_ALL:Pa,Manager:ia,Input:x,TouchAction:V,TouchInput:P,MouseInput:L,PointerEventInput:M,TouchMouseInput:R,SingleTouchInput:N,Recognizer:Y,AttrRecognizer:aa,Tap:ga,Pan:ba,Swipe:fa,Pinch:ca,Rotate:ea,Press:da,on:m,off:n,each:g,merge:ta,extend:sa,assign:la,inherit:i,bindFn:j,prefixed:u});var wb="undefined"!=typeof a?a:"undefined"!=typeof self?self:{};wb.Hammer=ha,"function"==typeof define&&define.amd?define(function(){return ha}):"undefined"!=typeof module&&module.exports?module.exports=ha:a[c]=ha}(window,document,"Hammer");

// svg-pan-zoom v3.6.0 modified by scorpin46
// https://github.com/ariutta/svg-pan-zoom
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var svgPanZoom = require('./svg-pan-zoom.js');

// UMD module definition
(function(window, document){
  // AMD
  if (typeof define === 'function' && define.amd) {
    define('svg-pan-zoom', function () {
      return svgPanZoom;
    });
  // CMD
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = svgPanZoom;

    // Browser
    // Keep exporting globally as module.exports is available because of browserify
    window.svgPanZoom = svgPanZoom;
  }
})(window, document)

},{"./svg-pan-zoom.js":4}],2:[function(require,module,exports){
var SvgUtils = require('./svg-utilities');
        module.exports = {
  enable: function(instance) {
    // Select (and create if necessary) defs
    var defs = instance.svg.querySelector('defs')
    if (!defs) {
      defs = document.createElementNS(SvgUtils.svgNS, 'defs')
      instance.svg.appendChild(defs)
    }

    // Check for style element, and create it if it doesn't exist
    var styleEl = defs.querySelector('style#svg-pan-zoom-controls-styles');
    if (!styleEl) {
      var style = document.createElementNS(SvgUtils.svgNS, 'style')
      style.setAttribute('id', 'svg-pan-zoom-controls-styles')
      style.setAttribute('type', 'text/css')
      style.textContent = '.svg-pan-zoom-control { cursor: pointer; fill: black; fill-opacity: 0.333; } .svg-pan-zoom-control:hover { fill-opacity: 0.8; } .svg-pan-zoom-control-background { fill: white; fill-opacity: 0.5; } .svg-pan-zoom-control-background { fill-opacity: 0.8; }'
      defs.appendChild(style)
    }

    // Zoom Group
    var zoomGroup = document.createElementNS(SvgUtils.svgNS, 'g');
    zoomGroup.setAttribute('id', 'svg-pan-zoom-controls');
    zoomGroup.setAttribute('transform', 'translate(' + ( instance.width - 70 ) + ' ' + ( instance.height - 76 ) + ') scale(0.75)');
    zoomGroup.setAttribute('class', 'svg-pan-zoom-control');

    // Control elements
    zoomGroup.appendChild(this._createZoomIn(instance))
    zoomGroup.appendChild(this._createZoomReset(instance))
    zoomGroup.appendChild(this._createZoomOut(instance))

    // Finally append created element
    instance.svg.appendChild(zoomGroup)

    // Cache control instance
    instance.controlIcons = zoomGroup
  }

, _createZoomIn: function(instance) {
    var zoomIn = document.createElementNS(SvgUtils.svgNS, 'g');
    zoomIn.setAttribute('id', 'svg-pan-zoom-zoom-in');
    zoomIn.setAttribute('transform', 'translate(30.5 5) scale(0.015)');
    zoomIn.setAttribute('class', 'svg-pan-zoom-control');
    zoomIn.addEventListener('click', function() {instance.getPublicInstance().zoomIn()}, false)
    zoomIn.addEventListener('touchstart', function() {instance.getPublicInstance().zoomIn()}, false)

    var zoomInBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
    zoomInBackground.setAttribute('x', '0');
    zoomInBackground.setAttribute('y', '0');
    zoomInBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
    zoomInBackground.setAttribute('height', '1400');
    zoomInBackground.setAttribute('class', 'svg-pan-zoom-control-background');
    zoomIn.appendChild(zoomInBackground);

    var zoomInShape = document.createElementNS(SvgUtils.svgNS, 'path');
    zoomInShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z');
    zoomInShape.setAttribute('class', 'svg-pan-zoom-control-element');
    zoomIn.appendChild(zoomInShape);

    return zoomIn
  }

, _createZoomReset: function(instance){
    // reset
    var resetPanZoomControl = document.createElementNS(SvgUtils.svgNS, 'g');
    resetPanZoomControl.setAttribute('id', 'svg-pan-zoom-reset-pan-zoom');
    resetPanZoomControl.setAttribute('transform', 'translate(5 35) scale(0.4)');
    resetPanZoomControl.setAttribute('class', 'svg-pan-zoom-control');
    resetPanZoomControl.addEventListener('click', function() {instance.getPublicInstance().reset()}, false);
    resetPanZoomControl.addEventListener('touchstart', function() {instance.getPublicInstance().reset()}, false);

    var resetPanZoomControlBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
    resetPanZoomControlBackground.setAttribute('x', '2');
    resetPanZoomControlBackground.setAttribute('y', '2');
    resetPanZoomControlBackground.setAttribute('width', '182'); // larger than expected because the whole group is transformed to scale down
    resetPanZoomControlBackground.setAttribute('height', '58');
    resetPanZoomControlBackground.setAttribute('class', 'svg-pan-zoom-control-background');
    resetPanZoomControl.appendChild(resetPanZoomControlBackground);

    var resetPanZoomControlShape1 = document.createElementNS(SvgUtils.svgNS, 'path');
    resetPanZoomControlShape1.setAttribute('d', 'M33.051,20.632c-0.742-0.406-1.854-0.609-3.338-0.609h-7.969v9.281h7.769c1.543,0,2.701-0.188,3.473-0.562c1.365-0.656,2.048-1.953,2.048-3.891C35.032,22.757,34.372,21.351,33.051,20.632z');
    resetPanZoomControlShape1.setAttribute('class', 'svg-pan-zoom-control-element');
    resetPanZoomControl.appendChild(resetPanZoomControlShape1);

    var resetPanZoomControlShape2 = document.createElementNS(SvgUtils.svgNS, 'path');
    resetPanZoomControlShape2.setAttribute('d', 'M170.231,0.5H15.847C7.102,0.5,0.5,5.708,0.5,11.84v38.861C0.5,56.833,7.102,61.5,15.847,61.5h154.384c8.745,0,15.269-4.667,15.269-10.798V11.84C185.5,5.708,178.976,0.5,170.231,0.5z M42.837,48.569h-7.969c-0.219-0.766-0.375-1.383-0.469-1.852c-0.188-0.969-0.289-1.961-0.305-2.977l-0.047-3.211c-0.03-2.203-0.41-3.672-1.142-4.406c-0.732-0.734-2.103-1.102-4.113-1.102h-7.05v13.547h-7.055V14.022h16.524c2.361,0.047,4.178,0.344,5.45,0.891c1.272,0.547,2.351,1.352,3.234,2.414c0.731,0.875,1.31,1.844,1.737,2.906s0.64,2.273,0.64,3.633c0,1.641-0.414,3.254-1.242,4.84s-2.195,2.707-4.102,3.363c1.594,0.641,2.723,1.551,3.387,2.73s0.996,2.98,0.996,5.402v2.32c0,1.578,0.063,2.648,0.19,3.211c0.19,0.891,0.635,1.547,1.333,1.969V48.569z M75.579,48.569h-26.18V14.022h25.336v6.117H56.454v7.336h16.781v6H56.454v8.883h19.125V48.569z M104.497,46.331c-2.44,2.086-5.887,3.129-10.34,3.129c-4.548,0-8.125-1.027-10.731-3.082s-3.909-4.879-3.909-8.473h6.891c0.224,1.578,0.662,2.758,1.316,3.539c1.196,1.422,3.246,2.133,6.15,2.133c1.739,0,3.151-0.188,4.236-0.562c2.058-0.719,3.087-2.055,3.087-4.008c0-1.141-0.504-2.023-1.512-2.648c-1.008-0.609-2.607-1.148-4.796-1.617l-3.74-0.82c-3.676-0.812-6.201-1.695-7.576-2.648c-2.328-1.594-3.492-4.086-3.492-7.477c0-3.094,1.139-5.664,3.417-7.711s5.623-3.07,10.036-3.07c3.685,0,6.829,0.965,9.431,2.895c2.602,1.93,3.966,4.73,4.093,8.402h-6.938c-0.128-2.078-1.057-3.555-2.787-4.43c-1.154-0.578-2.587-0.867-4.301-0.867c-1.907,0-3.428,0.375-4.565,1.125c-1.138,0.75-1.706,1.797-1.706,3.141c0,1.234,0.561,2.156,1.682,2.766c0.721,0.406,2.25,0.883,4.589,1.43l6.063,1.43c2.657,0.625,4.648,1.461,5.975,2.508c2.059,1.625,3.089,3.977,3.089,7.055C108.157,41.624,106.937,44.245,104.497,46.331z M139.61,48.569h-26.18V14.022h25.336v6.117h-18.281v7.336h16.781v6h-16.781v8.883h19.125V48.569z M170.337,20.14h-10.336v28.43h-7.266V20.14h-10.383v-6.117h27.984V20.14z');
    resetPanZoomControlShape2.setAttribute('class', 'svg-pan-zoom-control-element');
    resetPanZoomControl.appendChild(resetPanZoomControlShape2);

    return resetPanZoomControl
  }

, _createZoomOut: function(instance){
    // zoom out
    var zoomOut = document.createElementNS(SvgUtils.svgNS, 'g');
    zoomOut.setAttribute('id', 'svg-pan-zoom-zoom-out');
    zoomOut.setAttribute('transform', 'translate(30.5 70) scale(0.015)');
    zoomOut.setAttribute('class', 'svg-pan-zoom-control');
    zoomOut.addEventListener('click', function() {instance.getPublicInstance().zoomOut()}, false);
    zoomOut.addEventListener('touchstart', function() {instance.getPublicInstance().zoomOut()}, false);

    var zoomOutBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
    zoomOutBackground.setAttribute('x', '0');
    zoomOutBackground.setAttribute('y', '0');
    zoomOutBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
    zoomOutBackground.setAttribute('height', '1400');
    zoomOutBackground.setAttribute('class', 'svg-pan-zoom-control-background');
    zoomOut.appendChild(zoomOutBackground);

    var zoomOutShape = document.createElementNS(SvgUtils.svgNS, 'path');
    zoomOutShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z');
    zoomOutShape.setAttribute('class', 'svg-pan-zoom-control-element');
    zoomOut.appendChild(zoomOutShape);

    return zoomOut
  }

, disable: function(instance) {
    if (instance.controlIcons) {
      instance.controlIcons.parentNode.removeChild(instance.controlIcons)
      instance.controlIcons = null
    }
  }
}

},{"./svg-utilities":5}],3:[function(require,module,exports){
var SvgUtils = require('./svg-utilities')
  , Utils = require('./utilities')
  ;

var ShadowViewport = function(viewport, options){
  this.init(viewport, options)
}

/**
 * Initialization
 *
 * @param  {SVGElement} viewport
 * @param  {Object} options
 */
ShadowViewport.prototype.init = function(viewport, options) {
  // DOM Elements
  this.viewport = viewport
  this.options = options

  // State cache
  this.originalState = {zoom: 1, x: 0, y: 0}
  this.activeState = {zoom: 1, x: 0, y: 0}

  this.updateCTMCached = Utils.proxy(this.updateCTM, this)

  // Create a custom requestAnimationFrame taking in account refreshRate
  this.requestAnimationFrame = Utils.createRequestAnimationFrame(this.options.refreshRate)

  // ViewBox
  this.viewBox = {x: 0, y: 0, width: 0, height: 0}
  this.cacheViewBox()

  // Process CTM
  var newCTM = this.processCTM()

  // Update viewport CTM and cache zoom and pan
  this.setCTM(newCTM)

  // Update CTM in this frame
  this.updateCTM()
}

/**
 * Cache initial viewBox value
 * If no viewBox is defined, then use viewport size/position instead for viewBox values
 */
ShadowViewport.prototype.cacheViewBox = function() {
  var svgViewBox = this.options.svg.getAttribute('viewBox')

  if (svgViewBox) {
    var viewBoxValues = svgViewBox.split(/[\s\,]/).filter(function(v){return v}).map(parseFloat)

    // Cache viewbox x and y offset
    this.viewBox.x = viewBoxValues[0]
    this.viewBox.y = viewBoxValues[1]
    this.viewBox.width = viewBoxValues[2]
    this.viewBox.height = viewBoxValues[3]

    var zoom = Math.min(this.options.width / this.viewBox.width, this.options.height / this.viewBox.height)

    // Update active state
    this.activeState.zoom = zoom
    this.activeState.x = (this.options.width - this.viewBox.width * zoom) / 2
    this.activeState.y = (this.options.height - this.viewBox.height * zoom) / 2

    // Force updating CTM
    this.updateCTMOnNextFrame()

    this.options.svg.removeAttribute('viewBox')
  } else {
    this.simpleViewBoxCache()
  }
}

/**
 * Recalculate viewport sizes and update viewBox cache
 */
ShadowViewport.prototype.simpleViewBoxCache = function() {
  var bBox = this.viewport.getBBox()

  this.viewBox.x = bBox.x
  this.viewBox.y = bBox.y
  this.viewBox.width = bBox.width
  this.viewBox.height = bBox.height
}

/**
 * Returns a viewbox object. Safe to alter
 *
 * @return {Object} viewbox object
 */
ShadowViewport.prototype.getViewBox = function() {
  return Utils.extend({}, this.viewBox)
}

/**
 * Get initial zoom and pan values. Save them into originalState
 * Parses viewBox attribute to alter initial sizes
 *
 * @return {CTM} CTM object based on options
 */
ShadowViewport.prototype.processCTM = function() {
  var newCTM = this.getCTM()

  if (this.options.fit || this.options.contain) {
    var newScale;
    if (this.options.fit) {
      newScale = Math.min(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
    } else {
      newScale = Math.max(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
    }

    newCTM.a = newScale; //x-scale
    newCTM.d = newScale; //y-scale
    newCTM.e = -this.viewBox.x * newScale; //x-transform
    newCTM.f = -this.viewBox.y * newScale; //y-transform
  }

  if (this.options.center) {
    var offsetX = (this.options.width - (this.viewBox.width + this.viewBox.x * 2) * newCTM.a) * 0.5
      , offsetY = (this.options.height - (this.viewBox.height + this.viewBox.y * 2) * newCTM.a) * 0.5

    newCTM.e = offsetX
    newCTM.f = offsetY
  }

  // Cache initial values. Based on activeState and fix+center opitons
  this.originalState.zoom = newCTM.a
  this.originalState.x = newCTM.e
  this.originalState.y = newCTM.f

  return newCTM
}

/**
 * Return originalState object. Safe to alter
 *
 * @return {Object}
 */
ShadowViewport.prototype.getOriginalState = function() {
  return Utils.extend({}, this.originalState)
}

/**
 * Return actualState object. Safe to alter
 *
 * @return {Object}
 */
ShadowViewport.prototype.getState = function() {
  return Utils.extend({}, this.activeState)
}

/**
 * Get zoom scale
 *
 * @return {Float} zoom scale
 */
ShadowViewport.prototype.getZoom = function() {
  return this.activeState.zoom
}

/**
 * Get zoom scale for pubilc usage
 *
 * @return {Float} zoom scale
 */
ShadowViewport.prototype.getRelativeZoom = function() {
  return this.activeState.zoom / this.originalState.zoom
}

/**
 * Compute zoom scale for pubilc usage
 *
 * @return {Float} zoom scale
 */
ShadowViewport.prototype.computeRelativeZoom = function(scale) {
  return scale / this.originalState.zoom
}

/**
 * Get pan
 *
 * @return {Object}
 */
ShadowViewport.prototype.getPan = function() {
  return {x: this.activeState.x, y: this.activeState.y}
}

/**
 * Return cached viewport CTM value that can be safely modified
 *
 * @return {SVGMatrix}
 */
ShadowViewport.prototype.getCTM = function() {
  var safeCTM = this.options.svg.createSVGMatrix()

  // Copy values manually as in FF they are not itterable
  safeCTM.a = this.activeState.zoom
  safeCTM.b = 0
  safeCTM.c = 0
  safeCTM.d = this.activeState.zoom
  safeCTM.e = this.activeState.x
  safeCTM.f = this.activeState.y

  return safeCTM
}

/**
 * Set a new CTM
 *
 * @param {SVGMatrix} newCTM
 */
ShadowViewport.prototype.setCTM = function(newCTM) {
  var willZoom = this.isZoomDifferent(newCTM)
    , willPan = this.isPanDifferent(newCTM)

  if (willZoom || willPan) {
    // Before zoom
    if (willZoom) {
      // If returns false then cancel zooming
      if (this.options.beforeZoom(this.getRelativeZoom(), this.computeRelativeZoom(newCTM.a)) === false) {
        newCTM.a = newCTM.d = this.activeState.zoom
        willZoom = false
      } else {
        this.updateCache(newCTM);
        this.options.onZoom(this.getRelativeZoom())
      }
    }

    // Before pan
    if (willPan) {
      var preventPan = this.options.beforePan(this.getPan(), {x: newCTM.e, y: newCTM.f})
          // If prevent pan is an object
        , preventPanX = false
        , preventPanY = false

      // If prevent pan is Boolean false
      if (preventPan === false) {
        // Set x and y same as before
        newCTM.e = this.getPan().x
        newCTM.f = this.getPan().y

        preventPanX = preventPanY = true
      } else if (Utils.isObject(preventPan)) {
        // Check for X axes attribute
        if (preventPan.x === false) {
          // Prevent panning on x axes
          newCTM.e = this.getPan().x
          preventPanX = true
        } else if (Utils.isNumber(preventPan.x)) {
          // Set a custom pan value
          newCTM.e = preventPan.x
        }

        // Check for Y axes attribute
        if (preventPan.y === false) {
          // Prevent panning on x axes
          newCTM.f = this.getPan().y
          preventPanY = true
        } else if (Utils.isNumber(preventPan.y)) {
          // Set a custom pan value
          newCTM.f = preventPan.y
        }
      }

      // Update willPan flag
      // Check if newCTM is still different
      if ((preventPanX && preventPanY) || !this.isPanDifferent(newCTM)) {
        willPan = false
      } else {
        this.updateCache(newCTM);
        this.options.onPan(this.getPan());
      }
    }

    // Check again if should zoom or pan
    if (willZoom || willPan) {
      this.updateCTMOnNextFrame()
    }
  }
}

ShadowViewport.prototype.isZoomDifferent = function(newCTM) {
  return this.activeState.zoom !== newCTM.a
}

ShadowViewport.prototype.isPanDifferent = function(newCTM) {
  return this.activeState.x !== newCTM.e || this.activeState.y !== newCTM.f
}


/**
 * Update cached CTM and active state
 *
 * @param {SVGMatrix} newCTM
 */
ShadowViewport.prototype.updateCache = function(newCTM) {
  this.activeState.zoom = newCTM.a
  this.activeState.x = newCTM.e
  this.activeState.y = newCTM.f
}

ShadowViewport.prototype.pendingUpdate = false

/**
 * Place a request to update CTM on next Frame
 */
ShadowViewport.prototype.updateCTMOnNextFrame = function() {
  if (!this.pendingUpdate) {
    // Lock
    this.pendingUpdate = true

    // Throttle next update
    this.requestAnimationFrame.call(window, this.updateCTMCached)
  }
}

/**
 * Update viewport CTM with cached CTM
 */
ShadowViewport.prototype.updateCTM = function() {
  var ctm = this.getCTM()

  // Updates SVG element
  SvgUtils.setCTM(this.viewport, ctm, this.defs)

  // Free the lock
  this.pendingUpdate = false

  // Notify about the update
  if(this.options.onUpdatedCTM) {
    this.options.onUpdatedCTM(ctm)
  }
}

module.exports = function(viewport, options){
  return new ShadowViewport(viewport, options)
}

},{"./svg-utilities":5,"./utilities":7}],4:[function(require,module,exports){
var Wheel = require('./uniwheel')
, ControlIcons = require('./control-icons')
, Utils = require('./utilities')
, SvgUtils = require('./svg-utilities')
, ShadowViewport = require('./shadow-viewport')

var SvgPanZoom = function(svg, options) {
  this.init(svg, options)
}

var optionsDefaults = {
  viewportSelector: '.svg-pan-zoom_viewport' // Viewport selector. Can be querySelector string or SVGElement
, panEnabled: true // enable or disable panning (default enabled)
, controlIconsEnabled: false // insert icons to give user an option in addition to mouse events to control pan/zoom (default disabled)
, zoomEnabled: true // enable or disable zooming (default enabled)
, dblClickZoomEnabled: true // enable or disable zooming by double clicking (default enabled)
, mouseWheelZoomEnabled: true // enable or disable zooming by mouse wheel (default enabled)
, preventMouseEventsDefault: true // enable or disable preventDefault for mouse events
, zoomScaleSensitivity: 0.1 // Zoom sensitivity
, minZoom: 0.5 // Minimum Zoom level
, maxZoom: 10 // Maximum Zoom level
, fit: true // enable or disable viewport fit in SVG (default true)
, contain: false // enable or disable viewport contain the svg (default false)
, center: true // enable or disable viewport centering in SVG (default true)
, refreshRate: 'auto' // Maximum number of frames per second (altering SVG's viewport)
, beforeZoom: null
, onZoom: null
, beforePan: null
, onPan: null
, customEventsHandler: null
, eventsListenerElement: null
, onUpdatedCTM: null
}

var passiveListenerOption = {passive: true};

SvgPanZoom.prototype.init = function(svg, options) {
  var that = this

  this.svg = svg
  this.defs = svg.querySelector('defs')

  // Add default attributes to SVG
  SvgUtils.setupSvgAttributes(this.svg)

  // Set options
  this.options = Utils.extend(Utils.extend({}, optionsDefaults), options)

  // Set default state
  this.state = 'none'

  // Get dimensions
  var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(svg)
  this.width = boundingClientRectNormalized.width
  this.height = boundingClientRectNormalized.height

  // Init shadow viewport
  this.viewport = ShadowViewport(SvgUtils.getOrCreateViewport(this.svg, this.options.viewportSelector), {
    svg: this.svg
  , width: this.width
  , height: this.height
  , fit: this.options.fit
  , contain: this.options.contain
  , center: this.options.center
  , refreshRate: this.options.refreshRate
  // Put callbacks into functions as they can change through time
  , beforeZoom: function(oldScale, newScale) {
      if (that.viewport && that.options.beforeZoom) {return that.options.beforeZoom(oldScale, newScale)}
    }
  , onZoom: function(scale) {
      if (that.viewport && that.options.onZoom) {return that.options.onZoom(scale)}
    }
  , beforePan: function(oldPoint, newPoint) {
      if (that.viewport && that.options.beforePan) {return that.options.beforePan(oldPoint, newPoint)}
    }
  , onPan: function(point) {
      if (that.viewport && that.options.onPan) {return that.options.onPan(point)}
    }
  , onUpdatedCTM: function(ctm) {
      if (that.viewport && that.options.onUpdatedCTM) {return that.options.onUpdatedCTM(ctm)}
    }
  })

  // Wrap callbacks into public API context
  var publicInstance = this.getPublicInstance()
  publicInstance.setBeforeZoom(this.options.beforeZoom)
  publicInstance.setOnZoom(this.options.onZoom)
  publicInstance.setBeforePan(this.options.beforePan)
  publicInstance.setOnPan(this.options.onPan)
  publicInstance.setOnUpdatedCTM(this.options.onUpdatedCTM)

  if (this.options.controlIconsEnabled) {
    ControlIcons.enable(this)
  }

  // Init events handlers
  this.lastMouseWheelEventTime = Date.now()
  this.setupHandlers()
}

/**
 * Register event handlers
 */
SvgPanZoom.prototype.setupHandlers = function() {
  var that = this
    , prevEvt = null // use for touchstart event to detect double tap
    ;

  this.eventListeners = {
    // Mouse down group
    mousedown: function(evt) {
      var result = that.handleMouseDown(evt, prevEvt);
      prevEvt = evt
      return result;
    }
  , touchstart: function(evt) {
      var result = that.handleMouseDown(evt, prevEvt);
      prevEvt = evt
      return result;
    }

    // Mouse up group
  , mouseup: function(evt) {
      return that.handleMouseUp(evt);
    }
  , touchend: function(evt) {
      return that.handleMouseUp(evt);
    }

    // Mouse move group
  , mousemove: function(evt) {
      return that.handleMouseMove(evt);
    }
  , touchmove: function(evt) {
      return that.handleMouseMove(evt);
    }

    // Mouse leave group
  , mouseleave: function(evt) {
      return that.handleMouseUp(evt);
    }
  , touchleave: function(evt) {
      return that.handleMouseUp(evt);
    }
  , touchcancel: function(evt) {
      return that.handleMouseUp(evt);
    }
  }

  // Init custom events handler if available
  if (this.options.customEventsHandler != null) { // jshint ignore:line
    this.options.customEventsHandler.init({
      svgElement: this.svg
    , eventsListenerElement: this.options.eventsListenerElement
    , instance: this.getPublicInstance()
    })

    // Custom event handler may halt builtin listeners
    var haltEventListeners = this.options.customEventsHandler.haltEventListeners
    if (haltEventListeners && haltEventListeners.length) {
      for (var i = haltEventListeners.length - 1; i >= 0; i--) {
        if (this.eventListeners.hasOwnProperty(haltEventListeners[i])) {
          delete this.eventListeners[haltEventListeners[i]]
        }
      }
    }
  }

  // Bind eventListeners
    for (var event in this.eventListeners) {
        let target = (this.options.eventsListenerElement || this.svg);
        // Attach event to eventsListenerElement or SVG if not available
        if (event !== 'mousedown'){
            target = document;
        }
        target.addEventListener(event, this.eventListeners[event], !this.options.preventMouseEventsDefault ? passiveListenerOption : false)
    }

  // Zoom using mouse wheel
  if (this.options.mouseWheelZoomEnabled) {
    this.options.mouseWheelZoomEnabled = false // set to false as enable will set it back to true
    this.enableMouseWheelZoom()
  }
}

/**
 * Enable ability to zoom using mouse wheel
 */
SvgPanZoom.prototype.enableMouseWheelZoom = function() {
  if (!this.options.mouseWheelZoomEnabled) {
    var that = this

    // Mouse wheel listener
    this.wheelListener = function(evt) {
      return that.handleMouseWheel(evt);
    }

    // Bind wheelListener
    var isPassiveListener = !this.options.preventMouseEventsDefault
    Wheel.on(this.options.eventsListenerElement || this.svg, this.wheelListener, isPassiveListener)

    this.options.mouseWheelZoomEnabled = true
  }
}

/**
 * Disable ability to zoom using mouse wheel
 */
SvgPanZoom.prototype.disableMouseWheelZoom = function() {
  if (this.options.mouseWheelZoomEnabled) {
    var isPassiveListener = !this.options.preventMouseEventsDefault
    Wheel.off(this.options.eventsListenerElement || this.svg, this.wheelListener, isPassiveListener)
    this.options.mouseWheelZoomEnabled = false
  }
}

/**
 * Handle mouse wheel event
 *
 * @param  {Event} evt
 */
SvgPanZoom.prototype.handleMouseWheel = function(evt) {
  if (!this.options.zoomEnabled || this.state !== 'none') {
    return;
  }

  if (this.options.preventMouseEventsDefault){
    if (evt.preventDefault) {
      evt.preventDefault();
    } else {
      evt.returnValue = false;
    }
  }

  // Default delta in case that deltaY is not available
  var delta = evt.deltaY || 1
    , timeDelta = Date.now() - this.lastMouseWheelEventTime
    , divider = 3 + Math.max(0, 30 - timeDelta)

  // Update cache
  this.lastMouseWheelEventTime = Date.now()

  // Make empirical adjustments for browsers that give deltaY in pixels (deltaMode=0)
  if ('deltaMode' in evt && evt.deltaMode === 0 && evt.wheelDelta) {
    delta = evt.deltaY === 0 ? 0 :  Math.abs(evt.wheelDelta) / evt.deltaY
  }

  delta = -0.3 < delta && delta < 0.3 ? delta : (delta > 0 ? 1 : -1) * Math.log(Math.abs(delta) + 10) / divider

  var inversedScreenCTM = this.svg.getScreenCTM().inverse()
    , relativeMousePoint = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(inversedScreenCTM)
    , zoom = Math.pow(1 + this.options.zoomScaleSensitivity, (-1) * delta); // multiplying by neg. 1 so as to make zoom in/out behavior match Google maps behavior

  this.zoomAtPoint(zoom, relativeMousePoint)
}

/**
 * Zoom in at a SVG point
 *
 * @param  {SVGPoint} point
 * @param  {Float} zoomScale    Number representing how much to zoom
 * @param  {Boolean} zoomAbsolute Default false. If true, zoomScale is treated as an absolute value.
 *                                Otherwise, zoomScale is treated as a multiplied (e.g. 1.10 would zoom in 10%)
 */
SvgPanZoom.prototype.zoomAtPoint = function(zoomScale, point, zoomAbsolute) {
  var originalState = this.viewport.getOriginalState()

  if (!zoomAbsolute) {
    // Fit zoomScale in set bounds
    if (this.getZoom() * zoomScale < this.options.minZoom * originalState.zoom) {
      zoomScale = (this.options.minZoom * originalState.zoom) / this.getZoom()
    } else if (this.getZoom() * zoomScale > this.options.maxZoom * originalState.zoom) {
      zoomScale = (this.options.maxZoom * originalState.zoom) / this.getZoom()
    }
  } else {
    // Fit zoomScale in set bounds
    zoomScale = Math.max(this.options.minZoom * originalState.zoom, Math.min(this.options.maxZoom * originalState.zoom, zoomScale))
    // Find relative scale to achieve desired scale
    zoomScale = zoomScale/this.getZoom()
  }

  var oldCTM = this.viewport.getCTM()
    , relativePoint = point.matrixTransform(oldCTM.inverse())
    , modifier = this.svg.createSVGMatrix().translate(relativePoint.x, relativePoint.y).scale(zoomScale).translate(-relativePoint.x, -relativePoint.y)
    , newCTM = oldCTM.multiply(modifier)

  if (newCTM.a !== oldCTM.a) {
    this.viewport.setCTM(newCTM)
  }
}

/**
 * Zoom at center point
 *
 * @param  {Float} scale
 * @param  {Boolean} absolute Marks zoom scale as relative or absolute
 */
SvgPanZoom.prototype.zoom = function(scale, absolute) {
  this.zoomAtPoint(scale, SvgUtils.getSvgCenterPoint(this.svg, this.width, this.height), absolute)
}

/**
 * Zoom used by public instance
 *
 * @param  {Float} scale
 * @param  {Boolean} absolute Marks zoom scale as relative or absolute
 */
SvgPanZoom.prototype.publicZoom = function(scale, absolute) {
  if (absolute) {
    scale = this.computeFromRelativeZoom(scale)
  }

  this.zoom(scale, absolute)
}

/**
 * Zoom at point used by public instance
 *
 * @param  {Float} scale
 * @param  {SVGPoint|Object} point    An object that has x and y attributes
 * @param  {Boolean} absolute Marks zoom scale as relative or absolute
 */
SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
  if (absolute) {
    // Transform zoom into a relative value
    scale = this.computeFromRelativeZoom(scale)
  }

  // If not a SVGPoint but has x and y then create a SVGPoint
  if (Utils.getType(point) !== 'SVGPoint') {
    if('x' in point && 'y' in point) {
      point = SvgUtils.createSVGPoint(this.svg, point.x, point.y)
    } else {
      throw new Error('Given point is invalid')
    }
  }

  this.zoomAtPoint(scale, point, absolute)
}

/**
 * Get zoom scale
 *
 * @return {Float} zoom scale
 */
SvgPanZoom.prototype.getZoom = function() {
  return this.viewport.getZoom()
}

/**
 * Get zoom scale for public usage
 *
 * @return {Float} zoom scale
 */
SvgPanZoom.prototype.getRelativeZoom = function() {
  return this.viewport.getRelativeZoom()
}

/**
 * Compute actual zoom from public zoom
 *
 * @param  {Float} zoom
 * @return {Float} zoom scale
 */
SvgPanZoom.prototype.computeFromRelativeZoom = function(zoom) {
  return zoom * this.viewport.getOriginalState().zoom
}

/**
 * Set zoom to initial state
 */
SvgPanZoom.prototype.resetZoom = function() {
  var originalState = this.viewport.getOriginalState()

  this.zoom(originalState.zoom, true);
}

/**
 * Set pan to initial state
 */
SvgPanZoom.prototype.resetPan = function() {
  this.pan(this.viewport.getOriginalState());
}

/**
 * Set pan and zoom to initial state
 */
SvgPanZoom.prototype.reset = function() {
  this.resetZoom()
  this.resetPan()
}

/**
 * Handle double click event
 * See handleMouseDown() for alternate detection method
 *
 * @param {Event} evt
 */
SvgPanZoom.prototype.handleDblClick = function(evt) {
  if (this.options.preventMouseEventsDefault) {
    if (evt.preventDefault) {
      evt.preventDefault()
    } else {
      evt.returnValue = false
    }
  }

  // Check if target was a control button
  if (this.options.controlIconsEnabled) {
    var targetClass = evt.target.getAttribute('class') || ''
    if (targetClass.indexOf('svg-pan-zoom-control') > -1) {
      return false
    }
  }

  var zoomFactor

  if (evt.shiftKey) {
    zoomFactor = 1/((1 + this.options.zoomScaleSensitivity) * 2) // zoom out when shift key pressed
  } else {
    zoomFactor = (1 + this.options.zoomScaleSensitivity) * 2
  }

  var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.svg.getScreenCTM().inverse())
  this.zoomAtPoint(zoomFactor, point)
}

/**
 * Handle click event
 *
 * @param {Event} evt
 */
SvgPanZoom.prototype.handleMouseDown = function(evt, prevEvt) {
  if (this.options.preventMouseEventsDefault) {
      if (evt.preventDefault) {
      evt.preventDefault()
    } else {
      evt.returnValue = false
    }
  }

  Utils.mouseAndTouchNormalize(evt, this.svg)

  // Double click detection; more consistent than ondblclick
  if (this.options.dblClickZoomEnabled && Utils.isDblClick(evt, prevEvt)){
    this.handleDblClick(evt)
  } else {
    // Pan mode
    this.state = 'pan'
    this.firstEventCTM = this.viewport.getCTM()
    this.stateOrigin = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
  }
}

/**
 * Handle mouse move event
 *
 * @param  {Event} evt
 */
SvgPanZoom.prototype.handleMouseMove = function(evt) {
  if (this.options.preventMouseEventsDefault) {
    if (evt.preventDefault) {
      evt.preventDefault()
    } else {
      evt.returnValue = false
    }
  }

    if (this.state === 'pan' && this.options.panEnabled) {
        this.svg.classList.add('--dragging');
        setTimeout(() => {
            this.svg.classList.add('--delay-dragging');
        }, 150)
    // Pan mode
    var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
      , viewportCTM = this.firstEventCTM.translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y)

    this.viewport.setCTM(viewportCTM)
  }
}

/**
 * Handle mouse button release event
 *
 * @param {Event} evt
 */
SvgPanZoom.prototype.handleMouseUp = function(evt) {
    if (this.options.preventMouseEventsDefault) {
      if (evt.preventDefault) {
        evt.preventDefault()
    } else {
        evt.returnValue = false
    }
  }

  if (this.state === 'pan') {
      this.svg.classList.remove('--dragging');
      setTimeout(() => {
          this.svg.classList.remove('--delay-dragging');
      }, 200)
      // Quit pan mode
    this.state = 'none'
  }
}

/**
 * Adjust viewport size (only) so it will fit in SVG
 * Does not center image
 */
SvgPanZoom.prototype.fit = function() {
  var viewBox = this.viewport.getViewBox()
    , newScale = Math.min(this.width/viewBox.width, this.height/viewBox.height)

  this.zoom(newScale, true)
}

/**
 * Adjust viewport size (only) so it will contain the SVG
 * Does not center image
 */
SvgPanZoom.prototype.contain = function() {
  var viewBox = this.viewport.getViewBox()
    , newScale = Math.max(this.width/viewBox.width, this.height/viewBox.height)

  this.zoom(newScale, true)
}

/**
 * Adjust viewport pan (only) so it will be centered in SVG
 * Does not zoom/fit/contain image
 */
SvgPanZoom.prototype.center = function() {
  var viewBox = this.viewport.getViewBox()
    , offsetX = (this.width - (viewBox.width + viewBox.x * 2) * this.getZoom()) * 0.5
    , offsetY = (this.height - (viewBox.height + viewBox.y * 2) * this.getZoom()) * 0.5

  this.getPublicInstance().pan({x: offsetX, y: offsetY})
}

/**
 * Update content cached BorderBox
 * Use when viewport contents change
 */
SvgPanZoom.prototype.updateBBox = function() {
  this.viewport.simpleViewBoxCache()
}

/**
 * Pan to a rendered position
 *
 * @param  {Object} point {x: 0, y: 0}
 */
SvgPanZoom.prototype.pan = function(point) {
  var viewportCTM = this.viewport.getCTM()
  viewportCTM.e = point.x
  viewportCTM.f = point.y
  this.viewport.setCTM(viewportCTM)
}

/**
 * Relatively pan the graph by a specified rendered position vector
 *
 * @param  {Object} point {x: 0, y: 0}
 */
SvgPanZoom.prototype.panBy = function(point) {
  var viewportCTM = this.viewport.getCTM()
  viewportCTM.e += point.x
  viewportCTM.f += point.y
  this.viewport.setCTM(viewportCTM)
}

/**
 * Get pan vector
 *
 * @return {Object} {x: 0, y: 0}
 */
SvgPanZoom.prototype.getPan = function() {
  var state = this.viewport.getState()

  return {x: state.x, y: state.y}
}

/**
 * Recalculates cached svg dimensions and controls position
 */
SvgPanZoom.prototype.resize = function() {
  // Get dimensions
  var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(this.svg)
  this.width = boundingClientRectNormalized.width
  this.height = boundingClientRectNormalized.height

  // Recalculate original state
  var viewport = this.viewport
  viewport.options.width = this.width
  viewport.options.height = this.height
  viewport.processCTM()

  // Reposition control icons by re-enabling them
  if (this.options.controlIconsEnabled) {
    this.getPublicInstance().disableControlIcons()
    this.getPublicInstance().enableControlIcons()
  }
}

/**
 * Unbind mouse events, free callbacks and destroy public instance
 */
SvgPanZoom.prototype.destroy = function() {
  var that = this

  // Free callbacks
  this.beforeZoom = null
  this.onZoom = null
  this.beforePan = null
  this.onPan = null
  this.onUpdatedCTM = null

  // Destroy custom event handlers
  if (this.options.customEventsHandler != null) { // jshint ignore:line
    this.options.customEventsHandler.destroy({
      svgElement: this.svg
    , eventsListenerElement: this.options.eventsListenerElement
    , instance: this.getPublicInstance()
    })
  }

  // Unbind eventListeners
  for (var event in this.eventListeners) {
      let target = (this.options.eventsListenerElement || this.svg);
      if (event !== 'mousedown'){
          target = document;
      }

      target.removeEventListener(event, this.eventListeners[event], !this.options.preventMouseEventsDefault ? passiveListenerOption : false)
  }

  // Unbind wheelListener
  this.disableMouseWheelZoom()

  // Remove control icons
  this.getPublicInstance().disableControlIcons()

  // Reset zoom and pan
  this.reset()

  // Remove instance from instancesStore
  instancesStore = instancesStore.filter(function(instance){
    return instance.svg !== that.svg
  })

  // Delete options and its contents
  delete this.options

  // Delete viewport to make public shadow viewport functions uncallable
  delete this.viewport

  // Destroy public instance and rewrite getPublicInstance
  delete this.publicInstance
  delete this.pi
  this.getPublicInstance = function(){
    return null
  }
}

/**
 * Returns a public instance object
 *
 * @return {Object} Public instance object
 */
SvgPanZoom.prototype.getPublicInstance = function() {
  var that = this

  // Create cache
  if (!this.publicInstance) {
    this.publicInstance = this.pi = {
      // Pan
      enablePan: function() {that.options.panEnabled = true; return that.pi}
    , disablePan: function() {that.options.panEnabled = false; return that.pi}
    , isPanEnabled: function() {return !!that.options.panEnabled}
    , pan: function(point) {that.pan(point); return that.pi}
    , panBy: function(point) {that.panBy(point); return that.pi}
    , getPan: function() {return that.getPan()}
      // Pan event
    , setBeforePan: function(fn) {that.options.beforePan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
    , setOnPan: function(fn) {that.options.onPan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
      // Zoom and Control Icons
    , enableZoom: function() {that.options.zoomEnabled = true; return that.pi}
    , disableZoom: function() {that.options.zoomEnabled = false; return that.pi}
    , isZoomEnabled: function() {return !!that.options.zoomEnabled}
    , enableControlIcons: function() {
        if (!that.options.controlIconsEnabled) {
          that.options.controlIconsEnabled = true
          ControlIcons.enable(that)
        }
        return that.pi
      }
    , disableControlIcons: function() {
        if (that.options.controlIconsEnabled) {
          that.options.controlIconsEnabled = false;
          ControlIcons.disable(that)
        }
        return that.pi
      }
    , isControlIconsEnabled: function() {return !!that.options.controlIconsEnabled}
      // Double click zoom
    , enableDblClickZoom: function() {that.options.dblClickZoomEnabled = true; return that.pi}
    , disableDblClickZoom: function() {that.options.dblClickZoomEnabled = false; return that.pi}
    , isDblClickZoomEnabled: function() {return !!that.options.dblClickZoomEnabled}
      // Mouse wheel zoom
    , enableMouseWheelZoom: function() {that.enableMouseWheelZoom(); return that.pi}
    , disableMouseWheelZoom: function() {that.disableMouseWheelZoom(); return that.pi}
    , isMouseWheelZoomEnabled: function() {return !!that.options.mouseWheelZoomEnabled}
      // Zoom scale and bounds
    , setZoomScaleSensitivity: function(scale) {that.options.zoomScaleSensitivity = scale; return that.pi}
    , setMinZoom: function(zoom) {that.options.minZoom = zoom; return that.pi}
    , setMaxZoom: function(zoom) {that.options.maxZoom = zoom; return that.pi}
    , getMinZoom: function() {return that.options.minZoom}
    , getMaxZoom: function() {return that.options.maxZoom}
    , getSvg: function() {return that.svg}
    , invalidateSize: function() {that.resize(); that.fit(); that.center();}
      // Zoom event
    , setBeforeZoom: function(fn) {that.options.beforeZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
    , setOnZoom: function(fn) {that.options.onZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
      // Zooming
    , zoom: function(scale) {that.publicZoom(scale, true); return that.pi}
    , zoomBy: function(scale) {that.publicZoom(scale, false); return that.pi}
    , zoomAtPoint: function(scale, point) {that.publicZoomAtPoint(scale, point, true); return that.pi}
    , zoomAtPointBy: function(scale, point) {that.publicZoomAtPoint(scale, point, false); return that.pi}
    , zoomIn: function() {this.zoomBy(1 + that.options.zoomScaleSensitivity); return that.pi}
    , zoomOut: function() {this.zoomBy(1 / (1 + that.options.zoomScaleSensitivity)); return that.pi}
    , getZoom: function() {return that.getRelativeZoom()}
      // CTM update
    , setOnUpdatedCTM: function(fn) {that.options.onUpdatedCTM = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
      // Reset
    , resetZoom: function() {that.resetZoom(); return that.pi}
    , resetPan: function() {that.resetPan(); return that.pi}
    , reset: function() {that.reset(); return that.pi}
      // Fit, Contain and Center
    , fit: function() {that.fit(); return that.pi}
    , contain: function() {that.contain(); return that.pi}
    , center: function() {that.center(); return that.pi}
      // Size and Resize
    , updateBBox: function() {that.updateBBox(); return that.pi}
    , resize: function() {that.resize(); return that.pi}
    , getSizes: function() {
        return {
          width: that.width
        , height: that.height
        , realZoom: that.getZoom()
        , viewBox: that.viewport.getViewBox()
        }
      }
      // Destroy
    , destroy: function() {that.destroy(); return that.pi}
    }
  }

  return this.publicInstance
}

/**
 * Stores pairs of instances of SvgPanZoom and SVG
 * Each pair is represented by an object {svg: SVGSVGElement, instance: SvgPanZoom}
 *
 * @type {Array}
 */
var instancesStore = []

var svgPanZoom = function(elementOrSelector, options){
  var svg = Utils.getSvg(elementOrSelector)

  if (svg === null) {
    return null
  } else {
    // Look for existent instance
    for(var i = instancesStore.length - 1; i >= 0; i--) {
      if (instancesStore[i].svg === svg) {
        return instancesStore[i].instance.getPublicInstance()
      }
    }

    // If instance not found - create one
    instancesStore.push({
      svg: svg
    , instance: new SvgPanZoom(svg, options)
    })

    // Return just pushed instance
    return instancesStore[instancesStore.length - 1].instance.getPublicInstance()
  }
}

module.exports = svgPanZoom;

},{"./control-icons":2,"./shadow-viewport":3,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(require,module,exports){
var Utils = require('./utilities')
  , _browser = 'unknown'
  ;

// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer
  _browser = 'ie';
}

module.exports = {
  svgNS:  'http://www.w3.org/2000/svg'
, xmlNS:  'http://www.w3.org/XML/1998/namespace'
, xmlnsNS:  'http://www.w3.org/2000/xmlns/'
, xlinkNS:  'http://www.w3.org/1999/xlink'
, evNS:  'http://www.w3.org/2001/xml-events'

  /**
   * Get svg dimensions: width and height
   *
   * @param  {SVGSVGElement} svg
   * @return {Object}     {width: 0, height: 0}
   */
, getBoundingClientRectNormalized: function(svg) {
    if (svg.clientWidth && svg.clientHeight) {
      return {width: svg.clientWidth, height: svg.clientHeight}
    } else if (!!svg.getBoundingClientRect()) {
      return svg.getBoundingClientRect();
    } else {
      throw new Error('Cannot get BoundingClientRect for SVG.');
    }
  }

  /**
   * Gets g element with class of "viewport" or creates it if it doesn't exist
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGElement}     g (group) element
   */
, getOrCreateViewport: function(svg, selector) {
    var viewport = null

    if (Utils.isElement(selector)) {
      viewport = selector
    } else {
      viewport = svg.querySelector(selector)
    }

    // Check if there is just one main group in SVG
    if (!viewport) {
      var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){
        return el.nodeName !== 'defs' && el.nodeName !== '#text'
      })

      // Node name should be SVGGElement and should have no transform attribute
      // Groups with transform are not used as viewport because it involves parsing of all transform possibilities
      if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) {
        viewport = childNodes[0]
      }
    }

    // If no favorable group element exists then create one
    if (!viewport) {
      var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, '');
      viewport = document.createElementNS(this.svgNS, 'g');
      viewport.setAttribute('id', viewportId);

      // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes
      var svgChildren = svg.childNodes || svg.children;
      if (!!svgChildren && svgChildren.length > 0) {
        for (var i = svgChildren.length; i > 0; i--) {
          // Move everything into viewport except defs
          if (svgChildren[svgChildren.length - i].nodeName !== 'defs') {
            viewport.appendChild(svgChildren[svgChildren.length - i]);
          }
        }
      }
      svg.appendChild(viewport);
    }

    // Parse class names
    var classNames = [];
    if (viewport.getAttribute('class')) {
      classNames = viewport.getAttribute('class').split(' ')
    }

    // Set class (if not set already)
    if (!~classNames.indexOf('svg-pan-zoom_viewport')) {
      classNames.push('svg-pan-zoom_viewport')
      viewport.setAttribute('class', classNames.join(' '))
    }

    return viewport
  }

  /**
   * Set SVG attributes
   *
   * @param  {SVGSVGElement} svg
   */
  , setupSvgAttributes: function(svg) {
    // Setting default attributes
    svg.setAttribute('xmlns', this.svgNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS);

    // Needed for Internet Explorer, otherwise the viewport overflows
    if (svg.parentNode !== null) {
      var style = svg.getAttribute('style') || '';
      if (style.toLowerCase().indexOf('overflow') === -1) {
        svg.setAttribute('style', 'overflow: hidden; ' + style);
      }
    }
  }

/**
 * How long Internet Explorer takes to finish updating its display (ms).
 */
, internetExplorerRedisplayInterval: 300

/**
 * Forces the browser to redisplay all SVG elements that rely on an
 * element defined in a 'defs' section. It works globally, for every
 * available defs element on the page.
 * The throttling is intentionally global.
 *
 * This is only needed for IE. It is as a hack to make markers (and 'use' elements?)
 * visible after pan/zoom when there are multiple SVGs on the page.
 * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/
 * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62
 */
, refreshDefsGlobal: Utils.throttle(function() {
    var allDefs = document.querySelectorAll('defs');
    var allDefsCount = allDefs.length;
    for (var i = 0; i < allDefsCount; i++) {
      var thisDefs = allDefs[i];
      thisDefs.parentNode.insertBefore(thisDefs, thisDefs);
    }
  }, this ? this.internetExplorerRedisplayInterval : null)

  /**
   * Sets the current transform matrix of an element
   *
   * @param {SVGElement} element
   * @param {SVGMatrix} matrix  CTM
   * @param {SVGElement} defs
   */
, setCTM: function(element, matrix, defs) {
    var that = this
      , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';

    element.setAttributeNS(null, 'transform', s);
    if ('transform' in element.style) {
      element.style.transform = s;
    } else if ('-ms-transform' in element.style) {
      element.style['-ms-transform'] = s;
    } else if ('-webkit-transform' in element.style) {
      element.style['-webkit-transform'] = s;
    }

    // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
    // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
    // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
    if (_browser === 'ie' && !!defs) {
      // this refresh is intended for redisplaying the SVG during zooming
      defs.parentNode.insertBefore(defs, defs);
      // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG
      // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that
      // are located under any other element(s).
      window.setTimeout(function() {
        that.refreshDefsGlobal();
      }, that.internetExplorerRedisplayInterval);
    }
  }

  /**
   * Instantiate an SVGPoint object with given event coordinates
   *
   * @param {Event} evt
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}     point
   */
, getEventPoint: function(evt, svg) {
    var point = svg.createSVGPoint()

    Utils.mouseAndTouchNormalize(evt, svg)

    point.x = evt.clientX
    point.y = evt.clientY

    return point
  }

  /**
   * Get SVG center point
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}
   */
, getSvgCenterPoint: function(svg, width, height) {
    return this.createSVGPoint(svg, width / 2, height / 2)
  }

  /**
   * Create a SVGPoint with given x and y
   *
   * @param  {SVGSVGElement} svg
   * @param  {Number} x
   * @param  {Number} y
   * @return {SVGPoint}
   */
, createSVGPoint: function(svg, x, y) {
    var point = svg.createSVGPoint()
    point.x = x
    point.y = y

    return point
  }
}

},{"./utilities":7}],6:[function(require,module,exports){
// uniwheel 0.1.2 (customized)
// A unified cross browser mouse wheel event handler
// https://github.com/teemualap/uniwheel

module.exports = (function(){

  //Full details: https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel

  var prefix = "", _addEventListener, _removeEventListener, support, fns = [];
  var passiveOption = {passive: true};

  // detect event model
  if ( window.addEventListener ) {
    _addEventListener = "addEventListener";
    _removeEventListener = "removeEventListener";
  } else {
    _addEventListener = "attachEvent";
    _removeEventListener = "detachEvent";
    prefix = "on";
  }

  // detect available wheel event
  support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
            document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
            "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox


  function createCallback(element,callback) {

    var fn = function(originalEvent) {

      !originalEvent && ( originalEvent = window.event );

      // create a normalized event object
      var event = {
        // keep a ref to the original event object
        originalEvent: originalEvent,
        target: originalEvent.target || originalEvent.srcElement,
        type: "wheel",
        deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
        deltaX: 0,
        delatZ: 0,
        preventDefault: function() {
          originalEvent.preventDefault ?
            originalEvent.preventDefault() :
            originalEvent.returnValue = false;
        }
      };

      // calculate deltaY (and deltaX) according to the event
      if ( support == "mousewheel" ) {
        event.deltaY = - 1/40 * originalEvent.wheelDelta;
        // Webkit also support wheelDeltaX
        originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
      } else {
        event.deltaY = originalEvent.detail;
      }

      // it's time to fire the callback
      return callback( event );

    };

    fns.push({
      element: element,
      fn: fn,
    });

    return fn;
  }

  function getCallback(element) {
    for (var i = 0; i < fns.length; i++) {
      if (fns[i].element === element) {
        return fns[i].fn;
      }
    }
    return function(){};
  }

  function removeCallback(element) {
    for (var i = 0; i < fns.length; i++) {
      if (fns[i].element === element) {
        return fns.splice(i,1);
      }
    }
  }

  function _addWheelListener(elem, eventName, callback, isPassiveListener ) {
    var cb;

    if (support === "wheel") {
      cb = callback;
    } else {
      cb = createCallback(elem, callback);
    }

    elem[_addEventListener](prefix + eventName, cb, isPassiveListener ? passiveOption : false);
  }

  function _removeWheelListener(elem, eventName, callback, isPassiveListener ) {

    var cb;

    if (support === "wheel") {
      cb = callback;
    } else {
      cb = getCallback(elem);
    }

    elem[_removeEventListener](prefix + eventName, cb, isPassiveListener ? passiveOption : false);

    removeCallback(elem);
  }

  function addWheelListener( elem, callback, isPassiveListener ) {
    _addWheelListener(elem, support, callback, isPassiveListener );

    // handle MozMousePixelScroll in older Firefox
    if( support == "DOMMouseScroll" ) {
      _addWheelListener(elem, "MozMousePixelScroll", callback, isPassiveListener );
    }
  }

  function removeWheelListener(elem, callback, isPassiveListener){
    _removeWheelListener(elem, support, callback, isPassiveListener);

    // handle MozMousePixelScroll in older Firefox
    if( support == "DOMMouseScroll" ) {
      _removeWheelListener(elem, "MozMousePixelScroll", callback, isPassiveListener);
    }
  }

  return {
    on: addWheelListener,
    off: removeWheelListener
  };

})();

},{}],7:[function(require,module,exports){
module.exports = {
  /**
   * Extends an object
   *
   * @param  {Object} target object to extend
   * @param  {Object} source object to take properties from
   * @return {Object}        extended object
   */
  extend: function(target, source) {
    target = target || {};
    for (var prop in source) {
      // Go recursively
      if (this.isObject(source[prop])) {
        target[prop] = this.extend(target[prop], source[prop])
      } else {
        target[prop] = source[prop]
      }
    }
    return target;
  }

  /**
   * Checks if an object is a DOM element
   *
   * @param  {Object}  o HTML element or String
   * @return {Boolean}   returns true if object is a DOM element
   */
, isElement: function(o){
    return (
      o instanceof HTMLElement || o instanceof SVGElement || o instanceof SVGSVGElement || //DOM2
      (o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string')
    );
  }

  /**
   * Checks if an object is an Object
   *
   * @param  {Object}  o Object
   * @return {Boolean}   returns true if object is an Object
   */
, isObject: function(o){
    return Object.prototype.toString.call(o) === '[object Object]';
  }

  /**
   * Checks if variable is Number
   *
   * @param  {Integer|Float}  n
   * @return {Boolean}   returns true if variable is Number
   */
, isNumber: function(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  }

  /**
   * Search for an SVG element
   *
   * @param  {Object|String} elementOrSelector DOM Element or selector String
   * @return {Object|Null}                   SVG or null
   */
, getSvg: function(elementOrSelector) {
    var element
      , svg;

    if (!this.isElement(elementOrSelector)) {
      // If selector provided
      if (typeof elementOrSelector === 'string' || elementOrSelector instanceof String) {
        // Try to find the element
        element = document.querySelector(elementOrSelector)

        if (!element) {
          throw new Error('Provided selector did not find any elements. Selector: ' + elementOrSelector)
          return null
        }
      } else {
        throw new Error('Provided selector is not an HTML object nor String')
        return null
      }
    } else {
      element = elementOrSelector
    }

    if (element.tagName.toLowerCase() === 'svg') {
      svg = element;
    } else {
      if (element.tagName.toLowerCase() === 'object') {
        svg = element.contentDocument.documentElement;
      } else {
        if (element.tagName.toLowerCase() === 'embed') {
          svg = element.getSVGDocument().documentElement;
        } else {
          if (element.tagName.toLowerCase() === 'img') {
            throw new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.');
          } else {
            throw new Error('Cannot get SVG.');
          }
          return null
        }
      }
    }

    return svg
  }

  /**
   * Attach a given context to a function
   * @param  {Function} fn      Function
   * @param  {Object}   context Context
   * @return {Function}           Function with certain context
   */
, proxy: function(fn, context) {
    return function() {
      return fn.apply(context, arguments)
    }
  }

  /**
   * Returns object type
   * Uses toString that returns [object SVGPoint]
   * And than parses object type from string
   *
   * @param  {Object} o Any object
   * @return {String}   Object type
   */
, getType: function(o) {
    return Object.prototype.toString.apply(o).replace(/^\[object\s/, '').replace(/\]$/, '')
  }

  /**
   * If it is a touch event than add clientX and clientY to event object
   *
   * @param  {Event} evt
   * @param  {SVGSVGElement} svg
   */
, mouseAndTouchNormalize: function(evt, svg) {
    // If no clientX then fallback
    if (evt.clientX === void 0 || evt.clientX === null) {
      // Fallback
      evt.clientX = 0
      evt.clientY = 0

      // If it is a touch event
      if (evt.touches !== void 0 && evt.touches.length) {
        if (evt.touches[0].clientX !== void 0) {
          evt.clientX = evt.touches[0].clientX
          evt.clientY = evt.touches[0].clientY
        } else if (evt.touches[0].pageX !== void 0) {
          var rect = svg.getBoundingClientRect();

          evt.clientX = evt.touches[0].pageX - rect.left
          evt.clientY = evt.touches[0].pageY - rect.top
        }
      // If it is a custom event
      } else if (evt.originalEvent !== void 0) {
        if (evt.originalEvent.clientX !== void 0) {
          evt.clientX = evt.originalEvent.clientX
          evt.clientY = evt.originalEvent.clientY
        }
      }
    }
  }

  /**
   * Check if an event is a double click/tap
   * TODO: For touch gestures use a library (hammer.js) that takes in account other events
   * (touchmove and touchend). It should take in account tap duration and traveled distance
   *
   * @param  {Event}  evt
   * @param  {Event}  prevEvt Previous Event
   * @return {Boolean}
   */
, isDblClick: function(evt, prevEvt) {
    // Double click detected by browser
    if (evt.detail === 2) {
      return true;
    }
    // Try to compare events
    else if (prevEvt !== void 0 && prevEvt !== null) {
      var timeStampDiff = evt.timeStamp - prevEvt.timeStamp // should be lower than 250 ms
        , touchesDistance = Math.sqrt(Math.pow(evt.clientX - prevEvt.clientX, 2) + Math.pow(evt.clientY - prevEvt.clientY, 2))

      return timeStampDiff < 250 && touchesDistance < 10
    }

    // Nothing found
    return false;
  }

  /**
   * Returns current timestamp as an integer
   *
   * @return {Number}
   */
, now: Date.now || function() {
    return new Date().getTime();
  }

  // From underscore.
  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time. Normally, the throttled function will run
  // as much as it can, without ever going more than once per `wait` duration;
  // but if you'd like to disable the execution on the leading edge, pass
  // `{leading: false}`. To disable execution on the trailing edge, ditto.
// jscs:disable
// jshint ignore:start
, throttle: function(func, wait, options) {
    var that = this;
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : that.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = that.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        clearTimeout(timeout);
        timeout = null;
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  }
// jshint ignore:end
// jscs:enable

  /**
   * Create a requestAnimationFrame simulation
   *
   * @param  {Number|String} refreshRate
   * @return {Function}
   */
, createRequestAnimationFrame: function(refreshRate) {
    var timeout = null

    // Convert refreshRate to timeout
    if (refreshRate !== 'auto' && refreshRate < 60 && refreshRate > 1) {
      timeout = Math.floor(1000 / refreshRate)
    }

    if (timeout === null) {
      return window.requestAnimationFrame || requestTimeout(33)
    } else {
      return requestTimeout(timeout)
    }
  }
}

/**
 * Create a callback that will execute after a given timeout
 *
 * @param  {Function} timeout
 * @return {Function}
 */
function requestTimeout(timeout) {
  return function(callback) {
    window.setTimeout(callback, timeout)
  }
}

},{}]},{},[1]);

// виджет билетов
// todo переделть на typescript все, разбить на модули, прописать интерфейсы и обязательные типы, а также документировать всё
//todo вывести в отдельную библиотеку-директорию
//todo  https://medium.com/webbdev/7-%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B9-%D0%BF%D0%BE-%D0%BE%D1%84%D0%BE%D1%80%D0%BC%D0%BB%D0%B5%D0%BD%D0%B8%D1%8E-%D0%BA%D0%BE%D0%B4%D0%B0-%D0%BD%D0%B0-javascript-4d0d4c287281
//todo https://habr.com/ru/articles/231883/
//todo https://standardjs.com/
//todo мб сделать pull request в  panzoom по поводу анимации и по поводу режима ведения мыши за пределами зоны и invalidateSize например , также добавить колбеков например
// разбить less на части?

class TWCartsStorage {
    //todo добавить конструктор с передачей ключа
    //todo дававить в конструктор или иным образом подвязка на callback обновления хранилища, чтоб инородный код вычленить. Обзервер добавить в общем
    static get storageKey() {
        return 'carts';
    }

    static all(castCartObj = false) {
        let carts = JSON.parse(window.localStorage.getItem(this.storageKey)) || {};

        if (castCartObj) {
            for (const [cartId, cart] of Object.entries(carts)) {
                carts[cartId] = this.castCart(cart);
            }
        }

        return carts;
    }

    static cartsCount() {
        const carts = JSON.parse(window.localStorage.getItem(this.storageKey)) || {};
        return Object.keys(carts).length;
    }

    static getAllTotalQnt() {
        return Object.values(this.all(true)).reduce((result, cart) => result + cart.ticketsCount(), 0);
    }

    static findCart(cartId, cast = true) {
        return this.all(cast)[cartId] || null;
    }

    static castCart(cartOb) {
        if (Object.keys(cartOb).length) {
            return new TWCart(cartOb.event, cartOb);
        }

        return cartOb;
    }

    static save(cart) {
        const carts = this.all(true);
        carts[cart.id] = cart;

        for (const [cartId, cart] of Object.entries(carts)) {
            if ((cart.event.date * 1000) < new Date() || cart.isEmpty()) {
                delete carts[cartId];
            }
        }
        window.localStorage.setItem(this.storageKey, JSON.stringify(carts));
        TW.updateGlobalCartTotalQnt();
    }

    static clear() {
        window.localStorage.setItem(this.storageKey, JSON.stringify({}));
        TW.updateGlobalCartTotalQnt();
    }
}

class TWCartItem {
    constructor(elemOrObj) {
        this._domElem = undefined;

        if (elemOrObj.nodeName) { //любая сущность svg - circle, path и тп
            const attrs = Object.fromEntries(Array.from(elemOrObj.attributes).map(item => [item.name, item.value]));

            const sectorName = attrs['place-name'];
            const row = attrs['row'];
            const place = attrs['place'];
            const price = attrs['price'];
            const passport = attrs['data-passport'];
            const eTicket = parseInt(attrs['data-eticket']);
            const zr = attrs['data-zr'];
            const zrAgent = attrs['data-zr-agent'];
            const source = attrs['data-source'];

            const bySector = attrs['data-qnt-more'];
            const vip = attrs['data-vip'] || 0;
            const addQnt = attrs['data-qnt-more'] || 1;
            const payment = attrs['data-payment'] || 1;
            
            this.price_n = attrs['price_n'] || '';
            this.price_z = attrs['price_z'] || '';
            this.isGift = !!Number(attrs['data-gift'] || '');
            this.placeName = sectorName || '';
            this.row = row || '';
            this.place = place || '';
            this.onOrder = isNaN(price);
            this.price = this.onOrder ? 0 : +price;
            this.eTicket = +eTicket;
            this.bySector = +bySector;
            this.qnt = +addQnt;
            this.allowPayment = +payment;
            this.needPassport = +passport; //нужны
            this.zr = zr || ''; //нужны
            this.zrAgent = zrAgent || ''; //нужны
            this.isVip = +vip; //нужны
            this.sourceId = +source; //нужны

            this.id = TW.generateTicketId(this.row, this.place, this.placeName);
            this.group = this.zr ? this.zr : this.id;

            if (!elemOrObj.id) {
                elemOrObj.id = this.id;
            } else if (elemOrObj.id != this.id) {
                elemOrObj.id += ' ' + this.id;
            }

        } else {
            for (const [key, value] of Object.entries(elemOrObj)) {
                if (!['hash'].includes(key)) {
                    this[key] = value;
                }
            }
        }
    }

    get hash() {
        let actualKeys = Object.getOwnPropertyNames(this).filter(key => !['_domElem', 'hash', 'group', 'documentData'].includes(key));
        return window.btoa(actualKeys.sort().join(','));
    }

    get domElem() {
        return this._domElem || Array.from(TW.availableTicketsCircleElems).find(el => el.id?.includes(this.id));
    }

    toJSON() {
        let data = Object.assign({}, this, {hash: this.hash});

        if (data._domElem) {
            delete data._domElem;
        }

        return data;
    }
}

class TWCart {
    constructor(eventParams, data = {}) {
        this.createdAt = data.createdAt || +new Date();
        this.documentFields = data.documentFields || '';

        if (data.activatedAt) {
            this.activatedAt = data.activatedAt;
        }

        /**
         * @type {{date,id,name,desc,placeId,placeName,hallId,hallName,partId}}
         */
        this._event = {...eventParams};

        /**
         *
         * @type {TWCartItem[]}
         * @private
         */
        this._items = [];

        for (const item of (data.items || [])) {
            const castCartItem = new TWCartItem(item);
            if (item !== null && castCartItem.hash === item.hash) {
                this._items.push(castCartItem);
            }
        }
    }

    get id() {
        return `${this.event.id}-${this.event.date}-${this.event.placeId}-${this.event.hallId}`;
    }

    /**
     * @returns {Readonly<TWCartItem>[]}
     */
    get items() {
        // return Object.freeze([...this._items].map(item => Object.freeze(new TWCartItem(item))));
        // return [...this._items].map(item => new TWCartItem(item));
        return [...this._items];
    }

    getItemsAsObj(){
        let res = {};

        this._items.forEach((item, key) => {
            res[key] = item;
        });

        return res;
    }
    
    /**
     * @returns {Readonly<{date, id, name, desc, placeId, placeName, hallId, hallName, partId}>}
     */
    get event() {
        return Object.freeze(this._event);
    }

    actualize() {
        const cart = TWCartsStorage.findCart(this.id) || this;
        this.activatedAt = cart.activatedAt || 0;
        this.createdAt = cart.createdAt;

        let actualItems = [];

        if (TW.availableTicketsCircleElems.length) {
            for (let cartItem of cart.items) {
                for (const domElem of TW.availableTicketsCircleElems) {
                    const elemAsCartItem = new TWCartItem(domElem);

                    if (!elemAsCartItem.bySector) {
                        domElem.id = elemAsCartItem.id;
                    }

                    if (elemAsCartItem.id === cartItem.id) {
                        const qnt = cartItem.qnt;
                        const documentData = cartItem.documentData;
                        cartItem = elemAsCartItem;
                        cartItem.qnt = qnt;

                        if (documentData){
                            cartItem.documentData = documentData;
                        }

                        domElem.classList.add('active');
                    }
                }

                actualItems.push(cartItem);
            }
        } else if (TW.config.allPlaces) {
            for (const cartItem of cart.items) {
                if (cartItem.id && TW.config.allPlaces.find(place => place.id === cartItem.id)) {
                    const $domElem = TW.$$(`.tw__tickets-place[data-id="${cartItem.id}"]`).addClass('active');
                    
                    if ($domElem.length){
                        actualItems.push({...cartItem, _domElem: $domElem.get(0)});
                    } else {
                        actualItems.push(cartItem);
                    }
                }
            }
        }

        this._items = actualItems;

        TWCartsStorage.save(this); //для самоочистки хранилища, если на схеме какие-то билеты из хранилища не найдены
    }

    add(cartItem) {
        this.activatedAt = this._items.length > 0 && this.activatedAt ? this.activatedAt : +new Date();

        this._items.push(cartItem);
        TWCartsStorage.save(this);
    }

    update(id, fields) {
        let item = this._items.find(fields => fields.id === id);
        Object.assign(item, fields);
        TWCartsStorage.save(this);
    }

    delete(id) {
        const deleteIndex = this._items.findIndex(fields => fields.id == id);
        const deleteItem = this._items[deleteIndex];

        if (deleteItem) {
            let deleteIndexes = [deleteIndex];
            // this._items.forEach((_item, index) => {
            //     if (
            //         index !== deleteIndex &&
            //         deleteItem.group !== null &&
            //         deleteItem.group !== '0' &&
            //         _item.group === deleteItem.group
            //     ){
            //         deleteIndexes.push(index);
            //     }
            // })

            const cartItemsAsObj = this.getItemsAsObj();

            deleteIndexes.forEach(key => {
                cartItemsAsObj[key]?.domElem?.classList.remove('active');
                
                delete cartItemsAsObj[key];
            });

            this._items = Object.values(cartItemsAsObj);

            const giftItemIndex = this._items.findIndex(item => item.isGift);
            const giftItem = this._items[giftItemIndex] ?? null;
            const ticketsCount = this.ticketsCount(false);
            
            if (giftItem && giftItem.qnt > ticketsCount){
                if (ticketsCount > 0){
                    giftItem.qnt = ticketsCount;
                } else {
                    this._items.splice(giftItemIndex, 1);
                }
            }

            TWCartsStorage.save(this);
        }
    }

    reset() {
        for (const item of this._items) {
            item.domElem?.classList.remove('active');
        }
        this._items = [];
        this.activatedAt = 0;

        TWCartsStorage.save(this);
    }

    reactivate() {
        this.activatedAt = +new Date();
        TWCartsStorage.save(this);
    }

    sum() {
        return this._items.find(obj => obj.onOrder === true) !== undefined
            ? undefined
            : this._items.reduce((result, obj) => result + obj.price * obj.qnt, 0);
    }

    ticketsCount(includeGifts = true) {
        const items = includeGifts ? this._items : this._items.filter(item => ! item.isGift);
        
        return items.reduce((result, obj) => result + +obj.qnt, 0);
    }
    
    giftsCount() {
        return this._items.filter(item => item.isGift).reduce((result, obj) => result + +obj.qnt, 0);
    }

    isEmpty() {
        return !this._items.length;
    }

    toJSON() {
        return {
            items: this.items,
            event: this.event,
            activatedAt: this.activatedAt,
            createdAt: this.createdAt,
            documentFields: this.documentFields,
        }
    }
}

class TicketsWidget {
    constructor(selector, params = {}) {
        this.SCHEME_LIB_LEAFLET = 1;
        this.SCHEME_LIB_PANZOOM = 2;

        this.url = params.url || '/widget/index.php';
        this.version = params.version || 1;
        this.defaultSchemeLib = this.SCHEME_LIB_PANZOOM;
        this.schemeLib = this.defaultSchemeLib;

        this.$ = $(selector);
        this.$tooltip = $();
        this.$loader = $();
        this.$header = $();
        this.$twSvgMap = $();
        this.$confirm = $();
        this.$tools = $();
        this.$calendar = $();
        this.$morePopup = $();

        this.selector = selector;
        this.config = {};
        this.prevActivePanelId = null;
        this.prevActivePanelScrollTop = null;
        this.abortControllers = {
            svg: new AbortController(),
            eventDates: new AbortController(),
            headerDates: new AbortController(),
            config: new AbortController(),
        };
        this.resizeCallbackTimeout = null;
        this.availableTicketsCircleElems = [];
        this.lastClickedTicketElem = null;
        this.lastHoverTicketElem = null;
        this.isMobileWidth = false;
        this.isTouchDevice = false;
        this.isInitialized = false;
        this.filter = {
            uniqueSelector: null,
            class: 'tw-filter',
            scrollerClass: 'tw-filter__scroller',
            btnClass: 'tw-filter__btn', 
            priceGroupsElems: {},
            multipleMode: true,
            colorizeAfterInit: true,
            visible: true,
            visibleBtnsQnt: 0,
            minPrice: 0,
            maxPrice: 0,
        };
        this.scheme = null;
        this.schemeInit = false;
        this.schemeSvgOverlay = null;
        this.schemeSvgAttrs = {};
        this.cartInst = null;
        this.errors = [];

        this.clearEventParams();

        window.TW = this;

        TW.fetch({
            data: {
                action: 'init'
            }
        })
            .then(response => response.json())
            .then(data => {
                if (!data.success) {
                    throw Error('success is not true');
                }
                TW._init(data.html);

                TW.initCartsTimer();
                TW._initFiltersListeners();
                TW._initCalendarListeners();

                if (params.onInitialized && typeof params.onInitialized === 'function') {
                    params.onInitialized();
                }

                TW.isInitialized = true;
                document.documentElement.classList.add('tw-initialized');

                if (location.hash) {
                    TW.openByHash(location.hash);
                }
            })
            .catch(e => {
                console.error(e);
                // alert('Ошибка отправки данных! Попробуйте обновить страницу.');
            })
        ;
    }


    clearEventParams() {
        this.eventParams = {
            date: '',
            id: '',
            name: '',
            desc: '',
            placeId: '', //площадка ID
            placeName: '', // площадка name
            hallId: '', // зал/сцена ID на прощадке
            hallName: '', // зал/сцена NAME на прощадке
            partId: '', // код сектора
        };
    }

    initCartsTimer(limitMinutes = 20) {
        let initCartsTimestamp;

        TW.cartsTimerFunc = setInterval(() => {
            let carts = Object.values(TWCartsStorage.all(true));
            let $cartsTimers = TW.$$('[data-carts-timer]');

            if (!carts.length) {
                $cartsTimers.attr('data-carts-timer', '');
                return;
            }

            initCartsTimestamp = initCartsTimestamp || carts.reduce(
                (accumulator, cart, index, array) => cart.activatedAt && cart.activatedAt < accumulator ? cart.activatedAt : accumulator, +new Date()
            );

            let diff = (initCartsTimestamp + limitMinutes * 60000) - new Date();

            if (carts.length && diff < 0) {
                initCartsTimestamp = +new Date();
                diff = (initCartsTimestamp + limitMinutes * 60000) - new Date();

                carts.forEach(cart => {
                    cart.reactivate();
                });
                // $cartsTimers.attr('data-carts-timer', '');
                // TW.$$('.tw__minicart-delete').trigger('click');
                // TWCartsStorage.clear();
                // return;
            }

            const minutes = Math.floor(diff / 60000);
            let seconds = Math.round((diff % 60000) / 1000);
            seconds = seconds === 60 ? 59 : seconds;

            let timeTxt = minutes < 10 ? '0' + minutes : minutes;
            timeTxt += ':';
            timeTxt += seconds < 10 ? '0' + seconds : seconds;
            $cartsTimers.attr('data-carts-timer', timeTxt);
        }, 1000);
    }

    /**
     * Convert date to Moscow UTC
     *
     * @param unixTimeStamp
     * @param options
     * @returns {string}
     */
    moscowDate(unixTimeStamp, options = {}) {
        unixTimeStamp = !isNaN(unixTimeStamp) && unixTimeStamp.toString().length < 13
            ? unixTimeStamp * 1000
            : unixTimeStamp;
        return new Date(unixTimeStamp).toLocaleDateString('ru-RU', Object.assign({}, options, {timeZone: 'Europe/Moscow'}));
    }

    /**
     *
     * @param unixTimeStamp
     * @param options
     * @param cutMilliseconds
     * @returns {string}
     */
    moscowTime(unixTimeStamp, options = {}, cutMilliseconds = true) {
        unixTimeStamp = !isNaN(unixTimeStamp) && unixTimeStamp.toString().length < 13
            ? unixTimeStamp * 1000
            : unixTimeStamp;

        let date = new Date(unixTimeStamp).toLocaleTimeString('ru-RU', Object.assign({}, options, {timeZone: 'Europe/Moscow'}));
        if (cutMilliseconds) {
            date = date.replace(/(\d+:\d+):\d+$/, '$1');
        }
        return date;
    }

    /**
     * @param params
     * @returns {Promise<Response>}
     */
    fetch(params) {
        params.version = params.version || this.version;
        let debug = params.debug || false;
        let method = params.method ? params.method.toUpperCase() : 'GET';
        let url = typeof params === 'string' ? params : (params.url || TW.url);
        let fetchData = {
            method: method,
            headers: params.headers || {},
            signal: params.signal,
            mode: 'no-cors',
        };

        if (method === 'GET') {
            url += url.includes('?') ? '&' : '?';
            url += (new URLSearchParams(params.data)).toString();
            url = url.replace(/[?&]$/, '');
        } else {
            const body = typeof params.data === 'string' ? params.data : JSON.stringify(params.data);
            fetchData.body = body || undefined;
        }

        return fetch(url, fetchData)
            .then(async response => {
                if (debug) {
                    const clone = response.clone();
                    console.log('responseText: ', await clone.text());
                }
                return response;
            });
    }

    _init(html) {
        this.$.html(html);

        this.$tooltip = TW.$$('#tw-tooltip');
        this.$loader = TW.$$('#tw-loader');
        this.$header = TW.$$('#tw-header');
        this.$twSvgMap = TW.$$('#tw-svg-map');
        this.$confirm = TW.$$('#tw-confirm');
        this.$tools = TW.$$('#tw-tools');
        this.$calendar = TW.$$('#tw-calendar').empty();
        this.$morePopup = TW.$$('#tw-more-popup');
        this.$minicart = TW.$$('.tw__minicart');
        this.$multiCheckoutBtn = TW.$$('.tw-multi-checkout-btn');

        this.$multiCheckoutBtn.on('click', function () {
            TW.openPanel('tw-multi-checkout');
        });

        this.updateMaskElements();
        this.updateGlobalCartTotalQnt();

        this.checkAvailableSchemeZoom = () => {
            if (!TW.scheme){
                return
            }
            const zoom = +TW.scheme.getZoom().toFixed(2);
            const maxZoom = TW.scheme.getMaxZoom();
            const minZoom = TW.scheme.getMinZoom();

            TW.$$('.tw__scheme-zoom-in').toggleClass('--disabled', maxZoom <= zoom);
            TW.$$('.tw__scheme-zoom-out').toggleClass('--disabled', minZoom >= zoom);
        }

        this.resizeSvg = (reinitSizes = false) => {
            if (TW.schemeInit && TW.schemeLib === TW.SCHEME_LIB_PANZOOM && ! TW.schemeImageInit){
                const zoom = +TW.scheme.getZoom().toFixed(2);
                const minZoom = +TW.scheme.getMinZoom().toFixed(2);

                if (minZoom >= zoom || zoom > minZoom && zoom <= minZoom + TW.schemeZoomStep){
                    TW.scheme.setMinZoom(TW.schemeInitMinZoom);
                    TW.scheme.zoomBy(1);
                    TW.scheme.invalidateSize();
                    reinitSizes = true;
                } else {
                    TW.scheme.resize();
                }

                if (reinitSizes){
                    const schemeSizes = TW.scheme?.getSizes();

                    if (schemeSizes){
                        let newZoom = zoom; 
                        const viewportRect = TW.schemeInnerViewport.getBoundingClientRect();
                        const headerHeight = TW.$header.outerHeight();
                        const minicartHeight = TW.$minicart.filter('.active').outerHeight() || 0;

                        const schemeOffsetTop = TW.scheme.getPan().y;

                        if (schemeOffsetTop <= headerHeight) {
                            TW.scheme.panBy({x: 0, y: headerHeight - schemeOffsetTop});
                        }

                        if (viewportRect.width > schemeSizes.width) {
                            const wZoom = (schemeSizes.width / viewportRect.width).toFixed(2);
                            // newZoom = wZoom < newZoom ? newZoom * wZoom : newZoom;
                            while(wZoom < newZoom){
                                newZoom -= TW.schemeZoomStep;
                            }
                        }

                        if (viewportRect.height > (schemeSizes.height - headerHeight - minicartHeight)) {
                            const hZoom = ((schemeSizes.height - headerHeight - minicartHeight) / viewportRect.height).toFixed(2);
                            while(hZoom < newZoom){
                                newZoom -= TW.schemeZoomStep;
                            }

                            const newOffset = -(TW.scheme.getPan().y - (TW.scheme.getPan().y * newZoom)) ;
                            TW.scheme.panBy({x: 0, y: newOffset + 10});
                        }

                        TW.scheme.zoomBy(newZoom);
                        TW.scheme.setMinZoom(+newZoom.toFixed(2));
                    }
                }
            }

            TW.checkAvailableSchemeZoom();
        }

        this.resizeCallback = function () {
            clearTimeout(TW.resizeCallbackTimeout);

            TW.isMobileWidth = window.innerWidth < 768;
            TW.isTouchDevice = (('ontouchstart' in window) || (navigator.maxTouchPoints & 0xFF) || (navigator.msMaxTouchPoints > 0));

            TW.resizeCallbackTimeout = setTimeout(function () {
                document.documentElement.classList.toggle('is-mobile', TW.isMobileWidth);
                document.documentElement.classList.toggle('is-touch-device', TW.isTouchDevice);

                TW.resizeSvg();
                TW.updateFloatingMargins();
            }, 200);
        }

        this.hashchange = function () {
            TW.openByHash(location.hash);
        }

        $(window).on('resize', this.resizeCallback).trigger('resize');
        $(window).on('hashchange', this.hashchange);
        
        document.addEventListener('touchmove', (e) => {
            window.moveTouchesCnt = e.touches?.length || e.changedTouches?.length || e.targetTouches?.length || 1;
        }, {passive: true});

        TW.$morePopup.on('mousedown', '.tw__close', function (e) {
            TW.$morePopup.removeClass('active');
        });

        TW.$morePopup.on('input', '.tw__popup-changer-qnt', function (e) {
            let $input = $(this);
            let cartItemOb = new TWCartItem(TW.lastClickedTicketElem);
            let cartItem = TW.cartInst.items.find(item => item.id === cartItemOb.id);
            const max = +$input.attr('data-max');
            // const max = 10;
            let value = Math.abs(+$input.val());

            if (value < 0) {
                value = 0;
            } else if (value > max) {
                value = max;
            }

            const freeCount = max - value;

            if (!cartItem) {
                cartItemOb.qnt = value || 1;
                TW.cartInst.add(cartItemOb);
            } else if (value > 0) {
                TW.cartInst.update(cartItem.id, {qnt: value});
            } else {
                TW.cartInst.delete(cartItem.id);
            }

            $input.parents('.tw__popup-changer').find('[data-free-count]').text(freeCount);
            $input.prev('.tw__popup-changer-btn')[0].toggleAttribute('disabled', value <= 0);
            $input.next('.tw__popup-changer-btn')[0].toggleAttribute('disabled', freeCount === 0);

            const totalSum = value * cartItemOb.price;
            TW.$morePopup.find('[data-sum]').text(totalSum ? `${totalSum} ₽` : 'под заказ');
            TW.$morePopup.find('[data-tickets]').text(value + ' ' + TW.declOfNum(value, ['билет', 'билета', 'билетов']));
            TW.$morePopup.find('.tw__popup-qnt-alert').toggleClass('active', value >= max).find('[data-one-hand-max]').text(max);
            TW.$morePopup.find('.tw__popup-buy').toggleClass('tw-hidden', !value);

            $input.val(value);

            TW.redrawMinicart();
        })

        TW.$morePopup.on('mousedown', '.tw__popup-changer-btn', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            let step = parseInt(this.dataset.step);
            let $input = $(this).parent().find('.tw__popup-changer-qnt');
            let newQnt = parseInt($input.val()) + step;

            $input.val(newQnt).trigger('input');
        });

        TW.$morePopup.on('click', '.tw__popup-buy', function () {
            // let cartItemOb = new TWCartItem(TW.lastClickedTicketElem);
            //
            // if (TW.cartInst.items.findIndex(item => item.id === cartItemOb.id) === -1) {
            //     TW.cartInst.add(cartItemOb);
            //     TW.redrawMinicart();
            // }
            //
            // TW.$morePopup.removeClass('active');
            //
            // TW.openPanel('tw-checkout');

            // TW.$$('.js-tw-btn-to-checkout').trigger('click');

            TW.$morePopup.removeClass('active');
        });

        TW.$.on('click', '.tw__number-input-up, .tw__number-input-down', function (e) {
            const input = this.parentElement.querySelector('input');
            this.classList.contains('tw__number-input-up')
                ? input.stepUp()
                : input.stepDown()
        });

        TW.$.on('click', '.js-open-hash', function (e) {
            if (location.hash === new URL(this.href).hash) {
                TW.openPanel('tw-scheme');
            } else {
                TW.openByHash(location.hash);
            }
        });

        TW.$.on('mousedown', '#tw-main-close', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();
            TW.closePanel();
        });

        TW.$.on('click', '.tw__scheme-to-table-btn', function () {
            TW.$$('.tw__scheme-to-table-btn').addClass('tw-hidden');
            TW.openPanel('tw-tickets', {missLoadConfig: true});
            TW.redrawMinicart();
        });

        TW.$.on('mousedown', '.tw__scheme-zoom-in', function (e) {
            e.preventDefault();
            TW?.schemeInnerViewport?.classList.add('smooth');
            TW.scheme.zoomIn();
            TW.checkAvailableSchemeZoom();
        });

        TW.$.on('mousedown', '.tw__scheme-zoom-out', function (e) {
            e.preventDefault();
            TW?.schemeInnerViewport?.classList.add('smooth');
            TW.scheme.zoomOut();
            TW.checkAvailableSchemeZoom();
        });

        TW.$.on('click', '.tw__tickets-to-scheme', function () {
            let imageUrl = this.getAttribute('data-image');
            TW.openPanel('tw-scheme'); //сначала нужно открыть панель, чтобы прорисовались блоки
            TW.$$('.tw__scheme-to-table-btn').removeClass('tw-hidden');

            if (!TW.schemeImageInit || !TW.scheme || !TW.schemeSvgOverlay) {
                if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET) {
                    TW.schemeSvgOverlay?.remove().removeEventListener();
                    TW.scheme?.remove();
                }

                const mapBlock = TW.$twSvgMap.get(0);
                TW.$twSvgMap.empty();

                //всегда leaflet, либо засунуть img и object или svg, чтобы работать с panzoom
                TW.scheme = L.map(mapBlock, {
                    minZoom: 1,
                    maxZoom: 3,
                    zoom: 1,
                    center: [0, 0],
                    attributionControl: false,
                    zoomControl: false,
                    scrollWheelZoom: true,
                    doubleClickZoom: false,
                    closePopupOnClick: false,
                    tapHold: false,
                    boxZoom: false,
                    keyboard: false,
                    fadeAnimation: false,
                    inertia: false,
                    bounceAtZoomLimits: false,
                    crs: L.CRS.Simple,
                });

                TW.schemeImageInit = true;
                TW.schemeLib = TW.SCHEME_LIB_LEAFLET;

                const getBounds = (width = 500, height = 500) => {
                    let southWest = TW.scheme.unproject([0, height], TW.scheme.getMaxZoom());
                    let northEast = TW.scheme.unproject([width, 0], TW.scheme.getMaxZoom());
                    return new L.LatLngBounds(southWest, northEast);
                }

                TW.schemeSvgOverlay = L.imageOverlay(imageUrl, getBounds()).addTo(TW.scheme);

                TW.schemeSvgOverlay._image.onload = function () {
                    const h = this.naturalHeight;
                    const w = this.naturalWidth;
                    const mapRect = mapBlock.getBoundingClientRect();
                    const ratioH = Math.ceil(h / mapRect.height);
                    const ratioW = Math.ceil(w / mapRect.width);
                    let maxZoom = TW.scheme.getMaxZoom();
                    let updateMinZoom = false;

                    if (ratioW > maxZoom || ratioH > maxZoom) {
                        TW.scheme.setMaxZoom(Math.max(ratioW, ratioH));
                        updateMinZoom = true;
                    }

                    const newBounds = getBounds(w, h);
                    TW.schemeSvgOverlay.setBounds(newBounds);
                    TW.scheme.fitBounds(newBounds, {animate: false});
                    TW.scheme.setMaxBounds(newBounds);

                    if (ratioH === 1 && ratioW === 1) {
                        TW.scheme.setMinZoom(TW.scheme.getMaxZoom() - 1, {animate: false});
                        TW.scheme.setMaxZoom(TW.scheme.getMaxZoom() + 1, {animate: false});
                        TW.scheme.setZoom(maxZoom, {animate: false});
                    }

                    if (updateMinZoom) {
                        TW.scheme.setMinZoom(TW.scheme.getZoom() - 1);
                    }
                }
            }


            // TW.$$('.tw__tickets').removeAttr('data-sector-open');
        });

        TW.$.on('click', '.tw__tickets-place', function () {
            const id = this.getAttribute('data-id');
            const sectorId = this.getAttribute('data-sector-id');
            const place = TW.config.sectors.find(sector => sector.id == sectorId).places.find(item => item.id === id);
            const cartItemOb = new TWCartItem({...place, _domElem: this});
            const existCartItem = TW.cartInst.items.find(item => item.id === cartItemOb.id);

            if (!existCartItem) {
                TW.cartInst.add(cartItemOb);
                this.classList.add('active');
            } else {
                // this.classList.remove('active'); //удаление происходит внутри  TW.cartInst.delete
                TW.cartInst.delete(existCartItem.id);
            }

            TW.redrawMinicart();
        });

        TW.$.on('click', '.tw__tickets-to-sectors', function () {
            TW.$$('.tw__tickets').removeAttr('data-sector-open')
                .find('.tw__tickets-sector-opener').removeClass('active');
        });

        TW.$.on('click', '.tw__tickets-sector-opener', function () {
            const $selfBtn = $(this).toggleClass('active');
            const $panel = $selfBtn.parents('.tw__tickets');
            const $sector = $selfBtn.parents('.tw__tickets-sector');
            let sectorId = $sector.data('id');
            let sectorIsOpened = $selfBtn.hasClass('active');
            let isLandingMode = $panel.is('[data-landing-mode]');
            const sectorData = TW.config.sectors.find(sector => sector.id == sectorId) || {};
            let $rowsBlock = $('<div class="tw__tickets-rows"></div>');
            let placeItems = [];

            if (isLandingMode) {
                if (!$sector.find('.tw__tickets-rows').length) {
                    $sector.append($rowsBlock);
                }
            } else {
                $panel.find('.tw__tickets-rows').remove();
                $panel.find('.tw__tickets-sectors').after($rowsBlock);

                if (sectorIsOpened) {
                    $panel.attr('data-sector-open', sectorId).find('.tw__tickets-sector-name').html(sectorData.name);
                } else {
                    $panel.removeAttr('data-sector-open').find('.tw__tickets-sector-name').html('');
                }
            }

            $sector.find('.tw__tickets-rows').toggleClass('tw-hidden', !sectorIsOpened);

            if (sectorIsOpened && sectorData.places) {
                let row;
                let $rowItem;
                let index = 0;
                let placeOb = sectorData.places[index] || null;

                while (placeOb) {
                    if (sectorData.display_max_count && index + 1 >= sectorData.display_max_count) {
                        break;
                    }
                    if (placeOb.row !== row) {
                        $rowItem = $('<div>', {
                            'class': "tw__tickets-row",
                            'data-caption': "Ряд",
                            'data-row': placeOb.row,
                        });
                        $rowsBlock.append($rowItem);
                    }

                    if ($rowItem) {
                        const $placeItem = $('<span>', {
                            'class': "tw__tickets-place",
                            'data-price': placeOb.price,
                            'data-place': placeOb.place,
                            'data-id': placeOb.id,
                            'data-sector-id': sectorId,
                        }).text(placeOb.place);

                        $rowItem.append($placeItem);
                        placeItems.push($placeItem.get(0));
                    }

                    row = placeOb.row;

                    placeOb = sectorData.places[++index];
                }

                TW._initFilter('.tw__tickets-filter', placeItems, {
                    colorizeAfterInit: true,
                });

                $panel.find('.tw__tickets-row').each(function () {
                    let $self = $(this);
                    if (!$self.find('.tw__tickets-places').length) {
                        $self.wrapInner('<div class="tw__tickets-places"></div>');
                    }
                });
            }

            TW.cartInst.actualize();
            TW.redrawMinicart();
        });

        TW.$.on('mousedown', '.tw__scheme-back, .js-tw-checkout-close', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            if (TW.eventParams.partId) {
                TW.getEvent({partId: ''}, true, true);
                return;
            }

            TW.closePanel();
        });

        TW.$.on('mouseenter', '.tw__minicart-count', function () {
            TW.$minicart.find('.tw__minicart-items').addClass('floating');
        });

        TW.$.on('mouseleave', '.tw__minicart-count, .tw__minicart-btn, .tw__minicart-items', function (e) {
            if (e.relatedTarget && !e.relatedTarget.closest('.tw__minicart-items') && !e.relatedTarget.closest('.tw__minicart-btn')) {
                TW.$minicart.find('.tw__minicart-items').removeClass('floating');
            }
        });

        TW.$.on('mousedown', '.tw__minicart-delete', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            TW.cartInst?.reset();
            TW.$tooltip.hide();
            TW.$morePopup.removeClass('active');

            TW.redrawMinicart();
        });

        TW.$.on('mousedown', '.js-reset-carts', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            TW.cartInst?.reset();
            TWCartsStorage.clear();

            TW.updateCheckout();
        });

        TW.$.on('mousedown', '.js-reset-cart', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            const cartId = this.closest('[data-cart]').getAttribute('data-cart');
            const cart = TW.cartInst && TW.cartInst.id == cartId ? TW.cartInst : TWCartsStorage.findCart(cartId);
            cart?.reset();

            TW.updateCheckout();
        });

        TW.$tooltip.on('touchend', function () {
            TW.$tooltip.hide();
        });


        TW.$.on('mouseover', '#tw-svg-map svg:not(.--dragging):not(.--delay-dragging) .tickets_avail', function (e) {
            if (!TW.isTouchDevice) {
                TW.showTooltip(this);
                e.stopPropagation();
                e.stopImmediatePropagation();

                e.target.classList.add('--hover');
            }
        });

        TW.$.on('mouseout', '#tw-svg-map svg .tickets_avail', function (e) {
            if (!TW.isTouchDevice) {
                TW.$tooltip.hide();

                e.target.classList.remove('--hover');
            }

            // if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET){
            //     TW.scheme.dragging.enable();
            // }
        });


        // TW.$.on('mousedown', '#tw-svg-map svg .tickets_avail', function (e) {
        //     if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET){
        //         TW.scheme.dragging.disable();
        //     }
        // });

        TW.$.on('mousedown', '#tw-svg-map svg', function (e) {
            if (TW.isTouchDevice) {
                TW.$tooltip.hide();
            }
        });

        //todo протестить click событие с остановкой preventDefault and propogation, Также протестить если ли разница когда события навешиваются прямо на элементы, а не родителя
        TW.$.on('mouseup', '#tw-svg-map svg:not(.--delay-dragging) .tickets_avail', function (e) {
            if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET) {
                TW.scheme.dragging.enable();
            }

            let cartItemOb = new TWCartItem(this);

            // if (cartItemOb.group !== '0') {
            //     const elems = document.querySelectorAll(`[data-zr="${cartItemOb.group}"]`);
            //     if (elems.length) {
            //         elems.forEach((elem) => selectTicket(elem));
            //     }
            // } else {
                selectTicket(this);
            // }
        });


        TW.$.on('mousedown', '.tw__minicart-item-delete', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            const itemId = $(this).parents('[data-cart-item]').attr('data-cart-item');
            TW.cartInst?.delete(itemId);
            TW.redrawMinicart();
        });

        TW.$.on('mousedown', '.js-tw-checkout-delete-item', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            const $item = $(this).parents('[data-cart-item]');
            const cartId = $item.parents('[data-cart]').attr('data-cart');
            const checkoutPanelId = $item.parents('.tw__checkout').attr('id');

            const cart = TW.cartInst && TW.cartInst.id == cartId
                ? TW.cartInst
                : TWCartsStorage.findCart(cartId);

            cart?.delete($item.attr('data-cart-item'));
            TW.updateCheckout(checkoutPanelId);
        });

        TW.$.on('mousedown', '.tw-summary', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            $(this).parents('.tw-details').toggleClass('open');
        });

        TW.$.on('mousedown', '.js-tw-btn-to-checkout', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            TW.metrikaReachGoal('widget_btn-to-checkout_click');

            //todo сделать единую

            // if (TWCartsStorage.cartsCount() > 1) {
            TW.openPanel('tw-multi-checkout');
            // } else {
            //     TW.openPanel('tw-checkout');
            // }
        });

        TW.$.on('keyup', '.tw__checkout-form', function () {
            if (!TW.blockCheckoutFormFillMetrika){
                TW.metrikaReachGoal('widget_form_fill');

                TW.blockCheckoutFormFillMetrika = true;
            }
        });

        TW.$.on('keyup change', '.tw__checkout-form input', function () {
            if (this.value.length && !this.checkValidity()) {
                this.setCustomValidity('');
            }
        });

        TW.$.on('submit', 'form.tw__checkout-form', function (e) {
            e.preventDefault();
        });

        TW.$.on('mousedown', '.js-tw-btn-to-payment', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            const $checkout = $(this).parents('.tw__checkout');
            const $form = $checkout.find('form.tw__checkout-form');
            const serializeData = $form.serializeArray();
            const $invalidElems = $form.find(':invalid');
            const cartItems = $checkout[0].querySelectorAll('.tw-cart-item:not(.tw-hidden)');
            const hasDataFields = TW.cartInst?.documentFields ?? $checkout.find('[data-viewer-data]:visible').length;
            const usersData = [];
            let isValidViewerData = true;
            let dataFields = [];

            if (cartItems.length && hasDataFields) {
                cartItems.forEach((cartItem, index) => {
                    const viewerData = cartItem.querySelectorAll('[data-viewer-data-content]');

                    if (viewerData.length) {
                        viewerData.forEach((item) => {
                            const fieldsViewerData = item.querySelectorAll('input[name]');

                            if (fieldsViewerData.length) {

                                fieldsViewerData.forEach((field) => {
                                    const name = field.getAttribute('name');
                                    const fieldValue = field.value.trim();

                                    let isValid = fieldValue.length > 0 && field.reportValidity();

                                    dataFields.push(field);

                                    if (isValid) {
                                        let value = field.value.trim();

                                        field.classList.remove('invalid');
                                        usersData[index] = {
                                            ...usersData[index],
                                            [name]: value
                                        };
                                    } else {
                                        const viewerData = field.closest('[data-viewer-data]');

                                        const checkoutCarts = cartItem.closest('.tw__checkout-carts');
                                        const cartDetails = cartItem.closest('.js-tw-checkout-cart-details');
                                        cartDetails.classList.add('open');
                                        setTimeout(() => {
                                            checkoutCarts?.scrollIntoView({ behavior: 'smooth' });
                                        })
                                        
                                        viewerData.classList.add('active');
                                        field.classList.add('invalid');
                                        isValidViewerData = false;
                                    }
                                });
                            }
                        });
                    }
                })
            }

            if (!$invalidElems.length && isValidViewerData) {
                TW.metrikaReachGoal('widget_button_pay_click');

                let params = {};
                let resetOrderCartsFunc;
                let carts = Object.entries(TWCartsStorage.all(true));
                let orders = [];

                if (hasDataFields) {
                    carts = carts.map((cart) => {
                        if (typeof cart === 'string') return cart;
    
                        let items = [];
    
                        if (cart[1].items.length) {
                            items = cart[1].items.map((item, indexItem) => ({
                                ...item,
                                ...usersData[indexItem]
                            }));
                        }
    
                        return [
                            cart[0],
                            { ...cart[1], items }
                        ];
                    });
                }

                for (const [cartId, cart] of carts) {
                    orders.push({
                        event: cart._event,
                        tickets: cart.items,
                    });
                }

                params = {
                    action: 'do_orders',
                    panelType: 'checkout',
                    orders: orders
                }

                if ($checkout.attr('id').includes('tw-multi-checkout') && TWCartsStorage.cartsCount() > 1) {
                    resetOrderCartsFunc = function () {
                        TWCartsStorage.clear();
                        TW.cartInst = null;
                    }
                } else {
                    resetOrderCartsFunc = function () {
                        TW.cartInst.reset();
                    }
                }

                $.each(serializeData, function () {
                    params[this.name] = this.value;
                });

                params.errors = TW.errors;

                TW.fetch({
                    method: 'POST',
                    data: params,
                })
                    .then(response => response.json())
                    .then(data => {
                        if (window.sendSuccessStatisticRequest && typeof window.sendSuccessStatisticRequest === 'function') {
                            try {
                                window.sendSuccessStatisticRequest();
                            } catch (e) {
                                console.error(e);
                            }
                        }

                        if (!data.success) {
                            throw Error('Не удалось оформить заказ');
                        }

                        $form.get(0).reset();

                        if (data.redirectUrl) {
                            TW.errors = [];
                            const $redirectForm = $('<form>', {
                                'action': data.redirectUrl,
                                'method': data.method || 'post',
                            });

                            for (const [key, val] of Object.entries(data.redirectParams || [])){
                                $redirectForm.append($('<input>', {
                                    name: key,
                                    value: val,
                                    type: 'hidden'
                                }));
                            }

                            $('body').append($redirectForm.hide());
                            $redirectForm.trigger('submit');
                        } else {
                            TW.openPanel('payment-success-page')
                                .find('[data-name]').text(data.name).end()
                                .find('[data-order-id]').text(data.number).end()
                                .find('[data-email]').text(data.email).end()
                        }

                        resetOrderCartsFunc();
                    })
                    .catch(e => {
                        console.error(e);
                        TW.errors.push(e.toString());
                        TW.openPanel('tw-request');
                        // alert('Ошибка отправки данных! Попробуйте обновить страницу.');
                    })
                ;
            } else {
                $invalidElems.first().trigger('focus');

                if (dataFields.length > 0) {
                    const invalidItem = dataFields.find((item) => item.classList.contains('invalid'))

                    if (invalidItem) {
                        const value = invalidItem.value;
                        const name = invalidItem.getAttribute('name');
                        const nextElem = invalidItem.nextElementSibling;

                        if (name === 'document' && value.length > 0) {
                            if (!nextElem.classList.contains('tw-error-message')) {
                                const errorElement = document.createElement('span');
                                errorElement.classList.add('tw-error-message');
                                errorElement.textContent = 'Номер документа не может совпадать с другим';
                                invalidItem.after(errorElement);
                            }
                        } else {
                            const errorMessages = document.querySelectorAll('.tw-error-message');

                            if (errorMessages.length > 0) {
                                errorMessages.forEach((item) => item.remove());
                            }
                        }

                        if (TW.isMobileWidth) {
                            invalidItem.scrollIntoView(
                                {
                                    behavior: 'smooth',
                                    block: 'center',
                                    inline: 'start'
                                }
                            )
                        }
                    } else {
                        const errorMessages = document.querySelectorAll('.tw-error-message');

                        if (errorMessages.length > 0) {
                            errorMessages.forEach((item) => item.remove());
                        }
                    }
                } else if (TW.isMobileWidth) {
                    $checkout.get(0).scroll({
                        top: 0,
                        behavior: 'smooth'
                    });
                }
            }
        });

        TW.$.on('click', '.js-tw-give-request', function () {
            const $form = $(this).parents('form');
            const serializeData = $form.serializeArray();

            $form.find('[data-required-group]').each(function () {
                let $group = $form.find(`[data-required-group="${this.dataset.requiredGroup}"]`);
                let groupIsValid = $group.filter((index, el) => el.value.length > 0).length > 0;

                if (!groupIsValid) {
                    $group.prop('required', true).addClass('invalid').one('input', function () {
                        $group.prop('required', false).removeClass('invalid');
                    });
                }
            });

            let $invalidElems = $form.find(':invalid');

            if (!$invalidElems.length) {
                let params = {
                    action: 'do_orders',
                    panelType: 'requestForm',
                    orders: [{
                        event: TW.eventParams,
                    }]
                }

                $.each(serializeData, function () {
                    params[this.name] = this.value;
                });

                params.errors = TW.errors;

                TW.fetch({
                    method: 'POST',
                    data: params,
                })
                    .then(response => response.json())
                    .then(data => {
                        if (!data.success) {
                            throw Error('Не удалось оформить заказ из формы заявки');
                        }

                        TW.errors = [];

                        $form.addClass('submitted');
                    })
                    .catch(e => {
                        TW.errors.push(e.toString());
                        console.error(e);
                        // alert('Ошибка отправки данных! Попробуйте обновить страницу.');
                    })
                ;
            } else {
                $invalidElems.first().trigger('focus');

                if (TW.isMobileWidth) {
                    $form.parent().get(0).scroll({
                        top: 0,
                        behavior: 'smooth'
                    });
                }
            }
        });

        TW.$.on('change', '.js-request-date-select', function () {
            const $selectedOption = $(this).children(':selected').first();
            if ($selectedOption.attr('data-tickets-count') > 0) {
                TW.openPanel('tw-scheme');
                TW.getEvent({
                    date: $selectedOption.val(),
                    hallId: $selectedOption.attr('data-hall-id'),
                    hallName: $selectedOption.attr('data-hall-name'),
                    placeId: $selectedOption.attr('data-place-id'),
                    placeName: $selectedOption.attr('data-place-name'),
                });
            }
        });

        TW.$.on('mousedown', '.js-request-reset-form', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();

            TW.closePanel();
            TW.$$('.tw__request-form').removeClass('submitted').get(0).reset();
        });

        TW.$.on('click', '.js-tw-page-link', function (e) {
            e.preventDefault();
            TW.openPanel(this.href.split('#')[1]);
        });

        TW.$.on('change', '[data-viewer-data-content]', function (e) {
            const carts = TWCartsStorage.all(true);
            const cartId = $(this).closest('[data-cart]').attr('data-cart');
            
            const cartInst = carts[cartId];
            
            const cartItemId = e.target.closest('[data-cart-item]')?.getAttribute('data-cart-item');
            const cartItem = cartInst.items.find(fields => fields.id === cartItemId);
            
            if (cartItem){
                const documentAlreadyUsed = !! cartInst.items.filter(item => item.id !== cartItem.id && item.documentData?.document.replace(/\s+/g, '') === e.target.value.replace(/\s+/g, '')).length;
                if (e.target.name === 'document' && documentAlreadyUsed){
                    alert('Такие паспортные данные уже используются в другом билете!');
                    e.target.classList.add('invalid');
                    e.target.value = '';
                    return;
                }
                cartItem.documentData ??= {};
                cartItem.documentData[e.target.name] = e.target.value;
                cartInst.update(cartItemId, {
                    documentData: cartItem.documentData
                });
            }
        });

        TW.$.on('click', '.js-details .js-summary', function (e) {
            e.preventDefault();
            
            if ( ! TW.$$('.tw__header-date-select .tw__header-date-option').length){
                return;
            }
            
            const $details = $(this).parents('.js-details');

            $details.toggleClass('open');

            if ($details.hasClass('open')) {
                TW.$.one('click', function () {
                    $details.removeClass('open');
                });
            }
        });
    }

    /**
     * Init listeners for calendar panel
     *
     * @private
     */
    _initCalendarListeners() {
        TW.$.on('click', '.tw__header-picker:not(.--locked)', function () {
            if (TW.$calendar.hasClass('active')) {
                TW.closePanel('tw-calendar');
            } else {
                TW.getCalendar(true, false);
            }
        });

        TW.$.on('click', '.tw__calendar-month', function () {
            let $month = $(this).addClass('active');
            $month.siblings().removeClass('active');
            const monthNumber = $month.data('month');

            TW.$$(`.tw__calendar-days[data-month="${monthNumber}"]`).addClass('active')
                .siblings().removeClass('active')
        });

        TW.$.on('click', '.tw__calendar-day.available', function () {
            const $self = $(this);
            const date = $self.data('date');

            const $pickers = TW.$calendar.find('.tw-datepicker').addClass('tw-hidden')
                .filter(`[data-date="${date}"]`).removeClass('tw-hidden')
            ;

            if ($pickers.length === 1) {
                $pickers.first().trigger('click');
                // } else if ($pickers.length > 1 && TW.isMobileWidth) {
            } else if ($pickers.length > 1) {
                TW.$calendar.get(0).scroll({
                    top: $pickers.first().position().top,
                    behavior: 'smooth'
                });
            }

            $('.tw__calendar-select-caption').toggle($pickers.length > 0);
        });

        TW.$.on('click', '.js-tw-time-picker', function (e) {
            const $self = $(this);
            TW.$$('.tw__header-date').prop('open', false);

            TW.openPanel('tw-scheme');
            TW.getEvent({
                date: $self.attr('data-timestamp'),
                name: $self.attr('data-event-date-name') || $self.attr('data-event-name'),
                placeId: $self.attr('data-place-id'),
                hallId: $self.attr('data-hall-id'),
                hallName: $self.attr('data-hall-name'),
                placeName: $self.attr('data-place-name'),
                partId: '',
            });


            // alternative way for change map with reload widget
            // TW.reload({
            //     date: $self.attr('data-timestamp'),
            //     placeId: $self.attr('data-place-id'),
            //     hallId: $self.attr('data-hall-id'),
            //     hallName: $self.attr('data-hall-name'),
            //     placeName: $self.attr('data-place-name'),
            //     partId: '',
            // });
        });

    }

    _initFilter(uniqueSelector, filteringDomElements = [], additionalParams = {}) {
        TW.filter = Object.assign(TW.filter, additionalParams, {uniqueSelector: uniqueSelector});
        TW.filter.$ = TW.$$(uniqueSelector);
        TW.filter.$scroller = TW.filter.$.find('.' + TW.filter.scrollerClass);
        TW.filter.$getButtons = (jqFilter = '') => {
            let $res = TW.filter.$.find('.' + TW.filter.btnClass);
            return jqFilter ? $res.filter(jqFilter) : $res;
        };

        const getDivisionTextByVal = (val, postfix = '') => {
            if (isNaN(val)) {
                return val;
            }

            return (val < 20000 ? val : val / 1000 + ' тыс') + postfix
        };

        TW.filter.$getButtons().each(function (index, btn) {
            const next = this.nextElementSibling;
            let priceTo = +btn.getAttribute('data-price-to');

            if (!priceTo) {
                priceTo = next
                    ? next.getAttribute('data-price-from') - 1
                    : Infinity;
                btn.setAttribute('data-price-to', priceTo);
            }
        });

        TW.filter.priceGroupsElems = {};

        for (const placeElem of filteringDomElements) {
            const ticketPrice = +placeElem.getAttribute('price') || +placeElem.getAttribute('data-price');
            TW.filter.priceGroupsElems[ticketPrice] = TW.filter.priceGroupsElems[ticketPrice] || [];
            TW.filter.priceGroupsElems[ticketPrice].push(placeElem);


            if (ticketPrice < TW.filter.minPrice || !TW.filter.minPrice) {
                TW.filter.minPrice = ticketPrice;
            }
            if (ticketPrice > TW.filter.maxPrice) {
                TW.filter.maxPrice = ticketPrice;
            }
        }

        const useLoadedFilterBtns = Object.keys(TW.filter.priceGroupsElems).length > 14;
        TW.filter.$getButtons(':not([data-default])').remove();

        if (!useLoadedFilterBtns) {
            window.TWfilterDefaultBtns = window.TWfilterDefaultBtns || TW.filter.$getButtons('[data-default]');
            window.TWfilterDefaultBtns.detach();

            for (const price of Object.keys(TW.filter.priceGroupsElems)) {
                if (!isNaN(price) && price > 0) {
                    TW.filter.$scroller.append(
                        $('<div>', {
                            'class': TW.filter.btnClass,
                            'data-price-from': price,
                            'data-price-to': price,
                            // }).text(parseFloat(price).toLocaleString('ru-RU'))
                        }).text(getDivisionTextByVal(price, '+'))
                    );
                }
            }
        } else {
            if (!TW.filter.$getButtons().length) {
                TW.filter.$scroller.append(window.TWfilterDefaultBtns);
            }

            const firstBtnPriceFrom = TW.filter.$getButtons().first().attr('data-price-from');

            if (TW.filter.minPrice < firstBtnPriceFrom) {
                const $btn = $('<div>', {
                    'class': TW.filter.btnClass,
                    'data-price-from': TW.filter.minPrice,
                    'data-price-to': firstBtnPriceFrom - 1,
                    // }).text('от ' + getDivisionTextByVal(TW.filter.minPrice)));
                }).text(getDivisionTextByVal(TW.filter.minPrice, '+'));

                TW.filter.$scroller.prepend($btn);
            }
        }

        TW.filter.visibleBtnsQnt = 0;
        TW.filter.$getButtons().each(function (index, btn) {
            const priceFrom = +btn.getAttribute('data-price-from');
            const priceTo = +btn.getAttribute('data-price-to');

            const color = window.getComputedStyle(btn, ':before').getPropertyValue('background-color');
            let count = 0;
            let isSvg = false;

            for (const [price, group] of Object.entries(TW.filter.priceGroupsElems)) {
                if (!isNaN(price) && price >= priceFrom && price <= priceTo) {
                    for (const placeEl of group) {
                        count++;

                        if (TW.filter.colorizeAfterInit && color) {
                            isSvg = isSvg || !!placeEl.closest('svg');

                            if (isSvg) {
                                placeEl.setAttribute('fill', color);
                                placeEl.setAttribute('stroke', color);
                            } else {
                                placeEl.style.borderColor = color;
                            }
                        }
                    }
                }
            }

            $(btn).attr('data-counter', count);

            if (count) {
                TW.filter.visibleBtnsQnt++;
            }
        });
        TW.filter.$
            .toggleClass('tw-hidden', !TW.filter.visible || TW.filter.visibleBtnsQnt < 2)
            .attr('data-visible-qnt', TW.filter.visibleBtnsQnt)
            .attr('data-default-mode', useLoadedFilterBtns)
        ;
    }

    _initFiltersListeners() {
        TW.$.on('wheel', `.${TW.filter.scrollerClass}`, function (e) {
            if (!TW.isTouchDevice) {
                e.preventDefault();
                this.scrollLeft += e.originalEvent.deltaY;
            }
        }).on('mousedown', `.${TW.filter.scrollerClass}`, function (e) {
            if (!TW.isTouchDevice && this.clientWidth < this.scrollWidth) {
                this.classList.add('--draginit');
                this.setAttribute('startX', e.pageX - this.offsetLeft);
                this.setAttribute('scrollLeft', this.scrollLeft);
            }
        }).on('mousemove', `.${TW.filter.scrollerClass}.--draginit`, function (e) {
            if (!TW.isTouchDevice) {
                this.classList.add('--dragging');
                const x = e.pageX - this.offsetLeft;
                const scroll = x - this.getAttribute('startX');
                this.scrollLeft = this.getAttribute('scrollLeft') - scroll;
            }
        }).on('mouseup mouseleave', `.${TW.filter.scrollerClass}.--draginit`, function (e) {
            if (!TW.isTouchDevice) {
                this.classList.remove('--draginit');
                setTimeout(() => {
                    this.classList.remove('--dragging');
                }, 50);
            }
        });

        TW.$.on('mousedown', `.${TW.filter.btnClass}`, function (e) {
            e.preventDefault();
            // e.stopImmediatePropagation();

            if (!TW.filter.multipleMode) {
                [].forEach.call(this.parentNode.children, function (el) {
                    el.classList.remove('active');
                });
            }

            this.classList.toggle('active');

            let divisions = [];

            TW.filter.$getButtons('.active').each(function () {
                divisions.push({
                    from: +this.getAttribute('data-price-from'),
                    to: +this.getAttribute('data-price-to'),
                })
            });

            for (const [price, elements] of Object.entries(TW.filter.priceGroupsElems)) {
                let filtered = true;
                if (divisions.length) {
                    filtered = !!divisions.find(division => price >= division.from && price <= division.to);
                    for (const element of elements) {
                        element.setAttribute('data-filtered', +filtered);
                    }
                } else {
                    for (const element of elements) {
                        element.removeAttribute('data-filtered')
                    }
                }
            }
        });
    }


    /**
     * Выбор слова с правильным окончанием в зависимости от кол-ва
     *
     * @param number
     * @param words
     * @returns {*}
     */
    declOfNum(number, words) {
        return words[(number % 100 > 4 && number % 100 < 20) ? 2 : [2, 0, 1, 1, 1, 2][(number % 10 < 5) ? Math.abs(number) % 10 : 5]];
    }

    /**
     * Jquery find helper
     *
     * @param selector
     * @returns {*}
     */
    $$(selector) {
        return TW.$.find(selector);
    }

    /**
     * Сбрасывает eventParams до правильной изначальной структуры
     */
    resetEventParams() {
        Object.keys(TW.eventParams).forEach(key => TW.eventParams[key] = '')
    }

    /**
     * Opens widget
     *
     * @param eventParams
     * @param onlyCalendar
     */
    open(eventParams, onlyCalendar = false) {
        onlyCalendar = +onlyCalendar;
        window.savedScrollTop = window.scrollY;
        document.documentElement.classList.add('tw-opened');

        const activateWidget = () => {
            if (TW.isInitialized) {
                TW.$header.hide();
                if (TW.schemeInit) {
                    // TW.reload(onlyCalendar);
                    // return;
                    TW.prevActivePanelId = null;

                    TW.resetEventParams();
                }
                Object.assign(TW.eventParams, eventParams);

                if (onlyCalendar) {
                    TW.getCalendar(true, true);
                } else {
                    TW.openPanel('tw-scheme');
                    TW.getEvent(eventParams);
                    TW.getCalendar(false, true);
                }
            } else {
                setTimeout(activateWidget, 100);
            }
        };

        activateWidget();
    }

    openByHash(hash) {
        hash = hash.replace(/^#/, '');
        const decodedHash = b64Decode(hash);

        if (typeof decodedHash !== 'object') {
            return;
        }
        let hashKeys = Object.keys(decodedHash).sort();
        let validKeys = Object.keys(TW.eventParams).sort();

        if (hashKeys.every(key => validKeys.includes(key))) {
            TW.open(decodedHash);
        }
    }

    /**
     * Close widget
     */
    close() {
        if (window.location.hash) {
            history.replaceState(null, null, window.location.href.split('#')[0]);
        }

        TW.$$('.tw__panel.active').removeClass('active');

        TW.prevActivePanelId = null;
        TW.availableTicketsCircleElems = [];
        TW.eventParams.partId = '';
        document.documentElement.classList.remove('tw-opened');

        window.scrollTo(0, window.savedScrollTop);

        if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET) {
            TW.schemeSvgOverlay?.remove().removeEventListener();
            TW.scheme?.remove();
        }

        TW.$twSvgMap.empty();
        TW.schemeInit = false;
        TW.schemeImageInit = false;
        TW.availableTicketsCircleElems = [];
        TW.clearEventParams();
    }

    /**
     * Reload widget
     */
    reload(onlyCalendar = false) {
        const selector = TW.selector;
        const eventParams = {...TW.eventParams, partId: ''};
        let initParams = {};

        initParams.onInitialized = function () {
            window.TW.open(eventParams, onlyCalendar);
        }
        initParams.url = TW.url;

        TW.destruct();

        window.TW = new this.constructor(selector, initParams);
    }

    /**
     * Custom destructor
     */
    destruct() {
        clearInterval(TW.cartsTimerFunc);
        $(window).off('resize', TW.resizeCallback).off('hashchange', TW.hashchange);
        TW.$.off('touchmove');
        TW.$.empty().off();
        document.documentElement.classList.remove('tw-opened');

        delete window.TW;
    }

    /**
     * @param panelId
     * @param params
     * @returns {*}
     */
    openPanel(panelId, params = {}) {
        const $prevPanel = TW.$$('.tw__panel.active');
        let $nextPanel = TW.$$('#' + panelId);

        TW.prevActivePanelId = $prevPanel.attr('id') || null;
        TW.prevActivePanelScrollTop = $prevPanel.scrollTop();

        TW.$tools.removeClass('tw-hidden');
        TW.$minicart.removeClass('active');
        TW.$header.removeClass('floating');

        if (panelId === 'tw-scheme') {
            TW.prevActivePanelId = null;
            TW.$tooltip.hide();
            TW.$morePopup.removeClass('active');
            TW.$header.addClass('floating');
            TW.$header.show();
            TW.redrawMinicart();
        } else if (panelId === 'tw-calendar') {
            TW.$header.show();
            $nextPanel.find('.tw-datepicker').addClass('tw-hidden');

            const $months = $nextPanel.find('.tw__calendar-month');

            if (!$months.filter('.active').length) {
                $months.first().trigger('click');
            }

        } else if (panelId === 'tw-checkout') {
            if (panelId !== TW.prevActivePanelId) {
                TW.updateCheckout(panelId);
            }
        } else if (panelId === 'tw-multi-checkout') {
            document.documentElement.classList.add('tw-opened');
            TW.$header.hide();
            TW.$tools.addClass('tw-hidden');
            if (!$nextPanel.length) {
                const $checkout = TW.$$('#tw-checkout');
                $nextPanel = $checkout.clone().insertAfter($checkout).attr('id', panelId);
            }

            if (panelId !== TW.prevActivePanelId) {
                TW.updateCheckout(panelId);
            }
        } else if (panelId === 'tw-payment') {
            TW.$header.hide();
        } else if (panelId === 'tw-request') {
            TW.prevActivePanelId = null;
            TW.$header.hide()//.addClass('centered');
            TW.loadRequestEventDates();
        } else if (panelId === 'tw-tickets') {
            if (!params.missLoadConfig) {
                TW.loadConfig();
            }
            // TW.redrawMinicart();
            TW.$header.show();
        } else {
            TW.$header.hide();
        }

        $nextPanel.addClass('active').siblings('.tw__panel').removeClass('active');

        if (params.scrollTop) {
            $nextPanel.get(0).scrollTo(0, params.scrollTop);
        }

        return $nextPanel;
    }

    /**
     * Close active panel of widget and open previous panel if exist
     */
    closePanel(activePanelId = null) {
        const $activePanel = activePanelId ? TW.$$(`#${activePanelId}.active`) : TW.$$('.tw__panel.active');
        if (!$activePanel.length) {
            return;
        }

        activePanelId = activePanelId || $activePanel.attr('id');

        if (activePanelId === 'tw-scheme') {
            TW.prevActivePanelId = null;

            if (TW.eventParams.partId) {
                TW.getEvent({partId: ''});

                return;
            }
        } else if (activePanelId === 'payment-success-page') {
            // TW.prevActivePanelId = TW.schemeInit ? 'tw-scheme' : TW.prevActivePanelId;
            TW.prevActivePanelId = null;
        } else if (activePanelId === 'tw-request') {
            TW.prevActivePanelId = null;
        } else if (activePanelId === 'tw-payment') {
            $activePanel.find('iframe').attr('src', '');
            // TW.prevActivePanelId = TW.schemeInit ? 'tw-scheme' : TW.prevActivePanelId;
            TW.prevActivePanelId = null;
        } else if (activePanelId === 'tw-multi-checkout') {
            TW.$header.toggle(!!TW.prevActivePanelId);
            TW.$tools.removeClass('tw-hidden');
        } else if (activePanelId === 'tw-tickets') {
            TW.prevActivePanelId = null;
        } else if (activePanelId === 'tw-calendar' && !TW.prevActivePanelId) {
            TW.filter?.$?.addClass('tw-hidden');
        }

        if (TW.prevActivePanelId) {
            TW.openPanel(TW.prevActivePanelId, {scrollTop: TW.prevActivePanelScrollTop});
            TW.prevActivePanelId = TW.schemeInit ? 'tw-scheme' : null;
        } else {
            const finishWidget = () => {
                for (const [key, abortController] of Object.entries(TW.abortControllers)) {
                    abortController.abort();
                    TW.abortControllers[key] = new AbortController();
                }

                if (activePanelId !== 'tw-calendar' && TW.onlyCalendar) {
                    TW.openPanel('tw-calendar', {scrollTop: TW.prevActivePanelScrollTop});
                    TW.prevActivePanelId = null;
                    return;
                }

                TW.close();
            }

            if (activePanelId === 'tw-multi-checkout') {
                finishWidget();
            } else {
                TW.confirm('Вы действительно хотите закрыть окно покупки билетов?', finishWidget);
            }
        }
    }

    /**
     * Display tooltip for map ticket
     *
     * @param elem
     */
    showTooltip(elem) {
        TW.lastHoverTicketElem = elem;
        const attrs = Object.fromEntries(Array.from(elem.attributes).map(item => [item.name, item.value]));

        const sectorName = attrs['place-name'];
        const row = attrs['row'];
        const place = attrs['place'];
        const eTicket = parseInt(attrs['data-eticket']);
        const bySector = +attrs['data-qnt-more'];
        const sectorQnt = +attrs['data-sector-qnt'];
        const priceMin = Number(attrs['data-price-min']);
        const priceMax = Number(attrs['data-price-max']);
        const moreText = attrs['data-qnt-more-text'];
        const needPassport = +attrs['data-passport'];
        const bySectorPriceForText = attrs['data-price-for'];
        const priceDirect = attrs['price-direct'];
        const isGift = +(attrs['data-gift'] || '');

        let price = attrs['price'];

        TW.$tooltip.find('[data-ticket-type]').text(eTicket ? "электронный" : "бумажный");
        TW.$tooltip.find('[data-more-text]').html(moreText);
        TW.$tooltip.find('[data-need-passport]').toggle(!!needPassport);
        const $freeCountEl = TW.$tooltip.find('[data-free-place-count]');

        if (bySector) {
            TW.$tooltip.find('[data-ticket-position]').hide();
            TW.$tooltip.find('[data-sector-name]').html('<b>' + sectorName + '</b>');

            if (isGift){
                $freeCountEl.text(TW.cartInst.ticketsCount(false) - TW.cartInst.giftsCount());
            } else {
                $freeCountEl.text(sectorQnt);
            }

            $freeCountEl.parent().toggle(!!sectorQnt);
        } else {
            let ticketPositionText = '';
            if (row && row !== '-') {
                ticketPositionText += `<b>${row}</b> ряд • `;
            }
            if (place && place !== '-') {
                ticketPositionText += `<b>${place}</b> место`;
            }
            TW.$tooltip.find('[data-ticket-position]').html(ticketPositionText).show();
            TW.$tooltip.find('[data-sector-name]').html(sectorName);
            $freeCountEl.parent().hide();
        }

        if (bySectorPriceForText !== undefined) {
            $freeCountEl.parent().hide()
        }

        let priceText = '';

        if (priceMin && priceMin === priceMax) {
            price = priceMin;
        }

        if (priceDirect) {
            priceText = `${priceDirect} ₽`;
        } else if (priceMin > 0 && priceMax > 0 && priceMin < priceMax) {
            priceText = bySectorPriceForText !== undefined
                ? `${price} ₽`
                : `${priceMin} - ${priceMax} ₽`;
        } else if (!isNaN(price)) {
            priceText = `${price} ₽`;
        } else {
            priceText = price;
        }

        TW.$tooltip.find('[data-price]').text(priceText);
        TW.$tooltip.show();

        TW.updateTooltipPosition();
    }

    loader(display = true, alertTimeoutDelay) {
        TW.$loader.toggleClass('tw-hidden', !display);
        clearTimeout(TW.loaderTimeoutId);
    }

    sendTgMsg(){
        const schemeLoaded = window.schemeLoadFinish && window.schemeLoadFinish > window.schemeLoadStart;
        window.schemeLoadFinish = window.schemeLoadFinish || + new Date();

        const loadTimeSec = Math.round((window.schemeLoadFinish - window.schemeLoadStart) / 1000);

        if (schemeLoaded && loadTimeSec < 10){
            return;
        }

        let msg = '';


        if (loadTimeSec){
            msg += `\nВремя загрузки - ${loadTimeSec} сек`;
        }

        msg += '\nОшибки: ' + (TW.errors.join(', ') || 'нет');

        for(const [param, val] of Object.entries(TW.eventParams)){
            if (val.length){
                msg += `\n${param}=${val}`;
            }
        }
        msg += '\n' + window.navigator.userAgent;
        msg += '\n' + location.href;

        TW.fetch({
            url: 'https://biletservis.agency/tele/telegram_tech.php?code=biletservis2022&send=1&text=7: ' + encodeURIComponent(msg),
        }).catch(e => {
            console.error(e);
        })
    }

    updateTooltipPosition() {
        if (!TW.lastHoverTicketElem) {
            return;
        }
        const elemClientRect = TW.lastHoverTicketElem.getBoundingClientRect();
        const tooltipWidth = parseInt(TW.$tooltip.outerWidth());
        const tooltipHeight = parseInt(TW.$tooltip.outerHeight());

        let posX, posY;

        // posX = elemClientRect.left - tooltipWidth / 2;
        posX = elemClientRect.left - tooltipWidth / 2 + elemClientRect.width / 2;
        posY = elemClientRect.top - tooltipHeight - 15;

        if (posX < 0) {
            posX = 0;
        } else if (posX + tooltipWidth > window.innerWidth) {
            posX = window.innerWidth - tooltipWidth;
        }

        TW.$tooltip.css({
            'transform': `translate(${posX}px, ${posY}px)`,
        });

        const $corner = TW.$tooltip.find('.tw__scheme-tooltip-corner');

        $corner.css({
            'transform': `translate(${elemClientRect.left - TW.$tooltip.offset().left + elemClientRect.width / 2 - $corner.width() / 2}px) rotate(45deg)`
        });
    }

    /**
     * Loading and drawing event map
     *
     * @param eventParams
     * @param showMap
     */
    getEvent(eventParams, showMap = true) {
        TW.errors = [];
        TW.blockCheckoutFormFillMetrika = false;

        Object.assign(TW.eventParams, eventParams);
        history.replaceState(null, null, window.location.href.split('#')[0] + '#' + b64Encode(TW.eventParams))

        TW.availableTicketsCircleElems = [];
        TW.lastClickedTicketElem = null;
        TW.lastHoverTicketElem = null;

        TW.updateHeaderInfo(TW.eventParams);

        TW.$minicart.removeClass('active');
        if (TW.schemeLib === TW.SCHEME_LIB_PANZOOM) {
            TW.scheme?.disablePan();
        }
        TW.loader(true);
        TW.$tooltip.hide();
        TW.$morePopup.removeClass('active');

        TW.$$('.tw__scheme-to-table-btn').addClass('tw-hidden');
        TW.$$('.tw__tickets').removeAttr('data-sector-open').find('.tw__tickets-sectors, .tw__tickets-rows').empty();


        for (const [key, abortController] of Object.entries(TW.abortControllers)) {
            abortController.abort();
            TW.abortControllers[key] = new AbortController();
        }
        // TW.abortControllers.svg.abort();
        // TW.abortControllers.svg = new AbortController();

        TW.fetch({
            signal: TW.abortControllers.svg.signal,
            data: {
                action: 'get_svg',
                widget: 1,
                date: TW.eventParams.date,
                event_id: TW.eventParams.id,
                place_id: TW.eventParams.placeId,
                hall_id: TW.eventParams.hallId,
                part_id: TW.eventParams.partId,
            }
        })
            .then(async response => {
                if (response.headers.get('Content-Type').includes('image/svg+xml')) {
                    return response.text();
                } else {
                    const responseText = await response.text();

                    if (!responseText.trim()) {
                        throw Error('SVG response пуст...');
                    }

                    const json = JSON.parse(responseText);
                    if (json?.error || !json?.svg) {
                        throw Error(json?.error || 'SVG отсутствует...');
                    }
                }
            })
            .then(svgData => {
                TW.updateHeaderDates();

                if (TW.schemeInit && TW.schemeSvgOverlay) {
                    TW.schemeSvgOverlay.remove().removeEventListener();
                    TW.schemeSvgOverlay = null;
                    TW.schemeSvgAttrs = {};
                    TW.scheme.invalidateSize(false);
                }

                if (TW.schemeImageInit) {
                    // TW.scheme?.destroy();
                    TW.schemeInit = false;
                    TW.schemeImageInit = false;
                }

                TW.schemeLib = TW.defaultSchemeLib;

                TW.$twSvgMap.empty();

                const svgMapRect = TW.$twSvgMap.get(0).getBoundingClientRect();

                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = svgData;
                const svgElem = tempDiv.querySelector('svg');

                TW.schemeSvgAttrs = Object.fromEntries(Array.from(svgElem.attributes).map(item => [item.name, item.value]));

                // [elem="true"] //если не true , то это не место, а сектор или элемент декора
                TW.availableTicketsCircleElems = TW.schemeSvgAttrs.sheme && TW.schemeSvgAttrs.sheme === 'true'
                    ? svgElem.querySelectorAll('.tickets_avail')
                    : svgElem.querySelectorAll('.tickets_avail[elem="true"]')
                ;

                const svgTicketElemObserver = new MutationObserver((mutationList, observer) => {
                    for (const mutation of mutationList) {
                        if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.r) {
                            let strokeWidth = mutation.target.r.baseVal.value;
                            strokeWidth = Math.ceil(strokeWidth / 2);

                            if (mutation.target.classList.contains('active')) {
                                mutation.target.setAttribute('stroke-width', strokeWidth);
                            } else {
                                mutation.target.removeAttribute('stroke-width');
                            }

                            if (mutation.target.classList.contains('--hover')) {
                                mutation.target.setAttribute('stroke-width', strokeWidth);
                            } else if (!mutation.target.classList.contains('active')) {
                                mutation.target.removeAttribute('stroke-width');
                            }
                        }
                    }
                });

                for (const elem of TW.availableTicketsCircleElems) {
                    svgTicketElemObserver.observe(elem, {
                        attributes: true
                    });
                }

                if (TW.schemeSvgAttrs.no_tickets == 1) {
                    if (TW.schemeSvgAttrs.no_tickets_type == -1) {
                        TW.errors.push('Нет билетов вообще');
                        TW.openPanel('tw-request');
                    } else if (TW.schemeSvgAttrs.no_tickets_type == 0) {
                        TW.errors.push('Билеты присутствуют, но нет совпадений по схеме');
                        TW.openPanel('tw-tickets');
                    }

                    return;
                }

                TW.$twSvgMap.get(0).replaceChildren(svgElem);

                // для корректной работы hover нужно будет упаковать эти теги в <g> и с ним работать, тогда нужно будет наверное на mouseOver создавать их все-таки, чтоб не перенагружать DOM

                // TW.availableTicketsCircleElems.forEach(elem => {
                //     const placeNum = elem.getAttribute('place');
                //
                //     if (placeNum){
                //         const textTag = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                //         textTag.setAttributeNS(null, 'x', elem.getAttribute('cx'));
                //         textTag.setAttributeNS(null, 'y', elem.getAttribute('cy'));
                //         textTag.setAttributeNS(null, 'font-size', elem.getAttribute('r') + 'px');
                //         textTag.setAttributeNS(null, 'fill', '#000');
                //         textTag.setAttributeNS(null, 'dy', '0.3em');
                //         textTag.setAttributeNS(null, 'text-anchor', 'middle');
                //         textTag.classList.add('place-caption');
                //         textTag.innerHTML = placeNum;
                //         elem.after(textTag);
                //     }
                // });

                const viewBox = TW.schemeSvgAttrs.viewBox;
                let latLngCorner2;

                if (viewBox) {
                    latLngCorner2 = [
                        parseInt(viewBox.split(' ')[2]) / 2,
                        parseInt(viewBox.split(' ')[3]) / 2
                    ];
                } else {
                    latLngCorner2 = [
                        mapRect.width / 2,
                        mapRect.height / 2
                    ];
                }

                let bounds = [
                    [0, 0],
                    latLngCorner2,
                ]

                TW._initFilter('.tw__scheme-divisions', TW.availableTicketsCircleElems, {
                    visible: !TW.schemeSvgAttrs.nopricefiler,
                });

                TW.$$('.tw__scheme-back').toggleClass('tw-hidden', !TW.eventParams.partId);

                TW.$minicart.removeClass('active');
                TW.closePanel('tw-checkout');
                TW.$tooltip.hide();
                TW.cartInst = new TWCart(TW.eventParams, {documentFields: TW.schemeSvgAttrs['data-fields']});
                TW.cartInst.actualize();
                TW.redrawMinicart();

                if (!TW.schemeInit && TW.schemeLib === TW.SCHEME_LIB_LEAFLET) {
                    TW.scheme = L.map('tw-svg-map', {
                        zoomDelta: 1,
                        minZoom: 1,
                        maxZoom: 6,
                        attributionControl: false,
                        zoomControl: false,
                        scrollWheelZoom: true,
                        doubleClickZoom: false,
                        closePopupOnClick: false,
                        tapHold: false,
                        boxZoom: false,
                        keyboard: false,
                        // zoomAnimation: ! L.Browser.mobile,
                        zoomAnimation: false,
                        fadeAnimation: false,
                        inertia: false,
                        bounceAtZoomLimits: false,
                        // fullscreenControl: true,
                        // fullscreenControlOptions: {
                        //     fullscreenElement: document.querySelector('#tw-scheme'),
                        // },
                    });

                    TW.scheme.on('movestart', function (e) {
                        TW.$tooltip.hide();
                    });

                    TW.scheme.on('moveend', function (e) {
                        if (TW.$tooltip.is(':visible')) {
                            TW.$tooltip.hide();
                            TW.showTooltip(TW.lastHoverTicketElem);
                        }
                    });

                    TW.schemeInit = true;
                } else if (TW.schemeLib === TW.SCHEME_LIB_PANZOOM) {
                    // TW.scheme?.destroy();

                    const hammerTouchEventsHandler = {
                        haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel'],
                        init: function (options) {
                            let instance = options.instance
                                , initialScale = 1
                                , pannedX = 0
                                , pannedY = 0

                            // Init Hammer
                            // Listen only for pointer and touch events
                            this.hammer = Hammer(options.svgElement, {
                                inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput
                            })

                            // Enable pinch
                            this.hammer.get('pinch').set({enable: true})

                            // // Handle double tap
                            // this.hammer.on('doubletap', function(ev){
                            //     instance.zoomIn()
                            // })

                            // Handle pan
                            this.hammer.on('panstart panmove', function (ev) {
                                //todo возможно для panstart нужно давать return false, чтобы не разрешалось panmove когда в таче находится др элемент
                                if (TW.isTouchDevice && window.moveTouchesCnt <= 1) {
                                    // On pan start reset panned variables
                                    if (ev.type === 'panstart') {
                                        pannedX = 0
                                        pannedY = 0
                                        TW.$tooltip.hide();
                                    }

                                    // Pan only the difference
                                    instance.panBy({x: ev.deltaX - pannedX, y: ev.deltaY - pannedY})
                                    pannedX = ev.deltaX
                                    pannedY = ev.deltaY
                                }
                            })

                            // Handle pinch
                            this.hammer.on('pinchstart pinchmove', function (ev) {
                                // On pinch start remember initial zoom
                                if (ev.type === 'pinchstart') {
                                    initialScale = instance.getZoom()
                                    instance.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y})
                                }

                                instance.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y});
                            });

                            // Prevent moving the page on some devices when panning over SVG
                            options.svgElement.addEventListener('touchmove', function (e) {
                                e.preventDefault();
                            }, { passive: false });
                        },
                        destroy: function () {
                            this.hammer.destroy()
                        }
                    }

                    // svgElem.style.width = svgMapRect.width + 'px';
                    // svgElem.style.height = svgMapRect.height + 'px';

                    // TW.$$('#tw-svg-map').get(0).contentDocument.write(svgElem.outerHTML);

                    TW.scheme = svgPanZoom('#tw-svg-map svg', {
                        zoomEnabled: true,
                        controlIconsEnabled: false,
                        center: true,
                        panEnabled: true,
                        dblClickZoomEnabled: false,
                        mouseWheelZoomEnabled: true,
                        preventMouseEventsDefault: false,
                        zoomScaleSensitivity: (TW.schemeZoomStep = .3),
                        minZoom: (TW.schemeInitMinZoom = 0.4),
                        maxZoom: 100,
                        contain: false,
                        fit: true,
                        refreshRate: 'auto',
                        beforeZoom: function () {
                            // TW.$tooltip.filter(':visible').hide().addClass('show-after-zoom');
                        },
                        onZoom: function () {
                            TW.checkAvailableSchemeZoom();
                            TW.updateTooltipPosition();

                            // if ( ! TW.isTouchDevice){
                            clearTimeout(TW.smoothZoomTimeout);
                            TW.smoothZoomTimeout = setTimeout(() => {
                                TW?.schemeInnerViewport?.classList.remove('smooth');
                            }, 700);
                            // }
                        },
                        beforePan: function () {
                        },
                        onPan: function () {
                        },
                        onUpdatedCTM: function () {
                        },
                        customEventsHandler: hammerTouchEventsHandler,
                    });

                    TW.scheme.reset();

                    TW.schemeInnerViewport = svgElem.querySelector(':scope > .svg-pan-zoom_viewport');

                    TW.schemeInit = true;

                    TW.scheme.invalidateSize();
                    TW.resizeSvg(true);
                }

                if (TW.schemeLib === TW.SCHEME_LIB_LEAFLET) {
                    TW.schemeSvgOverlay = L.svgOverlay(svgDocLayer,
                        bounds,
                        {
                            interactive: false,
                            bubblingMouseEvents: false,
                        }
                    ).addTo(TW.scheme);


                    TW.scheme.fitBounds(bounds, {animate: false});
                    TW.scheme.setMaxBounds(bounds);

                    let svgContentWidth;
                    let svgContentHeight;

                    const svgInnerGroupElem = svgElem.querySelector(':scope > g:only-child');
                    if (svgInnerGroupElem) {
                        let svgInnerRect = svgInnerGroupElem.getBoundingClientRect();
                        svgContentWidth = svgInnerRect.width;
                        svgContentHeight = svgInnerRect.height;
                    } else {
                        svgContentWidth = parseInt(svgElem.style.width);
                        svgContentHeight = parseInt(svgElem.style.height);
                    }

                    if (svgContentHeight * 2 <= mapRect.height && svgContentWidth * 2 <= mapRect.width) {
                        TW.scheme.setZoom(2, {animate: false});
                    }
                }

                TW.loader(false);
            })
            .catch(e => {
                if (e.toString().includes('AbortError')){
                    TW.errors.push('Загрузку данных виджета прервали');
                } else {
                    TW.errors.push(e.toString());
                }
                console.trace(e);
                TW.openPanel('tw-request');
            })
            .finally(() => {
                TW.loader(false);
                TW.sendTgMsg();
            })
        ;
    }

    updateHeaderInfo() {
        TW.$$('.tw__header-caption').attr('data-platform', TW.eventParams.placeName).attr('data-hall', TW.eventParams.hallName);
        TW.$$('.tw__header-title').html(TW.eventParams.name).show();
        TW.$$('.tw__header-subtitle').html(TW.eventParams.desc);

        const pickerTimestamp = TW.eventParams.date;
        const $picker = TW.$$('.tw__header-picker').attr('data-timestamp', pickerTimestamp || '');

        if (pickerTimestamp) {
            const day = TW.moscowDate(pickerTimestamp, {day: '2-digit'});
            const weekday = TW.moscowDate(pickerTimestamp, {weekday: 'short'});
            const time = TW.moscowTime(pickerTimestamp);
            const month = TW.moscowDate(pickerTimestamp, {month: 'long'})
                .replace(/ь$/, 'я')
                .replace(/т$/, 'та')
                .replace(/й$/, 'я')

            $picker.find('[data-day]').text(day);
            $picker.find('[data-weekday]').text(weekday.toUpperCase());
            $picker.find('[data-time]').text(time);
            $picker.find('[data-month]').text(month.toUpperCase());
        }
    }
    
    loadConfig(showLoader = true){
        showLoader && TW.loader(true);
        for (const [key, abortController] of Object.entries(TW.abortControllers)) {
            abortController.abort();
            TW.abortControllers[key] = new AbortController();
        }

        return TW.fetch({
            signal: TW.abortControllers.config.signal,
            data: {
                action: 'get_config',
                widget: 1,
                date: TW.eventParams.date,
                event_id: TW.eventParams.id,
                place_id: TW.eventParams.placeId,
                hall_id: TW.eventParams.hallId,
                part_id: TW.eventParams.partId,
            }
        })
            .then(async response => {
                const json = await response.json();

                if (json?.error) {
                    throw Error(json?.error || 'Данные get_config отсутствуют...');
                }

                return json;
            })
            .then(config => {
                if (!config.tickets_count) {
                    throw Error(`Пустой tickets_count в get_config`);
                }
                TW.config = config;
                // TW.$$('.tw__scheme-as-table-btn').show();
                TW.$$('.tw__tickets-sectors').html('');

                if (TW.config.sectors) {
                    for (const [key, sectorData] of Object.entries(TW.config.sectors)) {
                        const sectorId = sectorData.id;
                        const $sectorItem = $('<div>', {
                            'class': 'tw__tickets-sector',
                            'data-id': sectorId,
                        });

                        const priceFrom = sectorData.placesMinPrice || sectorData.places.reduce((minPrice, place) => +place.price < minPrice ? +place.price : minPrice, Infinity);
                        const placesQnt = sectorData.placesQnt || sectorData.places.length;

                        $sectorItem.append($(`<span data-sector-cell>${sectorData.name}</span>`));
                        $sectorItem.append($(`<span data-count-cell>${placesQnt} шт</span>`));
                        if (priceFrom) {
                            $sectorItem.append($(`<span data-price-cell>от ${priceFrom} ₽</span>`));
                        } else {
                            $sectorItem.append($(`<span data-price-cell>Под заказ</span>`));
                        }

                        let btnTxt = 'Выбрать билеты';
                        let btnSmText = priceFrom > 0 ? `от ${priceFrom} ₽` : btnTxt;
                        $sectorItem.append($(`<span data-btn-cell><span class="tw-btn tw-btn--sm tw__tickets-sector-opener" data-text="${btnTxt}" data-sm-text="${btnSmText}"></span></span>`));

                        TW.$$('.tw__tickets-sectors').append($sectorItem);

                        TW.config.allPlaces = TW.config.allPlaces || [];

                        for (const item of sectorData.places) {
                            item.id = TW.generateTicketId(item.row, item.place, sectorData.name, config.event.id, config.event.date, config.place.id, config.hall.id);
                            item.placeName = sectorData.name;
                            TW.config.allPlaces.push(item);
                        }
                    }
                }

                TW.$$('.tw__tickets-to-scheme').attr('data-image', TW.config['hall']['scheme'] || '');
                TW.$$('.tw__tickets-footer').removeClass('tw-hidden');

                TW.cartInst = new TWCart(TW.eventParams, {documentFields: TW.config?.fields});
                TW.cartInst.actualize();
                TW.redrawMinicart();

                TW.loader(false);
            })
            .catch(e => {
                TW.errors.push(e.toString());
                TW.openPanel('tw-request');
                console.log(e);
            })
    }

    updateHeaderDates() {
        const $headerDateSelect = TW.$header.find('.tw__header-date-select');
        $headerDateSelect.empty();

        const evDay = TW.moscowDate(TW.eventParams.date, {day: 'numeric'});
        const evMonth = TW.moscowDate(TW.eventParams.date, {month: 'long'})
            .replace(/ь$/i, 'я')
            .replace(/т$/i, 'та')
            .replace(/й$/i, 'я')
        ;
        const $headerDateMainOption = TW.$header.find('.tw__header-date-option');
        $headerDateMainOption.find('[data-week-day]').text(TW.moscowDate(TW.eventParams.date, {weekday: 'short'}).toUpperCase());
        $headerDateMainOption.find('[data-date]').text(`${evDay} ${evMonth}`);
        $headerDateMainOption.find('[data-time]').text(TW.moscowTime(TW.eventParams.date, {
            hour: '2-digit',
            minute: '2-digit'
        }));

        return TW.fetch({
            signal: TW.abortControllers.headerDates.signal,
            data: {
                action: 'get_event_dates',
                event_id: TW.eventParams.id,
            }
        })
            .then(async response => {
                const json = await response.json();

                if (json?.error) {
                    throw Error(json?.error || 'Данные по датам события для мини-календаря отсутствуют...');
                }

                return json;
            })
            .then(json => {
                const hasAnyDates = !(Object.keys(json.dates).length === 1 && json.dates[TW.eventParams.date] || !Object.keys(json.dates).length);

                TW.$header.find('.tw__header-date-toggle').toggle(hasAnyDates);
                TW.$header.find('.tw__header-other-dates').toggleClass('tw-hidden', !hasAnyDates);

                if (hasAnyDates) {
                    for (const [dateInt, dateInfo] of Object.entries(json.dates)) {
                        if (dateInt == TW.eventParams.date) {
                            continue;
                        }

                        const weekdayShort = TW.moscowDate(dateInt, {weekday: 'short'}).toUpperCase();
                        const day = TW.moscowDate(dateInt, {day: 'numeric'});
                        const time = TW.moscowTime(dateInt, {hour: '2-digit', minute: '2-digit'});
                        const month = TW.moscowDate(dateInt, {month: 'long'})
                            .replace(/ь$/i, 'я')
                            .replace(/т$/i, 'та')
                            .replace(/й$/i, 'я')
                        ;

                        const $option = $(`<div class="tw__header-date-option js-tw-time-picker">
                            <span data-week-day>${weekdayShort}</span>
                            <span data-date>${day} ${month}</span>
                            <span data-time>${time}</span>
                        </div>`);

                        $option.attr({
                            'data-timestamp': dateInt,
                            'data-event-name': json.event_name,
                            'data-place-id': dateInfo.place_id,
                            'data-hall-id': dateInfo.hall_id,
                            'data-hall-name': dateInfo.hall_name,
                            'data-place-name': dateInfo.place_name,
                        })

                        $headerDateSelect.append($option);
                    }
                }
            })
    }

    loadRequestEventDates(showLoader = true){
        showLoader && TW.loader(true);
        
        for (const [key, abortController] of Object.entries(TW.abortControllers)) {
            abortController.abort();
            TW.abortControllers[key] = new AbortController();
        }

        return TW.fetch({
            signal: TW.abortControllers.eventDates.signal,
            data: {
                action: 'get_event_dates',
                event_id: TW.eventParams.id,
                // place_id: TW.eventParams.placeId,
                // hall_id: TW.eventParams.hallId,
            }
        })
            .then(async response => {
                const json = await response.json();

                if (json?.error) {
                    throw Error(json?.error || 'Данные по датам события отсутствуют...');
                }

                return json;
            })
            .then(json => {
                json.dates = json.dates || {};
                const $select = TW.$$('.js-request-date-select');
                $select.children().first().prop('selected', true).siblings().remove();
                let hasAnyPlaces = false;
                let prevDateEventInfo;

                for (const [dateInt, eventInfo] of Object.entries(json.dates)) {
                    const place = eventInfo.place_city;
                    const localeDate = TW.moscowDate(dateInt, {year: '2-digit', month: '2-digit', day: '2-digit'});
                    const weekday = TW.moscowDate(dateInt, {weekday: 'long'});
                    const time = TW.moscowTime(dateInt, {hour: '2-digit', minute: '2-digit'});
                    const $option = $('<option>', {
                        value: dateInt,
                        'data-tickets-count': eventInfo.tickets_count,
                        'data-hall-id': eventInfo.hall_id,
                        'data-hall-name': eventInfo.hall_name,
                        'data-place-id': eventInfo.place_id,
                        'data-place-name': eventInfo.place_name,
                    });

                    $option.text(`${localeDate} ${time} (${weekday}), ${place}`).prop('selected', dateInt == TW.eventParams.date || Object.keys(json.dates).length == 1);
                    $select.append($option);

                    if (prevDateEventInfo && (prevDateEventInfo.place_id !== eventInfo.place_id || prevDateEventInfo.hall_id !== eventInfo.hall_id)) {
                        hasAnyPlaces = true;
                    }

                    prevDateEventInfo = eventInfo;
                }

                $select.prop('disabled', !Object.keys(json.dates).length).toggle(Object.keys(json.dates).length > 0);

                if (hasAnyPlaces) {
                    TW.$header.find('.tw__header-caption').attr('data-platform', '').attr('data-hall', '');
                }

                TW.loader(false);

                fetch(`https://svg-gen.ru/_request.php?code=mos2019&date=${TW.eventParams.date}&event_id=${TW.eventParams.id}&place_id=${TW.eventParams.placeId}&hall_id=${TW.eventParams.hallId}&host=${location.host}`);
            })
            .catch(e => {
                alert('Извините, временно отсутствует информация по датам выбранного события. Попробуйте посмотреть позже');
                console.log(e);
            })
    }

    /**
     * Load and display of calendar
     *
     * @param open
     * @param reload
     */
    getCalendar(open = true, reload = true) {
        const doFetch = TW.$calendar.is(':empty') || reload;

        if (open && !doFetch) {
            TW.openPanel('tw-calendar');
        } else if (doFetch) {
            if (open) {
                TW.loader(true);
            }
            // const action = 'get_calendar';
            const action = 'get_simple_calendar';
            TW.fetch({
                data: {
                    action: action,
                    date: TW.eventParams.date,
                    event_id: TW.eventParams.id,
                    place_id: TW.eventParams.placeId,
                    hall_id: TW.eventParams.hallId,
                }
            })
                .then(response => response.json())
                .then(data => {
                    if (!data.success) {
                        throw Error('success is not true');
                    }

                    TW.$calendar.html(data.html);
                    TW.$$('.tw__header-picker').toggleClass('--locked', TW.eventParams.id.toString().includes('-'))
                    TW.$header.find('.tw__header-other-dates').toggleClass('tw-hidden', true);
                    
                    if (open) {
                        TW.updateHeaderInfo();
                        TW.loader(false);
                        TW.openPanel('tw-calendar');
                    }

                    if (action === 'get_simple_calendar') {
                        TW.$calendar.addClass('tw__calendar-simple');
                        TW.$calendar.find('.tw__calendar-simple-title').html(TW.eventParams.name);
                        TW.$$('.tw__header-title').hide()
                    }
                })
                .catch(e => {
                    console.error(e);
                    // alert('Ошибка отправки данных! Попробуйте обновить страницу.');
                })
            ;
        }
    }

    updateFloatingMargins() {
        const $absolutePositionFooterElements = $('.tw__scheme-back, .tw__scheme-to-table-btn, .tw__tickets-footer').css('margin-bottom', '');
        $absolutePositionFooterElements.each(function () {
            const curValue = parseInt($(this).css('margin-bottom'));
            $(this).css('margin-bottom', curValue + parseInt(TW.$minicart.filter('active').outerHeight()));
        });
    }

    redrawMinicart() {
        const $items = TW.$minicart.find('.tw__minicart-items').empty();

        if (!TW.cartInst) {
            return;
        }

        const ticketsCount = TW.cartInst?.ticketsCount();

        if (ticketsCount > 0) {
            const ticketsCountCaption = TW.declOfNum(ticketsCount, ['билет', 'билета', 'билетов']);

            TW.$minicart.find('[data-cart-qnt]').text(ticketsCount + ' ' + ticketsCountCaption);

            const sum = TW.cartInst.sum();
            let prefix;
            let sumText;

            if (!isNaN(sum)) {
                prefix = '';
                sumText = `${sum} ₽`;
            } else {
                prefix = '';
                sumText = 'Под заказ';
            }

            TW.$minicart.find('[data-cart-sum]').attr('data-cart-sum', sumText).attr('data-cart-sum-prefix', prefix);
            TW.$minicart.addClass('active');
            TW.updateFloatingMargins();

            for (const cartItem of TW.cartInst.items) {
                const $item = $(`<div class="tw__minicart-item" data-cart-item="${cartItem.id}"></div>`);
                const row = cartItem.row.replace(/^[-0]$/, '');
                const place = cartItem.place.replace(/^[-0]$/, '');

                $item.append(`<span data-sector="${cartItem.placeName}"></span>`);

                if (row) {
                    $item.append(`<span data-row="${row}"> ряд</span>`);
                }
                if (place) {
                    $item.append(`<span data-place="${place}"> место</span>`);
                }
                if (cartItem.qnt > 1) {
                    $item.append(`<span data-qnt="${cartItem.qnt}"> шт</span>`);
                }

                $item.append(`<span><i class="tw__close tw__minicart-item-delete"></i></span>`);
                $items.append($item);
            }
        } else {
            TW.$tooltip.hide();
            TW.cartInst?.reset();

            TW.$minicart.removeClass('active');
            TW.closePanel('tw-checkout');
        }
    }

    updateMaskElements() {
        if ($().inputmask) {
            TW.$$('input[data-mask="phone"]')
                .css('pattern', "\+7\s*\(\d{3}\)\s*\d{3}-\d{2}-\d{2}")
                .inputmask({
                    mask: '+7 (999) 999-99-99',
                    showMaskOnHover: false,
                })
            ;
        }
    }

    updateCheckout(checkoutPanelId) {
        checkoutPanelId = checkoutPanelId || TW.$$('.tw__checkout.active').attr('id') || 'tw-checkout';

        const $checkout = TW.$$('#' + checkoutPanelId);
        const $drawnCarts = $checkout.find('[data-cart]:not(.tw-hidden)');
        const openedCarts = [];

        $drawnCarts.each(function () {
            if ($(this).find('.js-tw-checkout-cart-details[open]').length) {
                openedCarts.push(this.getAttribute('data-cart'));
            }
        });
        $drawnCarts.remove();

        let ticketsDelivery = 'Электронный билет';
        let allowPay = false;
        let hasPaperTicket = false;
        let totalSum = 0;
        let totalQnt = 0;

        const parseDataFields = (input) => {
            const pairs = input.split("|");

            return pairs.map(pair => {
                const [name, placeholder, type, options] = pair.split(";");
                let formatOptions = [];

                if (type === 'select' && options) {
                    formatOptions = options.split("!");
                }

                return {
                    name,
                    placeholder,
                    type,
                    options: formatOptions
                };
            });
        }
        
        const fillCartData = (cartInstance) => {
            if (!cartInstance.isEmpty()) {
                const $cartTpl = $checkout.find('[data-cart=""].tw-hidden');
                const $cart =
                    $cartTpl
                        .clone()
                        .attr('data-cart', cartInstance.id)
                        .removeClass('tw-hidden')
                        .insertAfter($cartTpl);
                if (openedCarts.includes(cartInstance.id)) {
                    $cart.find('.js-tw-checkout-cart-details').prop('open', true);
                }

                const $cartList = $cart.find('[data-cart-list]');
                const cartSum = cartInstance.sum();
                const ticketsCount = cartInstance.ticketsCount();
                const ticketsCountCaption = TW.declOfNum(ticketsCount, ['билет', 'билета', 'билетов']);
                const cartSumText = !isNaN(cartSum) ? cartSum + '₽' : 'Под заказ';

                totalSum += cartSum;
                totalQnt += ticketsCount;

                const day = TW.moscowDate(cartInstance.event.date, {day: '2-digit'});
                const weekday = TW.moscowDate(cartInstance.event.date, {weekday: 'short'});
                const time = TW.moscowTime(cartInstance.event.date);
                const month = TW.moscowDate(cartInstance.event.date, {month: 'long'})
                    .replace(/ь$/, 'я')
                    .replace(/т$/, 'та')
                    .replace(/й$/, 'я')


                const $cartEventDate = $cart.find('[data-cart-date]');
                $cartEventDate.find('[data-weekday]').text(weekday.toUpperCase());
                $cartEventDate.find('[data-date]').text(day + ' ' + month);
                $cartEventDate.find('[data-time]').text(time);

                $cart.find('[data-cart-event]').html(cartInstance.event.name);
                $cart.find('[data-cart-platform]').attr('data-cart-platform', cartInstance.event.placeName);
                $cart.find('[data-cart-hall]').attr('data-cart-hall', cartInstance.event.hallName);

                $cart.find('[data-cart-sum]').text(cartSumText);
                $cart.find('[data-cart-tickets]').text(ticketsCount + ' ' + ticketsCountCaption + ': ');
                $cart.find('.js-open-hash').attr('href', '#' + b64Encode(cartInstance.event));

                $cartList.find('[data-cart-item]:not(.tw-hidden)').remove();

                let i = 1

                for (const cartItem of cartInstance.items) {
                    let price = parseInt(cartItem.price) * cartItem.qnt;
                    const sumText = !cartItem.onOrder ? price.toLocaleString() : 'Под заказ'

                    if (cartItem.allowPayment) {
                        allowPay = true;
                    }

                    let $cartItemEl =
                        $cartList
                            .find('[data-cart-item].tw-hidden')
                            .clone()
                            .removeClass('tw-hidden')
                            .attr('data-cart-item', cartItem.id);

                    const viewerData = $cartItemEl.find('[data-viewer-data]');

                    $cartItemEl.find('[data-cart-item-index]').text(`${i}.`);
                  
                    if (cartItem.isGift){
                        $cartItemEl.find('[data-place]').parent().html(cartItem.placeName + ' - ' + cartItem.qnt);
                        $cartItemEl.find('[data-sector-name]').parent().html('');
                    } else {
                        $cartItemEl.find('[data-sector-name]').html(cartItem.placeName);
                        $cartItemEl.find('[data-row]').text(cartItem.row);
                        $cartItemEl.find('[data-place]').text(cartItem.place);
                    }
                  
                    $cartItemEl.find('[data-sum]').text(sumText);

                    if (!cartItem.eTicket) {
                        ticketsDelivery = 'Бумажный билет - Курьерская доставка';
                        hasPaperTicket = true;
                    }

                    const dataFields = cartInstance.documentFields;
                    if (dataFields) {
                        const fieldStruct = parseDataFields(dataFields);

                        if (viewerData) {
                            const header = viewerData.find('[data-viewer-data-header]')[0];
                            const content = viewerData.find('[data-viewer-data-content]')[0];

                            if (header && content) {
                                const savedDocumentData = cartItem.documentData || {};
                                const fieldsHTML = fieldStruct.map(item => {
                                    const selectedValue = savedDocumentData[item.name] || '';

                                    const options = () => {
                                        if (!item?.options) return '';
                                        return item.options.map(option =>
                                            (`<li>${option}</li>`)
                                        ).join('');
                                    };
                                    const $input = $(`
                                        <input
                                            type="text"
                                            value="${selectedValue}"
                                            name="${item.name}"
                                            placeholder="${item.placeholder} *"
                                            required
                                        />
                                    `);

                                    if ($input.attr('name') === 'fio'){
                                        $input.attr({
                                            pattern: "^[A-Za-zА-Яа-яЁё]+(?: [A-Za-zА-Яа-яЁё]+)*$",
                                            title: "Введите корректное ФИО"
                                        })
                                    }

                                    const $select = $(`
                                            <div
                                                class="tw-select"
                                                data-select="main"
                                            >
                                                <input type="hidden" name="${item.name}" value="${selectedValue}">
                                                <div class="tw-select__field">
                                                    <div class="tw-select__head" data-select="head">
                                                        <span class="tw-select__value" data-select="value">${selectedValue || (item.placeholder + ' *')}</span>
                                                        <svg
                                                            class="tw-select__icon"
                                                            xmlns="http://www.w3.org/2000/svg"
                                                            width="24"
                                                            height="25"
                                                            viewBox="0 0 24 25"
                                                            fill="none"
                                                        >
                                                            <path
                                                                d="M16 10.5L12 14.5L8 10.5"
                                                                stroke="#242424"
                                                                stroke-width="1.2"
                                                                stroke-linecap="square"
                                                            />
                                                        </svg>
                                                    </div>
                                                    <div class="tw-select__body" data-select="body">
                                                        <ul class="tw-select__list list-reset">
                                                            ${ options() }
                                                        </ul>
                                                    </div>
                                                </div>
                                            </div>
                                    `);

                                    switch (item.type) {
                                        case 'text':
                                            return $input[0].outerHTML;
                                        case 'select':
                                            return $select[0].outerHTML;
                                        default:
                                            return $input[0].outerHTML;
                                    }
                                }).join("");

                                content.insertAdjacentHTML('beforeend', fieldsHTML);

                                const fields = content.querySelectorAll('input');

                                header.addEventListener('click', () => {
                                    viewerData[0].classList.toggle('active');
                                });

                                if (fields.length) {
                                    fields.forEach((field) => {
                                        field.addEventListener('blur', (event) => {
                                            field.classList.toggle('invalid', ! event.target.value.length)
                                        });
                                    });
                                }
                            }
                        }
                    } else if (viewerData) {
                        viewerData.remove();
                    }


                    $cartList.append($cartItemEl);

                    i++;
                }

                twSelect()
            }
        }

        if (checkoutPanelId === 'tw-multi-checkout') {
            TW.updateMaskElements();
            TW.$header.hide();

            const carts = TWCartsStorage.all(true);
            const arCarts = Object.values(carts).sort((a, b) => b.createdAt - a.createdAt);

            for (const cart of arCarts) {
                fillCartData(cart);
            }

            if (!TWCartsStorage.getAllTotalQnt()) {
                TW.closePanel(checkoutPanelId);
            }
        } else {
            if (!TW.cartInst) {
                return
            }
            TW.$header.show();

            fillCartData(TW.cartInst);

            if (TW.cartInst.isEmpty()) {
                TW.closePanel(checkoutPanelId);
            }
        }

        $checkout.find('[data-total-qnt]').text(totalQnt);
        TW.updateGlobalCartTotalQnt();

        const totalSumText = !isNaN(totalSum)
            ? totalSum + ' ₽'
            : 'Под заказ';
        $checkout.find('[data-total-sum]').text(totalSumText);


        if (allowPay && parseInt(TW.schemeSvgAttrs.no_tickets) !== 1) {
            $checkout.find('.js-tw-btn-to-payment').attr('data-text', 'Оплатить заказ:');
        } else {
            $checkout.find('.js-tw-btn-to-payment').attr('data-text', 'Оформить заявку').find('[data-total-sum]').text('');
        }

        $checkout.find('.tw__checkout-off-vpn').toggle(allowPay);
        $checkout.find('[data-paper-ticket-block="1"]').toggle(hasPaperTicket);
        $checkout.find('[data-paper-ticket-block="0"]').toggle(!hasPaperTicket);
        $checkout.find('[name="delivery_address"]').prop('required', hasPaperTicket);
        $checkout.find('[data-delivery-type]').html(ticketsDelivery);
    }

    updateGlobalCartTotalQnt() {
        const qnt = TWCartsStorage.getAllTotalQnt();
        TW.$multiCheckoutBtn?.attr('data-qnt', qnt);
        document.documentElement.setAttribute('data-global-cart-total-qnt', qnt);
    }

    generateTicketId(row = '', place = '', sectorName = '', evtId = TW.eventParams.id, evtDate = TW.eventParams.date, areaId = TW.eventParams.placeId, hallId = TW.eventParams.hallId) {
        let str = [evtId, evtDate, areaId, hallId, sectorName, row, place].join('');
        return b64Encode(str);
    }

    confirm(text, callback) {
        TW.$confirm.removeClass('tw-hidden')
        TW.$confirm.find('#tw-confirm-text').html(text)
        TW.$confirm.find('#tw-confirm-success').off('click').one('click', () => {
            if (typeof callback === 'function') {
                callback();
            }
            TW.$confirm.addClass('tw-hidden');
        })
        TW.$confirm.find('#tw-confirm-cancel').off('click').one('click', () => TW.$confirm.addClass('tw-hidden'))
    }

    metrikaReachGoal(key) {
        try {
            for (const [key, obj] of Object.entries(window.Ya)) {
                if (key.startsWith('Metrika') && typeof obj.counters === 'function') {
                    window.ym(obj.counters()[0].id, 'reachGoal', key);
                }
            }
        } catch (e) {
            console.log(`Failed send yandex metrika reachGoal ${key}`, e);
        }

        try {
            fetch(`https://svg-gen.ru/stat.php?code=mos2019&date=${TW.eventParams.date}&event_id=${TW.eventParams.id}&place_id=${TW.eventParams.placeId}&hall_id=${TW.eventParams.hallId}&host=${location.hostname}&event_type=${key}`)
        } catch (e) {
            console.log(`Failed send custom metrika stat ${key}`, e);
        }
    }
}

function b64Encode(input) {
    input = typeof input === 'object' ? JSON.stringify(input) : input.toString();

    return btoa(encodeURIComponent(input).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode('0x' + p1);
        })
    );
}

function b64Decode(str) {
    let result;

    try {
        result = decodeURIComponent(atob(str).split('').map(function (c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    } catch (e) {
        console.log(e);
        result = undefined;
    }

    try {
        return result && JSON.stringify(JSON.parse(result)) === result ? JSON.parse(result) : result;
    } catch (e) {
        console.log(e);
    }

    return result;
}

function selectTicket(elem) {
    const sectorId = elem.getAttribute('data-click');
    TW.lastClickedTicketElem = elem;

    if (sectorId) {
        TW.getEvent({partId: sectorId});
        return false;
    }

    let cartItemOb = new TWCartItem(elem);

    //todo может перевести на атрибуты корзины?

    let bySectorText = elem.getAttribute('data-qnt-more-text') || '';
    let bySectorPriceForText = elem.getAttribute('data-price-for') || '';
    let existCartItem = TW.cartInst.items.find(item => item.id === cartItemOb.id);

    if (cartItemOb.bySector) {
        let priceText = !cartItemOb.onOrder
            ? cartItemOb.price + ' ₽'
            : 'Под заказ'
        ;

        TW.$morePopup.find('[data-title]').html(cartItemOb.placeName);
        TW.$morePopup.find('[data-desc]').html(bySectorText);
        TW.$morePopup.find('[data-price]').text(priceText);
        const sectorQnt = cartItemOb.domElem.getAttribute('data-sector-qnt');

        let inputMax = sectorQnt;

        if (cartItemOb.isGift) {
            inputMax = TW.cartInst.ticketsCount(false);

            if (! inputMax){
                alert('Чтобы выбрать подарок, нужно добавить билеты в корзину!');
                return;
            }
        }

        const currentQnt = existCartItem?.qnt || 0;
        const freeCount = inputMax - currentQnt;

        TW.$morePopup.find('[data-free-count]').html(freeCount);
        TW.$morePopup.find('.tw__popup-changer-btn').removeAttr('disabled');

        if (bySectorPriceForText !== '') {
            TW.$morePopup.find('.tw__popup-price-one').hide();
            TW.$morePopup.find('.tw__popup-price-sector').html(bySectorPriceForText).show();
            TW.$morePopup.find('.tw__popup-changer').hide();
            TW.$morePopup.find('[data-tickets]').text('');

            // TW.$morePopup.find('.tw__popup-buy').removeClass('tw-hidden');
        } else {
            TW.$morePopup.find('.tw__popup-price-one').show();
            TW.$morePopup.find('.tw__popup-price-sector').hide();
            TW.$morePopup.find('.tw__popup-changer').show();
            TW.$morePopup.find('.tw__popup-changer-qnt')
                .val(currentQnt)
                .attr('data-max', inputMax)
                .trigger('input')
            ;

            // TW.$morePopup.find('.tw__popup-buy').addClass('tw-hidden');
        }

        TW.$tooltip.hide();
        TW.$morePopup.addClass('active');
    } else {
        TW.$morePopup.removeClass('active');

        if (!existCartItem) {
            TW.cartInst.add(cartItemOb);
            elem.classList.add('active');

            TW.showTooltip(elem);
        } else {
            TW.cartInst.delete(existCartItem.id);

            if (TW.isTouchDevice) {
                TW.$tooltip.hide();
            }
        }

        TW.redrawMinicart();
    }
}

let hasListener = false

function twSelect() {
    const mains = document.querySelectorAll('[data-select="main"]');

    if (!mains.length) return;

    const removeActive = (elems) => {
        elems.forEach(main => main.classList.remove('active'));
    };

    if (hasListener) return;

    hasListener = true

    document.addEventListener('click', (event) => {
        if (event.target.closest('[data-select="main"]')) {
            mains.forEach(main => {
                if (main !== event.target.closest('[data-select="main"]'))
                    main.classList.remove('active')
            })

            const select = event.target.closest('[data-select="main"]'),
                value = select.querySelector('[data-select="value"]'),
                listItems = select.querySelectorAll('li')

            if (event.target.closest('[data-select="head"]')) {
                select.classList.toggle('active')
            }

            if (event.target.closest('li')) {
                const listItem = event.target.closest('li')

                if (!listItem.classList.contains('active')) {
                    removeActive(listItems)
                    value.textContent = listItem.textContent
                    listItem.classList.add('active')
                    $(select).children(`input[type=hidden]`).val(listItem.textContent).trigger('change').removeClass('invalid');
                    select.classList.remove('active')
                }
            }
        } else {
            removeActive(mains)
        }
    })
}

twSelect()//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhhbW1lci5taW4uanMiLCJzdmctcGFuLXpvb20uanMiLCJqcy9UVy52Mi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ05BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUM5M0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoid2lkZ2V0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyohIEhhbW1lci5KUyAtIHYyLjAuOCAtIDIwMTYtMDQtMjNcbiAqIGh0dHA6Ly9oYW1tZXJqcy5naXRodWIuaW8vXG4gKlxuICogQ29weXJpZ2h0IChjKSAyMDE2IEpvcmlrIFRhbmdlbGRlcjtcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSAqL1xuIWZ1bmN0aW9uKGEsYixjLGQpe1widXNlIHN0cmljdFwiO2Z1bmN0aW9uIGUoYSxiLGMpe3JldHVybiBzZXRUaW1lb3V0KGooYSxjKSxiKX1mdW5jdGlvbiBmKGEsYixjKXtyZXR1cm4gQXJyYXkuaXNBcnJheShhKT8oZyhhLGNbYl0sYyksITApOiExfWZ1bmN0aW9uIGcoYSxiLGMpe3ZhciBlO2lmKGEpaWYoYS5mb3JFYWNoKWEuZm9yRWFjaChiLGMpO2Vsc2UgaWYoYS5sZW5ndGghPT1kKWZvcihlPTA7ZTxhLmxlbmd0aDspYi5jYWxsKGMsYVtlXSxlLGEpLGUrKztlbHNlIGZvcihlIGluIGEpYS5oYXNPd25Qcm9wZXJ0eShlKSYmYi5jYWxsKGMsYVtlXSxlLGEpfWZ1bmN0aW9uIGgoYixjLGQpe3ZhciBlPVwiREVQUkVDQVRFRCBNRVRIT0Q6IFwiK2MrXCJcXG5cIitkK1wiIEFUIFxcblwiO3JldHVybiBmdW5jdGlvbigpe3ZhciBjPW5ldyBFcnJvcihcImdldC1zdGFjay10cmFjZVwiKSxkPWMmJmMuc3RhY2s/Yy5zdGFjay5yZXBsYWNlKC9eW15cXChdKz9bXFxuJF0vZ20sXCJcIikucmVwbGFjZSgvXlxccythdFxccysvZ20sXCJcIikucmVwbGFjZSgvXk9iamVjdC48YW5vbnltb3VzPlxccypcXCgvZ20sXCJ7YW5vbnltb3VzfSgpQFwiKTpcIlVua25vd24gU3RhY2sgVHJhY2VcIixmPWEuY29uc29sZSYmKGEuY29uc29sZS53YXJufHxhLmNvbnNvbGUubG9nKTtyZXR1cm4gZiYmZi5jYWxsKGEuY29uc29sZSxlLGQpLGIuYXBwbHkodGhpcyxhcmd1bWVudHMpfX1mdW5jdGlvbiBpKGEsYixjKXt2YXIgZCxlPWIucHJvdG90eXBlO2Q9YS5wcm90b3R5cGU9T2JqZWN0LmNyZWF0ZShlKSxkLmNvbnN0cnVjdG9yPWEsZC5fc3VwZXI9ZSxjJiZsYShkLGMpfWZ1bmN0aW9uIGooYSxiKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gYS5hcHBseShiLGFyZ3VtZW50cyl9fWZ1bmN0aW9uIGsoYSxiKXtyZXR1cm4gdHlwZW9mIGE9PW9hP2EuYXBwbHkoYj9iWzBdfHxkOmQsYik6YX1mdW5jdGlvbiBsKGEsYil7cmV0dXJuIGE9PT1kP2I6YX1mdW5jdGlvbiBtKGEsYixjKXtnKHEoYiksZnVuY3Rpb24oYil7YS5hZGRFdmVudExpc3RlbmVyKGIsYywhMSl9KX1mdW5jdGlvbiBuKGEsYixjKXtnKHEoYiksZnVuY3Rpb24oYil7YS5yZW1vdmVFdmVudExpc3RlbmVyKGIsYywhMSl9KX1mdW5jdGlvbiBvKGEsYil7Zm9yKDthOyl7aWYoYT09YilyZXR1cm4hMDthPWEucGFyZW50Tm9kZX1yZXR1cm4hMX1mdW5jdGlvbiBwKGEsYil7cmV0dXJuIGEuaW5kZXhPZihiKT4tMX1mdW5jdGlvbiBxKGEpe3JldHVybiBhLnRyaW0oKS5zcGxpdCgvXFxzKy9nKX1mdW5jdGlvbiByKGEsYixjKXtpZihhLmluZGV4T2YmJiFjKXJldHVybiBhLmluZGV4T2YoYik7Zm9yKHZhciBkPTA7ZDxhLmxlbmd0aDspe2lmKGMmJmFbZF1bY109PWJ8fCFjJiZhW2RdPT09YilyZXR1cm4gZDtkKyt9cmV0dXJuLTF9ZnVuY3Rpb24gcyhhKXtyZXR1cm4gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYSwwKX1mdW5jdGlvbiB0KGEsYixjKXtmb3IodmFyIGQ9W10sZT1bXSxmPTA7ZjxhLmxlbmd0aDspe3ZhciBnPWI/YVtmXVtiXTphW2ZdO3IoZSxnKTwwJiZkLnB1c2goYVtmXSksZVtmXT1nLGYrK31yZXR1cm4gYyYmKGQ9Yj9kLnNvcnQoZnVuY3Rpb24oYSxjKXtyZXR1cm4gYVtiXT5jW2JdfSk6ZC5zb3J0KCkpLGR9ZnVuY3Rpb24gdShhLGIpe2Zvcih2YXIgYyxlLGY9YlswXS50b1VwcGVyQ2FzZSgpK2Iuc2xpY2UoMSksZz0wO2c8bWEubGVuZ3RoOyl7aWYoYz1tYVtnXSxlPWM/YytmOmIsZSBpbiBhKXJldHVybiBlO2crK31yZXR1cm4gZH1mdW5jdGlvbiB2KCl7cmV0dXJuIHVhKyt9ZnVuY3Rpb24gdyhiKXt2YXIgYz1iLm93bmVyRG9jdW1lbnR8fGI7cmV0dXJuIGMuZGVmYXVsdFZpZXd8fGMucGFyZW50V2luZG93fHxhfWZ1bmN0aW9uIHgoYSxiKXt2YXIgYz10aGlzO3RoaXMubWFuYWdlcj1hLHRoaXMuY2FsbGJhY2s9Yix0aGlzLmVsZW1lbnQ9YS5lbGVtZW50LHRoaXMudGFyZ2V0PWEub3B0aW9ucy5pbnB1dFRhcmdldCx0aGlzLmRvbUhhbmRsZXI9ZnVuY3Rpb24oYil7ayhhLm9wdGlvbnMuZW5hYmxlLFthXSkmJmMuaGFuZGxlcihiKX0sdGhpcy5pbml0KCl9ZnVuY3Rpb24geShhKXt2YXIgYixjPWEub3B0aW9ucy5pbnB1dENsYXNzO3JldHVybiBuZXcoYj1jP2M6eGE/TTp5YT9QOndhP1I6TCkoYSx6KX1mdW5jdGlvbiB6KGEsYixjKXt2YXIgZD1jLnBvaW50ZXJzLmxlbmd0aCxlPWMuY2hhbmdlZFBvaW50ZXJzLmxlbmd0aCxmPWImRWEmJmQtZT09PTAsZz1iJihHYXxIYSkmJmQtZT09PTA7Yy5pc0ZpcnN0PSEhZixjLmlzRmluYWw9ISFnLGYmJihhLnNlc3Npb249e30pLGMuZXZlbnRUeXBlPWIsQShhLGMpLGEuZW1pdChcImhhbW1lci5pbnB1dFwiLGMpLGEucmVjb2duaXplKGMpLGEuc2Vzc2lvbi5wcmV2SW5wdXQ9Y31mdW5jdGlvbiBBKGEsYil7dmFyIGM9YS5zZXNzaW9uLGQ9Yi5wb2ludGVycyxlPWQubGVuZ3RoO2MuZmlyc3RJbnB1dHx8KGMuZmlyc3RJbnB1dD1EKGIpKSxlPjEmJiFjLmZpcnN0TXVsdGlwbGU/Yy5maXJzdE11bHRpcGxlPUQoYik6MT09PWUmJihjLmZpcnN0TXVsdGlwbGU9ITEpO3ZhciBmPWMuZmlyc3RJbnB1dCxnPWMuZmlyc3RNdWx0aXBsZSxoPWc/Zy5jZW50ZXI6Zi5jZW50ZXIsaT1iLmNlbnRlcj1FKGQpO2IudGltZVN0YW1wPXJhKCksYi5kZWx0YVRpbWU9Yi50aW1lU3RhbXAtZi50aW1lU3RhbXAsYi5hbmdsZT1JKGgsaSksYi5kaXN0YW5jZT1IKGgsaSksQihjLGIpLGIub2Zmc2V0RGlyZWN0aW9uPUcoYi5kZWx0YVgsYi5kZWx0YVkpO3ZhciBqPUYoYi5kZWx0YVRpbWUsYi5kZWx0YVgsYi5kZWx0YVkpO2Iub3ZlcmFsbFZlbG9jaXR5WD1qLngsYi5vdmVyYWxsVmVsb2NpdHlZPWoueSxiLm92ZXJhbGxWZWxvY2l0eT1xYShqLngpPnFhKGoueSk/ai54OmoueSxiLnNjYWxlPWc/SyhnLnBvaW50ZXJzLGQpOjEsYi5yb3RhdGlvbj1nP0ooZy5wb2ludGVycyxkKTowLGIubWF4UG9pbnRlcnM9Yy5wcmV2SW5wdXQ/Yi5wb2ludGVycy5sZW5ndGg+Yy5wcmV2SW5wdXQubWF4UG9pbnRlcnM/Yi5wb2ludGVycy5sZW5ndGg6Yy5wcmV2SW5wdXQubWF4UG9pbnRlcnM6Yi5wb2ludGVycy5sZW5ndGgsQyhjLGIpO3ZhciBrPWEuZWxlbWVudDtvKGIuc3JjRXZlbnQudGFyZ2V0LGspJiYoaz1iLnNyY0V2ZW50LnRhcmdldCksYi50YXJnZXQ9a31mdW5jdGlvbiBCKGEsYil7dmFyIGM9Yi5jZW50ZXIsZD1hLm9mZnNldERlbHRhfHx7fSxlPWEucHJldkRlbHRhfHx7fSxmPWEucHJldklucHV0fHx7fTtiLmV2ZW50VHlwZSE9PUVhJiZmLmV2ZW50VHlwZSE9PUdhfHwoZT1hLnByZXZEZWx0YT17eDpmLmRlbHRhWHx8MCx5OmYuZGVsdGFZfHwwfSxkPWEub2Zmc2V0RGVsdGE9e3g6Yy54LHk6Yy55fSksYi5kZWx0YVg9ZS54KyhjLngtZC54KSxiLmRlbHRhWT1lLnkrKGMueS1kLnkpfWZ1bmN0aW9uIEMoYSxiKXt2YXIgYyxlLGYsZyxoPWEubGFzdEludGVydmFsfHxiLGk9Yi50aW1lU3RhbXAtaC50aW1lU3RhbXA7aWYoYi5ldmVudFR5cGUhPUhhJiYoaT5EYXx8aC52ZWxvY2l0eT09PWQpKXt2YXIgaj1iLmRlbHRhWC1oLmRlbHRhWCxrPWIuZGVsdGFZLWguZGVsdGFZLGw9RihpLGosayk7ZT1sLngsZj1sLnksYz1xYShsLngpPnFhKGwueSk/bC54OmwueSxnPUcoaixrKSxhLmxhc3RJbnRlcnZhbD1ifWVsc2UgYz1oLnZlbG9jaXR5LGU9aC52ZWxvY2l0eVgsZj1oLnZlbG9jaXR5WSxnPWguZGlyZWN0aW9uO2IudmVsb2NpdHk9YyxiLnZlbG9jaXR5WD1lLGIudmVsb2NpdHlZPWYsYi5kaXJlY3Rpb249Z31mdW5jdGlvbiBEKGEpe2Zvcih2YXIgYj1bXSxjPTA7YzxhLnBvaW50ZXJzLmxlbmd0aDspYltjXT17Y2xpZW50WDpwYShhLnBvaW50ZXJzW2NdLmNsaWVudFgpLGNsaWVudFk6cGEoYS5wb2ludGVyc1tjXS5jbGllbnRZKX0sYysrO3JldHVybnt0aW1lU3RhbXA6cmEoKSxwb2ludGVyczpiLGNlbnRlcjpFKGIpLGRlbHRhWDphLmRlbHRhWCxkZWx0YVk6YS5kZWx0YVl9fWZ1bmN0aW9uIEUoYSl7dmFyIGI9YS5sZW5ndGg7aWYoMT09PWIpcmV0dXJue3g6cGEoYVswXS5jbGllbnRYKSx5OnBhKGFbMF0uY2xpZW50WSl9O2Zvcih2YXIgYz0wLGQ9MCxlPTA7Yj5lOyljKz1hW2VdLmNsaWVudFgsZCs9YVtlXS5jbGllbnRZLGUrKztyZXR1cm57eDpwYShjL2IpLHk6cGEoZC9iKX19ZnVuY3Rpb24gRihhLGIsYyl7cmV0dXJue3g6Yi9hfHwwLHk6Yy9hfHwwfX1mdW5jdGlvbiBHKGEsYil7cmV0dXJuIGE9PT1iP0lhOnFhKGEpPj1xYShiKT8wPmE/SmE6S2E6MD5iP0xhOk1hfWZ1bmN0aW9uIEgoYSxiLGMpe2N8fChjPVFhKTt2YXIgZD1iW2NbMF1dLWFbY1swXV0sZT1iW2NbMV1dLWFbY1sxXV07cmV0dXJuIE1hdGguc3FydChkKmQrZSplKX1mdW5jdGlvbiBJKGEsYixjKXtjfHwoYz1RYSk7dmFyIGQ9YltjWzBdXS1hW2NbMF1dLGU9YltjWzFdXS1hW2NbMV1dO3JldHVybiAxODAqTWF0aC5hdGFuMihlLGQpL01hdGguUEl9ZnVuY3Rpb24gSihhLGIpe3JldHVybiBJKGJbMV0sYlswXSxSYSkrSShhWzFdLGFbMF0sUmEpfWZ1bmN0aW9uIEsoYSxiKXtyZXR1cm4gSChiWzBdLGJbMV0sUmEpL0goYVswXSxhWzFdLFJhKX1mdW5jdGlvbiBMKCl7dGhpcy5ldkVsPVRhLHRoaXMuZXZXaW49VWEsdGhpcy5wcmVzc2VkPSExLHguYXBwbHkodGhpcyxhcmd1bWVudHMpfWZ1bmN0aW9uIE0oKXt0aGlzLmV2RWw9WGEsdGhpcy5ldldpbj1ZYSx4LmFwcGx5KHRoaXMsYXJndW1lbnRzKSx0aGlzLnN0b3JlPXRoaXMubWFuYWdlci5zZXNzaW9uLnBvaW50ZXJFdmVudHM9W119ZnVuY3Rpb24gTigpe3RoaXMuZXZUYXJnZXQ9JGEsdGhpcy5ldldpbj1fYSx0aGlzLnN0YXJ0ZWQ9ITEseC5hcHBseSh0aGlzLGFyZ3VtZW50cyl9ZnVuY3Rpb24gTyhhLGIpe3ZhciBjPXMoYS50b3VjaGVzKSxkPXMoYS5jaGFuZ2VkVG91Y2hlcyk7cmV0dXJuIGImKEdhfEhhKSYmKGM9dChjLmNvbmNhdChkKSxcImlkZW50aWZpZXJcIiwhMCkpLFtjLGRdfWZ1bmN0aW9uIFAoKXt0aGlzLmV2VGFyZ2V0PWJiLHRoaXMudGFyZ2V0SWRzPXt9LHguYXBwbHkodGhpcyxhcmd1bWVudHMpfWZ1bmN0aW9uIFEoYSxiKXt2YXIgYz1zKGEudG91Y2hlcyksZD10aGlzLnRhcmdldElkcztpZihiJihFYXxGYSkmJjE9PT1jLmxlbmd0aClyZXR1cm4gZFtjWzBdLmlkZW50aWZpZXJdPSEwLFtjLGNdO3ZhciBlLGYsZz1zKGEuY2hhbmdlZFRvdWNoZXMpLGg9W10saT10aGlzLnRhcmdldDtpZihmPWMuZmlsdGVyKGZ1bmN0aW9uKGEpe3JldHVybiBvKGEudGFyZ2V0LGkpfSksYj09PUVhKWZvcihlPTA7ZTxmLmxlbmd0aDspZFtmW2VdLmlkZW50aWZpZXJdPSEwLGUrKztmb3IoZT0wO2U8Zy5sZW5ndGg7KWRbZ1tlXS5pZGVudGlmaWVyXSYmaC5wdXNoKGdbZV0pLGImKEdhfEhhKSYmZGVsZXRlIGRbZ1tlXS5pZGVudGlmaWVyXSxlKys7cmV0dXJuIGgubGVuZ3RoP1t0KGYuY29uY2F0KGgpLFwiaWRlbnRpZmllclwiLCEwKSxoXTp2b2lkIDB9ZnVuY3Rpb24gUigpe3guYXBwbHkodGhpcyxhcmd1bWVudHMpO3ZhciBhPWoodGhpcy5oYW5kbGVyLHRoaXMpO3RoaXMudG91Y2g9bmV3IFAodGhpcy5tYW5hZ2VyLGEpLHRoaXMubW91c2U9bmV3IEwodGhpcy5tYW5hZ2VyLGEpLHRoaXMucHJpbWFyeVRvdWNoPW51bGwsdGhpcy5sYXN0VG91Y2hlcz1bXX1mdW5jdGlvbiBTKGEsYil7YSZFYT8odGhpcy5wcmltYXJ5VG91Y2g9Yi5jaGFuZ2VkUG9pbnRlcnNbMF0uaWRlbnRpZmllcixULmNhbGwodGhpcyxiKSk6YSYoR2F8SGEpJiZULmNhbGwodGhpcyxiKX1mdW5jdGlvbiBUKGEpe3ZhciBiPWEuY2hhbmdlZFBvaW50ZXJzWzBdO2lmKGIuaWRlbnRpZmllcj09PXRoaXMucHJpbWFyeVRvdWNoKXt2YXIgYz17eDpiLmNsaWVudFgseTpiLmNsaWVudFl9O3RoaXMubGFzdFRvdWNoZXMucHVzaChjKTt2YXIgZD10aGlzLmxhc3RUb3VjaGVzLGU9ZnVuY3Rpb24oKXt2YXIgYT1kLmluZGV4T2YoYyk7YT4tMSYmZC5zcGxpY2UoYSwxKX07c2V0VGltZW91dChlLGNiKX19ZnVuY3Rpb24gVShhKXtmb3IodmFyIGI9YS5zcmNFdmVudC5jbGllbnRYLGM9YS5zcmNFdmVudC5jbGllbnRZLGQ9MDtkPHRoaXMubGFzdFRvdWNoZXMubGVuZ3RoO2QrKyl7dmFyIGU9dGhpcy5sYXN0VG91Y2hlc1tkXSxmPU1hdGguYWJzKGItZS54KSxnPU1hdGguYWJzKGMtZS55KTtpZihkYj49ZiYmZGI+PWcpcmV0dXJuITB9cmV0dXJuITF9ZnVuY3Rpb24gVihhLGIpe3RoaXMubWFuYWdlcj1hLHRoaXMuc2V0KGIpfWZ1bmN0aW9uIFcoYSl7aWYocChhLGpiKSlyZXR1cm4gamI7dmFyIGI9cChhLGtiKSxjPXAoYSxsYik7cmV0dXJuIGImJmM/amI6Ynx8Yz9iP2tiOmxiOnAoYSxpYik/aWI6aGJ9ZnVuY3Rpb24gWCgpe2lmKCFmYilyZXR1cm4hMTt2YXIgYj17fSxjPWEuQ1NTJiZhLkNTUy5zdXBwb3J0cztyZXR1cm5bXCJhdXRvXCIsXCJtYW5pcHVsYXRpb25cIixcInBhbi15XCIsXCJwYW4teFwiLFwicGFuLXggcGFuLXlcIixcIm5vbmVcIl0uZm9yRWFjaChmdW5jdGlvbihkKXtiW2RdPWM/YS5DU1Muc3VwcG9ydHMoXCJ0b3VjaC1hY3Rpb25cIixkKTohMH0pLGJ9ZnVuY3Rpb24gWShhKXt0aGlzLm9wdGlvbnM9bGEoe30sdGhpcy5kZWZhdWx0cyxhfHx7fSksdGhpcy5pZD12KCksdGhpcy5tYW5hZ2VyPW51bGwsdGhpcy5vcHRpb25zLmVuYWJsZT1sKHRoaXMub3B0aW9ucy5lbmFibGUsITApLHRoaXMuc3RhdGU9bmIsdGhpcy5zaW11bHRhbmVvdXM9e30sdGhpcy5yZXF1aXJlRmFpbD1bXX1mdW5jdGlvbiBaKGEpe3JldHVybiBhJnNiP1wiY2FuY2VsXCI6YSZxYj9cImVuZFwiOmEmcGI/XCJtb3ZlXCI6YSZvYj9cInN0YXJ0XCI6XCJcIn1mdW5jdGlvbiAkKGEpe3JldHVybiBhPT1NYT9cImRvd25cIjphPT1MYT9cInVwXCI6YT09SmE/XCJsZWZ0XCI6YT09S2E/XCJyaWdodFwiOlwiXCJ9ZnVuY3Rpb24gXyhhLGIpe3ZhciBjPWIubWFuYWdlcjtyZXR1cm4gYz9jLmdldChhKTphfWZ1bmN0aW9uIGFhKCl7WS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9ZnVuY3Rpb24gYmEoKXthYS5hcHBseSh0aGlzLGFyZ3VtZW50cyksdGhpcy5wWD1udWxsLHRoaXMucFk9bnVsbH1mdW5jdGlvbiBjYSgpe2FhLmFwcGx5KHRoaXMsYXJndW1lbnRzKX1mdW5jdGlvbiBkYSgpe1kuYXBwbHkodGhpcyxhcmd1bWVudHMpLHRoaXMuX3RpbWVyPW51bGwsdGhpcy5faW5wdXQ9bnVsbH1mdW5jdGlvbiBlYSgpe2FhLmFwcGx5KHRoaXMsYXJndW1lbnRzKX1mdW5jdGlvbiBmYSgpe2FhLmFwcGx5KHRoaXMsYXJndW1lbnRzKX1mdW5jdGlvbiBnYSgpe1kuYXBwbHkodGhpcyxhcmd1bWVudHMpLHRoaXMucFRpbWU9ITEsdGhpcy5wQ2VudGVyPSExLHRoaXMuX3RpbWVyPW51bGwsdGhpcy5faW5wdXQ9bnVsbCx0aGlzLmNvdW50PTB9ZnVuY3Rpb24gaGEoYSxiKXtyZXR1cm4gYj1ifHx7fSxiLnJlY29nbml6ZXJzPWwoYi5yZWNvZ25pemVycyxoYS5kZWZhdWx0cy5wcmVzZXQpLG5ldyBpYShhLGIpfWZ1bmN0aW9uIGlhKGEsYil7dGhpcy5vcHRpb25zPWxhKHt9LGhhLmRlZmF1bHRzLGJ8fHt9KSx0aGlzLm9wdGlvbnMuaW5wdXRUYXJnZXQ9dGhpcy5vcHRpb25zLmlucHV0VGFyZ2V0fHxhLHRoaXMuaGFuZGxlcnM9e30sdGhpcy5zZXNzaW9uPXt9LHRoaXMucmVjb2duaXplcnM9W10sdGhpcy5vbGRDc3NQcm9wcz17fSx0aGlzLmVsZW1lbnQ9YSx0aGlzLmlucHV0PXkodGhpcyksdGhpcy50b3VjaEFjdGlvbj1uZXcgVih0aGlzLHRoaXMub3B0aW9ucy50b3VjaEFjdGlvbiksamEodGhpcywhMCksZyh0aGlzLm9wdGlvbnMucmVjb2duaXplcnMsZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5hZGQobmV3IGFbMF0oYVsxXSkpO2FbMl0mJmIucmVjb2duaXplV2l0aChhWzJdKSxhWzNdJiZiLnJlcXVpcmVGYWlsdXJlKGFbM10pfSx0aGlzKX1mdW5jdGlvbiBqYShhLGIpe3ZhciBjPWEuZWxlbWVudDtpZihjLnN0eWxlKXt2YXIgZDtnKGEub3B0aW9ucy5jc3NQcm9wcyxmdW5jdGlvbihlLGYpe2Q9dShjLnN0eWxlLGYpLGI/KGEub2xkQ3NzUHJvcHNbZF09Yy5zdHlsZVtkXSxjLnN0eWxlW2RdPWUpOmMuc3R5bGVbZF09YS5vbGRDc3NQcm9wc1tkXXx8XCJcIn0pLGJ8fChhLm9sZENzc1Byb3BzPXt9KX19ZnVuY3Rpb24ga2EoYSxjKXt2YXIgZD1iLmNyZWF0ZUV2ZW50KFwiRXZlbnRcIik7ZC5pbml0RXZlbnQoYSwhMCwhMCksZC5nZXN0dXJlPWMsYy50YXJnZXQuZGlzcGF0Y2hFdmVudChkKX12YXIgbGEsbWE9W1wiXCIsXCJ3ZWJraXRcIixcIk1velwiLFwiTVNcIixcIm1zXCIsXCJvXCJdLG5hPWIuY3JlYXRlRWxlbWVudChcImRpdlwiKSxvYT1cImZ1bmN0aW9uXCIscGE9TWF0aC5yb3VuZCxxYT1NYXRoLmFicyxyYT1EYXRlLm5vdztsYT1cImZ1bmN0aW9uXCIhPXR5cGVvZiBPYmplY3QuYXNzaWduP2Z1bmN0aW9uKGEpe2lmKGE9PT1kfHxudWxsPT09YSl0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNvbnZlcnQgdW5kZWZpbmVkIG9yIG51bGwgdG8gb2JqZWN0XCIpO2Zvcih2YXIgYj1PYmplY3QoYSksYz0xO2M8YXJndW1lbnRzLmxlbmd0aDtjKyspe3ZhciBlPWFyZ3VtZW50c1tjXTtpZihlIT09ZCYmbnVsbCE9PWUpZm9yKHZhciBmIGluIGUpZS5oYXNPd25Qcm9wZXJ0eShmKSYmKGJbZl09ZVtmXSl9cmV0dXJuIGJ9Ok9iamVjdC5hc3NpZ247dmFyIHNhPWgoZnVuY3Rpb24oYSxiLGMpe2Zvcih2YXIgZT1PYmplY3Qua2V5cyhiKSxmPTA7ZjxlLmxlbmd0aDspKCFjfHxjJiZhW2VbZl1dPT09ZCkmJihhW2VbZl1dPWJbZVtmXV0pLGYrKztyZXR1cm4gYX0sXCJleHRlbmRcIixcIlVzZSBgYXNzaWduYC5cIiksdGE9aChmdW5jdGlvbihhLGIpe3JldHVybiBzYShhLGIsITApfSxcIm1lcmdlXCIsXCJVc2UgYGFzc2lnbmAuXCIpLHVhPTEsdmE9L21vYmlsZXx0YWJsZXR8aXAoYWR8aG9uZXxvZCl8YW5kcm9pZC9pLHdhPVwib250b3VjaHN0YXJ0XCJpbiBhLHhhPXUoYSxcIlBvaW50ZXJFdmVudFwiKSE9PWQseWE9d2EmJnZhLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCksemE9XCJ0b3VjaFwiLEFhPVwicGVuXCIsQmE9XCJtb3VzZVwiLENhPVwia2luZWN0XCIsRGE9MjUsRWE9MSxGYT0yLEdhPTQsSGE9OCxJYT0xLEphPTIsS2E9NCxMYT04LE1hPTE2LE5hPUphfEthLE9hPUxhfE1hLFBhPU5hfE9hLFFhPVtcInhcIixcInlcIl0sUmE9W1wiY2xpZW50WFwiLFwiY2xpZW50WVwiXTt4LnByb3RvdHlwZT17aGFuZGxlcjpmdW5jdGlvbigpe30saW5pdDpmdW5jdGlvbigpe3RoaXMuZXZFbCYmbSh0aGlzLmVsZW1lbnQsdGhpcy5ldkVsLHRoaXMuZG9tSGFuZGxlciksdGhpcy5ldlRhcmdldCYmbSh0aGlzLnRhcmdldCx0aGlzLmV2VGFyZ2V0LHRoaXMuZG9tSGFuZGxlciksdGhpcy5ldldpbiYmbSh3KHRoaXMuZWxlbWVudCksdGhpcy5ldldpbix0aGlzLmRvbUhhbmRsZXIpfSxkZXN0cm95OmZ1bmN0aW9uKCl7dGhpcy5ldkVsJiZuKHRoaXMuZWxlbWVudCx0aGlzLmV2RWwsdGhpcy5kb21IYW5kbGVyKSx0aGlzLmV2VGFyZ2V0JiZuKHRoaXMudGFyZ2V0LHRoaXMuZXZUYXJnZXQsdGhpcy5kb21IYW5kbGVyKSx0aGlzLmV2V2luJiZuKHcodGhpcy5lbGVtZW50KSx0aGlzLmV2V2luLHRoaXMuZG9tSGFuZGxlcil9fTt2YXIgU2E9e21vdXNlZG93bjpFYSxtb3VzZW1vdmU6RmEsbW91c2V1cDpHYX0sVGE9XCJtb3VzZWRvd25cIixVYT1cIm1vdXNlbW92ZSBtb3VzZXVwXCI7aShMLHgse2hhbmRsZXI6ZnVuY3Rpb24oYSl7dmFyIGI9U2FbYS50eXBlXTtiJkVhJiYwPT09YS5idXR0b24mJih0aGlzLnByZXNzZWQ9ITApLGImRmEmJjEhPT1hLndoaWNoJiYoYj1HYSksdGhpcy5wcmVzc2VkJiYoYiZHYSYmKHRoaXMucHJlc3NlZD0hMSksdGhpcy5jYWxsYmFjayh0aGlzLm1hbmFnZXIsYix7cG9pbnRlcnM6W2FdLGNoYW5nZWRQb2ludGVyczpbYV0scG9pbnRlclR5cGU6QmEsc3JjRXZlbnQ6YX0pKX19KTt2YXIgVmE9e3BvaW50ZXJkb3duOkVhLHBvaW50ZXJtb3ZlOkZhLHBvaW50ZXJ1cDpHYSxwb2ludGVyY2FuY2VsOkhhLHBvaW50ZXJvdXQ6SGF9LFdhPXsyOnphLDM6QWEsNDpCYSw1OkNhfSxYYT1cInBvaW50ZXJkb3duXCIsWWE9XCJwb2ludGVybW92ZSBwb2ludGVydXAgcG9pbnRlcmNhbmNlbFwiO2EuTVNQb2ludGVyRXZlbnQmJiFhLlBvaW50ZXJFdmVudCYmKFhhPVwiTVNQb2ludGVyRG93blwiLFlhPVwiTVNQb2ludGVyTW92ZSBNU1BvaW50ZXJVcCBNU1BvaW50ZXJDYW5jZWxcIiksaShNLHgse2hhbmRsZXI6ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5zdG9yZSxjPSExLGQ9YS50eXBlLnRvTG93ZXJDYXNlKCkucmVwbGFjZShcIm1zXCIsXCJcIiksZT1WYVtkXSxmPVdhW2EucG9pbnRlclR5cGVdfHxhLnBvaW50ZXJUeXBlLGc9Zj09emEsaD1yKGIsYS5wb2ludGVySWQsXCJwb2ludGVySWRcIik7ZSZFYSYmKDA9PT1hLmJ1dHRvbnx8Zyk/MD5oJiYoYi5wdXNoKGEpLGg9Yi5sZW5ndGgtMSk6ZSYoR2F8SGEpJiYoYz0hMCksMD5ofHwoYltoXT1hLHRoaXMuY2FsbGJhY2sodGhpcy5tYW5hZ2VyLGUse3BvaW50ZXJzOmIsY2hhbmdlZFBvaW50ZXJzOlthXSxwb2ludGVyVHlwZTpmLHNyY0V2ZW50OmF9KSxjJiZiLnNwbGljZShoLDEpKX19KTt2YXIgWmE9e3RvdWNoc3RhcnQ6RWEsdG91Y2htb3ZlOkZhLHRvdWNoZW5kOkdhLHRvdWNoY2FuY2VsOkhhfSwkYT1cInRvdWNoc3RhcnRcIixfYT1cInRvdWNoc3RhcnQgdG91Y2htb3ZlIHRvdWNoZW5kIHRvdWNoY2FuY2VsXCI7aShOLHgse2hhbmRsZXI6ZnVuY3Rpb24oYSl7dmFyIGI9WmFbYS50eXBlXTtpZihiPT09RWEmJih0aGlzLnN0YXJ0ZWQ9ITApLHRoaXMuc3RhcnRlZCl7dmFyIGM9Ty5jYWxsKHRoaXMsYSxiKTtiJihHYXxIYSkmJmNbMF0ubGVuZ3RoLWNbMV0ubGVuZ3RoPT09MCYmKHRoaXMuc3RhcnRlZD0hMSksdGhpcy5jYWxsYmFjayh0aGlzLm1hbmFnZXIsYix7cG9pbnRlcnM6Y1swXSxjaGFuZ2VkUG9pbnRlcnM6Y1sxXSxwb2ludGVyVHlwZTp6YSxzcmNFdmVudDphfSl9fX0pO3ZhciBhYj17dG91Y2hzdGFydDpFYSx0b3VjaG1vdmU6RmEsdG91Y2hlbmQ6R2EsdG91Y2hjYW5jZWw6SGF9LGJiPVwidG91Y2hzdGFydCB0b3VjaG1vdmUgdG91Y2hlbmQgdG91Y2hjYW5jZWxcIjtpKFAseCx7aGFuZGxlcjpmdW5jdGlvbihhKXt2YXIgYj1hYlthLnR5cGVdLGM9US5jYWxsKHRoaXMsYSxiKTtjJiZ0aGlzLmNhbGxiYWNrKHRoaXMubWFuYWdlcixiLHtwb2ludGVyczpjWzBdLGNoYW5nZWRQb2ludGVyczpjWzFdLHBvaW50ZXJUeXBlOnphLHNyY0V2ZW50OmF9KX19KTt2YXIgY2I9MjUwMCxkYj0yNTtpKFIseCx7aGFuZGxlcjpmdW5jdGlvbihhLGIsYyl7dmFyIGQ9Yy5wb2ludGVyVHlwZT09emEsZT1jLnBvaW50ZXJUeXBlPT1CYTtpZighKGUmJmMuc291cmNlQ2FwYWJpbGl0aWVzJiZjLnNvdXJjZUNhcGFiaWxpdGllcy5maXJlc1RvdWNoRXZlbnRzKSl7aWYoZClTLmNhbGwodGhpcyxiLGMpO2Vsc2UgaWYoZSYmVS5jYWxsKHRoaXMsYykpcmV0dXJuO3RoaXMuY2FsbGJhY2soYSxiLGMpfX0sZGVzdHJveTpmdW5jdGlvbigpe3RoaXMudG91Y2guZGVzdHJveSgpLHRoaXMubW91c2UuZGVzdHJveSgpfX0pO3ZhciBlYj11KG5hLnN0eWxlLFwidG91Y2hBY3Rpb25cIiksZmI9ZWIhPT1kLGdiPVwiY29tcHV0ZVwiLGhiPVwiYXV0b1wiLGliPVwibWFuaXB1bGF0aW9uXCIsamI9XCJub25lXCIsa2I9XCJwYW4teFwiLGxiPVwicGFuLXlcIixtYj1YKCk7Vi5wcm90b3R5cGU9e3NldDpmdW5jdGlvbihhKXthPT1nYiYmKGE9dGhpcy5jb21wdXRlKCkpLGZiJiZ0aGlzLm1hbmFnZXIuZWxlbWVudC5zdHlsZSYmbWJbYV0mJih0aGlzLm1hbmFnZXIuZWxlbWVudC5zdHlsZVtlYl09YSksdGhpcy5hY3Rpb25zPWEudG9Mb3dlckNhc2UoKS50cmltKCl9LHVwZGF0ZTpmdW5jdGlvbigpe3RoaXMuc2V0KHRoaXMubWFuYWdlci5vcHRpb25zLnRvdWNoQWN0aW9uKX0sY29tcHV0ZTpmdW5jdGlvbigpe3ZhciBhPVtdO3JldHVybiBnKHRoaXMubWFuYWdlci5yZWNvZ25pemVycyxmdW5jdGlvbihiKXtrKGIub3B0aW9ucy5lbmFibGUsW2JdKSYmKGE9YS5jb25jYXQoYi5nZXRUb3VjaEFjdGlvbigpKSl9KSxXKGEuam9pbihcIiBcIikpfSxwcmV2ZW50RGVmYXVsdHM6ZnVuY3Rpb24oYSl7dmFyIGI9YS5zcmNFdmVudCxjPWEub2Zmc2V0RGlyZWN0aW9uO2lmKHRoaXMubWFuYWdlci5zZXNzaW9uLnByZXZlbnRlZClyZXR1cm4gdm9pZCBiLnByZXZlbnREZWZhdWx0KCk7dmFyIGQ9dGhpcy5hY3Rpb25zLGU9cChkLGpiKSYmIW1iW2piXSxmPXAoZCxsYikmJiFtYltsYl0sZz1wKGQsa2IpJiYhbWJba2JdO2lmKGUpe3ZhciBoPTE9PT1hLnBvaW50ZXJzLmxlbmd0aCxpPWEuZGlzdGFuY2U8MixqPWEuZGVsdGFUaW1lPDI1MDtpZihoJiZpJiZqKXJldHVybn1yZXR1cm4gZyYmZj92b2lkIDA6ZXx8ZiYmYyZOYXx8ZyYmYyZPYT90aGlzLnByZXZlbnRTcmMoYik6dm9pZCAwfSxwcmV2ZW50U3JjOmZ1bmN0aW9uKGEpe3RoaXMubWFuYWdlci5zZXNzaW9uLnByZXZlbnRlZD0hMCxhLnByZXZlbnREZWZhdWx0KCl9fTt2YXIgbmI9MSxvYj0yLHBiPTQscWI9OCxyYj1xYixzYj0xNix0Yj0zMjtZLnByb3RvdHlwZT17ZGVmYXVsdHM6e30sc2V0OmZ1bmN0aW9uKGEpe3JldHVybiBsYSh0aGlzLm9wdGlvbnMsYSksdGhpcy5tYW5hZ2VyJiZ0aGlzLm1hbmFnZXIudG91Y2hBY3Rpb24udXBkYXRlKCksdGhpc30scmVjb2duaXplV2l0aDpmdW5jdGlvbihhKXtpZihmKGEsXCJyZWNvZ25pemVXaXRoXCIsdGhpcykpcmV0dXJuIHRoaXM7dmFyIGI9dGhpcy5zaW11bHRhbmVvdXM7cmV0dXJuIGE9XyhhLHRoaXMpLGJbYS5pZF18fChiW2EuaWRdPWEsYS5yZWNvZ25pemVXaXRoKHRoaXMpKSx0aGlzfSxkcm9wUmVjb2duaXplV2l0aDpmdW5jdGlvbihhKXtyZXR1cm4gZihhLFwiZHJvcFJlY29nbml6ZVdpdGhcIix0aGlzKT90aGlzOihhPV8oYSx0aGlzKSxkZWxldGUgdGhpcy5zaW11bHRhbmVvdXNbYS5pZF0sdGhpcyl9LHJlcXVpcmVGYWlsdXJlOmZ1bmN0aW9uKGEpe2lmKGYoYSxcInJlcXVpcmVGYWlsdXJlXCIsdGhpcykpcmV0dXJuIHRoaXM7dmFyIGI9dGhpcy5yZXF1aXJlRmFpbDtyZXR1cm4gYT1fKGEsdGhpcyksLTE9PT1yKGIsYSkmJihiLnB1c2goYSksYS5yZXF1aXJlRmFpbHVyZSh0aGlzKSksdGhpc30sZHJvcFJlcXVpcmVGYWlsdXJlOmZ1bmN0aW9uKGEpe2lmKGYoYSxcImRyb3BSZXF1aXJlRmFpbHVyZVwiLHRoaXMpKXJldHVybiB0aGlzO2E9XyhhLHRoaXMpO3ZhciBiPXIodGhpcy5yZXF1aXJlRmFpbCxhKTtyZXR1cm4gYj4tMSYmdGhpcy5yZXF1aXJlRmFpbC5zcGxpY2UoYiwxKSx0aGlzfSxoYXNSZXF1aXJlRmFpbHVyZXM6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5yZXF1aXJlRmFpbC5sZW5ndGg+MH0sY2FuUmVjb2duaXplV2l0aDpmdW5jdGlvbihhKXtyZXR1cm4hIXRoaXMuc2ltdWx0YW5lb3VzW2EuaWRdfSxlbWl0OmZ1bmN0aW9uKGEpe2Z1bmN0aW9uIGIoYil7Yy5tYW5hZ2VyLmVtaXQoYixhKX12YXIgYz10aGlzLGQ9dGhpcy5zdGF0ZTtxYj5kJiZiKGMub3B0aW9ucy5ldmVudCtaKGQpKSxiKGMub3B0aW9ucy5ldmVudCksYS5hZGRpdGlvbmFsRXZlbnQmJmIoYS5hZGRpdGlvbmFsRXZlbnQpLGQ+PXFiJiZiKGMub3B0aW9ucy5ldmVudCtaKGQpKX0sdHJ5RW1pdDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5jYW5FbWl0KCk/dGhpcy5lbWl0KGEpOnZvaWQodGhpcy5zdGF0ZT10Yil9LGNhbkVtaXQ6ZnVuY3Rpb24oKXtmb3IodmFyIGE9MDthPHRoaXMucmVxdWlyZUZhaWwubGVuZ3RoOyl7aWYoISh0aGlzLnJlcXVpcmVGYWlsW2FdLnN0YXRlJih0YnxuYikpKXJldHVybiExO2ErK31yZXR1cm4hMH0scmVjb2duaXplOmZ1bmN0aW9uKGEpe3ZhciBiPWxhKHt9LGEpO3JldHVybiBrKHRoaXMub3B0aW9ucy5lbmFibGUsW3RoaXMsYl0pPyh0aGlzLnN0YXRlJihyYnxzYnx0YikmJih0aGlzLnN0YXRlPW5iKSx0aGlzLnN0YXRlPXRoaXMucHJvY2VzcyhiKSx2b2lkKHRoaXMuc3RhdGUmKG9ifHBifHFifHNiKSYmdGhpcy50cnlFbWl0KGIpKSk6KHRoaXMucmVzZXQoKSx2b2lkKHRoaXMuc3RhdGU9dGIpKX0scHJvY2VzczpmdW5jdGlvbihhKXt9LGdldFRvdWNoQWN0aW9uOmZ1bmN0aW9uKCl7fSxyZXNldDpmdW5jdGlvbigpe319LGkoYWEsWSx7ZGVmYXVsdHM6e3BvaW50ZXJzOjF9LGF0dHJUZXN0OmZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMub3B0aW9ucy5wb2ludGVycztyZXR1cm4gMD09PWJ8fGEucG9pbnRlcnMubGVuZ3RoPT09Yn0scHJvY2VzczpmdW5jdGlvbihhKXt2YXIgYj10aGlzLnN0YXRlLGM9YS5ldmVudFR5cGUsZD1iJihvYnxwYiksZT10aGlzLmF0dHJUZXN0KGEpO3JldHVybiBkJiYoYyZIYXx8IWUpP2J8c2I6ZHx8ZT9jJkdhP2J8cWI6YiZvYj9ifHBiOm9iOnRifX0pLGkoYmEsYWEse2RlZmF1bHRzOntldmVudDpcInBhblwiLHRocmVzaG9sZDoxMCxwb2ludGVyczoxLGRpcmVjdGlvbjpQYX0sZ2V0VG91Y2hBY3Rpb246ZnVuY3Rpb24oKXt2YXIgYT10aGlzLm9wdGlvbnMuZGlyZWN0aW9uLGI9W107cmV0dXJuIGEmTmEmJmIucHVzaChsYiksYSZPYSYmYi5wdXNoKGtiKSxifSxkaXJlY3Rpb25UZXN0OmZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMub3B0aW9ucyxjPSEwLGQ9YS5kaXN0YW5jZSxlPWEuZGlyZWN0aW9uLGY9YS5kZWx0YVgsZz1hLmRlbHRhWTtyZXR1cm4gZSZiLmRpcmVjdGlvbnx8KGIuZGlyZWN0aW9uJk5hPyhlPTA9PT1mP0lhOjA+Zj9KYTpLYSxjPWYhPXRoaXMucFgsZD1NYXRoLmFicyhhLmRlbHRhWCkpOihlPTA9PT1nP0lhOjA+Zz9MYTpNYSxjPWchPXRoaXMucFksZD1NYXRoLmFicyhhLmRlbHRhWSkpKSxhLmRpcmVjdGlvbj1lLGMmJmQ+Yi50aHJlc2hvbGQmJmUmYi5kaXJlY3Rpb259LGF0dHJUZXN0OmZ1bmN0aW9uKGEpe3JldHVybiBhYS5wcm90b3R5cGUuYXR0clRlc3QuY2FsbCh0aGlzLGEpJiYodGhpcy5zdGF0ZSZvYnx8ISh0aGlzLnN0YXRlJm9iKSYmdGhpcy5kaXJlY3Rpb25UZXN0KGEpKX0sZW1pdDpmdW5jdGlvbihhKXt0aGlzLnBYPWEuZGVsdGFYLHRoaXMucFk9YS5kZWx0YVk7dmFyIGI9JChhLmRpcmVjdGlvbik7YiYmKGEuYWRkaXRpb25hbEV2ZW50PXRoaXMub3B0aW9ucy5ldmVudCtiKSx0aGlzLl9zdXBlci5lbWl0LmNhbGwodGhpcyxhKX19KSxpKGNhLGFhLHtkZWZhdWx0czp7ZXZlbnQ6XCJwaW5jaFwiLHRocmVzaG9sZDowLHBvaW50ZXJzOjJ9LGdldFRvdWNoQWN0aW9uOmZ1bmN0aW9uKCl7cmV0dXJuW2piXX0sYXR0clRlc3Q6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuX3N1cGVyLmF0dHJUZXN0LmNhbGwodGhpcyxhKSYmKE1hdGguYWJzKGEuc2NhbGUtMSk+dGhpcy5vcHRpb25zLnRocmVzaG9sZHx8dGhpcy5zdGF0ZSZvYil9LGVtaXQ6ZnVuY3Rpb24oYSl7aWYoMSE9PWEuc2NhbGUpe3ZhciBiPWEuc2NhbGU8MT9cImluXCI6XCJvdXRcIjthLmFkZGl0aW9uYWxFdmVudD10aGlzLm9wdGlvbnMuZXZlbnQrYn10aGlzLl9zdXBlci5lbWl0LmNhbGwodGhpcyxhKX19KSxpKGRhLFkse2RlZmF1bHRzOntldmVudDpcInByZXNzXCIscG9pbnRlcnM6MSx0aW1lOjI1MSx0aHJlc2hvbGQ6OX0sZ2V0VG91Y2hBY3Rpb246ZnVuY3Rpb24oKXtyZXR1cm5baGJdfSxwcm9jZXNzOmZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMub3B0aW9ucyxjPWEucG9pbnRlcnMubGVuZ3RoPT09Yi5wb2ludGVycyxkPWEuZGlzdGFuY2U8Yi50aHJlc2hvbGQsZj1hLmRlbHRhVGltZT5iLnRpbWU7aWYodGhpcy5faW5wdXQ9YSwhZHx8IWN8fGEuZXZlbnRUeXBlJihHYXxIYSkmJiFmKXRoaXMucmVzZXQoKTtlbHNlIGlmKGEuZXZlbnRUeXBlJkVhKXRoaXMucmVzZXQoKSx0aGlzLl90aW1lcj1lKGZ1bmN0aW9uKCl7dGhpcy5zdGF0ZT1yYix0aGlzLnRyeUVtaXQoKX0sYi50aW1lLHRoaXMpO2Vsc2UgaWYoYS5ldmVudFR5cGUmR2EpcmV0dXJuIHJiO3JldHVybiB0Yn0scmVzZXQ6ZnVuY3Rpb24oKXtjbGVhclRpbWVvdXQodGhpcy5fdGltZXIpfSxlbWl0OmZ1bmN0aW9uKGEpe3RoaXMuc3RhdGU9PT1yYiYmKGEmJmEuZXZlbnRUeXBlJkdhP3RoaXMubWFuYWdlci5lbWl0KHRoaXMub3B0aW9ucy5ldmVudCtcInVwXCIsYSk6KHRoaXMuX2lucHV0LnRpbWVTdGFtcD1yYSgpLHRoaXMubWFuYWdlci5lbWl0KHRoaXMub3B0aW9ucy5ldmVudCx0aGlzLl9pbnB1dCkpKX19KSxpKGVhLGFhLHtkZWZhdWx0czp7ZXZlbnQ6XCJyb3RhdGVcIix0aHJlc2hvbGQ6MCxwb2ludGVyczoyfSxnZXRUb3VjaEFjdGlvbjpmdW5jdGlvbigpe3JldHVybltqYl19LGF0dHJUZXN0OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLl9zdXBlci5hdHRyVGVzdC5jYWxsKHRoaXMsYSkmJihNYXRoLmFicyhhLnJvdGF0aW9uKT50aGlzLm9wdGlvbnMudGhyZXNob2xkfHx0aGlzLnN0YXRlJm9iKX19KSxpKGZhLGFhLHtkZWZhdWx0czp7ZXZlbnQ6XCJzd2lwZVwiLHRocmVzaG9sZDoxMCx2ZWxvY2l0eTouMyxkaXJlY3Rpb246TmF8T2EscG9pbnRlcnM6MX0sZ2V0VG91Y2hBY3Rpb246ZnVuY3Rpb24oKXtyZXR1cm4gYmEucHJvdG90eXBlLmdldFRvdWNoQWN0aW9uLmNhbGwodGhpcyl9LGF0dHJUZXN0OmZ1bmN0aW9uKGEpe3ZhciBiLGM9dGhpcy5vcHRpb25zLmRpcmVjdGlvbjtyZXR1cm4gYyYoTmF8T2EpP2I9YS5vdmVyYWxsVmVsb2NpdHk6YyZOYT9iPWEub3ZlcmFsbFZlbG9jaXR5WDpjJk9hJiYoYj1hLm92ZXJhbGxWZWxvY2l0eVkpLHRoaXMuX3N1cGVyLmF0dHJUZXN0LmNhbGwodGhpcyxhKSYmYyZhLm9mZnNldERpcmVjdGlvbiYmYS5kaXN0YW5jZT50aGlzLm9wdGlvbnMudGhyZXNob2xkJiZhLm1heFBvaW50ZXJzPT10aGlzLm9wdGlvbnMucG9pbnRlcnMmJnFhKGIpPnRoaXMub3B0aW9ucy52ZWxvY2l0eSYmYS5ldmVudFR5cGUmR2F9LGVtaXQ6ZnVuY3Rpb24oYSl7dmFyIGI9JChhLm9mZnNldERpcmVjdGlvbik7YiYmdGhpcy5tYW5hZ2VyLmVtaXQodGhpcy5vcHRpb25zLmV2ZW50K2IsYSksdGhpcy5tYW5hZ2VyLmVtaXQodGhpcy5vcHRpb25zLmV2ZW50LGEpfX0pLGkoZ2EsWSx7ZGVmYXVsdHM6e2V2ZW50OlwidGFwXCIscG9pbnRlcnM6MSx0YXBzOjEsaW50ZXJ2YWw6MzAwLHRpbWU6MjUwLHRocmVzaG9sZDo5LHBvc1RocmVzaG9sZDoxMH0sZ2V0VG91Y2hBY3Rpb246ZnVuY3Rpb24oKXtyZXR1cm5baWJdfSxwcm9jZXNzOmZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMub3B0aW9ucyxjPWEucG9pbnRlcnMubGVuZ3RoPT09Yi5wb2ludGVycyxkPWEuZGlzdGFuY2U8Yi50aHJlc2hvbGQsZj1hLmRlbHRhVGltZTxiLnRpbWU7aWYodGhpcy5yZXNldCgpLGEuZXZlbnRUeXBlJkVhJiYwPT09dGhpcy5jb3VudClyZXR1cm4gdGhpcy5mYWlsVGltZW91dCgpO2lmKGQmJmYmJmMpe2lmKGEuZXZlbnRUeXBlIT1HYSlyZXR1cm4gdGhpcy5mYWlsVGltZW91dCgpO3ZhciBnPXRoaXMucFRpbWU/YS50aW1lU3RhbXAtdGhpcy5wVGltZTxiLmludGVydmFsOiEwLGg9IXRoaXMucENlbnRlcnx8SCh0aGlzLnBDZW50ZXIsYS5jZW50ZXIpPGIucG9zVGhyZXNob2xkO3RoaXMucFRpbWU9YS50aW1lU3RhbXAsdGhpcy5wQ2VudGVyPWEuY2VudGVyLGgmJmc/dGhpcy5jb3VudCs9MTp0aGlzLmNvdW50PTEsdGhpcy5faW5wdXQ9YTt2YXIgaT10aGlzLmNvdW50JWIudGFwcztpZigwPT09aSlyZXR1cm4gdGhpcy5oYXNSZXF1aXJlRmFpbHVyZXMoKT8odGhpcy5fdGltZXI9ZShmdW5jdGlvbigpe3RoaXMuc3RhdGU9cmIsdGhpcy50cnlFbWl0KCl9LGIuaW50ZXJ2YWwsdGhpcyksb2IpOnJifXJldHVybiB0Yn0sZmFpbFRpbWVvdXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fdGltZXI9ZShmdW5jdGlvbigpe3RoaXMuc3RhdGU9dGJ9LHRoaXMub3B0aW9ucy5pbnRlcnZhbCx0aGlzKSx0Yn0scmVzZXQ6ZnVuY3Rpb24oKXtjbGVhclRpbWVvdXQodGhpcy5fdGltZXIpfSxlbWl0OmZ1bmN0aW9uKCl7dGhpcy5zdGF0ZT09cmImJih0aGlzLl9pbnB1dC50YXBDb3VudD10aGlzLmNvdW50LHRoaXMubWFuYWdlci5lbWl0KHRoaXMub3B0aW9ucy5ldmVudCx0aGlzLl9pbnB1dCkpfX0pLGhhLlZFUlNJT049XCIyLjAuOFwiLGhhLmRlZmF1bHRzPXtkb21FdmVudHM6ITEsdG91Y2hBY3Rpb246Z2IsZW5hYmxlOiEwLGlucHV0VGFyZ2V0Om51bGwsaW5wdXRDbGFzczpudWxsLHByZXNldDpbW2VhLHtlbmFibGU6ITF9XSxbY2Ese2VuYWJsZTohMX0sW1wicm90YXRlXCJdXSxbZmEse2RpcmVjdGlvbjpOYX1dLFtiYSx7ZGlyZWN0aW9uOk5hfSxbXCJzd2lwZVwiXV0sW2dhXSxbZ2Ese2V2ZW50OlwiZG91YmxldGFwXCIsdGFwczoyfSxbXCJ0YXBcIl1dLFtkYV1dLGNzc1Byb3BzOnt1c2VyU2VsZWN0Olwibm9uZVwiLHRvdWNoU2VsZWN0Olwibm9uZVwiLHRvdWNoQ2FsbG91dDpcIm5vbmVcIixjb250ZW50Wm9vbWluZzpcIm5vbmVcIix1c2VyRHJhZzpcIm5vbmVcIix0YXBIaWdobGlnaHRDb2xvcjpcInJnYmEoMCwwLDAsMClcIn19O3ZhciB1Yj0xLHZiPTI7aWEucHJvdG90eXBlPXtzZXQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGxhKHRoaXMub3B0aW9ucyxhKSxhLnRvdWNoQWN0aW9uJiZ0aGlzLnRvdWNoQWN0aW9uLnVwZGF0ZSgpLGEuaW5wdXRUYXJnZXQmJih0aGlzLmlucHV0LmRlc3Ryb3koKSx0aGlzLmlucHV0LnRhcmdldD1hLmlucHV0VGFyZ2V0LHRoaXMuaW5wdXQuaW5pdCgpKSx0aGlzfSxzdG9wOmZ1bmN0aW9uKGEpe3RoaXMuc2Vzc2lvbi5zdG9wcGVkPWE/dmI6dWJ9LHJlY29nbml6ZTpmdW5jdGlvbihhKXt2YXIgYj10aGlzLnNlc3Npb247aWYoIWIuc3RvcHBlZCl7dGhpcy50b3VjaEFjdGlvbi5wcmV2ZW50RGVmYXVsdHMoYSk7dmFyIGMsZD10aGlzLnJlY29nbml6ZXJzLGU9Yi5jdXJSZWNvZ25pemVyOyghZXx8ZSYmZS5zdGF0ZSZyYikmJihlPWIuY3VyUmVjb2duaXplcj1udWxsKTtmb3IodmFyIGY9MDtmPGQubGVuZ3RoOyljPWRbZl0sYi5zdG9wcGVkPT09dmJ8fGUmJmMhPWUmJiFjLmNhblJlY29nbml6ZVdpdGgoZSk/Yy5yZXNldCgpOmMucmVjb2duaXplKGEpLCFlJiZjLnN0YXRlJihvYnxwYnxxYikmJihlPWIuY3VyUmVjb2duaXplcj1jKSxmKyt9fSxnZXQ6ZnVuY3Rpb24oYSl7aWYoYSBpbnN0YW5jZW9mIFkpcmV0dXJuIGE7Zm9yKHZhciBiPXRoaXMucmVjb2duaXplcnMsYz0wO2M8Yi5sZW5ndGg7YysrKWlmKGJbY10ub3B0aW9ucy5ldmVudD09YSlyZXR1cm4gYltjXTtyZXR1cm4gbnVsbH0sYWRkOmZ1bmN0aW9uKGEpe2lmKGYoYSxcImFkZFwiLHRoaXMpKXJldHVybiB0aGlzO3ZhciBiPXRoaXMuZ2V0KGEub3B0aW9ucy5ldmVudCk7cmV0dXJuIGImJnRoaXMucmVtb3ZlKGIpLHRoaXMucmVjb2duaXplcnMucHVzaChhKSxhLm1hbmFnZXI9dGhpcyx0aGlzLnRvdWNoQWN0aW9uLnVwZGF0ZSgpLGF9LHJlbW92ZTpmdW5jdGlvbihhKXtpZihmKGEsXCJyZW1vdmVcIix0aGlzKSlyZXR1cm4gdGhpcztpZihhPXRoaXMuZ2V0KGEpKXt2YXIgYj10aGlzLnJlY29nbml6ZXJzLGM9cihiLGEpOy0xIT09YyYmKGIuc3BsaWNlKGMsMSksdGhpcy50b3VjaEFjdGlvbi51cGRhdGUoKSl9cmV0dXJuIHRoaXN9LG9uOmZ1bmN0aW9uKGEsYil7aWYoYSE9PWQmJmIhPT1kKXt2YXIgYz10aGlzLmhhbmRsZXJzO3JldHVybiBnKHEoYSksZnVuY3Rpb24oYSl7Y1thXT1jW2FdfHxbXSxjW2FdLnB1c2goYil9KSx0aGlzfX0sb2ZmOmZ1bmN0aW9uKGEsYil7aWYoYSE9PWQpe3ZhciBjPXRoaXMuaGFuZGxlcnM7cmV0dXJuIGcocShhKSxmdW5jdGlvbihhKXtiP2NbYV0mJmNbYV0uc3BsaWNlKHIoY1thXSxiKSwxKTpkZWxldGUgY1thXX0pLHRoaXN9fSxlbWl0OmZ1bmN0aW9uKGEsYil7dGhpcy5vcHRpb25zLmRvbUV2ZW50cyYma2EoYSxiKTt2YXIgYz10aGlzLmhhbmRsZXJzW2FdJiZ0aGlzLmhhbmRsZXJzW2FdLnNsaWNlKCk7aWYoYyYmYy5sZW5ndGgpe2IudHlwZT1hLGIucHJldmVudERlZmF1bHQ9ZnVuY3Rpb24oKXtiLnNyY0V2ZW50LnByZXZlbnREZWZhdWx0KCl9O2Zvcih2YXIgZD0wO2Q8Yy5sZW5ndGg7KWNbZF0oYiksZCsrfX0sZGVzdHJveTpmdW5jdGlvbigpe3RoaXMuZWxlbWVudCYmamEodGhpcywhMSksdGhpcy5oYW5kbGVycz17fSx0aGlzLnNlc3Npb249e30sdGhpcy5pbnB1dC5kZXN0cm95KCksdGhpcy5lbGVtZW50PW51bGx9fSxsYShoYSx7SU5QVVRfU1RBUlQ6RWEsSU5QVVRfTU9WRTpGYSxJTlBVVF9FTkQ6R2EsSU5QVVRfQ0FOQ0VMOkhhLFNUQVRFX1BPU1NJQkxFOm5iLFNUQVRFX0JFR0FOOm9iLFNUQVRFX0NIQU5HRUQ6cGIsU1RBVEVfRU5ERUQ6cWIsU1RBVEVfUkVDT0dOSVpFRDpyYixTVEFURV9DQU5DRUxMRUQ6c2IsU1RBVEVfRkFJTEVEOnRiLERJUkVDVElPTl9OT05FOklhLERJUkVDVElPTl9MRUZUOkphLERJUkVDVElPTl9SSUdIVDpLYSxESVJFQ1RJT05fVVA6TGEsRElSRUNUSU9OX0RPV046TWEsRElSRUNUSU9OX0hPUklaT05UQUw6TmEsRElSRUNUSU9OX1ZFUlRJQ0FMOk9hLERJUkVDVElPTl9BTEw6UGEsTWFuYWdlcjppYSxJbnB1dDp4LFRvdWNoQWN0aW9uOlYsVG91Y2hJbnB1dDpQLE1vdXNlSW5wdXQ6TCxQb2ludGVyRXZlbnRJbnB1dDpNLFRvdWNoTW91c2VJbnB1dDpSLFNpbmdsZVRvdWNoSW5wdXQ6TixSZWNvZ25pemVyOlksQXR0clJlY29nbml6ZXI6YWEsVGFwOmdhLFBhbjpiYSxTd2lwZTpmYSxQaW5jaDpjYSxSb3RhdGU6ZWEsUHJlc3M6ZGEsb246bSxvZmY6bixlYWNoOmcsbWVyZ2U6dGEsZXh0ZW5kOnNhLGFzc2lnbjpsYSxpbmhlcml0OmksYmluZEZuOmoscHJlZml4ZWQ6dX0pO3ZhciB3Yj1cInVuZGVmaW5lZFwiIT10eXBlb2YgYT9hOlwidW5kZWZpbmVkXCIhPXR5cGVvZiBzZWxmP3NlbGY6e307d2IuSGFtbWVyPWhhLFwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoZnVuY3Rpb24oKXtyZXR1cm4gaGF9KTpcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlJiZtb2R1bGUuZXhwb3J0cz9tb2R1bGUuZXhwb3J0cz1oYTphW2NdPWhhfSh3aW5kb3csZG9jdW1lbnQsXCJIYW1tZXJcIik7XG4iLCIvLyBzdmctcGFuLXpvb20gdjMuNi4wIG1vZGlmaWVkIGJ5IHNjb3JwaW40NlxuLy8gaHR0cHM6Ly9naXRodWIuY29tL2FyaXV0dGEvc3ZnLXBhbi16b29tXG4oZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSh7MTpbZnVuY3Rpb24ocmVxdWlyZSxtb2R1bGUsZXhwb3J0cyl7XG52YXIgc3ZnUGFuWm9vbSA9IHJlcXVpcmUoJy4vc3ZnLXBhbi16b29tLmpzJyk7XG5cbi8vIFVNRCBtb2R1bGUgZGVmaW5pdGlvblxuKGZ1bmN0aW9uKHdpbmRvdywgZG9jdW1lbnQpe1xuICAvLyBBTURcbiAgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xuICAgIGRlZmluZSgnc3ZnLXBhbi16b29tJywgZnVuY3Rpb24gKCkge1xuICAgICAgcmV0dXJuIHN2Z1Bhblpvb207XG4gICAgfSk7XG4gIC8vIENNRFxuICB9IGVsc2UgaWYgKHR5cGVvZiBtb2R1bGUgIT09ICd1bmRlZmluZWQnICYmIG1vZHVsZS5leHBvcnRzKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBzdmdQYW5ab29tO1xuXG4gICAgLy8gQnJvd3NlclxuICAgIC8vIEtlZXAgZXhwb3J0aW5nIGdsb2JhbGx5IGFzIG1vZHVsZS5leHBvcnRzIGlzIGF2YWlsYWJsZSBiZWNhdXNlIG9mIGJyb3dzZXJpZnlcbiAgICB3aW5kb3cuc3ZnUGFuWm9vbSA9IHN2Z1Bhblpvb207XG4gIH1cbn0pKHdpbmRvdywgZG9jdW1lbnQpXG5cbn0se1wiLi9zdmctcGFuLXpvb20uanNcIjo0fV0sMjpbZnVuY3Rpb24ocmVxdWlyZSxtb2R1bGUsZXhwb3J0cyl7XG52YXIgU3ZnVXRpbHMgPSByZXF1aXJlKCcuL3N2Zy11dGlsaXRpZXMnKTtcbiAgICAgICAgbW9kdWxlLmV4cG9ydHMgPSB7XG4gIGVuYWJsZTogZnVuY3Rpb24oaW5zdGFuY2UpIHtcbiAgICAvLyBTZWxlY3QgKGFuZCBjcmVhdGUgaWYgbmVjZXNzYXJ5KSBkZWZzXG4gICAgdmFyIGRlZnMgPSBpbnN0YW5jZS5zdmcucXVlcnlTZWxlY3RvcignZGVmcycpXG4gICAgaWYgKCFkZWZzKSB7XG4gICAgICBkZWZzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFN2Z1V0aWxzLnN2Z05TLCAnZGVmcycpXG4gICAgICBpbnN0YW5jZS5zdmcuYXBwZW5kQ2hpbGQoZGVmcylcbiAgICB9XG5cbiAgICAvLyBDaGVjayBmb3Igc3R5bGUgZWxlbWVudCwgYW5kIGNyZWF0ZSBpdCBpZiBpdCBkb2Vzbid0IGV4aXN0XG4gICAgdmFyIHN0eWxlRWwgPSBkZWZzLnF1ZXJ5U2VsZWN0b3IoJ3N0eWxlI3N2Zy1wYW4tem9vbS1jb250cm9scy1zdHlsZXMnKTtcbiAgICBpZiAoIXN0eWxlRWwpIHtcbiAgICAgIHZhciBzdHlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhTdmdVdGlscy5zdmdOUywgJ3N0eWxlJylcbiAgICAgIHN0eWxlLnNldEF0dHJpYnV0ZSgnaWQnLCAnc3ZnLXBhbi16b29tLWNvbnRyb2xzLXN0eWxlcycpXG4gICAgICBzdHlsZS5zZXRBdHRyaWJ1dGUoJ3R5cGUnLCAndGV4dC9jc3MnKVxuICAgICAgc3R5bGUudGV4dENvbnRlbnQgPSAnLnN2Zy1wYW4tem9vbS1jb250cm9sIHsgY3Vyc29yOiBwb2ludGVyOyBmaWxsOiBibGFjazsgZmlsbC1vcGFjaXR5OiAwLjMzMzsgfSAuc3ZnLXBhbi16b29tLWNvbnRyb2w6aG92ZXIgeyBmaWxsLW9wYWNpdHk6IDAuODsgfSAuc3ZnLXBhbi16b29tLWNvbnRyb2wtYmFja2dyb3VuZCB7IGZpbGw6IHdoaXRlOyBmaWxsLW9wYWNpdHk6IDAuNTsgfSAuc3ZnLXBhbi16b29tLWNvbnRyb2wtYmFja2dyb3VuZCB7IGZpbGwtb3BhY2l0eTogMC44OyB9J1xuICAgICAgZGVmcy5hcHBlbmRDaGlsZChzdHlsZSlcbiAgICB9XG5cbiAgICAvLyBab29tIEdyb3VwXG4gICAgdmFyIHpvb21Hcm91cCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhTdmdVdGlscy5zdmdOUywgJ2cnKTtcbiAgICB6b29tR3JvdXAuc2V0QXR0cmlidXRlKCdpZCcsICdzdmctcGFuLXpvb20tY29udHJvbHMnKTtcbiAgICB6b29tR3JvdXAuc2V0QXR0cmlidXRlKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcgKyAoIGluc3RhbmNlLndpZHRoIC0gNzAgKSArICcgJyArICggaW5zdGFuY2UuaGVpZ2h0IC0gNzYgKSArICcpIHNjYWxlKDAuNzUpJyk7XG4gICAgem9vbUdyb3VwLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAnc3ZnLXBhbi16b29tLWNvbnRyb2wnKTtcblxuICAgIC8vIENvbnRyb2wgZWxlbWVudHNcbiAgICB6b29tR3JvdXAuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlWm9vbUluKGluc3RhbmNlKSlcbiAgICB6b29tR3JvdXAuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlWm9vbVJlc2V0KGluc3RhbmNlKSlcbiAgICB6b29tR3JvdXAuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlWm9vbU91dChpbnN0YW5jZSkpXG5cbiAgICAvLyBGaW5hbGx5IGFwcGVuZCBjcmVhdGVkIGVsZW1lbnRcbiAgICBpbnN0YW5jZS5zdmcuYXBwZW5kQ2hpbGQoem9vbUdyb3VwKVxuXG4gICAgLy8gQ2FjaGUgY29udHJvbCBpbnN0YW5jZVxuICAgIGluc3RhbmNlLmNvbnRyb2xJY29ucyA9IHpvb21Hcm91cFxuICB9XG5cbiwgX2NyZWF0ZVpvb21JbjogZnVuY3Rpb24oaW5zdGFuY2UpIHtcbiAgICB2YXIgem9vbUluID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFN2Z1V0aWxzLnN2Z05TLCAnZycpO1xuICAgIHpvb21Jbi5zZXRBdHRyaWJ1dGUoJ2lkJywgJ3N2Zy1wYW4tem9vbS16b29tLWluJyk7XG4gICAgem9vbUluLnNldEF0dHJpYnV0ZSgndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgzMC41IDUpIHNjYWxlKDAuMDE1KScpO1xuICAgIHpvb21Jbi5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ3N2Zy1wYW4tem9vbS1jb250cm9sJyk7XG4gICAgem9vbUluLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24oKSB7aW5zdGFuY2UuZ2V0UHVibGljSW5zdGFuY2UoKS56b29tSW4oKX0sIGZhbHNlKVxuICAgIHpvb21Jbi5hZGRFdmVudExpc3RlbmVyKCd0b3VjaHN0YXJ0JywgZnVuY3Rpb24oKSB7aW5zdGFuY2UuZ2V0UHVibGljSW5zdGFuY2UoKS56b29tSW4oKX0sIGZhbHNlKVxuXG4gICAgdmFyIHpvb21JbkJhY2tncm91bmQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoU3ZnVXRpbHMuc3ZnTlMsICdyZWN0Jyk7IC8vIFRPRE8gY2hhbmdlIHRoZXNlIGJhY2tncm91bmQgc3BhY2UgZmlsbGVycyB0byByb3VuZGVkIHJlY3RhbmdsZXMgc28gdGhleSBsb29rIHByZXR0aWVyXG4gICAgem9vbUluQmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ3gnLCAnMCcpO1xuICAgIHpvb21JbkJhY2tncm91bmQuc2V0QXR0cmlidXRlKCd5JywgJzAnKTtcbiAgICB6b29tSW5CYWNrZ3JvdW5kLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCAnMTUwMCcpOyAvLyBsYXJnZXIgdGhhbiBleHBlY3RlZCBiZWNhdXNlIHRoZSB3aG9sZSBncm91cCBpcyB0cmFuc2Zvcm1lZCB0byBzY2FsZSBkb3duXG4gICAgem9vbUluQmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ2hlaWdodCcsICcxNDAwJyk7XG4gICAgem9vbUluQmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ3N2Zy1wYW4tem9vbS1jb250cm9sLWJhY2tncm91bmQnKTtcbiAgICB6b29tSW4uYXBwZW5kQ2hpbGQoem9vbUluQmFja2dyb3VuZCk7XG5cbiAgICB2YXIgem9vbUluU2hhcGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoU3ZnVXRpbHMuc3ZnTlMsICdwYXRoJyk7XG4gICAgem9vbUluU2hhcGUuc2V0QXR0cmlidXRlKCdkJywgJ00xMjgwIDU3NnYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC0zMjB2MzIwcTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMzIwaC0zMjBxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgzMjB2LTMyMHEwIC0yNiAxOSAtNDV0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1djMyMGgzMjBxMjYgMCA0NSAxOXQxOSA0NXpNMTUzNiAxMTIwdi05NjAgcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eicpO1xuICAgIHpvb21JblNoYXBlLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAnc3ZnLXBhbi16b29tLWNvbnRyb2wtZWxlbWVudCcpO1xuICAgIHpvb21Jbi5hcHBlbmRDaGlsZCh6b29tSW5TaGFwZSk7XG5cbiAgICByZXR1cm4gem9vbUluXG4gIH1cblxuLCBfY3JlYXRlWm9vbVJlc2V0OiBmdW5jdGlvbihpbnN0YW5jZSl7XG4gICAgLy8gcmVzZXRcbiAgICB2YXIgcmVzZXRQYW5ab29tQ29udHJvbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhTdmdVdGlscy5zdmdOUywgJ2cnKTtcbiAgICByZXNldFBhblpvb21Db250cm9sLnNldEF0dHJpYnV0ZSgnaWQnLCAnc3ZnLXBhbi16b29tLXJlc2V0LXBhbi16b29tJyk7XG4gICAgcmVzZXRQYW5ab29tQ29udHJvbC5zZXRBdHRyaWJ1dGUoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoNSAzNSkgc2NhbGUoMC40KScpO1xuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2wuc2V0QXR0cmlidXRlKCdjbGFzcycsICdzdmctcGFuLXpvb20tY29udHJvbCcpO1xuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2wuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpIHtpbnN0YW5jZS5nZXRQdWJsaWNJbnN0YW5jZSgpLnJlc2V0KCl9LCBmYWxzZSk7XG4gICAgcmVzZXRQYW5ab29tQ29udHJvbC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaHN0YXJ0JywgZnVuY3Rpb24oKSB7aW5zdGFuY2UuZ2V0UHVibGljSW5zdGFuY2UoKS5yZXNldCgpfSwgZmFsc2UpO1xuXG4gICAgdmFyIHJlc2V0UGFuWm9vbUNvbnRyb2xCYWNrZ3JvdW5kID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFN2Z1V0aWxzLnN2Z05TLCAncmVjdCcpOyAvLyBUT0RPIGNoYW5nZSB0aGVzZSBiYWNrZ3JvdW5kIHNwYWNlIGZpbGxlcnMgdG8gcm91bmRlZCByZWN0YW5nbGVzIHNvIHRoZXkgbG9vayBwcmV0dGllclxuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2xCYWNrZ3JvdW5kLnNldEF0dHJpYnV0ZSgneCcsICcyJyk7XG4gICAgcmVzZXRQYW5ab29tQ29udHJvbEJhY2tncm91bmQuc2V0QXR0cmlidXRlKCd5JywgJzInKTtcbiAgICByZXNldFBhblpvb21Db250cm9sQmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ3dpZHRoJywgJzE4MicpOyAvLyBsYXJnZXIgdGhhbiBleHBlY3RlZCBiZWNhdXNlIHRoZSB3aG9sZSBncm91cCBpcyB0cmFuc2Zvcm1lZCB0byBzY2FsZSBkb3duXG4gICAgcmVzZXRQYW5ab29tQ29udHJvbEJhY2tncm91bmQuc2V0QXR0cmlidXRlKCdoZWlnaHQnLCAnNTgnKTtcbiAgICByZXNldFBhblpvb21Db250cm9sQmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ3N2Zy1wYW4tem9vbS1jb250cm9sLWJhY2tncm91bmQnKTtcbiAgICByZXNldFBhblpvb21Db250cm9sLmFwcGVuZENoaWxkKHJlc2V0UGFuWm9vbUNvbnRyb2xCYWNrZ3JvdW5kKTtcblxuICAgIHZhciByZXNldFBhblpvb21Db250cm9sU2hhcGUxID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFN2Z1V0aWxzLnN2Z05TLCAncGF0aCcpO1xuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2xTaGFwZTEuc2V0QXR0cmlidXRlKCdkJywgJ00zMy4wNTEsMjAuNjMyYy0wLjc0Mi0wLjQwNi0xLjg1NC0wLjYwOS0zLjMzOC0wLjYwOWgtNy45Njl2OS4yODFoNy43NjljMS41NDMsMCwyLjcwMS0wLjE4OCwzLjQ3My0wLjU2MmMxLjM2NS0wLjY1NiwyLjA0OC0xLjk1MywyLjA0OC0zLjg5MUMzNS4wMzIsMjIuNzU3LDM0LjM3MiwyMS4zNTEsMzMuMDUxLDIwLjYzMnonKTtcbiAgICByZXNldFBhblpvb21Db250cm9sU2hhcGUxLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAnc3ZnLXBhbi16b29tLWNvbnRyb2wtZWxlbWVudCcpO1xuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2wuYXBwZW5kQ2hpbGQocmVzZXRQYW5ab29tQ29udHJvbFNoYXBlMSk7XG5cbiAgICB2YXIgcmVzZXRQYW5ab29tQ29udHJvbFNoYXBlMiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhTdmdVdGlscy5zdmdOUywgJ3BhdGgnKTtcbiAgICByZXNldFBhblpvb21Db250cm9sU2hhcGUyLnNldEF0dHJpYnV0ZSgnZCcsICdNMTcwLjIzMSwwLjVIMTUuODQ3QzcuMTAyLDAuNSwwLjUsNS43MDgsMC41LDExLjg0djM4Ljg2MUMwLjUsNTYuODMzLDcuMTAyLDYxLjUsMTUuODQ3LDYxLjVoMTU0LjM4NGM4Ljc0NSwwLDE1LjI2OS00LjY2NywxNS4yNjktMTAuNzk4VjExLjg0QzE4NS41LDUuNzA4LDE3OC45NzYsMC41LDE3MC4yMzEsMC41eiBNNDIuODM3LDQ4LjU2OWgtNy45NjljLTAuMjE5LTAuNzY2LTAuMzc1LTEuMzgzLTAuNDY5LTEuODUyYy0wLjE4OC0wLjk2OS0wLjI4OS0xLjk2MS0wLjMwNS0yLjk3N2wtMC4wNDctMy4yMTFjLTAuMDMtMi4yMDMtMC40MS0zLjY3Mi0xLjE0Mi00LjQwNmMtMC43MzItMC43MzQtMi4xMDMtMS4xMDItNC4xMTMtMS4xMDJoLTcuMDV2MTMuNTQ3aC03LjA1NVYxNC4wMjJoMTYuNTI0YzIuMzYxLDAuMDQ3LDQuMTc4LDAuMzQ0LDUuNDUsMC44OTFjMS4yNzIsMC41NDcsMi4zNTEsMS4zNTIsMy4yMzQsMi40MTRjMC43MzEsMC44NzUsMS4zMSwxLjg0NCwxLjczNywyLjkwNnMwLjY0LDIuMjczLDAuNjQsMy42MzNjMCwxLjY0MS0wLjQxNCwzLjI1NC0xLjI0Miw0Ljg0cy0yLjE5NSwyLjcwNy00LjEwMiwzLjM2M2MxLjU5NCwwLjY0MSwyLjcyMywxLjU1MSwzLjM4NywyLjczczAuOTk2LDIuOTgsMC45OTYsNS40MDJ2Mi4zMmMwLDEuNTc4LDAuMDYzLDIuNjQ4LDAuMTksMy4yMTFjMC4xOSwwLjg5MSwwLjYzNSwxLjU0NywxLjMzMywxLjk2OVY0OC41Njl6IE03NS41NzksNDguNTY5aC0yNi4xOFYxNC4wMjJoMjUuMzM2djYuMTE3SDU2LjQ1NHY3LjMzNmgxNi43ODF2Nkg1Ni40NTR2OC44ODNoMTkuMTI1VjQ4LjU2OXogTTEwNC40OTcsNDYuMzMxYy0yLjQ0LDIuMDg2LTUuODg3LDMuMTI5LTEwLjM0LDMuMTI5Yy00LjU0OCwwLTguMTI1LTEuMDI3LTEwLjczMS0zLjA4MnMtMy45MDktNC44NzktMy45MDktOC40NzNoNi44OTFjMC4yMjQsMS41NzgsMC42NjIsMi43NTgsMS4zMTYsMy41MzljMS4xOTYsMS40MjIsMy4yNDYsMi4xMzMsNi4xNSwyLjEzM2MxLjczOSwwLDMuMTUxLTAuMTg4LDQuMjM2LTAuNTYyYzIuMDU4LTAuNzE5LDMuMDg3LTIuMDU1LDMuMDg3LTQuMDA4YzAtMS4xNDEtMC41MDQtMi4wMjMtMS41MTItMi42NDhjLTEuMDA4LTAuNjA5LTIuNjA3LTEuMTQ4LTQuNzk2LTEuNjE3bC0zLjc0LTAuODJjLTMuNjc2LTAuODEyLTYuMjAxLTEuNjk1LTcuNTc2LTIuNjQ4Yy0yLjMyOC0xLjU5NC0zLjQ5Mi00LjA4Ni0zLjQ5Mi03LjQ3N2MwLTMuMDk0LDEuMTM5LTUuNjY0LDMuNDE3LTcuNzExczUuNjIzLTMuMDcsMTAuMDM2LTMuMDdjMy42ODUsMCw2LjgyOSwwLjk2NSw5LjQzMSwyLjg5NWMyLjYwMiwxLjkzLDMuOTY2LDQuNzMsNC4wOTMsOC40MDJoLTYuOTM4Yy0wLjEyOC0yLjA3OC0xLjA1Ny0zLjU1NS0yLjc4Ny00LjQzYy0xLjE1NC0wLjU3OC0yLjU4Ny0wLjg2Ny00LjMwMS0wLjg2N2MtMS45MDcsMC0zLjQyOCwwLjM3NS00LjU2NSwxLjEyNWMtMS4xMzgsMC43NS0xLjcwNiwxLjc5Ny0xLjcwNiwzLjE0MWMwLDEuMjM0LDAuNTYxLDIuMTU2LDEuNjgyLDIuNzY2YzAuNzIxLDAuNDA2LDIuMjUsMC44ODMsNC41ODksMS40M2w2LjA2MywxLjQzYzIuNjU3LDAuNjI1LDQuNjQ4LDEuNDYxLDUuOTc1LDIuNTA4YzIuMDU5LDEuNjI1LDMuMDg5LDMuOTc3LDMuMDg5LDcuMDU1QzEwOC4xNTcsNDEuNjI0LDEwNi45MzcsNDQuMjQ1LDEwNC40OTcsNDYuMzMxeiBNMTM5LjYxLDQ4LjU2OWgtMjYuMThWMTQuMDIyaDI1LjMzNnY2LjExN2gtMTguMjgxdjcuMzM2aDE2Ljc4MXY2aC0xNi43ODF2OC44ODNoMTkuMTI1VjQ4LjU2OXogTTE3MC4zMzcsMjAuMTRoLTEwLjMzNnYyOC40M2gtNy4yNjZWMjAuMTRoLTEwLjM4M3YtNi4xMTdoMjcuOTg0VjIwLjE0eicpO1xuICAgIHJlc2V0UGFuWm9vbUNvbnRyb2xTaGFwZTIuc2V0QXR0cmlidXRlKCdjbGFzcycsICdzdmctcGFuLXpvb20tY29udHJvbC1lbGVtZW50Jyk7XG4gICAgcmVzZXRQYW5ab29tQ29udHJvbC5hcHBlbmRDaGlsZChyZXNldFBhblpvb21Db250cm9sU2hhcGUyKTtcblxuICAgIHJldHVybiByZXNldFBhblpvb21Db250cm9sXG4gIH1cblxuLCBfY3JlYXRlWm9vbU91dDogZnVuY3Rpb24oaW5zdGFuY2Upe1xuICAgIC8vIHpvb20gb3V0XG4gICAgdmFyIHpvb21PdXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoU3ZnVXRpbHMuc3ZnTlMsICdnJyk7XG4gICAgem9vbU91dC5zZXRBdHRyaWJ1dGUoJ2lkJywgJ3N2Zy1wYW4tem9vbS16b29tLW91dCcpO1xuICAgIHpvb21PdXQuc2V0QXR0cmlidXRlKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKDMwLjUgNzApIHNjYWxlKDAuMDE1KScpO1xuICAgIHpvb21PdXQuc2V0QXR0cmlidXRlKCdjbGFzcycsICdzdmctcGFuLXpvb20tY29udHJvbCcpO1xuICAgIHpvb21PdXQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbigpIHtpbnN0YW5jZS5nZXRQdWJsaWNJbnN0YW5jZSgpLnpvb21PdXQoKX0sIGZhbHNlKTtcbiAgICB6b29tT3V0LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBmdW5jdGlvbigpIHtpbnN0YW5jZS5nZXRQdWJsaWNJbnN0YW5jZSgpLnpvb21PdXQoKX0sIGZhbHNlKTtcblxuICAgIHZhciB6b29tT3V0QmFja2dyb3VuZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhTdmdVdGlscy5zdmdOUywgJ3JlY3QnKTsgLy8gVE9ETyBjaGFuZ2UgdGhlc2UgYmFja2dyb3VuZCBzcGFjZSBmaWxsZXJzIHRvIHJvdW5kZWQgcmVjdGFuZ2xlcyBzbyB0aGV5IGxvb2sgcHJldHRpZXJcbiAgICB6b29tT3V0QmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ3gnLCAnMCcpO1xuICAgIHpvb21PdXRCYWNrZ3JvdW5kLnNldEF0dHJpYnV0ZSgneScsICcwJyk7XG4gICAgem9vbU91dEJhY2tncm91bmQuc2V0QXR0cmlidXRlKCd3aWR0aCcsICcxNTAwJyk7IC8vIGxhcmdlciB0aGFuIGV4cGVjdGVkIGJlY2F1c2UgdGhlIHdob2xlIGdyb3VwIGlzIHRyYW5zZm9ybWVkIHRvIHNjYWxlIGRvd25cbiAgICB6b29tT3V0QmFja2dyb3VuZC5zZXRBdHRyaWJ1dGUoJ2hlaWdodCcsICcxNDAwJyk7XG4gICAgem9vbU91dEJhY2tncm91bmQuc2V0QXR0cmlidXRlKCdjbGFzcycsICdzdmctcGFuLXpvb20tY29udHJvbC1iYWNrZ3JvdW5kJyk7XG4gICAgem9vbU91dC5hcHBlbmRDaGlsZCh6b29tT3V0QmFja2dyb3VuZCk7XG5cbiAgICB2YXIgem9vbU91dFNoYXBlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFN2Z1V0aWxzLnN2Z05TLCAncGF0aCcpO1xuICAgIHpvb21PdXRTaGFwZS5zZXRBdHRyaWJ1dGUoJ2QnLCAnTTEyODAgNTc2djEyOHEwIDI2IC0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTEyOHEwIC0yNiAxOSAtNDV0NDUgLTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNSB0ODQuNSAtMjAzLjV6Jyk7XG4gICAgem9vbU91dFNoYXBlLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAnc3ZnLXBhbi16b29tLWNvbnRyb2wtZWxlbWVudCcpO1xuICAgIHpvb21PdXQuYXBwZW5kQ2hpbGQoem9vbU91dFNoYXBlKTtcblxuICAgIHJldHVybiB6b29tT3V0XG4gIH1cblxuLCBkaXNhYmxlOiBmdW5jdGlvbihpbnN0YW5jZSkge1xuICAgIGlmIChpbnN0YW5jZS5jb250cm9sSWNvbnMpIHtcbiAgICAgIGluc3RhbmNlLmNvbnRyb2xJY29ucy5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGluc3RhbmNlLmNvbnRyb2xJY29ucylcbiAgICAgIGluc3RhbmNlLmNvbnRyb2xJY29ucyA9IG51bGxcbiAgICB9XG4gIH1cbn1cblxufSx7XCIuL3N2Zy11dGlsaXRpZXNcIjo1fV0sMzpbZnVuY3Rpb24ocmVxdWlyZSxtb2R1bGUsZXhwb3J0cyl7XG52YXIgU3ZnVXRpbHMgPSByZXF1aXJlKCcuL3N2Zy11dGlsaXRpZXMnKVxuICAsIFV0aWxzID0gcmVxdWlyZSgnLi91dGlsaXRpZXMnKVxuICA7XG5cbnZhciBTaGFkb3dWaWV3cG9ydCA9IGZ1bmN0aW9uKHZpZXdwb3J0LCBvcHRpb25zKXtcbiAgdGhpcy5pbml0KHZpZXdwb3J0LCBvcHRpb25zKVxufVxuXG4vKipcbiAqIEluaXRpYWxpemF0aW9uXG4gKlxuICogQHBhcmFtICB7U1ZHRWxlbWVudH0gdmlld3BvcnRcbiAqIEBwYXJhbSAge09iamVjdH0gb3B0aW9uc1xuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUuaW5pdCA9IGZ1bmN0aW9uKHZpZXdwb3J0LCBvcHRpb25zKSB7XG4gIC8vIERPTSBFbGVtZW50c1xuICB0aGlzLnZpZXdwb3J0ID0gdmlld3BvcnRcbiAgdGhpcy5vcHRpb25zID0gb3B0aW9uc1xuXG4gIC8vIFN0YXRlIGNhY2hlXG4gIHRoaXMub3JpZ2luYWxTdGF0ZSA9IHt6b29tOiAxLCB4OiAwLCB5OiAwfVxuICB0aGlzLmFjdGl2ZVN0YXRlID0ge3pvb206IDEsIHg6IDAsIHk6IDB9XG5cbiAgdGhpcy51cGRhdGVDVE1DYWNoZWQgPSBVdGlscy5wcm94eSh0aGlzLnVwZGF0ZUNUTSwgdGhpcylcblxuICAvLyBDcmVhdGUgYSBjdXN0b20gcmVxdWVzdEFuaW1hdGlvbkZyYW1lIHRha2luZyBpbiBhY2NvdW50IHJlZnJlc2hSYXRlXG4gIHRoaXMucmVxdWVzdEFuaW1hdGlvbkZyYW1lID0gVXRpbHMuY3JlYXRlUmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMub3B0aW9ucy5yZWZyZXNoUmF0ZSlcblxuICAvLyBWaWV3Qm94XG4gIHRoaXMudmlld0JveCA9IHt4OiAwLCB5OiAwLCB3aWR0aDogMCwgaGVpZ2h0OiAwfVxuICB0aGlzLmNhY2hlVmlld0JveCgpXG5cbiAgLy8gUHJvY2VzcyBDVE1cbiAgdmFyIG5ld0NUTSA9IHRoaXMucHJvY2Vzc0NUTSgpXG5cbiAgLy8gVXBkYXRlIHZpZXdwb3J0IENUTSBhbmQgY2FjaGUgem9vbSBhbmQgcGFuXG4gIHRoaXMuc2V0Q1RNKG5ld0NUTSlcblxuICAvLyBVcGRhdGUgQ1RNIGluIHRoaXMgZnJhbWVcbiAgdGhpcy51cGRhdGVDVE0oKVxufVxuXG4vKipcbiAqIENhY2hlIGluaXRpYWwgdmlld0JveCB2YWx1ZVxuICogSWYgbm8gdmlld0JveCBpcyBkZWZpbmVkLCB0aGVuIHVzZSB2aWV3cG9ydCBzaXplL3Bvc2l0aW9uIGluc3RlYWQgZm9yIHZpZXdCb3ggdmFsdWVzXG4gKi9cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS5jYWNoZVZpZXdCb3ggPSBmdW5jdGlvbigpIHtcbiAgdmFyIHN2Z1ZpZXdCb3ggPSB0aGlzLm9wdGlvbnMuc3ZnLmdldEF0dHJpYnV0ZSgndmlld0JveCcpXG5cbiAgaWYgKHN2Z1ZpZXdCb3gpIHtcbiAgICB2YXIgdmlld0JveFZhbHVlcyA9IHN2Z1ZpZXdCb3guc3BsaXQoL1tcXHNcXCxdLykuZmlsdGVyKGZ1bmN0aW9uKHYpe3JldHVybiB2fSkubWFwKHBhcnNlRmxvYXQpXG5cbiAgICAvLyBDYWNoZSB2aWV3Ym94IHggYW5kIHkgb2Zmc2V0XG4gICAgdGhpcy52aWV3Qm94LnggPSB2aWV3Qm94VmFsdWVzWzBdXG4gICAgdGhpcy52aWV3Qm94LnkgPSB2aWV3Qm94VmFsdWVzWzFdXG4gICAgdGhpcy52aWV3Qm94LndpZHRoID0gdmlld0JveFZhbHVlc1syXVxuICAgIHRoaXMudmlld0JveC5oZWlnaHQgPSB2aWV3Qm94VmFsdWVzWzNdXG5cbiAgICB2YXIgem9vbSA9IE1hdGgubWluKHRoaXMub3B0aW9ucy53aWR0aCAvIHRoaXMudmlld0JveC53aWR0aCwgdGhpcy5vcHRpb25zLmhlaWdodCAvIHRoaXMudmlld0JveC5oZWlnaHQpXG5cbiAgICAvLyBVcGRhdGUgYWN0aXZlIHN0YXRlXG4gICAgdGhpcy5hY3RpdmVTdGF0ZS56b29tID0gem9vbVxuICAgIHRoaXMuYWN0aXZlU3RhdGUueCA9ICh0aGlzLm9wdGlvbnMud2lkdGggLSB0aGlzLnZpZXdCb3gud2lkdGggKiB6b29tKSAvIDJcbiAgICB0aGlzLmFjdGl2ZVN0YXRlLnkgPSAodGhpcy5vcHRpb25zLmhlaWdodCAtIHRoaXMudmlld0JveC5oZWlnaHQgKiB6b29tKSAvIDJcblxuICAgIC8vIEZvcmNlIHVwZGF0aW5nIENUTVxuICAgIHRoaXMudXBkYXRlQ1RNT25OZXh0RnJhbWUoKVxuXG4gICAgdGhpcy5vcHRpb25zLnN2Zy5yZW1vdmVBdHRyaWJ1dGUoJ3ZpZXdCb3gnKVxuICB9IGVsc2Uge1xuICAgIHRoaXMuc2ltcGxlVmlld0JveENhY2hlKClcbiAgfVxufVxuXG4vKipcbiAqIFJlY2FsY3VsYXRlIHZpZXdwb3J0IHNpemVzIGFuZCB1cGRhdGUgdmlld0JveCBjYWNoZVxuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUuc2ltcGxlVmlld0JveENhY2hlID0gZnVuY3Rpb24oKSB7XG4gIHZhciBiQm94ID0gdGhpcy52aWV3cG9ydC5nZXRCQm94KClcblxuICB0aGlzLnZpZXdCb3gueCA9IGJCb3gueFxuICB0aGlzLnZpZXdCb3gueSA9IGJCb3gueVxuICB0aGlzLnZpZXdCb3gud2lkdGggPSBiQm94LndpZHRoXG4gIHRoaXMudmlld0JveC5oZWlnaHQgPSBiQm94LmhlaWdodFxufVxuXG4vKipcbiAqIFJldHVybnMgYSB2aWV3Ym94IG9iamVjdC4gU2FmZSB0byBhbHRlclxuICpcbiAqIEByZXR1cm4ge09iamVjdH0gdmlld2JveCBvYmplY3RcbiAqL1xuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLmdldFZpZXdCb3ggPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIFV0aWxzLmV4dGVuZCh7fSwgdGhpcy52aWV3Qm94KVxufVxuXG4vKipcbiAqIEdldCBpbml0aWFsIHpvb20gYW5kIHBhbiB2YWx1ZXMuIFNhdmUgdGhlbSBpbnRvIG9yaWdpbmFsU3RhdGVcbiAqIFBhcnNlcyB2aWV3Qm94IGF0dHJpYnV0ZSB0byBhbHRlciBpbml0aWFsIHNpemVzXG4gKlxuICogQHJldHVybiB7Q1RNfSBDVE0gb2JqZWN0IGJhc2VkIG9uIG9wdGlvbnNcbiAqL1xuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLnByb2Nlc3NDVE0gPSBmdW5jdGlvbigpIHtcbiAgdmFyIG5ld0NUTSA9IHRoaXMuZ2V0Q1RNKClcblxuICBpZiAodGhpcy5vcHRpb25zLmZpdCB8fCB0aGlzLm9wdGlvbnMuY29udGFpbikge1xuICAgIHZhciBuZXdTY2FsZTtcbiAgICBpZiAodGhpcy5vcHRpb25zLmZpdCkge1xuICAgICAgbmV3U2NhbGUgPSBNYXRoLm1pbih0aGlzLm9wdGlvbnMud2lkdGgvdGhpcy52aWV3Qm94LndpZHRoLCB0aGlzLm9wdGlvbnMuaGVpZ2h0L3RoaXMudmlld0JveC5oZWlnaHQpO1xuICAgIH0gZWxzZSB7XG4gICAgICBuZXdTY2FsZSA9IE1hdGgubWF4KHRoaXMub3B0aW9ucy53aWR0aC90aGlzLnZpZXdCb3gud2lkdGgsIHRoaXMub3B0aW9ucy5oZWlnaHQvdGhpcy52aWV3Qm94LmhlaWdodCk7XG4gICAgfVxuXG4gICAgbmV3Q1RNLmEgPSBuZXdTY2FsZTsgLy94LXNjYWxlXG4gICAgbmV3Q1RNLmQgPSBuZXdTY2FsZTsgLy95LXNjYWxlXG4gICAgbmV3Q1RNLmUgPSAtdGhpcy52aWV3Qm94LnggKiBuZXdTY2FsZTsgLy94LXRyYW5zZm9ybVxuICAgIG5ld0NUTS5mID0gLXRoaXMudmlld0JveC55ICogbmV3U2NhbGU7IC8veS10cmFuc2Zvcm1cbiAgfVxuXG4gIGlmICh0aGlzLm9wdGlvbnMuY2VudGVyKSB7XG4gICAgdmFyIG9mZnNldFggPSAodGhpcy5vcHRpb25zLndpZHRoIC0gKHRoaXMudmlld0JveC53aWR0aCArIHRoaXMudmlld0JveC54ICogMikgKiBuZXdDVE0uYSkgKiAwLjVcbiAgICAgICwgb2Zmc2V0WSA9ICh0aGlzLm9wdGlvbnMuaGVpZ2h0IC0gKHRoaXMudmlld0JveC5oZWlnaHQgKyB0aGlzLnZpZXdCb3gueSAqIDIpICogbmV3Q1RNLmEpICogMC41XG5cbiAgICBuZXdDVE0uZSA9IG9mZnNldFhcbiAgICBuZXdDVE0uZiA9IG9mZnNldFlcbiAgfVxuXG4gIC8vIENhY2hlIGluaXRpYWwgdmFsdWVzLiBCYXNlZCBvbiBhY3RpdmVTdGF0ZSBhbmQgZml4K2NlbnRlciBvcGl0b25zXG4gIHRoaXMub3JpZ2luYWxTdGF0ZS56b29tID0gbmV3Q1RNLmFcbiAgdGhpcy5vcmlnaW5hbFN0YXRlLnggPSBuZXdDVE0uZVxuICB0aGlzLm9yaWdpbmFsU3RhdGUueSA9IG5ld0NUTS5mXG5cbiAgcmV0dXJuIG5ld0NUTVxufVxuXG4vKipcbiAqIFJldHVybiBvcmlnaW5hbFN0YXRlIG9iamVjdC4gU2FmZSB0byBhbHRlclxuICpcbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLmdldE9yaWdpbmFsU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIFV0aWxzLmV4dGVuZCh7fSwgdGhpcy5vcmlnaW5hbFN0YXRlKVxufVxuXG4vKipcbiAqIFJldHVybiBhY3R1YWxTdGF0ZSBvYmplY3QuIFNhZmUgdG8gYWx0ZXJcbiAqXG4gKiBAcmV0dXJuIHtPYmplY3R9XG4gKi9cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS5nZXRTdGF0ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gVXRpbHMuZXh0ZW5kKHt9LCB0aGlzLmFjdGl2ZVN0YXRlKVxufVxuXG4vKipcbiAqIEdldCB6b29tIHNjYWxlXG4gKlxuICogQHJldHVybiB7RmxvYXR9IHpvb20gc2NhbGVcbiAqL1xuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLmdldFpvb20gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuYWN0aXZlU3RhdGUuem9vbVxufVxuXG4vKipcbiAqIEdldCB6b29tIHNjYWxlIGZvciBwdWJpbGMgdXNhZ2VcbiAqXG4gKiBAcmV0dXJuIHtGbG9hdH0gem9vbSBzY2FsZVxuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUuZ2V0UmVsYXRpdmVab29tID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmFjdGl2ZVN0YXRlLnpvb20gLyB0aGlzLm9yaWdpbmFsU3RhdGUuem9vbVxufVxuXG4vKipcbiAqIENvbXB1dGUgem9vbSBzY2FsZSBmb3IgcHViaWxjIHVzYWdlXG4gKlxuICogQHJldHVybiB7RmxvYXR9IHpvb20gc2NhbGVcbiAqL1xuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLmNvbXB1dGVSZWxhdGl2ZVpvb20gPSBmdW5jdGlvbihzY2FsZSkge1xuICByZXR1cm4gc2NhbGUgLyB0aGlzLm9yaWdpbmFsU3RhdGUuem9vbVxufVxuXG4vKipcbiAqIEdldCBwYW5cbiAqXG4gKiBAcmV0dXJuIHtPYmplY3R9XG4gKi9cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS5nZXRQYW4gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHt4OiB0aGlzLmFjdGl2ZVN0YXRlLngsIHk6IHRoaXMuYWN0aXZlU3RhdGUueX1cbn1cblxuLyoqXG4gKiBSZXR1cm4gY2FjaGVkIHZpZXdwb3J0IENUTSB2YWx1ZSB0aGF0IGNhbiBiZSBzYWZlbHkgbW9kaWZpZWRcbiAqXG4gKiBAcmV0dXJuIHtTVkdNYXRyaXh9XG4gKi9cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS5nZXRDVE0gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHNhZmVDVE0gPSB0aGlzLm9wdGlvbnMuc3ZnLmNyZWF0ZVNWR01hdHJpeCgpXG5cbiAgLy8gQ29weSB2YWx1ZXMgbWFudWFsbHkgYXMgaW4gRkYgdGhleSBhcmUgbm90IGl0dGVyYWJsZVxuICBzYWZlQ1RNLmEgPSB0aGlzLmFjdGl2ZVN0YXRlLnpvb21cbiAgc2FmZUNUTS5iID0gMFxuICBzYWZlQ1RNLmMgPSAwXG4gIHNhZmVDVE0uZCA9IHRoaXMuYWN0aXZlU3RhdGUuem9vbVxuICBzYWZlQ1RNLmUgPSB0aGlzLmFjdGl2ZVN0YXRlLnhcbiAgc2FmZUNUTS5mID0gdGhpcy5hY3RpdmVTdGF0ZS55XG5cbiAgcmV0dXJuIHNhZmVDVE1cbn1cblxuLyoqXG4gKiBTZXQgYSBuZXcgQ1RNXG4gKlxuICogQHBhcmFtIHtTVkdNYXRyaXh9IG5ld0NUTVxuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUuc2V0Q1RNID0gZnVuY3Rpb24obmV3Q1RNKSB7XG4gIHZhciB3aWxsWm9vbSA9IHRoaXMuaXNab29tRGlmZmVyZW50KG5ld0NUTSlcbiAgICAsIHdpbGxQYW4gPSB0aGlzLmlzUGFuRGlmZmVyZW50KG5ld0NUTSlcblxuICBpZiAod2lsbFpvb20gfHwgd2lsbFBhbikge1xuICAgIC8vIEJlZm9yZSB6b29tXG4gICAgaWYgKHdpbGxab29tKSB7XG4gICAgICAvLyBJZiByZXR1cm5zIGZhbHNlIHRoZW4gY2FuY2VsIHpvb21pbmdcbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuYmVmb3JlWm9vbSh0aGlzLmdldFJlbGF0aXZlWm9vbSgpLCB0aGlzLmNvbXB1dGVSZWxhdGl2ZVpvb20obmV3Q1RNLmEpKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgbmV3Q1RNLmEgPSBuZXdDVE0uZCA9IHRoaXMuYWN0aXZlU3RhdGUuem9vbVxuICAgICAgICB3aWxsWm9vbSA9IGZhbHNlXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnVwZGF0ZUNhY2hlKG5ld0NUTSk7XG4gICAgICAgIHRoaXMub3B0aW9ucy5vblpvb20odGhpcy5nZXRSZWxhdGl2ZVpvb20oKSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBCZWZvcmUgcGFuXG4gICAgaWYgKHdpbGxQYW4pIHtcbiAgICAgIHZhciBwcmV2ZW50UGFuID0gdGhpcy5vcHRpb25zLmJlZm9yZVBhbih0aGlzLmdldFBhbigpLCB7eDogbmV3Q1RNLmUsIHk6IG5ld0NUTS5mfSlcbiAgICAgICAgICAvLyBJZiBwcmV2ZW50IHBhbiBpcyBhbiBvYmplY3RcbiAgICAgICAgLCBwcmV2ZW50UGFuWCA9IGZhbHNlXG4gICAgICAgICwgcHJldmVudFBhblkgPSBmYWxzZVxuXG4gICAgICAvLyBJZiBwcmV2ZW50IHBhbiBpcyBCb29sZWFuIGZhbHNlXG4gICAgICBpZiAocHJldmVudFBhbiA9PT0gZmFsc2UpIHtcbiAgICAgICAgLy8gU2V0IHggYW5kIHkgc2FtZSBhcyBiZWZvcmVcbiAgICAgICAgbmV3Q1RNLmUgPSB0aGlzLmdldFBhbigpLnhcbiAgICAgICAgbmV3Q1RNLmYgPSB0aGlzLmdldFBhbigpLnlcblxuICAgICAgICBwcmV2ZW50UGFuWCA9IHByZXZlbnRQYW5ZID0gdHJ1ZVxuICAgICAgfSBlbHNlIGlmIChVdGlscy5pc09iamVjdChwcmV2ZW50UGFuKSkge1xuICAgICAgICAvLyBDaGVjayBmb3IgWCBheGVzIGF0dHJpYnV0ZVxuICAgICAgICBpZiAocHJldmVudFBhbi54ID09PSBmYWxzZSkge1xuICAgICAgICAgIC8vIFByZXZlbnQgcGFubmluZyBvbiB4IGF4ZXNcbiAgICAgICAgICBuZXdDVE0uZSA9IHRoaXMuZ2V0UGFuKCkueFxuICAgICAgICAgIHByZXZlbnRQYW5YID0gdHJ1ZVxuICAgICAgICB9IGVsc2UgaWYgKFV0aWxzLmlzTnVtYmVyKHByZXZlbnRQYW4ueCkpIHtcbiAgICAgICAgICAvLyBTZXQgYSBjdXN0b20gcGFuIHZhbHVlXG4gICAgICAgICAgbmV3Q1RNLmUgPSBwcmV2ZW50UGFuLnhcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGZvciBZIGF4ZXMgYXR0cmlidXRlXG4gICAgICAgIGlmIChwcmV2ZW50UGFuLnkgPT09IGZhbHNlKSB7XG4gICAgICAgICAgLy8gUHJldmVudCBwYW5uaW5nIG9uIHggYXhlc1xuICAgICAgICAgIG5ld0NUTS5mID0gdGhpcy5nZXRQYW4oKS55XG4gICAgICAgICAgcHJldmVudFBhblkgPSB0cnVlXG4gICAgICAgIH0gZWxzZSBpZiAoVXRpbHMuaXNOdW1iZXIocHJldmVudFBhbi55KSkge1xuICAgICAgICAgIC8vIFNldCBhIGN1c3RvbSBwYW4gdmFsdWVcbiAgICAgICAgICBuZXdDVE0uZiA9IHByZXZlbnRQYW4ueVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIFVwZGF0ZSB3aWxsUGFuIGZsYWdcbiAgICAgIC8vIENoZWNrIGlmIG5ld0NUTSBpcyBzdGlsbCBkaWZmZXJlbnRcbiAgICAgIGlmICgocHJldmVudFBhblggJiYgcHJldmVudFBhblkpIHx8ICF0aGlzLmlzUGFuRGlmZmVyZW50KG5ld0NUTSkpIHtcbiAgICAgICAgd2lsbFBhbiA9IGZhbHNlXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnVwZGF0ZUNhY2hlKG5ld0NUTSk7XG4gICAgICAgIHRoaXMub3B0aW9ucy5vblBhbih0aGlzLmdldFBhbigpKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDaGVjayBhZ2FpbiBpZiBzaG91bGQgem9vbSBvciBwYW5cbiAgICBpZiAod2lsbFpvb20gfHwgd2lsbFBhbikge1xuICAgICAgdGhpcy51cGRhdGVDVE1Pbk5leHRGcmFtZSgpXG4gICAgfVxuICB9XG59XG5cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS5pc1pvb21EaWZmZXJlbnQgPSBmdW5jdGlvbihuZXdDVE0pIHtcbiAgcmV0dXJuIHRoaXMuYWN0aXZlU3RhdGUuem9vbSAhPT0gbmV3Q1RNLmFcbn1cblxuU2hhZG93Vmlld3BvcnQucHJvdG90eXBlLmlzUGFuRGlmZmVyZW50ID0gZnVuY3Rpb24obmV3Q1RNKSB7XG4gIHJldHVybiB0aGlzLmFjdGl2ZVN0YXRlLnggIT09IG5ld0NUTS5lIHx8IHRoaXMuYWN0aXZlU3RhdGUueSAhPT0gbmV3Q1RNLmZcbn1cblxuXG4vKipcbiAqIFVwZGF0ZSBjYWNoZWQgQ1RNIGFuZCBhY3RpdmUgc3RhdGVcbiAqXG4gKiBAcGFyYW0ge1NWR01hdHJpeH0gbmV3Q1RNXG4gKi9cblNoYWRvd1ZpZXdwb3J0LnByb3RvdHlwZS51cGRhdGVDYWNoZSA9IGZ1bmN0aW9uKG5ld0NUTSkge1xuICB0aGlzLmFjdGl2ZVN0YXRlLnpvb20gPSBuZXdDVE0uYVxuICB0aGlzLmFjdGl2ZVN0YXRlLnggPSBuZXdDVE0uZVxuICB0aGlzLmFjdGl2ZVN0YXRlLnkgPSBuZXdDVE0uZlxufVxuXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUucGVuZGluZ1VwZGF0ZSA9IGZhbHNlXG5cbi8qKlxuICogUGxhY2UgYSByZXF1ZXN0IHRvIHVwZGF0ZSBDVE0gb24gbmV4dCBGcmFtZVxuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUudXBkYXRlQ1RNT25OZXh0RnJhbWUgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCF0aGlzLnBlbmRpbmdVcGRhdGUpIHtcbiAgICAvLyBMb2NrXG4gICAgdGhpcy5wZW5kaW5nVXBkYXRlID0gdHJ1ZVxuXG4gICAgLy8gVGhyb3R0bGUgbmV4dCB1cGRhdGVcbiAgICB0aGlzLnJlcXVlc3RBbmltYXRpb25GcmFtZS5jYWxsKHdpbmRvdywgdGhpcy51cGRhdGVDVE1DYWNoZWQpXG4gIH1cbn1cblxuLyoqXG4gKiBVcGRhdGUgdmlld3BvcnQgQ1RNIHdpdGggY2FjaGVkIENUTVxuICovXG5TaGFkb3dWaWV3cG9ydC5wcm90b3R5cGUudXBkYXRlQ1RNID0gZnVuY3Rpb24oKSB7XG4gIHZhciBjdG0gPSB0aGlzLmdldENUTSgpXG5cbiAgLy8gVXBkYXRlcyBTVkcgZWxlbWVudFxuICBTdmdVdGlscy5zZXRDVE0odGhpcy52aWV3cG9ydCwgY3RtLCB0aGlzLmRlZnMpXG5cbiAgLy8gRnJlZSB0aGUgbG9ja1xuICB0aGlzLnBlbmRpbmdVcGRhdGUgPSBmYWxzZVxuXG4gIC8vIE5vdGlmeSBhYm91dCB0aGUgdXBkYXRlXG4gIGlmKHRoaXMub3B0aW9ucy5vblVwZGF0ZWRDVE0pIHtcbiAgICB0aGlzLm9wdGlvbnMub25VcGRhdGVkQ1RNKGN0bSlcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKHZpZXdwb3J0LCBvcHRpb25zKXtcbiAgcmV0dXJuIG5ldyBTaGFkb3dWaWV3cG9ydCh2aWV3cG9ydCwgb3B0aW9ucylcbn1cblxufSx7XCIuL3N2Zy11dGlsaXRpZXNcIjo1LFwiLi91dGlsaXRpZXNcIjo3fV0sNDpbZnVuY3Rpb24ocmVxdWlyZSxtb2R1bGUsZXhwb3J0cyl7XG52YXIgV2hlZWwgPSByZXF1aXJlKCcuL3VuaXdoZWVsJylcbiwgQ29udHJvbEljb25zID0gcmVxdWlyZSgnLi9jb250cm9sLWljb25zJylcbiwgVXRpbHMgPSByZXF1aXJlKCcuL3V0aWxpdGllcycpXG4sIFN2Z1V0aWxzID0gcmVxdWlyZSgnLi9zdmctdXRpbGl0aWVzJylcbiwgU2hhZG93Vmlld3BvcnQgPSByZXF1aXJlKCcuL3NoYWRvdy12aWV3cG9ydCcpXG5cbnZhciBTdmdQYW5ab29tID0gZnVuY3Rpb24oc3ZnLCBvcHRpb25zKSB7XG4gIHRoaXMuaW5pdChzdmcsIG9wdGlvbnMpXG59XG5cbnZhciBvcHRpb25zRGVmYXVsdHMgPSB7XG4gIHZpZXdwb3J0U2VsZWN0b3I6ICcuc3ZnLXBhbi16b29tX3ZpZXdwb3J0JyAvLyBWaWV3cG9ydCBzZWxlY3Rvci4gQ2FuIGJlIHF1ZXJ5U2VsZWN0b3Igc3RyaW5nIG9yIFNWR0VsZW1lbnRcbiwgcGFuRW5hYmxlZDogdHJ1ZSAvLyBlbmFibGUgb3IgZGlzYWJsZSBwYW5uaW5nIChkZWZhdWx0IGVuYWJsZWQpXG4sIGNvbnRyb2xJY29uc0VuYWJsZWQ6IGZhbHNlIC8vIGluc2VydCBpY29ucyB0byBnaXZlIHVzZXIgYW4gb3B0aW9uIGluIGFkZGl0aW9uIHRvIG1vdXNlIGV2ZW50cyB0byBjb250cm9sIHBhbi96b29tIChkZWZhdWx0IGRpc2FibGVkKVxuLCB6b29tRW5hYmxlZDogdHJ1ZSAvLyBlbmFibGUgb3IgZGlzYWJsZSB6b29taW5nIChkZWZhdWx0IGVuYWJsZWQpXG4sIGRibENsaWNrWm9vbUVuYWJsZWQ6IHRydWUgLy8gZW5hYmxlIG9yIGRpc2FibGUgem9vbWluZyBieSBkb3VibGUgY2xpY2tpbmcgKGRlZmF1bHQgZW5hYmxlZClcbiwgbW91c2VXaGVlbFpvb21FbmFibGVkOiB0cnVlIC8vIGVuYWJsZSBvciBkaXNhYmxlIHpvb21pbmcgYnkgbW91c2Ugd2hlZWwgKGRlZmF1bHQgZW5hYmxlZClcbiwgcHJldmVudE1vdXNlRXZlbnRzRGVmYXVsdDogdHJ1ZSAvLyBlbmFibGUgb3IgZGlzYWJsZSBwcmV2ZW50RGVmYXVsdCBmb3IgbW91c2UgZXZlbnRzXG4sIHpvb21TY2FsZVNlbnNpdGl2aXR5OiAwLjEgLy8gWm9vbSBzZW5zaXRpdml0eVxuLCBtaW5ab29tOiAwLjUgLy8gTWluaW11bSBab29tIGxldmVsXG4sIG1heFpvb206IDEwIC8vIE1heGltdW0gWm9vbSBsZXZlbFxuLCBmaXQ6IHRydWUgLy8gZW5hYmxlIG9yIGRpc2FibGUgdmlld3BvcnQgZml0IGluIFNWRyAoZGVmYXVsdCB0cnVlKVxuLCBjb250YWluOiBmYWxzZSAvLyBlbmFibGUgb3IgZGlzYWJsZSB2aWV3cG9ydCBjb250YWluIHRoZSBzdmcgKGRlZmF1bHQgZmFsc2UpXG4sIGNlbnRlcjogdHJ1ZSAvLyBlbmFibGUgb3IgZGlzYWJsZSB2aWV3cG9ydCBjZW50ZXJpbmcgaW4gU1ZHIChkZWZhdWx0IHRydWUpXG4sIHJlZnJlc2hSYXRlOiAnYXV0bycgLy8gTWF4aW11bSBudW1iZXIgb2YgZnJhbWVzIHBlciBzZWNvbmQgKGFsdGVyaW5nIFNWRydzIHZpZXdwb3J0KVxuLCBiZWZvcmVab29tOiBudWxsXG4sIG9uWm9vbTogbnVsbFxuLCBiZWZvcmVQYW46IG51bGxcbiwgb25QYW46IG51bGxcbiwgY3VzdG9tRXZlbnRzSGFuZGxlcjogbnVsbFxuLCBldmVudHNMaXN0ZW5lckVsZW1lbnQ6IG51bGxcbiwgb25VcGRhdGVkQ1RNOiBudWxsXG59XG5cbnZhciBwYXNzaXZlTGlzdGVuZXJPcHRpb24gPSB7cGFzc2l2ZTogdHJ1ZX07XG5cblN2Z1Bhblpvb20ucHJvdG90eXBlLmluaXQgPSBmdW5jdGlvbihzdmcsIG9wdGlvbnMpIHtcbiAgdmFyIHRoYXQgPSB0aGlzXG5cbiAgdGhpcy5zdmcgPSBzdmdcbiAgdGhpcy5kZWZzID0gc3ZnLnF1ZXJ5U2VsZWN0b3IoJ2RlZnMnKVxuXG4gIC8vIEFkZCBkZWZhdWx0IGF0dHJpYnV0ZXMgdG8gU1ZHXG4gIFN2Z1V0aWxzLnNldHVwU3ZnQXR0cmlidXRlcyh0aGlzLnN2ZylcblxuICAvLyBTZXQgb3B0aW9uc1xuICB0aGlzLm9wdGlvbnMgPSBVdGlscy5leHRlbmQoVXRpbHMuZXh0ZW5kKHt9LCBvcHRpb25zRGVmYXVsdHMpLCBvcHRpb25zKVxuXG4gIC8vIFNldCBkZWZhdWx0IHN0YXRlXG4gIHRoaXMuc3RhdGUgPSAnbm9uZSdcblxuICAvLyBHZXQgZGltZW5zaW9uc1xuICB2YXIgYm91bmRpbmdDbGllbnRSZWN0Tm9ybWFsaXplZCA9IFN2Z1V0aWxzLmdldEJvdW5kaW5nQ2xpZW50UmVjdE5vcm1hbGl6ZWQoc3ZnKVxuICB0aGlzLndpZHRoID0gYm91bmRpbmdDbGllbnRSZWN0Tm9ybWFsaXplZC53aWR0aFxuICB0aGlzLmhlaWdodCA9IGJvdW5kaW5nQ2xpZW50UmVjdE5vcm1hbGl6ZWQuaGVpZ2h0XG5cbiAgLy8gSW5pdCBzaGFkb3cgdmlld3BvcnRcbiAgdGhpcy52aWV3cG9ydCA9IFNoYWRvd1ZpZXdwb3J0KFN2Z1V0aWxzLmdldE9yQ3JlYXRlVmlld3BvcnQodGhpcy5zdmcsIHRoaXMub3B0aW9ucy52aWV3cG9ydFNlbGVjdG9yKSwge1xuICAgIHN2ZzogdGhpcy5zdmdcbiAgLCB3aWR0aDogdGhpcy53aWR0aFxuICAsIGhlaWdodDogdGhpcy5oZWlnaHRcbiAgLCBmaXQ6IHRoaXMub3B0aW9ucy5maXRcbiAgLCBjb250YWluOiB0aGlzLm9wdGlvbnMuY29udGFpblxuICAsIGNlbnRlcjogdGhpcy5vcHRpb25zLmNlbnRlclxuICAsIHJlZnJlc2hSYXRlOiB0aGlzLm9wdGlvbnMucmVmcmVzaFJhdGVcbiAgLy8gUHV0IGNhbGxiYWNrcyBpbnRvIGZ1bmN0aW9ucyBhcyB0aGV5IGNhbiBjaGFuZ2UgdGhyb3VnaCB0aW1lXG4gICwgYmVmb3JlWm9vbTogZnVuY3Rpb24ob2xkU2NhbGUsIG5ld1NjYWxlKSB7XG4gICAgICBpZiAodGhhdC52aWV3cG9ydCAmJiB0aGF0Lm9wdGlvbnMuYmVmb3JlWm9vbSkge3JldHVybiB0aGF0Lm9wdGlvbnMuYmVmb3JlWm9vbShvbGRTY2FsZSwgbmV3U2NhbGUpfVxuICAgIH1cbiAgLCBvblpvb206IGZ1bmN0aW9uKHNjYWxlKSB7XG4gICAgICBpZiAodGhhdC52aWV3cG9ydCAmJiB0aGF0Lm9wdGlvbnMub25ab29tKSB7cmV0dXJuIHRoYXQub3B0aW9ucy5vblpvb20oc2NhbGUpfVxuICAgIH1cbiAgLCBiZWZvcmVQYW46IGZ1bmN0aW9uKG9sZFBvaW50LCBuZXdQb2ludCkge1xuICAgICAgaWYgKHRoYXQudmlld3BvcnQgJiYgdGhhdC5vcHRpb25zLmJlZm9yZVBhbikge3JldHVybiB0aGF0Lm9wdGlvbnMuYmVmb3JlUGFuKG9sZFBvaW50LCBuZXdQb2ludCl9XG4gICAgfVxuICAsIG9uUGFuOiBmdW5jdGlvbihwb2ludCkge1xuICAgICAgaWYgKHRoYXQudmlld3BvcnQgJiYgdGhhdC5vcHRpb25zLm9uUGFuKSB7cmV0dXJuIHRoYXQub3B0aW9ucy5vblBhbihwb2ludCl9XG4gICAgfVxuICAsIG9uVXBkYXRlZENUTTogZnVuY3Rpb24oY3RtKSB7XG4gICAgICBpZiAodGhhdC52aWV3cG9ydCAmJiB0aGF0Lm9wdGlvbnMub25VcGRhdGVkQ1RNKSB7cmV0dXJuIHRoYXQub3B0aW9ucy5vblVwZGF0ZWRDVE0oY3RtKX1cbiAgICB9XG4gIH0pXG5cbiAgLy8gV3JhcCBjYWxsYmFja3MgaW50byBwdWJsaWMgQVBJIGNvbnRleHRcbiAgdmFyIHB1YmxpY0luc3RhbmNlID0gdGhpcy5nZXRQdWJsaWNJbnN0YW5jZSgpXG4gIHB1YmxpY0luc3RhbmNlLnNldEJlZm9yZVpvb20odGhpcy5vcHRpb25zLmJlZm9yZVpvb20pXG4gIHB1YmxpY0luc3RhbmNlLnNldE9uWm9vbSh0aGlzLm9wdGlvbnMub25ab29tKVxuICBwdWJsaWNJbnN0YW5jZS5zZXRCZWZvcmVQYW4odGhpcy5vcHRpb25zLmJlZm9yZVBhbilcbiAgcHVibGljSW5zdGFuY2Uuc2V0T25QYW4odGhpcy5vcHRpb25zLm9uUGFuKVxuICBwdWJsaWNJbnN0YW5jZS5zZXRPblVwZGF0ZWRDVE0odGhpcy5vcHRpb25zLm9uVXBkYXRlZENUTSlcblxuICBpZiAodGhpcy5vcHRpb25zLmNvbnRyb2xJY29uc0VuYWJsZWQpIHtcbiAgICBDb250cm9sSWNvbnMuZW5hYmxlKHRoaXMpXG4gIH1cblxuICAvLyBJbml0IGV2ZW50cyBoYW5kbGVyc1xuICB0aGlzLmxhc3RNb3VzZVdoZWVsRXZlbnRUaW1lID0gRGF0ZS5ub3coKVxuICB0aGlzLnNldHVwSGFuZGxlcnMoKVxufVxuXG4vKipcbiAqIFJlZ2lzdGVyIGV2ZW50IGhhbmRsZXJzXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnNldHVwSGFuZGxlcnMgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHRoYXQgPSB0aGlzXG4gICAgLCBwcmV2RXZ0ID0gbnVsbCAvLyB1c2UgZm9yIHRvdWNoc3RhcnQgZXZlbnQgdG8gZGV0ZWN0IGRvdWJsZSB0YXBcbiAgICA7XG5cbiAgdGhpcy5ldmVudExpc3RlbmVycyA9IHtcbiAgICAvLyBNb3VzZSBkb3duIGdyb3VwXG4gICAgbW91c2Vkb3duOiBmdW5jdGlvbihldnQpIHtcbiAgICAgIHZhciByZXN1bHQgPSB0aGF0LmhhbmRsZU1vdXNlRG93bihldnQsIHByZXZFdnQpO1xuICAgICAgcHJldkV2dCA9IGV2dFxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICwgdG91Y2hzdGFydDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgICB2YXIgcmVzdWx0ID0gdGhhdC5oYW5kbGVNb3VzZURvd24oZXZ0LCBwcmV2RXZ0KTtcbiAgICAgIHByZXZFdnQgPSBldnRcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgLy8gTW91c2UgdXAgZ3JvdXBcbiAgLCBtb3VzZXVwOiBmdW5jdGlvbihldnQpIHtcbiAgICAgIHJldHVybiB0aGF0LmhhbmRsZU1vdXNlVXAoZXZ0KTtcbiAgICB9XG4gICwgdG91Y2hlbmQ6IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgcmV0dXJuIHRoYXQuaGFuZGxlTW91c2VVcChldnQpO1xuICAgIH1cblxuICAgIC8vIE1vdXNlIG1vdmUgZ3JvdXBcbiAgLCBtb3VzZW1vdmU6IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgcmV0dXJuIHRoYXQuaGFuZGxlTW91c2VNb3ZlKGV2dCk7XG4gICAgfVxuICAsIHRvdWNobW92ZTogZnVuY3Rpb24oZXZ0KSB7XG4gICAgICByZXR1cm4gdGhhdC5oYW5kbGVNb3VzZU1vdmUoZXZ0KTtcbiAgICB9XG5cbiAgICAvLyBNb3VzZSBsZWF2ZSBncm91cFxuICAsIG1vdXNlbGVhdmU6IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgcmV0dXJuIHRoYXQuaGFuZGxlTW91c2VVcChldnQpO1xuICAgIH1cbiAgLCB0b3VjaGxlYXZlOiBmdW5jdGlvbihldnQpIHtcbiAgICAgIHJldHVybiB0aGF0LmhhbmRsZU1vdXNlVXAoZXZ0KTtcbiAgICB9XG4gICwgdG91Y2hjYW5jZWw6IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgcmV0dXJuIHRoYXQuaGFuZGxlTW91c2VVcChldnQpO1xuICAgIH1cbiAgfVxuXG4gIC8vIEluaXQgY3VzdG9tIGV2ZW50cyBoYW5kbGVyIGlmIGF2YWlsYWJsZVxuICBpZiAodGhpcy5vcHRpb25zLmN1c3RvbUV2ZW50c0hhbmRsZXIgIT0gbnVsbCkgeyAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbiAgICB0aGlzLm9wdGlvbnMuY3VzdG9tRXZlbnRzSGFuZGxlci5pbml0KHtcbiAgICAgIHN2Z0VsZW1lbnQ6IHRoaXMuc3ZnXG4gICAgLCBldmVudHNMaXN0ZW5lckVsZW1lbnQ6IHRoaXMub3B0aW9ucy5ldmVudHNMaXN0ZW5lckVsZW1lbnRcbiAgICAsIGluc3RhbmNlOiB0aGlzLmdldFB1YmxpY0luc3RhbmNlKClcbiAgICB9KVxuXG4gICAgLy8gQ3VzdG9tIGV2ZW50IGhhbmRsZXIgbWF5IGhhbHQgYnVpbHRpbiBsaXN0ZW5lcnNcbiAgICB2YXIgaGFsdEV2ZW50TGlzdGVuZXJzID0gdGhpcy5vcHRpb25zLmN1c3RvbUV2ZW50c0hhbmRsZXIuaGFsdEV2ZW50TGlzdGVuZXJzXG4gICAgaWYgKGhhbHRFdmVudExpc3RlbmVycyAmJiBoYWx0RXZlbnRMaXN0ZW5lcnMubGVuZ3RoKSB7XG4gICAgICBmb3IgKHZhciBpID0gaGFsdEV2ZW50TGlzdGVuZXJzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgIGlmICh0aGlzLmV2ZW50TGlzdGVuZXJzLmhhc093blByb3BlcnR5KGhhbHRFdmVudExpc3RlbmVyc1tpXSkpIHtcbiAgICAgICAgICBkZWxldGUgdGhpcy5ldmVudExpc3RlbmVyc1toYWx0RXZlbnRMaXN0ZW5lcnNbaV1dXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBCaW5kIGV2ZW50TGlzdGVuZXJzXG4gICAgZm9yICh2YXIgZXZlbnQgaW4gdGhpcy5ldmVudExpc3RlbmVycykge1xuICAgICAgICBsZXQgdGFyZ2V0ID0gKHRoaXMub3B0aW9ucy5ldmVudHNMaXN0ZW5lckVsZW1lbnQgfHwgdGhpcy5zdmcpO1xuICAgICAgICAvLyBBdHRhY2ggZXZlbnQgdG8gZXZlbnRzTGlzdGVuZXJFbGVtZW50IG9yIFNWRyBpZiBub3QgYXZhaWxhYmxlXG4gICAgICAgIGlmIChldmVudCAhPT0gJ21vdXNlZG93bicpe1xuICAgICAgICAgICAgdGFyZ2V0ID0gZG9jdW1lbnQ7XG4gICAgICAgIH1cbiAgICAgICAgdGFyZ2V0LmFkZEV2ZW50TGlzdGVuZXIoZXZlbnQsIHRoaXMuZXZlbnRMaXN0ZW5lcnNbZXZlbnRdLCAhdGhpcy5vcHRpb25zLnByZXZlbnRNb3VzZUV2ZW50c0RlZmF1bHQgPyBwYXNzaXZlTGlzdGVuZXJPcHRpb24gOiBmYWxzZSlcbiAgICB9XG5cbiAgLy8gWm9vbSB1c2luZyBtb3VzZSB3aGVlbFxuICBpZiAodGhpcy5vcHRpb25zLm1vdXNlV2hlZWxab29tRW5hYmxlZCkge1xuICAgIHRoaXMub3B0aW9ucy5tb3VzZVdoZWVsWm9vbUVuYWJsZWQgPSBmYWxzZSAvLyBzZXQgdG8gZmFsc2UgYXMgZW5hYmxlIHdpbGwgc2V0IGl0IGJhY2sgdG8gdHJ1ZVxuICAgIHRoaXMuZW5hYmxlTW91c2VXaGVlbFpvb20oKVxuICB9XG59XG5cbi8qKlxuICogRW5hYmxlIGFiaWxpdHkgdG8gem9vbSB1c2luZyBtb3VzZSB3aGVlbFxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5lbmFibGVNb3VzZVdoZWVsWm9vbSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIXRoaXMub3B0aW9ucy5tb3VzZVdoZWVsWm9vbUVuYWJsZWQpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXNcblxuICAgIC8vIE1vdXNlIHdoZWVsIGxpc3RlbmVyXG4gICAgdGhpcy53aGVlbExpc3RlbmVyID0gZnVuY3Rpb24oZXZ0KSB7XG4gICAgICByZXR1cm4gdGhhdC5oYW5kbGVNb3VzZVdoZWVsKGV2dCk7XG4gICAgfVxuXG4gICAgLy8gQmluZCB3aGVlbExpc3RlbmVyXG4gICAgdmFyIGlzUGFzc2l2ZUxpc3RlbmVyID0gIXRoaXMub3B0aW9ucy5wcmV2ZW50TW91c2VFdmVudHNEZWZhdWx0XG4gICAgV2hlZWwub24odGhpcy5vcHRpb25zLmV2ZW50c0xpc3RlbmVyRWxlbWVudCB8fCB0aGlzLnN2ZywgdGhpcy53aGVlbExpc3RlbmVyLCBpc1Bhc3NpdmVMaXN0ZW5lcilcblxuICAgIHRoaXMub3B0aW9ucy5tb3VzZVdoZWVsWm9vbUVuYWJsZWQgPSB0cnVlXG4gIH1cbn1cblxuLyoqXG4gKiBEaXNhYmxlIGFiaWxpdHkgdG8gem9vbSB1c2luZyBtb3VzZSB3aGVlbFxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5kaXNhYmxlTW91c2VXaGVlbFpvb20gPSBmdW5jdGlvbigpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy5tb3VzZVdoZWVsWm9vbUVuYWJsZWQpIHtcbiAgICB2YXIgaXNQYXNzaXZlTGlzdGVuZXIgPSAhdGhpcy5vcHRpb25zLnByZXZlbnRNb3VzZUV2ZW50c0RlZmF1bHRcbiAgICBXaGVlbC5vZmYodGhpcy5vcHRpb25zLmV2ZW50c0xpc3RlbmVyRWxlbWVudCB8fCB0aGlzLnN2ZywgdGhpcy53aGVlbExpc3RlbmVyLCBpc1Bhc3NpdmVMaXN0ZW5lcilcbiAgICB0aGlzLm9wdGlvbnMubW91c2VXaGVlbFpvb21FbmFibGVkID0gZmFsc2VcbiAgfVxufVxuXG4vKipcbiAqIEhhbmRsZSBtb3VzZSB3aGVlbCBldmVudFxuICpcbiAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUuaGFuZGxlTW91c2VXaGVlbCA9IGZ1bmN0aW9uKGV2dCkge1xuICBpZiAoIXRoaXMub3B0aW9ucy56b29tRW5hYmxlZCB8fCB0aGlzLnN0YXRlICE9PSAnbm9uZScpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBpZiAodGhpcy5vcHRpb25zLnByZXZlbnRNb3VzZUV2ZW50c0RlZmF1bHQpe1xuICAgIGlmIChldnQucHJldmVudERlZmF1bHQpIHtcbiAgICAgIGV2dC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBldnQucmV0dXJuVmFsdWUgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvLyBEZWZhdWx0IGRlbHRhIGluIGNhc2UgdGhhdCBkZWx0YVkgaXMgbm90IGF2YWlsYWJsZVxuICB2YXIgZGVsdGEgPSBldnQuZGVsdGFZIHx8IDFcbiAgICAsIHRpbWVEZWx0YSA9IERhdGUubm93KCkgLSB0aGlzLmxhc3RNb3VzZVdoZWVsRXZlbnRUaW1lXG4gICAgLCBkaXZpZGVyID0gMyArIE1hdGgubWF4KDAsIDMwIC0gdGltZURlbHRhKVxuXG4gIC8vIFVwZGF0ZSBjYWNoZVxuICB0aGlzLmxhc3RNb3VzZVdoZWVsRXZlbnRUaW1lID0gRGF0ZS5ub3coKVxuXG4gIC8vIE1ha2UgZW1waXJpY2FsIGFkanVzdG1lbnRzIGZvciBicm93c2VycyB0aGF0IGdpdmUgZGVsdGFZIGluIHBpeGVscyAoZGVsdGFNb2RlPTApXG4gIGlmICgnZGVsdGFNb2RlJyBpbiBldnQgJiYgZXZ0LmRlbHRhTW9kZSA9PT0gMCAmJiBldnQud2hlZWxEZWx0YSkge1xuICAgIGRlbHRhID0gZXZ0LmRlbHRhWSA9PT0gMCA/IDAgOiAgTWF0aC5hYnMoZXZ0LndoZWVsRGVsdGEpIC8gZXZ0LmRlbHRhWVxuICB9XG5cbiAgZGVsdGEgPSAtMC4zIDwgZGVsdGEgJiYgZGVsdGEgPCAwLjMgPyBkZWx0YSA6IChkZWx0YSA+IDAgPyAxIDogLTEpICogTWF0aC5sb2coTWF0aC5hYnMoZGVsdGEpICsgMTApIC8gZGl2aWRlclxuXG4gIHZhciBpbnZlcnNlZFNjcmVlbkNUTSA9IHRoaXMuc3ZnLmdldFNjcmVlbkNUTSgpLmludmVyc2UoKVxuICAgICwgcmVsYXRpdmVNb3VzZVBvaW50ID0gU3ZnVXRpbHMuZ2V0RXZlbnRQb2ludChldnQsIHRoaXMuc3ZnKS5tYXRyaXhUcmFuc2Zvcm0oaW52ZXJzZWRTY3JlZW5DVE0pXG4gICAgLCB6b29tID0gTWF0aC5wb3coMSArIHRoaXMub3B0aW9ucy56b29tU2NhbGVTZW5zaXRpdml0eSwgKC0xKSAqIGRlbHRhKTsgLy8gbXVsdGlwbHlpbmcgYnkgbmVnLiAxIHNvIGFzIHRvIG1ha2Ugem9vbSBpbi9vdXQgYmVoYXZpb3IgbWF0Y2ggR29vZ2xlIG1hcHMgYmVoYXZpb3JcblxuICB0aGlzLnpvb21BdFBvaW50KHpvb20sIHJlbGF0aXZlTW91c2VQb2ludClcbn1cblxuLyoqXG4gKiBab29tIGluIGF0IGEgU1ZHIHBvaW50XG4gKlxuICogQHBhcmFtICB7U1ZHUG9pbnR9IHBvaW50XG4gKiBAcGFyYW0gIHtGbG9hdH0gem9vbVNjYWxlICAgIE51bWJlciByZXByZXNlbnRpbmcgaG93IG11Y2ggdG8gem9vbVxuICogQHBhcmFtICB7Qm9vbGVhbn0gem9vbUFic29sdXRlIERlZmF1bHQgZmFsc2UuIElmIHRydWUsIHpvb21TY2FsZSBpcyB0cmVhdGVkIGFzIGFuIGFic29sdXRlIHZhbHVlLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE90aGVyd2lzZSwgem9vbVNjYWxlIGlzIHRyZWF0ZWQgYXMgYSBtdWx0aXBsaWVkIChlLmcuIDEuMTAgd291bGQgem9vbSBpbiAxMCUpXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnpvb21BdFBvaW50ID0gZnVuY3Rpb24oem9vbVNjYWxlLCBwb2ludCwgem9vbUFic29sdXRlKSB7XG4gIHZhciBvcmlnaW5hbFN0YXRlID0gdGhpcy52aWV3cG9ydC5nZXRPcmlnaW5hbFN0YXRlKClcblxuICBpZiAoIXpvb21BYnNvbHV0ZSkge1xuICAgIC8vIEZpdCB6b29tU2NhbGUgaW4gc2V0IGJvdW5kc1xuICAgIGlmICh0aGlzLmdldFpvb20oKSAqIHpvb21TY2FsZSA8IHRoaXMub3B0aW9ucy5taW5ab29tICogb3JpZ2luYWxTdGF0ZS56b29tKSB7XG4gICAgICB6b29tU2NhbGUgPSAodGhpcy5vcHRpb25zLm1pblpvb20gKiBvcmlnaW5hbFN0YXRlLnpvb20pIC8gdGhpcy5nZXRab29tKClcbiAgICB9IGVsc2UgaWYgKHRoaXMuZ2V0Wm9vbSgpICogem9vbVNjYWxlID4gdGhpcy5vcHRpb25zLm1heFpvb20gKiBvcmlnaW5hbFN0YXRlLnpvb20pIHtcbiAgICAgIHpvb21TY2FsZSA9ICh0aGlzLm9wdGlvbnMubWF4Wm9vbSAqIG9yaWdpbmFsU3RhdGUuem9vbSkgLyB0aGlzLmdldFpvb20oKVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyBGaXQgem9vbVNjYWxlIGluIHNldCBib3VuZHNcbiAgICB6b29tU2NhbGUgPSBNYXRoLm1heCh0aGlzLm9wdGlvbnMubWluWm9vbSAqIG9yaWdpbmFsU3RhdGUuem9vbSwgTWF0aC5taW4odGhpcy5vcHRpb25zLm1heFpvb20gKiBvcmlnaW5hbFN0YXRlLnpvb20sIHpvb21TY2FsZSkpXG4gICAgLy8gRmluZCByZWxhdGl2ZSBzY2FsZSB0byBhY2hpZXZlIGRlc2lyZWQgc2NhbGVcbiAgICB6b29tU2NhbGUgPSB6b29tU2NhbGUvdGhpcy5nZXRab29tKClcbiAgfVxuXG4gIHZhciBvbGRDVE0gPSB0aGlzLnZpZXdwb3J0LmdldENUTSgpXG4gICAgLCByZWxhdGl2ZVBvaW50ID0gcG9pbnQubWF0cml4VHJhbnNmb3JtKG9sZENUTS5pbnZlcnNlKCkpXG4gICAgLCBtb2RpZmllciA9IHRoaXMuc3ZnLmNyZWF0ZVNWR01hdHJpeCgpLnRyYW5zbGF0ZShyZWxhdGl2ZVBvaW50LngsIHJlbGF0aXZlUG9pbnQueSkuc2NhbGUoem9vbVNjYWxlKS50cmFuc2xhdGUoLXJlbGF0aXZlUG9pbnQueCwgLXJlbGF0aXZlUG9pbnQueSlcbiAgICAsIG5ld0NUTSA9IG9sZENUTS5tdWx0aXBseShtb2RpZmllcilcblxuICBpZiAobmV3Q1RNLmEgIT09IG9sZENUTS5hKSB7XG4gICAgdGhpcy52aWV3cG9ydC5zZXRDVE0obmV3Q1RNKVxuICB9XG59XG5cbi8qKlxuICogWm9vbSBhdCBjZW50ZXIgcG9pbnRcbiAqXG4gKiBAcGFyYW0gIHtGbG9hdH0gc2NhbGVcbiAqIEBwYXJhbSAge0Jvb2xlYW59IGFic29sdXRlIE1hcmtzIHpvb20gc2NhbGUgYXMgcmVsYXRpdmUgb3IgYWJzb2x1dGVcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUuem9vbSA9IGZ1bmN0aW9uKHNjYWxlLCBhYnNvbHV0ZSkge1xuICB0aGlzLnpvb21BdFBvaW50KHNjYWxlLCBTdmdVdGlscy5nZXRTdmdDZW50ZXJQb2ludCh0aGlzLnN2ZywgdGhpcy53aWR0aCwgdGhpcy5oZWlnaHQpLCBhYnNvbHV0ZSlcbn1cblxuLyoqXG4gKiBab29tIHVzZWQgYnkgcHVibGljIGluc3RhbmNlXG4gKlxuICogQHBhcmFtICB7RmxvYXR9IHNjYWxlXG4gKiBAcGFyYW0gIHtCb29sZWFufSBhYnNvbHV0ZSBNYXJrcyB6b29tIHNjYWxlIGFzIHJlbGF0aXZlIG9yIGFic29sdXRlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnB1YmxpY1pvb20gPSBmdW5jdGlvbihzY2FsZSwgYWJzb2x1dGUpIHtcbiAgaWYgKGFic29sdXRlKSB7XG4gICAgc2NhbGUgPSB0aGlzLmNvbXB1dGVGcm9tUmVsYXRpdmVab29tKHNjYWxlKVxuICB9XG5cbiAgdGhpcy56b29tKHNjYWxlLCBhYnNvbHV0ZSlcbn1cblxuLyoqXG4gKiBab29tIGF0IHBvaW50IHVzZWQgYnkgcHVibGljIGluc3RhbmNlXG4gKlxuICogQHBhcmFtICB7RmxvYXR9IHNjYWxlXG4gKiBAcGFyYW0gIHtTVkdQb2ludHxPYmplY3R9IHBvaW50ICAgIEFuIG9iamVjdCB0aGF0IGhhcyB4IGFuZCB5IGF0dHJpYnV0ZXNcbiAqIEBwYXJhbSAge0Jvb2xlYW59IGFic29sdXRlIE1hcmtzIHpvb20gc2NhbGUgYXMgcmVsYXRpdmUgb3IgYWJzb2x1dGVcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUucHVibGljWm9vbUF0UG9pbnQgPSBmdW5jdGlvbihzY2FsZSwgcG9pbnQsIGFic29sdXRlKSB7XG4gIGlmIChhYnNvbHV0ZSkge1xuICAgIC8vIFRyYW5zZm9ybSB6b29tIGludG8gYSByZWxhdGl2ZSB2YWx1ZVxuICAgIHNjYWxlID0gdGhpcy5jb21wdXRlRnJvbVJlbGF0aXZlWm9vbShzY2FsZSlcbiAgfVxuXG4gIC8vIElmIG5vdCBhIFNWR1BvaW50IGJ1dCBoYXMgeCBhbmQgeSB0aGVuIGNyZWF0ZSBhIFNWR1BvaW50XG4gIGlmIChVdGlscy5nZXRUeXBlKHBvaW50KSAhPT0gJ1NWR1BvaW50Jykge1xuICAgIGlmKCd4JyBpbiBwb2ludCAmJiAneScgaW4gcG9pbnQpIHtcbiAgICAgIHBvaW50ID0gU3ZnVXRpbHMuY3JlYXRlU1ZHUG9pbnQodGhpcy5zdmcsIHBvaW50LngsIHBvaW50LnkpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignR2l2ZW4gcG9pbnQgaXMgaW52YWxpZCcpXG4gICAgfVxuICB9XG5cbiAgdGhpcy56b29tQXRQb2ludChzY2FsZSwgcG9pbnQsIGFic29sdXRlKVxufVxuXG4vKipcbiAqIEdldCB6b29tIHNjYWxlXG4gKlxuICogQHJldHVybiB7RmxvYXR9IHpvb20gc2NhbGVcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUuZ2V0Wm9vbSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy52aWV3cG9ydC5nZXRab29tKClcbn1cblxuLyoqXG4gKiBHZXQgem9vbSBzY2FsZSBmb3IgcHVibGljIHVzYWdlXG4gKlxuICogQHJldHVybiB7RmxvYXR9IHpvb20gc2NhbGVcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUuZ2V0UmVsYXRpdmVab29tID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnZpZXdwb3J0LmdldFJlbGF0aXZlWm9vbSgpXG59XG5cbi8qKlxuICogQ29tcHV0ZSBhY3R1YWwgem9vbSBmcm9tIHB1YmxpYyB6b29tXG4gKlxuICogQHBhcmFtICB7RmxvYXR9IHpvb21cbiAqIEByZXR1cm4ge0Zsb2F0fSB6b29tIHNjYWxlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmNvbXB1dGVGcm9tUmVsYXRpdmVab29tID0gZnVuY3Rpb24oem9vbSkge1xuICByZXR1cm4gem9vbSAqIHRoaXMudmlld3BvcnQuZ2V0T3JpZ2luYWxTdGF0ZSgpLnpvb21cbn1cblxuLyoqXG4gKiBTZXQgem9vbSB0byBpbml0aWFsIHN0YXRlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnJlc2V0Wm9vbSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgb3JpZ2luYWxTdGF0ZSA9IHRoaXMudmlld3BvcnQuZ2V0T3JpZ2luYWxTdGF0ZSgpXG5cbiAgdGhpcy56b29tKG9yaWdpbmFsU3RhdGUuem9vbSwgdHJ1ZSk7XG59XG5cbi8qKlxuICogU2V0IHBhbiB0byBpbml0aWFsIHN0YXRlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnJlc2V0UGFuID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGFuKHRoaXMudmlld3BvcnQuZ2V0T3JpZ2luYWxTdGF0ZSgpKTtcbn1cblxuLyoqXG4gKiBTZXQgcGFuIGFuZCB6b29tIHRvIGluaXRpYWwgc3RhdGVcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUucmVzZXQgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5yZXNldFpvb20oKVxuICB0aGlzLnJlc2V0UGFuKClcbn1cblxuLyoqXG4gKiBIYW5kbGUgZG91YmxlIGNsaWNrIGV2ZW50XG4gKiBTZWUgaGFuZGxlTW91c2VEb3duKCkgZm9yIGFsdGVybmF0ZSBkZXRlY3Rpb24gbWV0aG9kXG4gKlxuICogQHBhcmFtIHtFdmVudH0gZXZ0XG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmhhbmRsZURibENsaWNrID0gZnVuY3Rpb24oZXZ0KSB7XG4gIGlmICh0aGlzLm9wdGlvbnMucHJldmVudE1vdXNlRXZlbnRzRGVmYXVsdCkge1xuICAgIGlmIChldnQucHJldmVudERlZmF1bHQpIHtcbiAgICAgIGV2dC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIGV2dC5yZXR1cm5WYWx1ZSA9IGZhbHNlXG4gICAgfVxuICB9XG5cbiAgLy8gQ2hlY2sgaWYgdGFyZ2V0IHdhcyBhIGNvbnRyb2wgYnV0dG9uXG4gIGlmICh0aGlzLm9wdGlvbnMuY29udHJvbEljb25zRW5hYmxlZCkge1xuICAgIHZhciB0YXJnZXRDbGFzcyA9IGV2dC50YXJnZXQuZ2V0QXR0cmlidXRlKCdjbGFzcycpIHx8ICcnXG4gICAgaWYgKHRhcmdldENsYXNzLmluZGV4T2YoJ3N2Zy1wYW4tem9vbS1jb250cm9sJykgPiAtMSkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICB9XG5cbiAgdmFyIHpvb21GYWN0b3JcblxuICBpZiAoZXZ0LnNoaWZ0S2V5KSB7XG4gICAgem9vbUZhY3RvciA9IDEvKCgxICsgdGhpcy5vcHRpb25zLnpvb21TY2FsZVNlbnNpdGl2aXR5KSAqIDIpIC8vIHpvb20gb3V0IHdoZW4gc2hpZnQga2V5IHByZXNzZWRcbiAgfSBlbHNlIHtcbiAgICB6b29tRmFjdG9yID0gKDEgKyB0aGlzLm9wdGlvbnMuem9vbVNjYWxlU2Vuc2l0aXZpdHkpICogMlxuICB9XG5cbiAgdmFyIHBvaW50ID0gU3ZnVXRpbHMuZ2V0RXZlbnRQb2ludChldnQsIHRoaXMuc3ZnKS5tYXRyaXhUcmFuc2Zvcm0odGhpcy5zdmcuZ2V0U2NyZWVuQ1RNKCkuaW52ZXJzZSgpKVxuICB0aGlzLnpvb21BdFBvaW50KHpvb21GYWN0b3IsIHBvaW50KVxufVxuXG4vKipcbiAqIEhhbmRsZSBjbGljayBldmVudFxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2dFxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5oYW5kbGVNb3VzZURvd24gPSBmdW5jdGlvbihldnQsIHByZXZFdnQpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy5wcmV2ZW50TW91c2VFdmVudHNEZWZhdWx0KSB7XG4gICAgICBpZiAoZXZ0LnByZXZlbnREZWZhdWx0KSB7XG4gICAgICBldnQucHJldmVudERlZmF1bHQoKVxuICAgIH0gZWxzZSB7XG4gICAgICBldnQucmV0dXJuVmFsdWUgPSBmYWxzZVxuICAgIH1cbiAgfVxuXG4gIFV0aWxzLm1vdXNlQW5kVG91Y2hOb3JtYWxpemUoZXZ0LCB0aGlzLnN2ZylcblxuICAvLyBEb3VibGUgY2xpY2sgZGV0ZWN0aW9uOyBtb3JlIGNvbnNpc3RlbnQgdGhhbiBvbmRibGNsaWNrXG4gIGlmICh0aGlzLm9wdGlvbnMuZGJsQ2xpY2tab29tRW5hYmxlZCAmJiBVdGlscy5pc0RibENsaWNrKGV2dCwgcHJldkV2dCkpe1xuICAgIHRoaXMuaGFuZGxlRGJsQ2xpY2soZXZ0KVxuICB9IGVsc2Uge1xuICAgIC8vIFBhbiBtb2RlXG4gICAgdGhpcy5zdGF0ZSA9ICdwYW4nXG4gICAgdGhpcy5maXJzdEV2ZW50Q1RNID0gdGhpcy52aWV3cG9ydC5nZXRDVE0oKVxuICAgIHRoaXMuc3RhdGVPcmlnaW4gPSBTdmdVdGlscy5nZXRFdmVudFBvaW50KGV2dCwgdGhpcy5zdmcpLm1hdHJpeFRyYW5zZm9ybSh0aGlzLmZpcnN0RXZlbnRDVE0uaW52ZXJzZSgpKVxuICB9XG59XG5cbi8qKlxuICogSGFuZGxlIG1vdXNlIG1vdmUgZXZlbnRcbiAqXG4gKiBAcGFyYW0gIHtFdmVudH0gZXZ0XG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmhhbmRsZU1vdXNlTW92ZSA9IGZ1bmN0aW9uKGV2dCkge1xuICBpZiAodGhpcy5vcHRpb25zLnByZXZlbnRNb3VzZUV2ZW50c0RlZmF1bHQpIHtcbiAgICBpZiAoZXZ0LnByZXZlbnREZWZhdWx0KSB7XG4gICAgICBldnQucHJldmVudERlZmF1bHQoKVxuICAgIH0gZWxzZSB7XG4gICAgICBldnQucmV0dXJuVmFsdWUgPSBmYWxzZVxuICAgIH1cbiAgfVxuXG4gICAgaWYgKHRoaXMuc3RhdGUgPT09ICdwYW4nICYmIHRoaXMub3B0aW9ucy5wYW5FbmFibGVkKSB7XG4gICAgICAgIHRoaXMuc3ZnLmNsYXNzTGlzdC5hZGQoJy0tZHJhZ2dpbmcnKTtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnN2Zy5jbGFzc0xpc3QuYWRkKCctLWRlbGF5LWRyYWdnaW5nJyk7XG4gICAgICAgIH0sIDE1MClcbiAgICAvLyBQYW4gbW9kZVxuICAgIHZhciBwb2ludCA9IFN2Z1V0aWxzLmdldEV2ZW50UG9pbnQoZXZ0LCB0aGlzLnN2ZykubWF0cml4VHJhbnNmb3JtKHRoaXMuZmlyc3RFdmVudENUTS5pbnZlcnNlKCkpXG4gICAgICAsIHZpZXdwb3J0Q1RNID0gdGhpcy5maXJzdEV2ZW50Q1RNLnRyYW5zbGF0ZShwb2ludC54IC0gdGhpcy5zdGF0ZU9yaWdpbi54LCBwb2ludC55IC0gdGhpcy5zdGF0ZU9yaWdpbi55KVxuXG4gICAgdGhpcy52aWV3cG9ydC5zZXRDVE0odmlld3BvcnRDVE0pXG4gIH1cbn1cblxuLyoqXG4gKiBIYW5kbGUgbW91c2UgYnV0dG9uIHJlbGVhc2UgZXZlbnRcbiAqXG4gKiBAcGFyYW0ge0V2ZW50fSBldnRcbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUuaGFuZGxlTW91c2VVcCA9IGZ1bmN0aW9uKGV2dCkge1xuICAgIGlmICh0aGlzLm9wdGlvbnMucHJldmVudE1vdXNlRXZlbnRzRGVmYXVsdCkge1xuICAgICAgaWYgKGV2dC5wcmV2ZW50RGVmYXVsdCkge1xuICAgICAgICBldnQucHJldmVudERlZmF1bHQoKVxuICAgIH0gZWxzZSB7XG4gICAgICAgIGV2dC5yZXR1cm5WYWx1ZSA9IGZhbHNlXG4gICAgfVxuICB9XG5cbiAgaWYgKHRoaXMuc3RhdGUgPT09ICdwYW4nKSB7XG4gICAgICB0aGlzLnN2Zy5jbGFzc0xpc3QucmVtb3ZlKCctLWRyYWdnaW5nJyk7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICB0aGlzLnN2Zy5jbGFzc0xpc3QucmVtb3ZlKCctLWRlbGF5LWRyYWdnaW5nJyk7XG4gICAgICB9LCAyMDApXG4gICAgICAvLyBRdWl0IHBhbiBtb2RlXG4gICAgdGhpcy5zdGF0ZSA9ICdub25lJ1xuICB9XG59XG5cbi8qKlxuICogQWRqdXN0IHZpZXdwb3J0IHNpemUgKG9ubHkpIHNvIGl0IHdpbGwgZml0IGluIFNWR1xuICogRG9lcyBub3QgY2VudGVyIGltYWdlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmZpdCA9IGZ1bmN0aW9uKCkge1xuICB2YXIgdmlld0JveCA9IHRoaXMudmlld3BvcnQuZ2V0Vmlld0JveCgpXG4gICAgLCBuZXdTY2FsZSA9IE1hdGgubWluKHRoaXMud2lkdGgvdmlld0JveC53aWR0aCwgdGhpcy5oZWlnaHQvdmlld0JveC5oZWlnaHQpXG5cbiAgdGhpcy56b29tKG5ld1NjYWxlLCB0cnVlKVxufVxuXG4vKipcbiAqIEFkanVzdCB2aWV3cG9ydCBzaXplIChvbmx5KSBzbyBpdCB3aWxsIGNvbnRhaW4gdGhlIFNWR1xuICogRG9lcyBub3QgY2VudGVyIGltYWdlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmNvbnRhaW4gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHZpZXdCb3ggPSB0aGlzLnZpZXdwb3J0LmdldFZpZXdCb3goKVxuICAgICwgbmV3U2NhbGUgPSBNYXRoLm1heCh0aGlzLndpZHRoL3ZpZXdCb3gud2lkdGgsIHRoaXMuaGVpZ2h0L3ZpZXdCb3guaGVpZ2h0KVxuXG4gIHRoaXMuem9vbShuZXdTY2FsZSwgdHJ1ZSlcbn1cblxuLyoqXG4gKiBBZGp1c3Qgdmlld3BvcnQgcGFuIChvbmx5KSBzbyBpdCB3aWxsIGJlIGNlbnRlcmVkIGluIFNWR1xuICogRG9lcyBub3Qgem9vbS9maXQvY29udGFpbiBpbWFnZVxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5jZW50ZXIgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHZpZXdCb3ggPSB0aGlzLnZpZXdwb3J0LmdldFZpZXdCb3goKVxuICAgICwgb2Zmc2V0WCA9ICh0aGlzLndpZHRoIC0gKHZpZXdCb3gud2lkdGggKyB2aWV3Qm94LnggKiAyKSAqIHRoaXMuZ2V0Wm9vbSgpKSAqIDAuNVxuICAgICwgb2Zmc2V0WSA9ICh0aGlzLmhlaWdodCAtICh2aWV3Qm94LmhlaWdodCArIHZpZXdCb3gueSAqIDIpICogdGhpcy5nZXRab29tKCkpICogMC41XG5cbiAgdGhpcy5nZXRQdWJsaWNJbnN0YW5jZSgpLnBhbih7eDogb2Zmc2V0WCwgeTogb2Zmc2V0WX0pXG59XG5cbi8qKlxuICogVXBkYXRlIGNvbnRlbnQgY2FjaGVkIEJvcmRlckJveFxuICogVXNlIHdoZW4gdmlld3BvcnQgY29udGVudHMgY2hhbmdlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnVwZGF0ZUJCb3ggPSBmdW5jdGlvbigpIHtcbiAgdGhpcy52aWV3cG9ydC5zaW1wbGVWaWV3Qm94Q2FjaGUoKVxufVxuXG4vKipcbiAqIFBhbiB0byBhIHJlbmRlcmVkIHBvc2l0aW9uXG4gKlxuICogQHBhcmFtICB7T2JqZWN0fSBwb2ludCB7eDogMCwgeTogMH1cbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUucGFuID0gZnVuY3Rpb24ocG9pbnQpIHtcbiAgdmFyIHZpZXdwb3J0Q1RNID0gdGhpcy52aWV3cG9ydC5nZXRDVE0oKVxuICB2aWV3cG9ydENUTS5lID0gcG9pbnQueFxuICB2aWV3cG9ydENUTS5mID0gcG9pbnQueVxuICB0aGlzLnZpZXdwb3J0LnNldENUTSh2aWV3cG9ydENUTSlcbn1cblxuLyoqXG4gKiBSZWxhdGl2ZWx5IHBhbiB0aGUgZ3JhcGggYnkgYSBzcGVjaWZpZWQgcmVuZGVyZWQgcG9zaXRpb24gdmVjdG9yXG4gKlxuICogQHBhcmFtICB7T2JqZWN0fSBwb2ludCB7eDogMCwgeTogMH1cbiAqL1xuU3ZnUGFuWm9vbS5wcm90b3R5cGUucGFuQnkgPSBmdW5jdGlvbihwb2ludCkge1xuICB2YXIgdmlld3BvcnRDVE0gPSB0aGlzLnZpZXdwb3J0LmdldENUTSgpXG4gIHZpZXdwb3J0Q1RNLmUgKz0gcG9pbnQueFxuICB2aWV3cG9ydENUTS5mICs9IHBvaW50LnlcbiAgdGhpcy52aWV3cG9ydC5zZXRDVE0odmlld3BvcnRDVE0pXG59XG5cbi8qKlxuICogR2V0IHBhbiB2ZWN0b3JcbiAqXG4gKiBAcmV0dXJuIHtPYmplY3R9IHt4OiAwLCB5OiAwfVxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5nZXRQYW4gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHN0YXRlID0gdGhpcy52aWV3cG9ydC5nZXRTdGF0ZSgpXG5cbiAgcmV0dXJuIHt4OiBzdGF0ZS54LCB5OiBzdGF0ZS55fVxufVxuXG4vKipcbiAqIFJlY2FsY3VsYXRlcyBjYWNoZWQgc3ZnIGRpbWVuc2lvbnMgYW5kIGNvbnRyb2xzIHBvc2l0aW9uXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLnJlc2l6ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBHZXQgZGltZW5zaW9uc1xuICB2YXIgYm91bmRpbmdDbGllbnRSZWN0Tm9ybWFsaXplZCA9IFN2Z1V0aWxzLmdldEJvdW5kaW5nQ2xpZW50UmVjdE5vcm1hbGl6ZWQodGhpcy5zdmcpXG4gIHRoaXMud2lkdGggPSBib3VuZGluZ0NsaWVudFJlY3ROb3JtYWxpemVkLndpZHRoXG4gIHRoaXMuaGVpZ2h0ID0gYm91bmRpbmdDbGllbnRSZWN0Tm9ybWFsaXplZC5oZWlnaHRcblxuICAvLyBSZWNhbGN1bGF0ZSBvcmlnaW5hbCBzdGF0ZVxuICB2YXIgdmlld3BvcnQgPSB0aGlzLnZpZXdwb3J0XG4gIHZpZXdwb3J0Lm9wdGlvbnMud2lkdGggPSB0aGlzLndpZHRoXG4gIHZpZXdwb3J0Lm9wdGlvbnMuaGVpZ2h0ID0gdGhpcy5oZWlnaHRcbiAgdmlld3BvcnQucHJvY2Vzc0NUTSgpXG5cbiAgLy8gUmVwb3NpdGlvbiBjb250cm9sIGljb25zIGJ5IHJlLWVuYWJsaW5nIHRoZW1cbiAgaWYgKHRoaXMub3B0aW9ucy5jb250cm9sSWNvbnNFbmFibGVkKSB7XG4gICAgdGhpcy5nZXRQdWJsaWNJbnN0YW5jZSgpLmRpc2FibGVDb250cm9sSWNvbnMoKVxuICAgIHRoaXMuZ2V0UHVibGljSW5zdGFuY2UoKS5lbmFibGVDb250cm9sSWNvbnMoKVxuICB9XG59XG5cbi8qKlxuICogVW5iaW5kIG1vdXNlIGV2ZW50cywgZnJlZSBjYWxsYmFja3MgYW5kIGRlc3Ryb3kgcHVibGljIGluc3RhbmNlXG4gKi9cblN2Z1Bhblpvb20ucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHRoYXQgPSB0aGlzXG5cbiAgLy8gRnJlZSBjYWxsYmFja3NcbiAgdGhpcy5iZWZvcmVab29tID0gbnVsbFxuICB0aGlzLm9uWm9vbSA9IG51bGxcbiAgdGhpcy5iZWZvcmVQYW4gPSBudWxsXG4gIHRoaXMub25QYW4gPSBudWxsXG4gIHRoaXMub25VcGRhdGVkQ1RNID0gbnVsbFxuXG4gIC8vIERlc3Ryb3kgY3VzdG9tIGV2ZW50IGhhbmRsZXJzXG4gIGlmICh0aGlzLm9wdGlvbnMuY3VzdG9tRXZlbnRzSGFuZGxlciAhPSBudWxsKSB7IC8vIGpzaGludCBpZ25vcmU6bGluZVxuICAgIHRoaXMub3B0aW9ucy5jdXN0b21FdmVudHNIYW5kbGVyLmRlc3Ryb3koe1xuICAgICAgc3ZnRWxlbWVudDogdGhpcy5zdmdcbiAgICAsIGV2ZW50c0xpc3RlbmVyRWxlbWVudDogdGhpcy5vcHRpb25zLmV2ZW50c0xpc3RlbmVyRWxlbWVudFxuICAgICwgaW5zdGFuY2U6IHRoaXMuZ2V0UHVibGljSW5zdGFuY2UoKVxuICAgIH0pXG4gIH1cblxuICAvLyBVbmJpbmQgZXZlbnRMaXN0ZW5lcnNcbiAgZm9yICh2YXIgZXZlbnQgaW4gdGhpcy5ldmVudExpc3RlbmVycykge1xuICAgICAgbGV0IHRhcmdldCA9ICh0aGlzLm9wdGlvbnMuZXZlbnRzTGlzdGVuZXJFbGVtZW50IHx8IHRoaXMuc3ZnKTtcbiAgICAgIGlmIChldmVudCAhPT0gJ21vdXNlZG93bicpe1xuICAgICAgICAgIHRhcmdldCA9IGRvY3VtZW50O1xuICAgICAgfVxuXG4gICAgICB0YXJnZXQucmVtb3ZlRXZlbnRMaXN0ZW5lcihldmVudCwgdGhpcy5ldmVudExpc3RlbmVyc1tldmVudF0sICF0aGlzLm9wdGlvbnMucHJldmVudE1vdXNlRXZlbnRzRGVmYXVsdCA/IHBhc3NpdmVMaXN0ZW5lck9wdGlvbiA6IGZhbHNlKVxuICB9XG5cbiAgLy8gVW5iaW5kIHdoZWVsTGlzdGVuZXJcbiAgdGhpcy5kaXNhYmxlTW91c2VXaGVlbFpvb20oKVxuXG4gIC8vIFJlbW92ZSBjb250cm9sIGljb25zXG4gIHRoaXMuZ2V0UHVibGljSW5zdGFuY2UoKS5kaXNhYmxlQ29udHJvbEljb25zKClcblxuICAvLyBSZXNldCB6b29tIGFuZCBwYW5cbiAgdGhpcy5yZXNldCgpXG5cbiAgLy8gUmVtb3ZlIGluc3RhbmNlIGZyb20gaW5zdGFuY2VzU3RvcmVcbiAgaW5zdGFuY2VzU3RvcmUgPSBpbnN0YW5jZXNTdG9yZS5maWx0ZXIoZnVuY3Rpb24oaW5zdGFuY2Upe1xuICAgIHJldHVybiBpbnN0YW5jZS5zdmcgIT09IHRoYXQuc3ZnXG4gIH0pXG5cbiAgLy8gRGVsZXRlIG9wdGlvbnMgYW5kIGl0cyBjb250ZW50c1xuICBkZWxldGUgdGhpcy5vcHRpb25zXG5cbiAgLy8gRGVsZXRlIHZpZXdwb3J0IHRvIG1ha2UgcHVibGljIHNoYWRvdyB2aWV3cG9ydCBmdW5jdGlvbnMgdW5jYWxsYWJsZVxuICBkZWxldGUgdGhpcy52aWV3cG9ydFxuXG4gIC8vIERlc3Ryb3kgcHVibGljIGluc3RhbmNlIGFuZCByZXdyaXRlIGdldFB1YmxpY0luc3RhbmNlXG4gIGRlbGV0ZSB0aGlzLnB1YmxpY0luc3RhbmNlXG4gIGRlbGV0ZSB0aGlzLnBpXG4gIHRoaXMuZ2V0UHVibGljSW5zdGFuY2UgPSBmdW5jdGlvbigpe1xuICAgIHJldHVybiBudWxsXG4gIH1cbn1cblxuLyoqXG4gKiBSZXR1cm5zIGEgcHVibGljIGluc3RhbmNlIG9iamVjdFxuICpcbiAqIEByZXR1cm4ge09iamVjdH0gUHVibGljIGluc3RhbmNlIG9iamVjdFxuICovXG5TdmdQYW5ab29tLnByb3RvdHlwZS5nZXRQdWJsaWNJbnN0YW5jZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgdGhhdCA9IHRoaXNcblxuICAvLyBDcmVhdGUgY2FjaGVcbiAgaWYgKCF0aGlzLnB1YmxpY0luc3RhbmNlKSB7XG4gICAgdGhpcy5wdWJsaWNJbnN0YW5jZSA9IHRoaXMucGkgPSB7XG4gICAgICAvLyBQYW5cbiAgICAgIGVuYWJsZVBhbjogZnVuY3Rpb24oKSB7dGhhdC5vcHRpb25zLnBhbkVuYWJsZWQgPSB0cnVlOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIGRpc2FibGVQYW46IGZ1bmN0aW9uKCkge3RoYXQub3B0aW9ucy5wYW5FbmFibGVkID0gZmFsc2U7IHJldHVybiB0aGF0LnBpfVxuICAgICwgaXNQYW5FbmFibGVkOiBmdW5jdGlvbigpIHtyZXR1cm4gISF0aGF0Lm9wdGlvbnMucGFuRW5hYmxlZH1cbiAgICAsIHBhbjogZnVuY3Rpb24ocG9pbnQpIHt0aGF0LnBhbihwb2ludCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgcGFuQnk6IGZ1bmN0aW9uKHBvaW50KSB7dGhhdC5wYW5CeShwb2ludCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgZ2V0UGFuOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhhdC5nZXRQYW4oKX1cbiAgICAgIC8vIFBhbiBldmVudFxuICAgICwgc2V0QmVmb3JlUGFuOiBmdW5jdGlvbihmbikge3RoYXQub3B0aW9ucy5iZWZvcmVQYW4gPSBmbiA9PT0gbnVsbCA/IG51bGwgOiBVdGlscy5wcm94eShmbiwgdGhhdC5wdWJsaWNJbnN0YW5jZSk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgc2V0T25QYW46IGZ1bmN0aW9uKGZuKSB7dGhhdC5vcHRpb25zLm9uUGFuID0gZm4gPT09IG51bGwgPyBudWxsIDogVXRpbHMucHJveHkoZm4sIHRoYXQucHVibGljSW5zdGFuY2UpOyByZXR1cm4gdGhhdC5waX1cbiAgICAgIC8vIFpvb20gYW5kIENvbnRyb2wgSWNvbnNcbiAgICAsIGVuYWJsZVpvb206IGZ1bmN0aW9uKCkge3RoYXQub3B0aW9ucy56b29tRW5hYmxlZCA9IHRydWU7IHJldHVybiB0aGF0LnBpfVxuICAgICwgZGlzYWJsZVpvb206IGZ1bmN0aW9uKCkge3RoYXQub3B0aW9ucy56b29tRW5hYmxlZCA9IGZhbHNlOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIGlzWm9vbUVuYWJsZWQ6IGZ1bmN0aW9uKCkge3JldHVybiAhIXRoYXQub3B0aW9ucy56b29tRW5hYmxlZH1cbiAgICAsIGVuYWJsZUNvbnRyb2xJY29uczogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICghdGhhdC5vcHRpb25zLmNvbnRyb2xJY29uc0VuYWJsZWQpIHtcbiAgICAgICAgICB0aGF0Lm9wdGlvbnMuY29udHJvbEljb25zRW5hYmxlZCA9IHRydWVcbiAgICAgICAgICBDb250cm9sSWNvbnMuZW5hYmxlKHRoYXQpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoYXQucGlcbiAgICAgIH1cbiAgICAsIGRpc2FibGVDb250cm9sSWNvbnM6IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAodGhhdC5vcHRpb25zLmNvbnRyb2xJY29uc0VuYWJsZWQpIHtcbiAgICAgICAgICB0aGF0Lm9wdGlvbnMuY29udHJvbEljb25zRW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICAgIENvbnRyb2xJY29ucy5kaXNhYmxlKHRoYXQpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoYXQucGlcbiAgICAgIH1cbiAgICAsIGlzQ29udHJvbEljb25zRW5hYmxlZDogZnVuY3Rpb24oKSB7cmV0dXJuICEhdGhhdC5vcHRpb25zLmNvbnRyb2xJY29uc0VuYWJsZWR9XG4gICAgICAvLyBEb3VibGUgY2xpY2sgem9vbVxuICAgICwgZW5hYmxlRGJsQ2xpY2tab29tOiBmdW5jdGlvbigpIHt0aGF0Lm9wdGlvbnMuZGJsQ2xpY2tab29tRW5hYmxlZCA9IHRydWU7IHJldHVybiB0aGF0LnBpfVxuICAgICwgZGlzYWJsZURibENsaWNrWm9vbTogZnVuY3Rpb24oKSB7dGhhdC5vcHRpb25zLmRibENsaWNrWm9vbUVuYWJsZWQgPSBmYWxzZTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCBpc0RibENsaWNrWm9vbUVuYWJsZWQ6IGZ1bmN0aW9uKCkge3JldHVybiAhIXRoYXQub3B0aW9ucy5kYmxDbGlja1pvb21FbmFibGVkfVxuICAgICAgLy8gTW91c2Ugd2hlZWwgem9vbVxuICAgICwgZW5hYmxlTW91c2VXaGVlbFpvb206IGZ1bmN0aW9uKCkge3RoYXQuZW5hYmxlTW91c2VXaGVlbFpvb20oKTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCBkaXNhYmxlTW91c2VXaGVlbFpvb206IGZ1bmN0aW9uKCkge3RoYXQuZGlzYWJsZU1vdXNlV2hlZWxab29tKCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgaXNNb3VzZVdoZWVsWm9vbUVuYWJsZWQ6IGZ1bmN0aW9uKCkge3JldHVybiAhIXRoYXQub3B0aW9ucy5tb3VzZVdoZWVsWm9vbUVuYWJsZWR9XG4gICAgICAvLyBab29tIHNjYWxlIGFuZCBib3VuZHNcbiAgICAsIHNldFpvb21TY2FsZVNlbnNpdGl2aXR5OiBmdW5jdGlvbihzY2FsZSkge3RoYXQub3B0aW9ucy56b29tU2NhbGVTZW5zaXRpdml0eSA9IHNjYWxlOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIHNldE1pblpvb206IGZ1bmN0aW9uKHpvb20pIHt0aGF0Lm9wdGlvbnMubWluWm9vbSA9IHpvb207IHJldHVybiB0aGF0LnBpfVxuICAgICwgc2V0TWF4Wm9vbTogZnVuY3Rpb24oem9vbSkge3RoYXQub3B0aW9ucy5tYXhab29tID0gem9vbTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCBnZXRNaW5ab29tOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhhdC5vcHRpb25zLm1pblpvb219XG4gICAgLCBnZXRNYXhab29tOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhhdC5vcHRpb25zLm1heFpvb219XG4gICAgLCBnZXRTdmc6IGZ1bmN0aW9uKCkge3JldHVybiB0aGF0LnN2Z31cbiAgICAsIGludmFsaWRhdGVTaXplOiBmdW5jdGlvbigpIHt0aGF0LnJlc2l6ZSgpOyB0aGF0LmZpdCgpOyB0aGF0LmNlbnRlcigpO31cbiAgICAgIC8vIFpvb20gZXZlbnRcbiAgICAsIHNldEJlZm9yZVpvb206IGZ1bmN0aW9uKGZuKSB7dGhhdC5vcHRpb25zLmJlZm9yZVpvb20gPSBmbiA9PT0gbnVsbCA/IG51bGwgOiBVdGlscy5wcm94eShmbiwgdGhhdC5wdWJsaWNJbnN0YW5jZSk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgc2V0T25ab29tOiBmdW5jdGlvbihmbikge3RoYXQub3B0aW9ucy5vblpvb20gPSBmbiA9PT0gbnVsbCA/IG51bGwgOiBVdGlscy5wcm94eShmbiwgdGhhdC5wdWJsaWNJbnN0YW5jZSk7IHJldHVybiB0aGF0LnBpfVxuICAgICAgLy8gWm9vbWluZ1xuICAgICwgem9vbTogZnVuY3Rpb24oc2NhbGUpIHt0aGF0LnB1YmxpY1pvb20oc2NhbGUsIHRydWUpOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIHpvb21CeTogZnVuY3Rpb24oc2NhbGUpIHt0aGF0LnB1YmxpY1pvb20oc2NhbGUsIGZhbHNlKTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCB6b29tQXRQb2ludDogZnVuY3Rpb24oc2NhbGUsIHBvaW50KSB7dGhhdC5wdWJsaWNab29tQXRQb2ludChzY2FsZSwgcG9pbnQsIHRydWUpOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIHpvb21BdFBvaW50Qnk6IGZ1bmN0aW9uKHNjYWxlLCBwb2ludCkge3RoYXQucHVibGljWm9vbUF0UG9pbnQoc2NhbGUsIHBvaW50LCBmYWxzZSk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgem9vbUluOiBmdW5jdGlvbigpIHt0aGlzLnpvb21CeSgxICsgdGhhdC5vcHRpb25zLnpvb21TY2FsZVNlbnNpdGl2aXR5KTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCB6b29tT3V0OiBmdW5jdGlvbigpIHt0aGlzLnpvb21CeSgxIC8gKDEgKyB0aGF0Lm9wdGlvbnMuem9vbVNjYWxlU2Vuc2l0aXZpdHkpKTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCBnZXRab29tOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhhdC5nZXRSZWxhdGl2ZVpvb20oKX1cbiAgICAgIC8vIENUTSB1cGRhdGVcbiAgICAsIHNldE9uVXBkYXRlZENUTTogZnVuY3Rpb24oZm4pIHt0aGF0Lm9wdGlvbnMub25VcGRhdGVkQ1RNID0gZm4gPT09IG51bGwgPyBudWxsIDogVXRpbHMucHJveHkoZm4sIHRoYXQucHVibGljSW5zdGFuY2UpOyByZXR1cm4gdGhhdC5waX1cbiAgICAgIC8vIFJlc2V0XG4gICAgLCByZXNldFpvb206IGZ1bmN0aW9uKCkge3RoYXQucmVzZXRab29tKCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgcmVzZXRQYW46IGZ1bmN0aW9uKCkge3RoYXQucmVzZXRQYW4oKTsgcmV0dXJuIHRoYXQucGl9XG4gICAgLCByZXNldDogZnVuY3Rpb24oKSB7dGhhdC5yZXNldCgpOyByZXR1cm4gdGhhdC5waX1cbiAgICAgIC8vIEZpdCwgQ29udGFpbiBhbmQgQ2VudGVyXG4gICAgLCBmaXQ6IGZ1bmN0aW9uKCkge3RoYXQuZml0KCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgY29udGFpbjogZnVuY3Rpb24oKSB7dGhhdC5jb250YWluKCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgY2VudGVyOiBmdW5jdGlvbigpIHt0aGF0LmNlbnRlcigpOyByZXR1cm4gdGhhdC5waX1cbiAgICAgIC8vIFNpemUgYW5kIFJlc2l6ZVxuICAgICwgdXBkYXRlQkJveDogZnVuY3Rpb24oKSB7dGhhdC51cGRhdGVCQm94KCk7IHJldHVybiB0aGF0LnBpfVxuICAgICwgcmVzaXplOiBmdW5jdGlvbigpIHt0aGF0LnJlc2l6ZSgpOyByZXR1cm4gdGhhdC5waX1cbiAgICAsIGdldFNpemVzOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB3aWR0aDogdGhhdC53aWR0aFxuICAgICAgICAsIGhlaWdodDogdGhhdC5oZWlnaHRcbiAgICAgICAgLCByZWFsWm9vbTogdGhhdC5nZXRab29tKClcbiAgICAgICAgLCB2aWV3Qm94OiB0aGF0LnZpZXdwb3J0LmdldFZpZXdCb3goKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBEZXN0cm95XG4gICAgLCBkZXN0cm95OiBmdW5jdGlvbigpIHt0aGF0LmRlc3Ryb3koKTsgcmV0dXJuIHRoYXQucGl9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRoaXMucHVibGljSW5zdGFuY2Vcbn1cblxuLyoqXG4gKiBTdG9yZXMgcGFpcnMgb2YgaW5zdGFuY2VzIG9mIFN2Z1Bhblpvb20gYW5kIFNWR1xuICogRWFjaCBwYWlyIGlzIHJlcHJlc2VudGVkIGJ5IGFuIG9iamVjdCB7c3ZnOiBTVkdTVkdFbGVtZW50LCBpbnN0YW5jZTogU3ZnUGFuWm9vbX1cbiAqXG4gKiBAdHlwZSB7QXJyYXl9XG4gKi9cbnZhciBpbnN0YW5jZXNTdG9yZSA9IFtdXG5cbnZhciBzdmdQYW5ab29tID0gZnVuY3Rpb24oZWxlbWVudE9yU2VsZWN0b3IsIG9wdGlvbnMpe1xuICB2YXIgc3ZnID0gVXRpbHMuZ2V0U3ZnKGVsZW1lbnRPclNlbGVjdG9yKVxuXG4gIGlmIChzdmcgPT09IG51bGwpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9IGVsc2Uge1xuICAgIC8vIExvb2sgZm9yIGV4aXN0ZW50IGluc3RhbmNlXG4gICAgZm9yKHZhciBpID0gaW5zdGFuY2VzU3RvcmUubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGlmIChpbnN0YW5jZXNTdG9yZVtpXS5zdmcgPT09IHN2Zykge1xuICAgICAgICByZXR1cm4gaW5zdGFuY2VzU3RvcmVbaV0uaW5zdGFuY2UuZ2V0UHVibGljSW5zdGFuY2UoKVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIElmIGluc3RhbmNlIG5vdCBmb3VuZCAtIGNyZWF0ZSBvbmVcbiAgICBpbnN0YW5jZXNTdG9yZS5wdXNoKHtcbiAgICAgIHN2Zzogc3ZnXG4gICAgLCBpbnN0YW5jZTogbmV3IFN2Z1Bhblpvb20oc3ZnLCBvcHRpb25zKVxuICAgIH0pXG5cbiAgICAvLyBSZXR1cm4ganVzdCBwdXNoZWQgaW5zdGFuY2VcbiAgICByZXR1cm4gaW5zdGFuY2VzU3RvcmVbaW5zdGFuY2VzU3RvcmUubGVuZ3RoIC0gMV0uaW5zdGFuY2UuZ2V0UHVibGljSW5zdGFuY2UoKVxuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gc3ZnUGFuWm9vbTtcblxufSx7XCIuL2NvbnRyb2wtaWNvbnNcIjoyLFwiLi9zaGFkb3ctdmlld3BvcnRcIjozLFwiLi9zdmctdXRpbGl0aWVzXCI6NSxcIi4vdW5pd2hlZWxcIjo2LFwiLi91dGlsaXRpZXNcIjo3fV0sNTpbZnVuY3Rpb24ocmVxdWlyZSxtb2R1bGUsZXhwb3J0cyl7XG52YXIgVXRpbHMgPSByZXF1aXJlKCcuL3V0aWxpdGllcycpXG4gICwgX2Jyb3dzZXIgPSAndW5rbm93bidcbiAgO1xuXG4vLyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzk4NDc1ODAvaG93LXRvLWRldGVjdC1zYWZhcmktY2hyb21lLWllLWZpcmVmb3gtYW5kLW9wZXJhLWJyb3dzZXJcbmlmICgvKkBjY19vbiFAKi9mYWxzZSB8fCAhIWRvY3VtZW50LmRvY3VtZW50TW9kZSkgeyAvLyBpbnRlcm5ldCBleHBsb3JlclxuICBfYnJvd3NlciA9ICdpZSc7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzdmdOUzogICdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZydcbiwgeG1sTlM6ICAnaHR0cDovL3d3dy53My5vcmcvWE1MLzE5OTgvbmFtZXNwYWNlJ1xuLCB4bWxuc05TOiAgJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAveG1sbnMvJ1xuLCB4bGlua05TOiAgJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnXG4sIGV2TlM6ICAnaHR0cDovL3d3dy53My5vcmcvMjAwMS94bWwtZXZlbnRzJ1xuXG4gIC8qKlxuICAgKiBHZXQgc3ZnIGRpbWVuc2lvbnM6IHdpZHRoIGFuZCBoZWlnaHRcbiAgICpcbiAgICogQHBhcmFtICB7U1ZHU1ZHRWxlbWVudH0gc3ZnXG4gICAqIEByZXR1cm4ge09iamVjdH0gICAgIHt3aWR0aDogMCwgaGVpZ2h0OiAwfVxuICAgKi9cbiwgZ2V0Qm91bmRpbmdDbGllbnRSZWN0Tm9ybWFsaXplZDogZnVuY3Rpb24oc3ZnKSB7XG4gICAgaWYgKHN2Zy5jbGllbnRXaWR0aCAmJiBzdmcuY2xpZW50SGVpZ2h0KSB7XG4gICAgICByZXR1cm4ge3dpZHRoOiBzdmcuY2xpZW50V2lkdGgsIGhlaWdodDogc3ZnLmNsaWVudEhlaWdodH1cbiAgICB9IGVsc2UgaWYgKCEhc3ZnLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpKSB7XG4gICAgICByZXR1cm4gc3ZnLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBnZXQgQm91bmRpbmdDbGllbnRSZWN0IGZvciBTVkcuJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgZyBlbGVtZW50IHdpdGggY2xhc3Mgb2YgXCJ2aWV3cG9ydFwiIG9yIGNyZWF0ZXMgaXQgaWYgaXQgZG9lc24ndCBleGlzdFxuICAgKlxuICAgKiBAcGFyYW0gIHtTVkdTVkdFbGVtZW50fSBzdmdcbiAgICogQHJldHVybiB7U1ZHRWxlbWVudH0gICAgIGcgKGdyb3VwKSBlbGVtZW50XG4gICAqL1xuLCBnZXRPckNyZWF0ZVZpZXdwb3J0OiBmdW5jdGlvbihzdmcsIHNlbGVjdG9yKSB7XG4gICAgdmFyIHZpZXdwb3J0ID0gbnVsbFxuXG4gICAgaWYgKFV0aWxzLmlzRWxlbWVudChzZWxlY3RvcikpIHtcbiAgICAgIHZpZXdwb3J0ID0gc2VsZWN0b3JcbiAgICB9IGVsc2Uge1xuICAgICAgdmlld3BvcnQgPSBzdmcucXVlcnlTZWxlY3RvcihzZWxlY3RvcilcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyBqdXN0IG9uZSBtYWluIGdyb3VwIGluIFNWR1xuICAgIGlmICghdmlld3BvcnQpIHtcbiAgICAgIHZhciBjaGlsZE5vZGVzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoc3ZnLmNoaWxkTm9kZXMgfHwgc3ZnLmNoaWxkcmVuKS5maWx0ZXIoZnVuY3Rpb24oZWwpe1xuICAgICAgICByZXR1cm4gZWwubm9kZU5hbWUgIT09ICdkZWZzJyAmJiBlbC5ub2RlTmFtZSAhPT0gJyN0ZXh0J1xuICAgICAgfSlcblxuICAgICAgLy8gTm9kZSBuYW1lIHNob3VsZCBiZSBTVkdHRWxlbWVudCBhbmQgc2hvdWxkIGhhdmUgbm8gdHJhbnNmb3JtIGF0dHJpYnV0ZVxuICAgICAgLy8gR3JvdXBzIHdpdGggdHJhbnNmb3JtIGFyZSBub3QgdXNlZCBhcyB2aWV3cG9ydCBiZWNhdXNlIGl0IGludm9sdmVzIHBhcnNpbmcgb2YgYWxsIHRyYW5zZm9ybSBwb3NzaWJpbGl0aWVzXG4gICAgICBpZiAoY2hpbGROb2Rlcy5sZW5ndGggPT09IDEgJiYgY2hpbGROb2Rlc1swXS5ub2RlTmFtZSA9PT0gJ2cnICYmIGNoaWxkTm9kZXNbMF0uZ2V0QXR0cmlidXRlKCd0cmFuc2Zvcm0nKSA9PT0gbnVsbCkge1xuICAgICAgICB2aWV3cG9ydCA9IGNoaWxkTm9kZXNbMF1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiBubyBmYXZvcmFibGUgZ3JvdXAgZWxlbWVudCBleGlzdHMgdGhlbiBjcmVhdGUgb25lXG4gICAgaWYgKCF2aWV3cG9ydCkge1xuICAgICAgdmFyIHZpZXdwb3J0SWQgPSAndmlld3BvcnQtJyArIG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKS5yZXBsYWNlKC9cXEQvZywgJycpO1xuICAgICAgdmlld3BvcnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlModGhpcy5zdmdOUywgJ2cnKTtcbiAgICAgIHZpZXdwb3J0LnNldEF0dHJpYnV0ZSgnaWQnLCB2aWV3cG9ydElkKTtcblxuICAgICAgLy8gSW50ZXJuZXQgRXhwbG9yZXIgKGFsbCB2ZXJzaW9ucz8pIGNhbid0IHVzZSBjaGlsZE5vZGVzLCBidXQgb3RoZXIgYnJvd3NlcnMgcHJlZmVyIChyZXF1aXJlPykgdXNpbmcgY2hpbGROb2Rlc1xuICAgICAgdmFyIHN2Z0NoaWxkcmVuID0gc3ZnLmNoaWxkTm9kZXMgfHwgc3ZnLmNoaWxkcmVuO1xuICAgICAgaWYgKCEhc3ZnQ2hpbGRyZW4gJiYgc3ZnQ2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgICBmb3IgKHZhciBpID0gc3ZnQ2hpbGRyZW4ubGVuZ3RoOyBpID4gMDsgaS0tKSB7XG4gICAgICAgICAgLy8gTW92ZSBldmVyeXRoaW5nIGludG8gdmlld3BvcnQgZXhjZXB0IGRlZnNcbiAgICAgICAgICBpZiAoc3ZnQ2hpbGRyZW5bc3ZnQ2hpbGRyZW4ubGVuZ3RoIC0gaV0ubm9kZU5hbWUgIT09ICdkZWZzJykge1xuICAgICAgICAgICAgdmlld3BvcnQuYXBwZW5kQ2hpbGQoc3ZnQ2hpbGRyZW5bc3ZnQ2hpbGRyZW4ubGVuZ3RoIC0gaV0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgc3ZnLmFwcGVuZENoaWxkKHZpZXdwb3J0KTtcbiAgICB9XG5cbiAgICAvLyBQYXJzZSBjbGFzcyBuYW1lc1xuICAgIHZhciBjbGFzc05hbWVzID0gW107XG4gICAgaWYgKHZpZXdwb3J0LmdldEF0dHJpYnV0ZSgnY2xhc3MnKSkge1xuICAgICAgY2xhc3NOYW1lcyA9IHZpZXdwb3J0LmdldEF0dHJpYnV0ZSgnY2xhc3MnKS5zcGxpdCgnICcpXG4gICAgfVxuXG4gICAgLy8gU2V0IGNsYXNzIChpZiBub3Qgc2V0IGFscmVhZHkpXG4gICAgaWYgKCF+Y2xhc3NOYW1lcy5pbmRleE9mKCdzdmctcGFuLXpvb21fdmlld3BvcnQnKSkge1xuICAgICAgY2xhc3NOYW1lcy5wdXNoKCdzdmctcGFuLXpvb21fdmlld3BvcnQnKVxuICAgICAgdmlld3BvcnQuc2V0QXR0cmlidXRlKCdjbGFzcycsIGNsYXNzTmFtZXMuam9pbignICcpKVxuICAgIH1cblxuICAgIHJldHVybiB2aWV3cG9ydFxuICB9XG5cbiAgLyoqXG4gICAqIFNldCBTVkcgYXR0cmlidXRlc1xuICAgKlxuICAgKiBAcGFyYW0gIHtTVkdTVkdFbGVtZW50fSBzdmdcbiAgICovXG4gICwgc2V0dXBTdmdBdHRyaWJ1dGVzOiBmdW5jdGlvbihzdmcpIHtcbiAgICAvLyBTZXR0aW5nIGRlZmF1bHQgYXR0cmlidXRlc1xuICAgIHN2Zy5zZXRBdHRyaWJ1dGUoJ3htbG5zJywgdGhpcy5zdmdOUyk7XG4gICAgc3ZnLnNldEF0dHJpYnV0ZU5TKHRoaXMueG1sbnNOUywgJ3htbG5zOnhsaW5rJywgdGhpcy54bGlua05TKTtcbiAgICBzdmcuc2V0QXR0cmlidXRlTlModGhpcy54bWxuc05TLCAneG1sbnM6ZXYnLCB0aGlzLmV2TlMpO1xuXG4gICAgLy8gTmVlZGVkIGZvciBJbnRlcm5ldCBFeHBsb3Jlciwgb3RoZXJ3aXNlIHRoZSB2aWV3cG9ydCBvdmVyZmxvd3NcbiAgICBpZiAoc3ZnLnBhcmVudE5vZGUgIT09IG51bGwpIHtcbiAgICAgIHZhciBzdHlsZSA9IHN2Zy5nZXRBdHRyaWJ1dGUoJ3N0eWxlJykgfHwgJyc7XG4gICAgICBpZiAoc3R5bGUudG9Mb3dlckNhc2UoKS5pbmRleE9mKCdvdmVyZmxvdycpID09PSAtMSkge1xuICAgICAgICBzdmcuc2V0QXR0cmlidXRlKCdzdHlsZScsICdvdmVyZmxvdzogaGlkZGVuOyAnICsgc3R5bGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4vKipcbiAqIEhvdyBsb25nIEludGVybmV0IEV4cGxvcmVyIHRha2VzIHRvIGZpbmlzaCB1cGRhdGluZyBpdHMgZGlzcGxheSAobXMpLlxuICovXG4sIGludGVybmV0RXhwbG9yZXJSZWRpc3BsYXlJbnRlcnZhbDogMzAwXG5cbi8qKlxuICogRm9yY2VzIHRoZSBicm93c2VyIHRvIHJlZGlzcGxheSBhbGwgU1ZHIGVsZW1lbnRzIHRoYXQgcmVseSBvbiBhblxuICogZWxlbWVudCBkZWZpbmVkIGluIGEgJ2RlZnMnIHNlY3Rpb24uIEl0IHdvcmtzIGdsb2JhbGx5LCBmb3IgZXZlcnlcbiAqIGF2YWlsYWJsZSBkZWZzIGVsZW1lbnQgb24gdGhlIHBhZ2UuXG4gKiBUaGUgdGhyb3R0bGluZyBpcyBpbnRlbnRpb25hbGx5IGdsb2JhbC5cbiAqXG4gKiBUaGlzIGlzIG9ubHkgbmVlZGVkIGZvciBJRS4gSXQgaXMgYXMgYSBoYWNrIHRvIG1ha2UgbWFya2VycyAoYW5kICd1c2UnIGVsZW1lbnRzPylcbiAqIHZpc2libGUgYWZ0ZXIgcGFuL3pvb20gd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgU1ZHcyBvbiB0aGUgcGFnZS5cbiAqIFNlZSBidWcgcmVwb3J0OiBodHRwczovL2Nvbm5lY3QubWljcm9zb2Z0LmNvbS9JRS9mZWVkYmFjay9kZXRhaWxzLzc4MTk2NC9cbiAqIGFsc28gc2VlIHN2Zy1wYW4tem9vbSBpc3N1ZTogaHR0cHM6Ly9naXRodWIuY29tL2FyaXV0dGEvc3ZnLXBhbi16b29tL2lzc3Vlcy82MlxuICovXG4sIHJlZnJlc2hEZWZzR2xvYmFsOiBVdGlscy50aHJvdHRsZShmdW5jdGlvbigpIHtcbiAgICB2YXIgYWxsRGVmcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2RlZnMnKTtcbiAgICB2YXIgYWxsRGVmc0NvdW50ID0gYWxsRGVmcy5sZW5ndGg7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhbGxEZWZzQ291bnQ7IGkrKykge1xuICAgICAgdmFyIHRoaXNEZWZzID0gYWxsRGVmc1tpXTtcbiAgICAgIHRoaXNEZWZzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKHRoaXNEZWZzLCB0aGlzRGVmcyk7XG4gICAgfVxuICB9LCB0aGlzID8gdGhpcy5pbnRlcm5ldEV4cGxvcmVyUmVkaXNwbGF5SW50ZXJ2YWwgOiBudWxsKVxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSBjdXJyZW50IHRyYW5zZm9ybSBtYXRyaXggb2YgYW4gZWxlbWVudFxuICAgKlxuICAgKiBAcGFyYW0ge1NWR0VsZW1lbnR9IGVsZW1lbnRcbiAgICogQHBhcmFtIHtTVkdNYXRyaXh9IG1hdHJpeCAgQ1RNXG4gICAqIEBwYXJhbSB7U1ZHRWxlbWVudH0gZGVmc1xuICAgKi9cbiwgc2V0Q1RNOiBmdW5jdGlvbihlbGVtZW50LCBtYXRyaXgsIGRlZnMpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXNcbiAgICAgICwgcyA9ICdtYXRyaXgoJyArIG1hdHJpeC5hICsgJywnICsgbWF0cml4LmIgKyAnLCcgKyBtYXRyaXguYyArICcsJyArIG1hdHJpeC5kICsgJywnICsgbWF0cml4LmUgKyAnLCcgKyBtYXRyaXguZiArICcpJztcblxuICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RyYW5zZm9ybScsIHMpO1xuICAgIGlmICgndHJhbnNmb3JtJyBpbiBlbGVtZW50LnN0eWxlKSB7XG4gICAgICBlbGVtZW50LnN0eWxlLnRyYW5zZm9ybSA9IHM7XG4gICAgfSBlbHNlIGlmICgnLW1zLXRyYW5zZm9ybScgaW4gZWxlbWVudC5zdHlsZSkge1xuICAgICAgZWxlbWVudC5zdHlsZVsnLW1zLXRyYW5zZm9ybSddID0gcztcbiAgICB9IGVsc2UgaWYgKCctd2Via2l0LXRyYW5zZm9ybScgaW4gZWxlbWVudC5zdHlsZSkge1xuICAgICAgZWxlbWVudC5zdHlsZVsnLXdlYmtpdC10cmFuc2Zvcm0nXSA9IHM7XG4gICAgfVxuXG4gICAgLy8gSUUgaGFzIGEgYnVnIHRoYXQgbWFrZXMgbWFya2VycyBkaXNhcHBlYXIgb24gem9vbSAod2hlbiB0aGUgbWF0cml4IFwiYVwiIGFuZC9vciBcImRcIiBlbGVtZW50cyBjaGFuZ2UpXG4gICAgLy8gc2VlIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTc2NTQ1Nzgvc3ZnLW1hcmtlci1kb2VzLW5vdC13b3JrLWluLWllOS0xMFxuICAgIC8vIGFuZCBodHRwOi8vc3JuZG9saGEud29yZHByZXNzLmNvbS8yMDEzLzExLzI1L3N2Zy1saW5lLW1hcmtlcnMtbWF5LWRpc2FwcGVhci1pbi1pbnRlcm5ldC1leHBsb3Jlci0xMS9cbiAgICBpZiAoX2Jyb3dzZXIgPT09ICdpZScgJiYgISFkZWZzKSB7XG4gICAgICAvLyB0aGlzIHJlZnJlc2ggaXMgaW50ZW5kZWQgZm9yIHJlZGlzcGxheWluZyB0aGUgU1ZHIGR1cmluZyB6b29taW5nXG4gICAgICBkZWZzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGRlZnMsIGRlZnMpO1xuICAgICAgLy8gdGhpcyByZWZyZXNoIGlzIGludGVuZGVkIGZvciByZWRpc3BsYXlpbmcgdGhlIG90aGVyIFNWR3Mgb24gYSBwYWdlIHdoZW4gcGFubmluZyBhIGdpdmVuIFNWR1xuICAgICAgLy8gaXQgaXMgYWxzbyBuZWVkZWQgZm9yIHRoZSBnaXZlbiBTVkcgaXRzZWxmLCBvbiB6b29tRW5kLCBpZiB0aGUgU1ZHIGNvbnRhaW5zIGFueSBtYXJrZXJzIHRoYXRcbiAgICAgIC8vIGFyZSBsb2NhdGVkIHVuZGVyIGFueSBvdGhlciBlbGVtZW50KHMpLlxuICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgIHRoYXQucmVmcmVzaERlZnNHbG9iYWwoKTtcbiAgICAgIH0sIHRoYXQuaW50ZXJuZXRFeHBsb3JlclJlZGlzcGxheUludGVydmFsKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSW5zdGFudGlhdGUgYW4gU1ZHUG9pbnQgb2JqZWN0IHdpdGggZ2l2ZW4gZXZlbnQgY29vcmRpbmF0ZXNcbiAgICpcbiAgICogQHBhcmFtIHtFdmVudH0gZXZ0XG4gICAqIEBwYXJhbSAge1NWR1NWR0VsZW1lbnR9IHN2Z1xuICAgKiBAcmV0dXJuIHtTVkdQb2ludH0gICAgIHBvaW50XG4gICAqL1xuLCBnZXRFdmVudFBvaW50OiBmdW5jdGlvbihldnQsIHN2Zykge1xuICAgIHZhciBwb2ludCA9IHN2Zy5jcmVhdGVTVkdQb2ludCgpXG5cbiAgICBVdGlscy5tb3VzZUFuZFRvdWNoTm9ybWFsaXplKGV2dCwgc3ZnKVxuXG4gICAgcG9pbnQueCA9IGV2dC5jbGllbnRYXG4gICAgcG9pbnQueSA9IGV2dC5jbGllbnRZXG5cbiAgICByZXR1cm4gcG9pbnRcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgU1ZHIGNlbnRlciBwb2ludFxuICAgKlxuICAgKiBAcGFyYW0gIHtTVkdTVkdFbGVtZW50fSBzdmdcbiAgICogQHJldHVybiB7U1ZHUG9pbnR9XG4gICAqL1xuLCBnZXRTdmdDZW50ZXJQb2ludDogZnVuY3Rpb24oc3ZnLCB3aWR0aCwgaGVpZ2h0KSB7XG4gICAgcmV0dXJuIHRoaXMuY3JlYXRlU1ZHUG9pbnQoc3ZnLCB3aWR0aCAvIDIsIGhlaWdodCAvIDIpXG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgU1ZHUG9pbnQgd2l0aCBnaXZlbiB4IGFuZCB5XG4gICAqXG4gICAqIEBwYXJhbSAge1NWR1NWR0VsZW1lbnR9IHN2Z1xuICAgKiBAcGFyYW0gIHtOdW1iZXJ9IHhcbiAgICogQHBhcmFtICB7TnVtYmVyfSB5XG4gICAqIEByZXR1cm4ge1NWR1BvaW50fVxuICAgKi9cbiwgY3JlYXRlU1ZHUG9pbnQ6IGZ1bmN0aW9uKHN2ZywgeCwgeSkge1xuICAgIHZhciBwb2ludCA9IHN2Zy5jcmVhdGVTVkdQb2ludCgpXG4gICAgcG9pbnQueCA9IHhcbiAgICBwb2ludC55ID0geVxuXG4gICAgcmV0dXJuIHBvaW50XG4gIH1cbn1cblxufSx7XCIuL3V0aWxpdGllc1wiOjd9XSw2OltmdW5jdGlvbihyZXF1aXJlLG1vZHVsZSxleHBvcnRzKXtcbi8vIHVuaXdoZWVsIDAuMS4yIChjdXN0b21pemVkKVxuLy8gQSB1bmlmaWVkIGNyb3NzIGJyb3dzZXIgbW91c2Ugd2hlZWwgZXZlbnQgaGFuZGxlclxuLy8gaHR0cHM6Ly9naXRodWIuY29tL3RlZW11YWxhcC91bml3aGVlbFxuXG5tb2R1bGUuZXhwb3J0cyA9IChmdW5jdGlvbigpe1xuXG4gIC8vRnVsbCBkZXRhaWxzOiBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9SZWZlcmVuY2UvRXZlbnRzL3doZWVsXG5cbiAgdmFyIHByZWZpeCA9IFwiXCIsIF9hZGRFdmVudExpc3RlbmVyLCBfcmVtb3ZlRXZlbnRMaXN0ZW5lciwgc3VwcG9ydCwgZm5zID0gW107XG4gIHZhciBwYXNzaXZlT3B0aW9uID0ge3Bhc3NpdmU6IHRydWV9O1xuXG4gIC8vIGRldGVjdCBldmVudCBtb2RlbFxuICBpZiAoIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyICkge1xuICAgIF9hZGRFdmVudExpc3RlbmVyID0gXCJhZGRFdmVudExpc3RlbmVyXCI7XG4gICAgX3JlbW92ZUV2ZW50TGlzdGVuZXIgPSBcInJlbW92ZUV2ZW50TGlzdGVuZXJcIjtcbiAgfSBlbHNlIHtcbiAgICBfYWRkRXZlbnRMaXN0ZW5lciA9IFwiYXR0YWNoRXZlbnRcIjtcbiAgICBfcmVtb3ZlRXZlbnRMaXN0ZW5lciA9IFwiZGV0YWNoRXZlbnRcIjtcbiAgICBwcmVmaXggPSBcIm9uXCI7XG4gIH1cblxuICAvLyBkZXRlY3QgYXZhaWxhYmxlIHdoZWVsIGV2ZW50XG4gIHN1cHBvcnQgPSBcIm9ud2hlZWxcIiBpbiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpID8gXCJ3aGVlbFwiIDogLy8gTW9kZXJuIGJyb3dzZXJzIHN1cHBvcnQgXCJ3aGVlbFwiXG4gICAgICAgICAgICBkb2N1bWVudC5vbm1vdXNld2hlZWwgIT09IHVuZGVmaW5lZCA/IFwibW91c2V3aGVlbFwiIDogLy8gV2Via2l0IGFuZCBJRSBzdXBwb3J0IGF0IGxlYXN0IFwibW91c2V3aGVlbFwiXG4gICAgICAgICAgICBcIkRPTU1vdXNlU2Nyb2xsXCI7IC8vIGxldCdzIGFzc3VtZSB0aGF0IHJlbWFpbmluZyBicm93c2VycyBhcmUgb2xkZXIgRmlyZWZveFxuXG5cbiAgZnVuY3Rpb24gY3JlYXRlQ2FsbGJhY2soZWxlbWVudCxjYWxsYmFjaykge1xuXG4gICAgdmFyIGZuID0gZnVuY3Rpb24ob3JpZ2luYWxFdmVudCkge1xuXG4gICAgICAhb3JpZ2luYWxFdmVudCAmJiAoIG9yaWdpbmFsRXZlbnQgPSB3aW5kb3cuZXZlbnQgKTtcblxuICAgICAgLy8gY3JlYXRlIGEgbm9ybWFsaXplZCBldmVudCBvYmplY3RcbiAgICAgIHZhciBldmVudCA9IHtcbiAgICAgICAgLy8ga2VlcCBhIHJlZiB0byB0aGUgb3JpZ2luYWwgZXZlbnQgb2JqZWN0XG4gICAgICAgIG9yaWdpbmFsRXZlbnQ6IG9yaWdpbmFsRXZlbnQsXG4gICAgICAgIHRhcmdldDogb3JpZ2luYWxFdmVudC50YXJnZXQgfHwgb3JpZ2luYWxFdmVudC5zcmNFbGVtZW50LFxuICAgICAgICB0eXBlOiBcIndoZWVsXCIsXG4gICAgICAgIGRlbHRhTW9kZTogb3JpZ2luYWxFdmVudC50eXBlID09IFwiTW96TW91c2VQaXhlbFNjcm9sbFwiID8gMCA6IDEsXG4gICAgICAgIGRlbHRhWDogMCxcbiAgICAgICAgZGVsYXRaOiAwLFxuICAgICAgICBwcmV2ZW50RGVmYXVsdDogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgb3JpZ2luYWxFdmVudC5wcmV2ZW50RGVmYXVsdCA/XG4gICAgICAgICAgICBvcmlnaW5hbEV2ZW50LnByZXZlbnREZWZhdWx0KCkgOlxuICAgICAgICAgICAgb3JpZ2luYWxFdmVudC5yZXR1cm5WYWx1ZSA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICAvLyBjYWxjdWxhdGUgZGVsdGFZIChhbmQgZGVsdGFYKSBhY2NvcmRpbmcgdG8gdGhlIGV2ZW50XG4gICAgICBpZiAoIHN1cHBvcnQgPT0gXCJtb3VzZXdoZWVsXCIgKSB7XG4gICAgICAgIGV2ZW50LmRlbHRhWSA9IC0gMS80MCAqIG9yaWdpbmFsRXZlbnQud2hlZWxEZWx0YTtcbiAgICAgICAgLy8gV2Via2l0IGFsc28gc3VwcG9ydCB3aGVlbERlbHRhWFxuICAgICAgICBvcmlnaW5hbEV2ZW50LndoZWVsRGVsdGFYICYmICggZXZlbnQuZGVsdGFYID0gLSAxLzQwICogb3JpZ2luYWxFdmVudC53aGVlbERlbHRhWCApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZXZlbnQuZGVsdGFZID0gb3JpZ2luYWxFdmVudC5kZXRhaWw7XG4gICAgICB9XG5cbiAgICAgIC8vIGl0J3MgdGltZSB0byBmaXJlIHRoZSBjYWxsYmFja1xuICAgICAgcmV0dXJuIGNhbGxiYWNrKCBldmVudCApO1xuXG4gICAgfTtcblxuICAgIGZucy5wdXNoKHtcbiAgICAgIGVsZW1lbnQ6IGVsZW1lbnQsXG4gICAgICBmbjogZm4sXG4gICAgfSk7XG5cbiAgICByZXR1cm4gZm47XG4gIH1cblxuICBmdW5jdGlvbiBnZXRDYWxsYmFjayhlbGVtZW50KSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBmbnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChmbnNbaV0uZWxlbWVudCA9PT0gZWxlbWVudCkge1xuICAgICAgICByZXR1cm4gZm5zW2ldLmZuO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZnVuY3Rpb24oKXt9O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtb3ZlQ2FsbGJhY2soZWxlbWVudCkge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZm5zLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoZm5zW2ldLmVsZW1lbnQgPT09IGVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIGZucy5zcGxpY2UoaSwxKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBfYWRkV2hlZWxMaXN0ZW5lcihlbGVtLCBldmVudE5hbWUsIGNhbGxiYWNrLCBpc1Bhc3NpdmVMaXN0ZW5lciApIHtcbiAgICB2YXIgY2I7XG5cbiAgICBpZiAoc3VwcG9ydCA9PT0gXCJ3aGVlbFwiKSB7XG4gICAgICBjYiA9IGNhbGxiYWNrO1xuICAgIH0gZWxzZSB7XG4gICAgICBjYiA9IGNyZWF0ZUNhbGxiYWNrKGVsZW0sIGNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICBlbGVtW19hZGRFdmVudExpc3RlbmVyXShwcmVmaXggKyBldmVudE5hbWUsIGNiLCBpc1Bhc3NpdmVMaXN0ZW5lciA/IHBhc3NpdmVPcHRpb24gOiBmYWxzZSk7XG4gIH1cblxuICBmdW5jdGlvbiBfcmVtb3ZlV2hlZWxMaXN0ZW5lcihlbGVtLCBldmVudE5hbWUsIGNhbGxiYWNrLCBpc1Bhc3NpdmVMaXN0ZW5lciApIHtcblxuICAgIHZhciBjYjtcblxuICAgIGlmIChzdXBwb3J0ID09PSBcIndoZWVsXCIpIHtcbiAgICAgIGNiID0gY2FsbGJhY2s7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNiID0gZ2V0Q2FsbGJhY2soZWxlbSk7XG4gICAgfVxuXG4gICAgZWxlbVtfcmVtb3ZlRXZlbnRMaXN0ZW5lcl0ocHJlZml4ICsgZXZlbnROYW1lLCBjYiwgaXNQYXNzaXZlTGlzdGVuZXIgPyBwYXNzaXZlT3B0aW9uIDogZmFsc2UpO1xuXG4gICAgcmVtb3ZlQ2FsbGJhY2soZWxlbSk7XG4gIH1cblxuICBmdW5jdGlvbiBhZGRXaGVlbExpc3RlbmVyKCBlbGVtLCBjYWxsYmFjaywgaXNQYXNzaXZlTGlzdGVuZXIgKSB7XG4gICAgX2FkZFdoZWVsTGlzdGVuZXIoZWxlbSwgc3VwcG9ydCwgY2FsbGJhY2ssIGlzUGFzc2l2ZUxpc3RlbmVyICk7XG5cbiAgICAvLyBoYW5kbGUgTW96TW91c2VQaXhlbFNjcm9sbCBpbiBvbGRlciBGaXJlZm94XG4gICAgaWYoIHN1cHBvcnQgPT0gXCJET01Nb3VzZVNjcm9sbFwiICkge1xuICAgICAgX2FkZFdoZWVsTGlzdGVuZXIoZWxlbSwgXCJNb3pNb3VzZVBpeGVsU2Nyb2xsXCIsIGNhbGxiYWNrLCBpc1Bhc3NpdmVMaXN0ZW5lciApO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZVdoZWVsTGlzdGVuZXIoZWxlbSwgY2FsbGJhY2ssIGlzUGFzc2l2ZUxpc3RlbmVyKXtcbiAgICBfcmVtb3ZlV2hlZWxMaXN0ZW5lcihlbGVtLCBzdXBwb3J0LCBjYWxsYmFjaywgaXNQYXNzaXZlTGlzdGVuZXIpO1xuXG4gICAgLy8gaGFuZGxlIE1vek1vdXNlUGl4ZWxTY3JvbGwgaW4gb2xkZXIgRmlyZWZveFxuICAgIGlmKCBzdXBwb3J0ID09IFwiRE9NTW91c2VTY3JvbGxcIiApIHtcbiAgICAgIF9yZW1vdmVXaGVlbExpc3RlbmVyKGVsZW0sIFwiTW96TW91c2VQaXhlbFNjcm9sbFwiLCBjYWxsYmFjaywgaXNQYXNzaXZlTGlzdGVuZXIpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgb246IGFkZFdoZWVsTGlzdGVuZXIsXG4gICAgb2ZmOiByZW1vdmVXaGVlbExpc3RlbmVyXG4gIH07XG5cbn0pKCk7XG5cbn0se31dLDc6W2Z1bmN0aW9uKHJlcXVpcmUsbW9kdWxlLGV4cG9ydHMpe1xubW9kdWxlLmV4cG9ydHMgPSB7XG4gIC8qKlxuICAgKiBFeHRlbmRzIGFuIG9iamVjdFxuICAgKlxuICAgKiBAcGFyYW0gIHtPYmplY3R9IHRhcmdldCBvYmplY3QgdG8gZXh0ZW5kXG4gICAqIEBwYXJhbSAge09iamVjdH0gc291cmNlIG9iamVjdCB0byB0YWtlIHByb3BlcnRpZXMgZnJvbVxuICAgKiBAcmV0dXJuIHtPYmplY3R9ICAgICAgICBleHRlbmRlZCBvYmplY3RcbiAgICovXG4gIGV4dGVuZDogZnVuY3Rpb24odGFyZ2V0LCBzb3VyY2UpIHtcbiAgICB0YXJnZXQgPSB0YXJnZXQgfHwge307XG4gICAgZm9yICh2YXIgcHJvcCBpbiBzb3VyY2UpIHtcbiAgICAgIC8vIEdvIHJlY3Vyc2l2ZWx5XG4gICAgICBpZiAodGhpcy5pc09iamVjdChzb3VyY2VbcHJvcF0pKSB7XG4gICAgICAgIHRhcmdldFtwcm9wXSA9IHRoaXMuZXh0ZW5kKHRhcmdldFtwcm9wXSwgc291cmNlW3Byb3BdKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGFyZ2V0W3Byb3BdID0gc291cmNlW3Byb3BdXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0YXJnZXQ7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIGFuIG9iamVjdCBpcyBhIERPTSBlbGVtZW50XG4gICAqXG4gICAqIEBwYXJhbSAge09iamVjdH0gIG8gSFRNTCBlbGVtZW50IG9yIFN0cmluZ1xuICAgKiBAcmV0dXJuIHtCb29sZWFufSAgIHJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgYSBET00gZWxlbWVudFxuICAgKi9cbiwgaXNFbGVtZW50OiBmdW5jdGlvbihvKXtcbiAgICByZXR1cm4gKFxuICAgICAgbyBpbnN0YW5jZW9mIEhUTUxFbGVtZW50IHx8IG8gaW5zdGFuY2VvZiBTVkdFbGVtZW50IHx8IG8gaW5zdGFuY2VvZiBTVkdTVkdFbGVtZW50IHx8IC8vRE9NMlxuICAgICAgKG8gJiYgdHlwZW9mIG8gPT09ICdvYmplY3QnICYmIG8gIT09IG51bGwgJiYgby5ub2RlVHlwZSA9PT0gMSAmJiB0eXBlb2Ygby5ub2RlTmFtZSA9PT0gJ3N0cmluZycpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgYW4gb2JqZWN0IGlzIGFuIE9iamVjdFxuICAgKlxuICAgKiBAcGFyYW0gIHtPYmplY3R9ICBvIE9iamVjdFxuICAgKiBAcmV0dXJuIHtCb29sZWFufSAgIHJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgYW4gT2JqZWN0XG4gICAqL1xuLCBpc09iamVjdDogZnVuY3Rpb24obyl7XG4gICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHZhcmlhYmxlIGlzIE51bWJlclxuICAgKlxuICAgKiBAcGFyYW0gIHtJbnRlZ2VyfEZsb2F0fSAgblxuICAgKiBAcmV0dXJuIHtCb29sZWFufSAgIHJldHVybnMgdHJ1ZSBpZiB2YXJpYWJsZSBpcyBOdW1iZXJcbiAgICovXG4sIGlzTnVtYmVyOiBmdW5jdGlvbihuKSB7XG4gICAgcmV0dXJuICFpc05hTihwYXJzZUZsb2F0KG4pKSAmJiBpc0Zpbml0ZShuKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWFyY2ggZm9yIGFuIFNWRyBlbGVtZW50XG4gICAqXG4gICAqIEBwYXJhbSAge09iamVjdHxTdHJpbmd9IGVsZW1lbnRPclNlbGVjdG9yIERPTSBFbGVtZW50IG9yIHNlbGVjdG9yIFN0cmluZ1xuICAgKiBAcmV0dXJuIHtPYmplY3R8TnVsbH0gICAgICAgICAgICAgICAgICAgU1ZHIG9yIG51bGxcbiAgICovXG4sIGdldFN2ZzogZnVuY3Rpb24oZWxlbWVudE9yU2VsZWN0b3IpIHtcbiAgICB2YXIgZWxlbWVudFxuICAgICAgLCBzdmc7XG5cbiAgICBpZiAoIXRoaXMuaXNFbGVtZW50KGVsZW1lbnRPclNlbGVjdG9yKSkge1xuICAgICAgLy8gSWYgc2VsZWN0b3IgcHJvdmlkZWRcbiAgICAgIGlmICh0eXBlb2YgZWxlbWVudE9yU2VsZWN0b3IgPT09ICdzdHJpbmcnIHx8IGVsZW1lbnRPclNlbGVjdG9yIGluc3RhbmNlb2YgU3RyaW5nKSB7XG4gICAgICAgIC8vIFRyeSB0byBmaW5kIHRoZSBlbGVtZW50XG4gICAgICAgIGVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGVsZW1lbnRPclNlbGVjdG9yKVxuXG4gICAgICAgIGlmICghZWxlbWVudCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgc2VsZWN0b3IgZGlkIG5vdCBmaW5kIGFueSBlbGVtZW50cy4gU2VsZWN0b3I6ICcgKyBlbGVtZW50T3JTZWxlY3RvcilcbiAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Byb3ZpZGVkIHNlbGVjdG9yIGlzIG5vdCBhbiBIVE1MIG9iamVjdCBub3IgU3RyaW5nJylcbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgZWxlbWVudCA9IGVsZW1lbnRPclNlbGVjdG9yXG4gICAgfVxuXG4gICAgaWYgKGVsZW1lbnQudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnc3ZnJykge1xuICAgICAgc3ZnID0gZWxlbWVudDtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGVsZW1lbnQudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnb2JqZWN0Jykge1xuICAgICAgICBzdmcgPSBlbGVtZW50LmNvbnRlbnREb2N1bWVudC5kb2N1bWVudEVsZW1lbnQ7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoZWxlbWVudC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdlbWJlZCcpIHtcbiAgICAgICAgICBzdmcgPSBlbGVtZW50LmdldFNWR0RvY3VtZW50KCkuZG9jdW1lbnRFbGVtZW50O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChlbGVtZW50LnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2ltZycpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHNjcmlwdCBhbiBTVkcgaW4gYW4gXCJpbWdcIiBlbGVtZW50LiBQbGVhc2UgdXNlIGFuIFwib2JqZWN0XCIgZWxlbWVudCBvciBhbiBpbi1saW5lIFNWRy4nKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgZ2V0IFNWRy4nKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzdmdcbiAgfVxuXG4gIC8qKlxuICAgKiBBdHRhY2ggYSBnaXZlbiBjb250ZXh0IHRvIGEgZnVuY3Rpb25cbiAgICogQHBhcmFtICB7RnVuY3Rpb259IGZuICAgICAgRnVuY3Rpb25cbiAgICogQHBhcmFtICB7T2JqZWN0fSAgIGNvbnRleHQgQ29udGV4dFxuICAgKiBAcmV0dXJuIHtGdW5jdGlvbn0gICAgICAgICAgIEZ1bmN0aW9uIHdpdGggY2VydGFpbiBjb250ZXh0XG4gICAqL1xuLCBwcm94eTogZnVuY3Rpb24oZm4sIGNvbnRleHQpIHtcbiAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gZm4uYXBwbHkoY29udGV4dCwgYXJndW1lbnRzKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIG9iamVjdCB0eXBlXG4gICAqIFVzZXMgdG9TdHJpbmcgdGhhdCByZXR1cm5zIFtvYmplY3QgU1ZHUG9pbnRdXG4gICAqIEFuZCB0aGFuIHBhcnNlcyBvYmplY3QgdHlwZSBmcm9tIHN0cmluZ1xuICAgKlxuICAgKiBAcGFyYW0gIHtPYmplY3R9IG8gQW55IG9iamVjdFxuICAgKiBAcmV0dXJuIHtTdHJpbmd9ICAgT2JqZWN0IHR5cGVcbiAgICovXG4sIGdldFR5cGU6IGZ1bmN0aW9uKG8pIHtcbiAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5hcHBseShvKS5yZXBsYWNlKC9eXFxbb2JqZWN0XFxzLywgJycpLnJlcGxhY2UoL1xcXSQvLCAnJylcbiAgfVxuXG4gIC8qKlxuICAgKiBJZiBpdCBpcyBhIHRvdWNoIGV2ZW50IHRoYW4gYWRkIGNsaWVudFggYW5kIGNsaWVudFkgdG8gZXZlbnQgb2JqZWN0XG4gICAqXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAgICogQHBhcmFtICB7U1ZHU1ZHRWxlbWVudH0gc3ZnXG4gICAqL1xuLCBtb3VzZUFuZFRvdWNoTm9ybWFsaXplOiBmdW5jdGlvbihldnQsIHN2Zykge1xuICAgIC8vIElmIG5vIGNsaWVudFggdGhlbiBmYWxsYmFja1xuICAgIGlmIChldnQuY2xpZW50WCA9PT0gdm9pZCAwIHx8IGV2dC5jbGllbnRYID09PSBudWxsKSB7XG4gICAgICAvLyBGYWxsYmFja1xuICAgICAgZXZ0LmNsaWVudFggPSAwXG4gICAgICBldnQuY2xpZW50WSA9IDBcblxuICAgICAgLy8gSWYgaXQgaXMgYSB0b3VjaCBldmVudFxuICAgICAgaWYgKGV2dC50b3VjaGVzICE9PSB2b2lkIDAgJiYgZXZ0LnRvdWNoZXMubGVuZ3RoKSB7XG4gICAgICAgIGlmIChldnQudG91Y2hlc1swXS5jbGllbnRYICE9PSB2b2lkIDApIHtcbiAgICAgICAgICBldnQuY2xpZW50WCA9IGV2dC50b3VjaGVzWzBdLmNsaWVudFhcbiAgICAgICAgICBldnQuY2xpZW50WSA9IGV2dC50b3VjaGVzWzBdLmNsaWVudFlcbiAgICAgICAgfSBlbHNlIGlmIChldnQudG91Y2hlc1swXS5wYWdlWCAhPT0gdm9pZCAwKSB7XG4gICAgICAgICAgdmFyIHJlY3QgPSBzdmcuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cbiAgICAgICAgICBldnQuY2xpZW50WCA9IGV2dC50b3VjaGVzWzBdLnBhZ2VYIC0gcmVjdC5sZWZ0XG4gICAgICAgICAgZXZ0LmNsaWVudFkgPSBldnQudG91Y2hlc1swXS5wYWdlWSAtIHJlY3QudG9wXG4gICAgICAgIH1cbiAgICAgIC8vIElmIGl0IGlzIGEgY3VzdG9tIGV2ZW50XG4gICAgICB9IGVsc2UgaWYgKGV2dC5vcmlnaW5hbEV2ZW50ICE9PSB2b2lkIDApIHtcbiAgICAgICAgaWYgKGV2dC5vcmlnaW5hbEV2ZW50LmNsaWVudFggIT09IHZvaWQgMCkge1xuICAgICAgICAgIGV2dC5jbGllbnRYID0gZXZ0Lm9yaWdpbmFsRXZlbnQuY2xpZW50WFxuICAgICAgICAgIGV2dC5jbGllbnRZID0gZXZ0Lm9yaWdpbmFsRXZlbnQuY2xpZW50WVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGFuIGV2ZW50IGlzIGEgZG91YmxlIGNsaWNrL3RhcFxuICAgKiBUT0RPOiBGb3IgdG91Y2ggZ2VzdHVyZXMgdXNlIGEgbGlicmFyeSAoaGFtbWVyLmpzKSB0aGF0IHRha2VzIGluIGFjY291bnQgb3RoZXIgZXZlbnRzXG4gICAqICh0b3VjaG1vdmUgYW5kIHRvdWNoZW5kKS4gSXQgc2hvdWxkIHRha2UgaW4gYWNjb3VudCB0YXAgZHVyYXRpb24gYW5kIHRyYXZlbGVkIGRpc3RhbmNlXG4gICAqXG4gICAqIEBwYXJhbSAge0V2ZW50fSAgZXZ0XG4gICAqIEBwYXJhbSAge0V2ZW50fSAgcHJldkV2dCBQcmV2aW91cyBFdmVudFxuICAgKiBAcmV0dXJuIHtCb29sZWFufVxuICAgKi9cbiwgaXNEYmxDbGljazogZnVuY3Rpb24oZXZ0LCBwcmV2RXZ0KSB7XG4gICAgLy8gRG91YmxlIGNsaWNrIGRldGVjdGVkIGJ5IGJyb3dzZXJcbiAgICBpZiAoZXZ0LmRldGFpbCA9PT0gMikge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIC8vIFRyeSB0byBjb21wYXJlIGV2ZW50c1xuICAgIGVsc2UgaWYgKHByZXZFdnQgIT09IHZvaWQgMCAmJiBwcmV2RXZ0ICE9PSBudWxsKSB7XG4gICAgICB2YXIgdGltZVN0YW1wRGlmZiA9IGV2dC50aW1lU3RhbXAgLSBwcmV2RXZ0LnRpbWVTdGFtcCAvLyBzaG91bGQgYmUgbG93ZXIgdGhhbiAyNTAgbXNcbiAgICAgICAgLCB0b3VjaGVzRGlzdGFuY2UgPSBNYXRoLnNxcnQoTWF0aC5wb3coZXZ0LmNsaWVudFggLSBwcmV2RXZ0LmNsaWVudFgsIDIpICsgTWF0aC5wb3coZXZ0LmNsaWVudFkgLSBwcmV2RXZ0LmNsaWVudFksIDIpKVxuXG4gICAgICByZXR1cm4gdGltZVN0YW1wRGlmZiA8IDI1MCAmJiB0b3VjaGVzRGlzdGFuY2UgPCAxMFxuICAgIH1cblxuICAgIC8vIE5vdGhpbmcgZm91bmRcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBjdXJyZW50IHRpbWVzdGFtcCBhcyBhbiBpbnRlZ2VyXG4gICAqXG4gICAqIEByZXR1cm4ge051bWJlcn1cbiAgICovXG4sIG5vdzogRGF0ZS5ub3cgfHwgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuICB9XG5cbiAgLy8gRnJvbSB1bmRlcnNjb3JlLlxuICAvLyBSZXR1cm5zIGEgZnVuY3Rpb24sIHRoYXQsIHdoZW4gaW52b2tlZCwgd2lsbCBvbmx5IGJlIHRyaWdnZXJlZCBhdCBtb3N0IG9uY2VcbiAgLy8gZHVyaW5nIGEgZ2l2ZW4gd2luZG93IG9mIHRpbWUuIE5vcm1hbGx5LCB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHdpbGwgcnVuXG4gIC8vIGFzIG11Y2ggYXMgaXQgY2FuLCB3aXRob3V0IGV2ZXIgZ29pbmcgbW9yZSB0aGFuIG9uY2UgcGVyIGB3YWl0YCBkdXJhdGlvbjtcbiAgLy8gYnV0IGlmIHlvdSdkIGxpa2UgdG8gZGlzYWJsZSB0aGUgZXhlY3V0aW9uIG9uIHRoZSBsZWFkaW5nIGVkZ2UsIHBhc3NcbiAgLy8gYHtsZWFkaW5nOiBmYWxzZX1gLiBUbyBkaXNhYmxlIGV4ZWN1dGlvbiBvbiB0aGUgdHJhaWxpbmcgZWRnZSwgZGl0dG8uXG4vLyBqc2NzOmRpc2FibGVcbi8vIGpzaGludCBpZ25vcmU6c3RhcnRcbiwgdGhyb3R0bGU6IGZ1bmN0aW9uKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgdmFyIGNvbnRleHQsIGFyZ3MsIHJlc3VsdDtcbiAgICB2YXIgdGltZW91dCA9IG51bGw7XG4gICAgdmFyIHByZXZpb3VzID0gMDtcbiAgICBpZiAoIW9wdGlvbnMpIG9wdGlvbnMgPSB7fTtcbiAgICB2YXIgbGF0ZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIHByZXZpb3VzID0gb3B0aW9ucy5sZWFkaW5nID09PSBmYWxzZSA/IDAgOiB0aGF0Lm5vdygpO1xuICAgICAgdGltZW91dCA9IG51bGw7XG4gICAgICByZXN1bHQgPSBmdW5jLmFwcGx5KGNvbnRleHQsIGFyZ3MpO1xuICAgICAgaWYgKCF0aW1lb3V0KSBjb250ZXh0ID0gYXJncyA9IG51bGw7XG4gICAgfTtcbiAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgbm93ID0gdGhhdC5ub3coKTtcbiAgICAgIGlmICghcHJldmlvdXMgJiYgb3B0aW9ucy5sZWFkaW5nID09PSBmYWxzZSkgcHJldmlvdXMgPSBub3c7XG4gICAgICB2YXIgcmVtYWluaW5nID0gd2FpdCAtIChub3cgLSBwcmV2aW91cyk7XG4gICAgICBjb250ZXh0ID0gdGhpcztcbiAgICAgIGFyZ3MgPSBhcmd1bWVudHM7XG4gICAgICBpZiAocmVtYWluaW5nIDw9IDAgfHwgcmVtYWluaW5nID4gd2FpdCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgICAgIHRpbWVvdXQgPSBudWxsO1xuICAgICAgICBwcmV2aW91cyA9IG5vdztcbiAgICAgICAgcmVzdWx0ID0gZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcbiAgICAgICAgaWYgKCF0aW1lb3V0KSBjb250ZXh0ID0gYXJncyA9IG51bGw7XG4gICAgICB9IGVsc2UgaWYgKCF0aW1lb3V0ICYmIG9wdGlvbnMudHJhaWxpbmcgIT09IGZhbHNlKSB7XG4gICAgICAgIHRpbWVvdXQgPSBzZXRUaW1lb3V0KGxhdGVyLCByZW1haW5pbmcpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuICB9XG4vLyBqc2hpbnQgaWdub3JlOmVuZFxuLy8ganNjczplbmFibGVcblxuICAvKipcbiAgICogQ3JlYXRlIGEgcmVxdWVzdEFuaW1hdGlvbkZyYW1lIHNpbXVsYXRpb25cbiAgICpcbiAgICogQHBhcmFtICB7TnVtYmVyfFN0cmluZ30gcmVmcmVzaFJhdGVcbiAgICogQHJldHVybiB7RnVuY3Rpb259XG4gICAqL1xuLCBjcmVhdGVSZXF1ZXN0QW5pbWF0aW9uRnJhbWU6IGZ1bmN0aW9uKHJlZnJlc2hSYXRlKSB7XG4gICAgdmFyIHRpbWVvdXQgPSBudWxsXG5cbiAgICAvLyBDb252ZXJ0IHJlZnJlc2hSYXRlIHRvIHRpbWVvdXRcbiAgICBpZiAocmVmcmVzaFJhdGUgIT09ICdhdXRvJyAmJiByZWZyZXNoUmF0ZSA8IDYwICYmIHJlZnJlc2hSYXRlID4gMSkge1xuICAgICAgdGltZW91dCA9IE1hdGguZmxvb3IoMTAwMCAvIHJlZnJlc2hSYXRlKVxuICAgIH1cblxuICAgIGlmICh0aW1lb3V0ID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fCByZXF1ZXN0VGltZW91dCgzMylcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHJlcXVlc3RUaW1lb3V0KHRpbWVvdXQpXG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlIGEgY2FsbGJhY2sgdGhhdCB3aWxsIGV4ZWN1dGUgYWZ0ZXIgYSBnaXZlbiB0aW1lb3V0XG4gKlxuICogQHBhcmFtICB7RnVuY3Rpb259IHRpbWVvdXRcbiAqIEByZXR1cm4ge0Z1bmN0aW9ufVxuICovXG5mdW5jdGlvbiByZXF1ZXN0VGltZW91dCh0aW1lb3V0KSB7XG4gIHJldHVybiBmdW5jdGlvbihjYWxsYmFjaykge1xuICAgIHdpbmRvdy5zZXRUaW1lb3V0KGNhbGxiYWNrLCB0aW1lb3V0KVxuICB9XG59XG5cbn0se31dfSx7fSxbMV0pO1xuIiwiLy8g0LLQuNC00LbQtdGCINCx0LjQu9C10YLQvtCyXG4vLyB0b2RvINC/0LXRgNC10LTQtdC70YLRjCDQvdCwIHR5cGVzY3JpcHQg0LLRgdC1LCDRgNCw0LfQsdC40YLRjCDQvdCwINC80L7QtNGD0LvQuCwg0L/RgNC+0L/QuNGB0LDRgtGMINC40L3RgtC10YDRhNC10LnRgdGLINC4INC+0LHRj9C30LDRgtC10LvRjNC90YvQtSDRgtC40L/Riywg0LAg0YLQsNC60LbQtSDQtNC+0LrRg9C80LXQvdGC0LjRgNC+0LLQsNGC0Ywg0LLRgdGRXG4vL3RvZG8g0LLRi9Cy0LXRgdGC0Lgg0LIg0L7RgtC00LXQu9GM0L3Rg9GOINCx0LjQsdC70LjQvtGC0LXQutGDLdC00LjRgNC10LrRgtC+0YDQuNGOXG4vL3RvZG8gIGh0dHBzOi8vbWVkaXVtLmNvbS93ZWJiZGV2LzctJUQxJTgwJUQwJUI1JUQwJUJBJUQwJUJFJUQwJUJDJUQwJUI1JUQwJUJEJUQwJUI0JUQwJUIwJUQxJTg2JUQwJUI4JUQwJUI5LSVEMCVCRiVEMCVCRS0lRDAlQkUlRDElODQlRDAlQkUlRDElODAlRDAlQkMlRDAlQkIlRDAlQjUlRDAlQkQlRDAlQjglRDElOEUtJUQwJUJBJUQwJUJFJUQwJUI0JUQwJUIwLSVEMCVCRCVEMCVCMC1qYXZhc2NyaXB0LTRkMGQ0YzI4NzI4MVxuLy90b2RvIGh0dHBzOi8vaGFici5jb20vcnUvYXJ0aWNsZXMvMjMxODgzL1xuLy90b2RvIGh0dHBzOi8vc3RhbmRhcmRqcy5jb20vXG4vL3RvZG8g0LzQsSDRgdC00LXQu9Cw0YLRjCBwdWxsIHJlcXVlc3Qg0LIgIHBhbnpvb20g0L/QviDQv9C+0LLQvtC00YMg0LDQvdC40LzQsNGG0LjQuCDQuCDQv9C+INC/0L7QstC+0LTRgyDRgNC10LbQuNC80LAg0LLQtdC00LXQvdC40Y8g0LzRi9GI0Lgg0LfQsCDQv9GA0LXQtNC10LvQsNC80Lgg0LfQvtC90Ysg0LggaW52YWxpZGF0ZVNpemUg0L3QsNC/0YDQuNC80LXRgCAsINGC0LDQutC20LUg0LTQvtCx0LDQstC40YLRjCDQutC+0LvQsdC10LrQvtCyINC90LDQv9GA0LjQvNC10YBcbi8vINGA0LDQt9Cx0LjRgtGMIGxlc3Mg0L3QsCDRh9Cw0YHRgtC4P1xuXG5jbGFzcyBUV0NhcnRzU3RvcmFnZSB7XG4gICAgLy90b2RvINC00L7QsdCw0LLQuNGC0Ywg0LrQvtC90YHRgtGA0YPQutGC0L7RgCDRgSDQv9C10YDQtdC00LDRh9C10Lkg0LrQu9GO0YfQsFxuICAgIC8vdG9kbyDQtNCw0LLQsNCy0LjRgtGMINCyINC60L7QvdGB0YLRgNGD0LrRgtC+0YAg0LjQu9C4INC40L3Ri9C8INC+0LHRgNCw0LfQvtC8INC/0L7QtNCy0Y/Qt9C60LAg0L3QsCBjYWxsYmFjayDQvtCx0L3QvtCy0LvQtdC90LjRjyDRhdGA0LDQvdC40LvQuNGJ0LAsINGH0YLQvtCxINC40L3QvtGA0L7QtNC90YvQuSDQutC+0LQg0LLRi9GH0LvQtdC90LjRgtGMLiDQntCx0LfQtdGA0LLQtdGAINC00L7QsdCw0LLQuNGC0Ywg0LIg0L7QsdGJ0LXQvFxuICAgIHN0YXRpYyBnZXQgc3RvcmFnZUtleSgpIHtcbiAgICAgICAgcmV0dXJuICdjYXJ0cyc7XG4gICAgfVxuXG4gICAgc3RhdGljIGFsbChjYXN0Q2FydE9iaiA9IGZhbHNlKSB7XG4gICAgICAgIGxldCBjYXJ0cyA9IEpTT04ucGFyc2Uod2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKHRoaXMuc3RvcmFnZUtleSkpIHx8IHt9O1xuXG4gICAgICAgIGlmIChjYXN0Q2FydE9iaikge1xuICAgICAgICAgICAgZm9yIChjb25zdCBbY2FydElkLCBjYXJ0XSBvZiBPYmplY3QuZW50cmllcyhjYXJ0cykpIHtcbiAgICAgICAgICAgICAgICBjYXJ0c1tjYXJ0SWRdID0gdGhpcy5jYXN0Q2FydChjYXJ0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjYXJ0cztcbiAgICB9XG5cbiAgICBzdGF0aWMgY2FydHNDb3VudCgpIHtcbiAgICAgICAgY29uc3QgY2FydHMgPSBKU09OLnBhcnNlKHdpbmRvdy5sb2NhbFN0b3JhZ2UuZ2V0SXRlbSh0aGlzLnN0b3JhZ2VLZXkpKSB8fCB7fTtcbiAgICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKGNhcnRzKS5sZW5ndGg7XG4gICAgfVxuXG4gICAgc3RhdGljIGdldEFsbFRvdGFsUW50KCkge1xuICAgICAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyh0aGlzLmFsbCh0cnVlKSkucmVkdWNlKChyZXN1bHQsIGNhcnQpID0+IHJlc3VsdCArIGNhcnQudGlja2V0c0NvdW50KCksIDApO1xuICAgIH1cblxuICAgIHN0YXRpYyBmaW5kQ2FydChjYXJ0SWQsIGNhc3QgPSB0cnVlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmFsbChjYXN0KVtjYXJ0SWRdIHx8IG51bGw7XG4gICAgfVxuXG4gICAgc3RhdGljIGNhc3RDYXJ0KGNhcnRPYikge1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMoY2FydE9iKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHJldHVybiBuZXcgVFdDYXJ0KGNhcnRPYi5ldmVudCwgY2FydE9iKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjYXJ0T2I7XG4gICAgfVxuXG4gICAgc3RhdGljIHNhdmUoY2FydCkge1xuICAgICAgICBjb25zdCBjYXJ0cyA9IHRoaXMuYWxsKHRydWUpO1xuICAgICAgICBjYXJ0c1tjYXJ0LmlkXSA9IGNhcnQ7XG5cbiAgICAgICAgZm9yIChjb25zdCBbY2FydElkLCBjYXJ0XSBvZiBPYmplY3QuZW50cmllcyhjYXJ0cykpIHtcbiAgICAgICAgICAgIGlmICgoY2FydC5ldmVudC5kYXRlICogMTAwMCkgPCBuZXcgRGF0ZSgpIHx8IGNhcnQuaXNFbXB0eSgpKSB7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGNhcnRzW2NhcnRJZF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgd2luZG93LmxvY2FsU3RvcmFnZS5zZXRJdGVtKHRoaXMuc3RvcmFnZUtleSwgSlNPTi5zdHJpbmdpZnkoY2FydHMpKTtcbiAgICAgICAgVFcudXBkYXRlR2xvYmFsQ2FydFRvdGFsUW50KCk7XG4gICAgfVxuXG4gICAgc3RhdGljIGNsZWFyKCkge1xuICAgICAgICB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0odGhpcy5zdG9yYWdlS2V5LCBKU09OLnN0cmluZ2lmeSh7fSkpO1xuICAgICAgICBUVy51cGRhdGVHbG9iYWxDYXJ0VG90YWxRbnQoKTtcbiAgICB9XG59XG5cbmNsYXNzIFRXQ2FydEl0ZW0ge1xuICAgIGNvbnN0cnVjdG9yKGVsZW1Pck9iaikge1xuICAgICAgICB0aGlzLl9kb21FbGVtID0gdW5kZWZpbmVkO1xuXG4gICAgICAgIGlmIChlbGVtT3JPYmoubm9kZU5hbWUpIHsgLy/Qu9GO0LHQsNGPINGB0YPRidC90L7RgdGC0Ywgc3ZnIC0gY2lyY2xlLCBwYXRoINC4INGC0L9cbiAgICAgICAgICAgIGNvbnN0IGF0dHJzID0gT2JqZWN0LmZyb21FbnRyaWVzKEFycmF5LmZyb20oZWxlbU9yT2JqLmF0dHJpYnV0ZXMpLm1hcChpdGVtID0+IFtpdGVtLm5hbWUsIGl0ZW0udmFsdWVdKSk7XG5cbiAgICAgICAgICAgIGNvbnN0IHNlY3Rvck5hbWUgPSBhdHRyc1sncGxhY2UtbmFtZSddO1xuICAgICAgICAgICAgY29uc3Qgcm93ID0gYXR0cnNbJ3JvdyddO1xuICAgICAgICAgICAgY29uc3QgcGxhY2UgPSBhdHRyc1sncGxhY2UnXTtcbiAgICAgICAgICAgIGNvbnN0IHByaWNlID0gYXR0cnNbJ3ByaWNlJ107XG4gICAgICAgICAgICBjb25zdCBwYXNzcG9ydCA9IGF0dHJzWydkYXRhLXBhc3Nwb3J0J107XG4gICAgICAgICAgICBjb25zdCBlVGlja2V0ID0gcGFyc2VJbnQoYXR0cnNbJ2RhdGEtZXRpY2tldCddKTtcbiAgICAgICAgICAgIGNvbnN0IHpyID0gYXR0cnNbJ2RhdGEtenInXTtcbiAgICAgICAgICAgIGNvbnN0IHpyQWdlbnQgPSBhdHRyc1snZGF0YS16ci1hZ2VudCddO1xuICAgICAgICAgICAgY29uc3Qgc291cmNlID0gYXR0cnNbJ2RhdGEtc291cmNlJ107XG5cbiAgICAgICAgICAgIGNvbnN0IGJ5U2VjdG9yID0gYXR0cnNbJ2RhdGEtcW50LW1vcmUnXTtcbiAgICAgICAgICAgIGNvbnN0IHZpcCA9IGF0dHJzWydkYXRhLXZpcCddIHx8IDA7XG4gICAgICAgICAgICBjb25zdCBhZGRRbnQgPSBhdHRyc1snZGF0YS1xbnQtbW9yZSddIHx8IDE7XG4gICAgICAgICAgICBjb25zdCBwYXltZW50ID0gYXR0cnNbJ2RhdGEtcGF5bWVudCddIHx8IDE7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHRoaXMucHJpY2VfbiA9IGF0dHJzWydwcmljZV9uJ10gfHwgJyc7XG4gICAgICAgICAgICB0aGlzLnByaWNlX3ogPSBhdHRyc1sncHJpY2VfeiddIHx8ICcnO1xuICAgICAgICAgICAgdGhpcy5pc0dpZnQgPSAhIU51bWJlcihhdHRyc1snZGF0YS1naWZ0J10gfHwgJycpO1xuICAgICAgICAgICAgdGhpcy5wbGFjZU5hbWUgPSBzZWN0b3JOYW1lIHx8ICcnO1xuICAgICAgICAgICAgdGhpcy5yb3cgPSByb3cgfHwgJyc7XG4gICAgICAgICAgICB0aGlzLnBsYWNlID0gcGxhY2UgfHwgJyc7XG4gICAgICAgICAgICB0aGlzLm9uT3JkZXIgPSBpc05hTihwcmljZSk7XG4gICAgICAgICAgICB0aGlzLnByaWNlID0gdGhpcy5vbk9yZGVyID8gMCA6ICtwcmljZTtcbiAgICAgICAgICAgIHRoaXMuZVRpY2tldCA9ICtlVGlja2V0O1xuICAgICAgICAgICAgdGhpcy5ieVNlY3RvciA9ICtieVNlY3RvcjtcbiAgICAgICAgICAgIHRoaXMucW50ID0gK2FkZFFudDtcbiAgICAgICAgICAgIHRoaXMuYWxsb3dQYXltZW50ID0gK3BheW1lbnQ7XG4gICAgICAgICAgICB0aGlzLm5lZWRQYXNzcG9ydCA9ICtwYXNzcG9ydDsgLy/QvdGD0LbQvdGLXG4gICAgICAgICAgICB0aGlzLnpyID0genIgfHwgJyc7IC8v0L3Rg9C20L3Ri1xuICAgICAgICAgICAgdGhpcy56ckFnZW50ID0genJBZ2VudCB8fCAnJzsgLy/QvdGD0LbQvdGLXG4gICAgICAgICAgICB0aGlzLmlzVmlwID0gK3ZpcDsgLy/QvdGD0LbQvdGLXG4gICAgICAgICAgICB0aGlzLnNvdXJjZUlkID0gK3NvdXJjZTsgLy/QvdGD0LbQvdGLXG5cbiAgICAgICAgICAgIHRoaXMuaWQgPSBUVy5nZW5lcmF0ZVRpY2tldElkKHRoaXMucm93LCB0aGlzLnBsYWNlLCB0aGlzLnBsYWNlTmFtZSk7XG4gICAgICAgICAgICB0aGlzLmdyb3VwID0gdGhpcy56ciA/IHRoaXMuenIgOiB0aGlzLmlkO1xuXG4gICAgICAgICAgICBpZiAoIWVsZW1Pck9iai5pZCkge1xuICAgICAgICAgICAgICAgIGVsZW1Pck9iai5pZCA9IHRoaXMuaWQ7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGVsZW1Pck9iai5pZCAhPSB0aGlzLmlkKSB7XG4gICAgICAgICAgICAgICAgZWxlbU9yT2JqLmlkICs9ICcgJyArIHRoaXMuaWQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGVsZW1Pck9iaikpIHtcbiAgICAgICAgICAgICAgICBpZiAoIVsnaGFzaCddLmluY2x1ZGVzKGtleSkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpc1trZXldID0gdmFsdWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2V0IGhhc2goKSB7XG4gICAgICAgIGxldCBhY3R1YWxLZXlzID0gT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXModGhpcykuZmlsdGVyKGtleSA9PiAhWydfZG9tRWxlbScsICdoYXNoJywgJ2dyb3VwJywgJ2RvY3VtZW50RGF0YSddLmluY2x1ZGVzKGtleSkpO1xuICAgICAgICByZXR1cm4gd2luZG93LmJ0b2EoYWN0dWFsS2V5cy5zb3J0KCkuam9pbignLCcpKTtcbiAgICB9XG5cbiAgICBnZXQgZG9tRWxlbSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2RvbUVsZW0gfHwgQXJyYXkuZnJvbShUVy5hdmFpbGFibGVUaWNrZXRzQ2lyY2xlRWxlbXMpLmZpbmQoZWwgPT4gZWwuaWQ/LmluY2x1ZGVzKHRoaXMuaWQpKTtcbiAgICB9XG5cbiAgICB0b0pTT04oKSB7XG4gICAgICAgIGxldCBkYXRhID0gT2JqZWN0LmFzc2lnbih7fSwgdGhpcywge2hhc2g6IHRoaXMuaGFzaH0pO1xuXG4gICAgICAgIGlmIChkYXRhLl9kb21FbGVtKSB7XG4gICAgICAgICAgICBkZWxldGUgZGF0YS5fZG9tRWxlbTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH1cbn1cblxuY2xhc3MgVFdDYXJ0IHtcbiAgICBjb25zdHJ1Y3RvcihldmVudFBhcmFtcywgZGF0YSA9IHt9KSB7XG4gICAgICAgIHRoaXMuY3JlYXRlZEF0ID0gZGF0YS5jcmVhdGVkQXQgfHwgK25ldyBEYXRlKCk7XG4gICAgICAgIHRoaXMuZG9jdW1lbnRGaWVsZHMgPSBkYXRhLmRvY3VtZW50RmllbGRzIHx8ICcnO1xuXG4gICAgICAgIGlmIChkYXRhLmFjdGl2YXRlZEF0KSB7XG4gICAgICAgICAgICB0aGlzLmFjdGl2YXRlZEF0ID0gZGF0YS5hY3RpdmF0ZWRBdDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBAdHlwZSB7e2RhdGUsaWQsbmFtZSxkZXNjLHBsYWNlSWQscGxhY2VOYW1lLGhhbGxJZCxoYWxsTmFtZSxwYXJ0SWR9fVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5fZXZlbnQgPSB7Li4uZXZlbnRQYXJhbXN9O1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKlxuICAgICAgICAgKiBAdHlwZSB7VFdDYXJ0SXRlbVtdfVxuICAgICAgICAgKiBAcHJpdmF0ZVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5faXRlbXMgPSBbXTtcblxuICAgICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgKGRhdGEuaXRlbXMgfHwgW10pKSB7XG4gICAgICAgICAgICBjb25zdCBjYXN0Q2FydEl0ZW0gPSBuZXcgVFdDYXJ0SXRlbShpdGVtKTtcbiAgICAgICAgICAgIGlmIChpdGVtICE9PSBudWxsICYmIGNhc3RDYXJ0SXRlbS5oYXNoID09PSBpdGVtLmhhc2gpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9pdGVtcy5wdXNoKGNhc3RDYXJ0SXRlbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXQgaWQoKSB7XG4gICAgICAgIHJldHVybiBgJHt0aGlzLmV2ZW50LmlkfS0ke3RoaXMuZXZlbnQuZGF0ZX0tJHt0aGlzLmV2ZW50LnBsYWNlSWR9LSR7dGhpcy5ldmVudC5oYWxsSWR9YDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAcmV0dXJucyB7UmVhZG9ubHk8VFdDYXJ0SXRlbT5bXX1cbiAgICAgKi9cbiAgICBnZXQgaXRlbXMoKSB7XG4gICAgICAgIC8vIHJldHVybiBPYmplY3QuZnJlZXplKFsuLi50aGlzLl9pdGVtc10ubWFwKGl0ZW0gPT4gT2JqZWN0LmZyZWV6ZShuZXcgVFdDYXJ0SXRlbShpdGVtKSkpKTtcbiAgICAgICAgLy8gcmV0dXJuIFsuLi50aGlzLl9pdGVtc10ubWFwKGl0ZW0gPT4gbmV3IFRXQ2FydEl0ZW0oaXRlbSkpO1xuICAgICAgICByZXR1cm4gWy4uLnRoaXMuX2l0ZW1zXTtcbiAgICB9XG5cbiAgICBnZXRJdGVtc0FzT2JqKCl7XG4gICAgICAgIGxldCByZXMgPSB7fTtcblxuICAgICAgICB0aGlzLl9pdGVtcy5mb3JFYWNoKChpdGVtLCBrZXkpID0+IHtcbiAgICAgICAgICAgIHJlc1trZXldID0gaXRlbTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQHJldHVybnMge1JlYWRvbmx5PHtkYXRlLCBpZCwgbmFtZSwgZGVzYywgcGxhY2VJZCwgcGxhY2VOYW1lLCBoYWxsSWQsIGhhbGxOYW1lLCBwYXJ0SWR9Pn1cbiAgICAgKi9cbiAgICBnZXQgZXZlbnQoKSB7XG4gICAgICAgIHJldHVybiBPYmplY3QuZnJlZXplKHRoaXMuX2V2ZW50KTtcbiAgICB9XG5cbiAgICBhY3R1YWxpemUoKSB7XG4gICAgICAgIGNvbnN0IGNhcnQgPSBUV0NhcnRzU3RvcmFnZS5maW5kQ2FydCh0aGlzLmlkKSB8fCB0aGlzO1xuICAgICAgICB0aGlzLmFjdGl2YXRlZEF0ID0gY2FydC5hY3RpdmF0ZWRBdCB8fCAwO1xuICAgICAgICB0aGlzLmNyZWF0ZWRBdCA9IGNhcnQuY3JlYXRlZEF0O1xuXG4gICAgICAgIGxldCBhY3R1YWxJdGVtcyA9IFtdO1xuXG4gICAgICAgIGlmIChUVy5hdmFpbGFibGVUaWNrZXRzQ2lyY2xlRWxlbXMubGVuZ3RoKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBjYXJ0SXRlbSBvZiBjYXJ0Lml0ZW1zKSB7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBkb21FbGVtIG9mIFRXLmF2YWlsYWJsZVRpY2tldHNDaXJjbGVFbGVtcykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBlbGVtQXNDYXJ0SXRlbSA9IG5ldyBUV0NhcnRJdGVtKGRvbUVsZW0pO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICghZWxlbUFzQ2FydEl0ZW0uYnlTZWN0b3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvbUVsZW0uaWQgPSBlbGVtQXNDYXJ0SXRlbS5pZDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChlbGVtQXNDYXJ0SXRlbS5pZCA9PT0gY2FydEl0ZW0uaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHFudCA9IGNhcnRJdGVtLnFudDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRvY3VtZW50RGF0YSA9IGNhcnRJdGVtLmRvY3VtZW50RGF0YTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhcnRJdGVtID0gZWxlbUFzQ2FydEl0ZW07XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXJ0SXRlbS5xbnQgPSBxbnQ7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkb2N1bWVudERhdGEpe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhcnRJdGVtLmRvY3VtZW50RGF0YSA9IGRvY3VtZW50RGF0YTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgZG9tRWxlbS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGFjdHVhbEl0ZW1zLnB1c2goY2FydEl0ZW0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKFRXLmNvbmZpZy5hbGxQbGFjZXMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2FydEl0ZW0gb2YgY2FydC5pdGVtcykge1xuICAgICAgICAgICAgICAgIGlmIChjYXJ0SXRlbS5pZCAmJiBUVy5jb25maWcuYWxsUGxhY2VzLmZpbmQocGxhY2UgPT4gcGxhY2UuaWQgPT09IGNhcnRJdGVtLmlkKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCAkZG9tRWxlbSA9IFRXLiQkKGAudHdfX3RpY2tldHMtcGxhY2VbZGF0YS1pZD1cIiR7Y2FydEl0ZW0uaWR9XCJdYCkuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKCRkb21FbGVtLmxlbmd0aCl7XG4gICAgICAgICAgICAgICAgICAgICAgICBhY3R1YWxJdGVtcy5wdXNoKHsuLi5jYXJ0SXRlbSwgX2RvbUVsZW06ICRkb21FbGVtLmdldCgwKX0pO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgYWN0dWFsSXRlbXMucHVzaChjYXJ0SXRlbSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9pdGVtcyA9IGFjdHVhbEl0ZW1zO1xuXG4gICAgICAgIFRXQ2FydHNTdG9yYWdlLnNhdmUodGhpcyk7IC8v0LTQu9GPINGB0LDQvNC+0L7Rh9C40YHRgtC60Lgg0YXRgNCw0L3QuNC70LjRidCwLCDQtdGB0LvQuCDQvdCwINGB0YXQtdC80LUg0LrQsNC60LjQtS3RgtC+INCx0LjQu9C10YLRiyDQuNC3INGF0YDQsNC90LjQu9C40YnQsCDQvdC1INC90LDQudC00LXQvdGLXG4gICAgfVxuXG4gICAgYWRkKGNhcnRJdGVtKSB7XG4gICAgICAgIHRoaXMuYWN0aXZhdGVkQXQgPSB0aGlzLl9pdGVtcy5sZW5ndGggPiAwICYmIHRoaXMuYWN0aXZhdGVkQXQgPyB0aGlzLmFjdGl2YXRlZEF0IDogK25ldyBEYXRlKCk7XG5cbiAgICAgICAgdGhpcy5faXRlbXMucHVzaChjYXJ0SXRlbSk7XG4gICAgICAgIFRXQ2FydHNTdG9yYWdlLnNhdmUodGhpcyk7XG4gICAgfVxuXG4gICAgdXBkYXRlKGlkLCBmaWVsZHMpIHtcbiAgICAgICAgbGV0IGl0ZW0gPSB0aGlzLl9pdGVtcy5maW5kKGZpZWxkcyA9PiBmaWVsZHMuaWQgPT09IGlkKTtcbiAgICAgICAgT2JqZWN0LmFzc2lnbihpdGVtLCBmaWVsZHMpO1xuICAgICAgICBUV0NhcnRzU3RvcmFnZS5zYXZlKHRoaXMpO1xuICAgIH1cblxuICAgIGRlbGV0ZShpZCkge1xuICAgICAgICBjb25zdCBkZWxldGVJbmRleCA9IHRoaXMuX2l0ZW1zLmZpbmRJbmRleChmaWVsZHMgPT4gZmllbGRzLmlkID09IGlkKTtcbiAgICAgICAgY29uc3QgZGVsZXRlSXRlbSA9IHRoaXMuX2l0ZW1zW2RlbGV0ZUluZGV4XTtcblxuICAgICAgICBpZiAoZGVsZXRlSXRlbSkge1xuICAgICAgICAgICAgbGV0IGRlbGV0ZUluZGV4ZXMgPSBbZGVsZXRlSW5kZXhdO1xuICAgICAgICAgICAgLy8gdGhpcy5faXRlbXMuZm9yRWFjaCgoX2l0ZW0sIGluZGV4KSA9PiB7XG4gICAgICAgICAgICAvLyAgICAgaWYgKFxuICAgICAgICAgICAgLy8gICAgICAgICBpbmRleCAhPT0gZGVsZXRlSW5kZXggJiZcbiAgICAgICAgICAgIC8vICAgICAgICAgZGVsZXRlSXRlbS5ncm91cCAhPT0gbnVsbCAmJlxuICAgICAgICAgICAgLy8gICAgICAgICBkZWxldGVJdGVtLmdyb3VwICE9PSAnMCcgJiZcbiAgICAgICAgICAgIC8vICAgICAgICAgX2l0ZW0uZ3JvdXAgPT09IGRlbGV0ZUl0ZW0uZ3JvdXBcbiAgICAgICAgICAgIC8vICAgICApe1xuICAgICAgICAgICAgLy8gICAgICAgICBkZWxldGVJbmRleGVzLnB1c2goaW5kZXgpO1xuICAgICAgICAgICAgLy8gICAgIH1cbiAgICAgICAgICAgIC8vIH0pXG5cbiAgICAgICAgICAgIGNvbnN0IGNhcnRJdGVtc0FzT2JqID0gdGhpcy5nZXRJdGVtc0FzT2JqKCk7XG5cbiAgICAgICAgICAgIGRlbGV0ZUluZGV4ZXMuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgICAgICAgIGNhcnRJdGVtc0FzT2JqW2tleV0/LmRvbUVsZW0/LmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGRlbGV0ZSBjYXJ0SXRlbXNBc09ialtrZXldO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHRoaXMuX2l0ZW1zID0gT2JqZWN0LnZhbHVlcyhjYXJ0SXRlbXNBc09iaik7XG5cbiAgICAgICAgICAgIGNvbnN0IGdpZnRJdGVtSW5kZXggPSB0aGlzLl9pdGVtcy5maW5kSW5kZXgoaXRlbSA9PiBpdGVtLmlzR2lmdCk7XG4gICAgICAgICAgICBjb25zdCBnaWZ0SXRlbSA9IHRoaXMuX2l0ZW1zW2dpZnRJdGVtSW5kZXhdID8/IG51bGw7XG4gICAgICAgICAgICBjb25zdCB0aWNrZXRzQ291bnQgPSB0aGlzLnRpY2tldHNDb3VudChmYWxzZSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmIChnaWZ0SXRlbSAmJiBnaWZ0SXRlbS5xbnQgPiB0aWNrZXRzQ291bnQpe1xuICAgICAgICAgICAgICAgIGlmICh0aWNrZXRzQ291bnQgPiAwKXtcbiAgICAgICAgICAgICAgICAgICAgZ2lmdEl0ZW0ucW50ID0gdGlja2V0c0NvdW50O1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2l0ZW1zLnNwbGljZShnaWZ0SXRlbUluZGV4LCAxKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFRXQ2FydHNTdG9yYWdlLnNhdmUodGhpcyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXNldCgpIHtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHRoaXMuX2l0ZW1zKSB7XG4gICAgICAgICAgICBpdGVtLmRvbUVsZW0/LmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2l0ZW1zID0gW107XG4gICAgICAgIHRoaXMuYWN0aXZhdGVkQXQgPSAwO1xuXG4gICAgICAgIFRXQ2FydHNTdG9yYWdlLnNhdmUodGhpcyk7XG4gICAgfVxuXG4gICAgcmVhY3RpdmF0ZSgpIHtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZWRBdCA9ICtuZXcgRGF0ZSgpO1xuICAgICAgICBUV0NhcnRzU3RvcmFnZS5zYXZlKHRoaXMpO1xuICAgIH1cblxuICAgIHN1bSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2l0ZW1zLmZpbmQob2JqID0+IG9iai5vbk9yZGVyID09PSB0cnVlKSAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgOiB0aGlzLl9pdGVtcy5yZWR1Y2UoKHJlc3VsdCwgb2JqKSA9PiByZXN1bHQgKyBvYmoucHJpY2UgKiBvYmoucW50LCAwKTtcbiAgICB9XG5cbiAgICB0aWNrZXRzQ291bnQoaW5jbHVkZUdpZnRzID0gdHJ1ZSkge1xuICAgICAgICBjb25zdCBpdGVtcyA9IGluY2x1ZGVHaWZ0cyA/IHRoaXMuX2l0ZW1zIDogdGhpcy5faXRlbXMuZmlsdGVyKGl0ZW0gPT4gISBpdGVtLmlzR2lmdCk7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gaXRlbXMucmVkdWNlKChyZXN1bHQsIG9iaikgPT4gcmVzdWx0ICsgK29iai5xbnQsIDApO1xuICAgIH1cbiAgICBcbiAgICBnaWZ0c0NvdW50KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5faXRlbXMuZmlsdGVyKGl0ZW0gPT4gaXRlbS5pc0dpZnQpLnJlZHVjZSgocmVzdWx0LCBvYmopID0+IHJlc3VsdCArICtvYmoucW50LCAwKTtcbiAgICB9XG5cbiAgICBpc0VtcHR5KCkge1xuICAgICAgICByZXR1cm4gIXRoaXMuX2l0ZW1zLmxlbmd0aDtcbiAgICB9XG5cbiAgICB0b0pTT04oKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBpdGVtczogdGhpcy5pdGVtcyxcbiAgICAgICAgICAgIGV2ZW50OiB0aGlzLmV2ZW50LFxuICAgICAgICAgICAgYWN0aXZhdGVkQXQ6IHRoaXMuYWN0aXZhdGVkQXQsXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IHRoaXMuY3JlYXRlZEF0LFxuICAgICAgICAgICAgZG9jdW1lbnRGaWVsZHM6IHRoaXMuZG9jdW1lbnRGaWVsZHMsXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmNsYXNzIFRpY2tldHNXaWRnZXQge1xuICAgIGNvbnN0cnVjdG9yKHNlbGVjdG9yLCBwYXJhbXMgPSB7fSkge1xuICAgICAgICB0aGlzLlNDSEVNRV9MSUJfTEVBRkxFVCA9IDE7XG4gICAgICAgIHRoaXMuU0NIRU1FX0xJQl9QQU5aT09NID0gMjtcblxuICAgICAgICB0aGlzLnVybCA9IHBhcmFtcy51cmwgfHwgJy93aWRnZXQvaW5kZXgucGhwJztcbiAgICAgICAgdGhpcy52ZXJzaW9uID0gcGFyYW1zLnZlcnNpb24gfHwgMTtcbiAgICAgICAgdGhpcy5kZWZhdWx0U2NoZW1lTGliID0gdGhpcy5TQ0hFTUVfTElCX1BBTlpPT007XG4gICAgICAgIHRoaXMuc2NoZW1lTGliID0gdGhpcy5kZWZhdWx0U2NoZW1lTGliO1xuXG4gICAgICAgIHRoaXMuJCA9ICQoc2VsZWN0b3IpO1xuICAgICAgICB0aGlzLiR0b29sdGlwID0gJCgpO1xuICAgICAgICB0aGlzLiRsb2FkZXIgPSAkKCk7XG4gICAgICAgIHRoaXMuJGhlYWRlciA9ICQoKTtcbiAgICAgICAgdGhpcy4kdHdTdmdNYXAgPSAkKCk7XG4gICAgICAgIHRoaXMuJGNvbmZpcm0gPSAkKCk7XG4gICAgICAgIHRoaXMuJHRvb2xzID0gJCgpO1xuICAgICAgICB0aGlzLiRjYWxlbmRhciA9ICQoKTtcbiAgICAgICAgdGhpcy4kbW9yZVBvcHVwID0gJCgpO1xuXG4gICAgICAgIHRoaXMuc2VsZWN0b3IgPSBzZWxlY3RvcjtcbiAgICAgICAgdGhpcy5jb25maWcgPSB7fTtcbiAgICAgICAgdGhpcy5wcmV2QWN0aXZlUGFuZWxJZCA9IG51bGw7XG4gICAgICAgIHRoaXMucHJldkFjdGl2ZVBhbmVsU2Nyb2xsVG9wID0gbnVsbDtcbiAgICAgICAgdGhpcy5hYm9ydENvbnRyb2xsZXJzID0ge1xuICAgICAgICAgICAgc3ZnOiBuZXcgQWJvcnRDb250cm9sbGVyKCksXG4gICAgICAgICAgICBldmVudERhdGVzOiBuZXcgQWJvcnRDb250cm9sbGVyKCksXG4gICAgICAgICAgICBoZWFkZXJEYXRlczogbmV3IEFib3J0Q29udHJvbGxlcigpLFxuICAgICAgICAgICAgY29uZmlnOiBuZXcgQWJvcnRDb250cm9sbGVyKCksXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMucmVzaXplQ2FsbGJhY2tUaW1lb3V0ID0gbnVsbDtcbiAgICAgICAgdGhpcy5hdmFpbGFibGVUaWNrZXRzQ2lyY2xlRWxlbXMgPSBbXTtcbiAgICAgICAgdGhpcy5sYXN0Q2xpY2tlZFRpY2tldEVsZW0gPSBudWxsO1xuICAgICAgICB0aGlzLmxhc3RIb3ZlclRpY2tldEVsZW0gPSBudWxsO1xuICAgICAgICB0aGlzLmlzTW9iaWxlV2lkdGggPSBmYWxzZTtcbiAgICAgICAgdGhpcy5pc1RvdWNoRGV2aWNlID0gZmFsc2U7XG4gICAgICAgIHRoaXMuaXNJbml0aWFsaXplZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLmZpbHRlciA9IHtcbiAgICAgICAgICAgIHVuaXF1ZVNlbGVjdG9yOiBudWxsLFxuICAgICAgICAgICAgY2xhc3M6ICd0dy1maWx0ZXInLFxuICAgICAgICAgICAgc2Nyb2xsZXJDbGFzczogJ3R3LWZpbHRlcl9fc2Nyb2xsZXInLFxuICAgICAgICAgICAgYnRuQ2xhc3M6ICd0dy1maWx0ZXJfX2J0bicsIFxuICAgICAgICAgICAgcHJpY2VHcm91cHNFbGVtczoge30sXG4gICAgICAgICAgICBtdWx0aXBsZU1vZGU6IHRydWUsXG4gICAgICAgICAgICBjb2xvcml6ZUFmdGVySW5pdDogdHJ1ZSxcbiAgICAgICAgICAgIHZpc2libGU6IHRydWUsXG4gICAgICAgICAgICB2aXNpYmxlQnRuc1FudDogMCxcbiAgICAgICAgICAgIG1pblByaWNlOiAwLFxuICAgICAgICAgICAgbWF4UHJpY2U6IDAsXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2NoZW1lID0gbnVsbDtcbiAgICAgICAgdGhpcy5zY2hlbWVJbml0ID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc2NoZW1lU3ZnT3ZlcmxheSA9IG51bGw7XG4gICAgICAgIHRoaXMuc2NoZW1lU3ZnQXR0cnMgPSB7fTtcbiAgICAgICAgdGhpcy5jYXJ0SW5zdCA9IG51bGw7XG4gICAgICAgIHRoaXMuZXJyb3JzID0gW107XG5cbiAgICAgICAgdGhpcy5jbGVhckV2ZW50UGFyYW1zKCk7XG5cbiAgICAgICAgd2luZG93LlRXID0gdGhpcztcblxuICAgICAgICBUVy5mZXRjaCh7XG4gICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnaW5pdCdcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKHJlc3BvbnNlID0+IHJlc3BvbnNlLmpzb24oKSlcbiAgICAgICAgICAgIC50aGVuKGRhdGEgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghZGF0YS5zdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IEVycm9yKCdzdWNjZXNzIGlzIG5vdCB0cnVlJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFRXLl9pbml0KGRhdGEuaHRtbCk7XG5cbiAgICAgICAgICAgICAgICBUVy5pbml0Q2FydHNUaW1lcigpO1xuICAgICAgICAgICAgICAgIFRXLl9pbml0RmlsdGVyc0xpc3RlbmVycygpO1xuICAgICAgICAgICAgICAgIFRXLl9pbml0Q2FsZW5kYXJMaXN0ZW5lcnMoKTtcblxuICAgICAgICAgICAgICAgIGlmIChwYXJhbXMub25Jbml0aWFsaXplZCAmJiB0eXBlb2YgcGFyYW1zLm9uSW5pdGlhbGl6ZWQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgcGFyYW1zLm9uSW5pdGlhbGl6ZWQoKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBUVy5pc0luaXRpYWxpemVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgndHctaW5pdGlhbGl6ZWQnKTtcblxuICAgICAgICAgICAgICAgIGlmIChsb2NhdGlvbi5oYXNoKSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLm9wZW5CeUhhc2gobG9jYXRpb24uaGFzaCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5jYXRjaChlID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgICAgICAgICAgIC8vIGFsZXJ0KCfQntGI0LjQsdC60LAg0L7RgtC/0YDQsNCy0LrQuCDQtNCw0L3QvdGL0YUhINCf0L7Qv9GA0L7QsdGD0LnRgtC1INC+0LHQvdC+0LLQuNGC0Ywg0YHRgtGA0LDQvdC40YbRgy4nKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgIDtcbiAgICB9XG5cblxuICAgIGNsZWFyRXZlbnRQYXJhbXMoKSB7XG4gICAgICAgIHRoaXMuZXZlbnRQYXJhbXMgPSB7XG4gICAgICAgICAgICBkYXRlOiAnJyxcbiAgICAgICAgICAgIGlkOiAnJyxcbiAgICAgICAgICAgIG5hbWU6ICcnLFxuICAgICAgICAgICAgZGVzYzogJycsXG4gICAgICAgICAgICBwbGFjZUlkOiAnJywgLy/Qv9C70L7RidCw0LTQutCwIElEXG4gICAgICAgICAgICBwbGFjZU5hbWU6ICcnLCAvLyDQv9C70L7RidCw0LTQutCwIG5hbWVcbiAgICAgICAgICAgIGhhbGxJZDogJycsIC8vINC30LDQuy/RgdGG0LXQvdCwIElEINC90LAg0L/RgNC+0YnQsNC00LrQtVxuICAgICAgICAgICAgaGFsbE5hbWU6ICcnLCAvLyDQt9Cw0Lsv0YHRhtC10L3QsCBOQU1FINC90LAg0L/RgNC+0YnQsNC00LrQtVxuICAgICAgICAgICAgcGFydElkOiAnJywgLy8g0LrQvtC0INGB0LXQutGC0L7RgNCwXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgaW5pdENhcnRzVGltZXIobGltaXRNaW51dGVzID0gMjApIHtcbiAgICAgICAgbGV0IGluaXRDYXJ0c1RpbWVzdGFtcDtcblxuICAgICAgICBUVy5jYXJ0c1RpbWVyRnVuYyA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgICAgIGxldCBjYXJ0cyA9IE9iamVjdC52YWx1ZXMoVFdDYXJ0c1N0b3JhZ2UuYWxsKHRydWUpKTtcbiAgICAgICAgICAgIGxldCAkY2FydHNUaW1lcnMgPSBUVy4kJCgnW2RhdGEtY2FydHMtdGltZXJdJyk7XG5cbiAgICAgICAgICAgIGlmICghY2FydHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgJGNhcnRzVGltZXJzLmF0dHIoJ2RhdGEtY2FydHMtdGltZXInLCAnJyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpbml0Q2FydHNUaW1lc3RhbXAgPSBpbml0Q2FydHNUaW1lc3RhbXAgfHwgY2FydHMucmVkdWNlKFxuICAgICAgICAgICAgICAgIChhY2N1bXVsYXRvciwgY2FydCwgaW5kZXgsIGFycmF5KSA9PiBjYXJ0LmFjdGl2YXRlZEF0ICYmIGNhcnQuYWN0aXZhdGVkQXQgPCBhY2N1bXVsYXRvciA/IGNhcnQuYWN0aXZhdGVkQXQgOiBhY2N1bXVsYXRvciwgK25ldyBEYXRlKClcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGxldCBkaWZmID0gKGluaXRDYXJ0c1RpbWVzdGFtcCArIGxpbWl0TWludXRlcyAqIDYwMDAwKSAtIG5ldyBEYXRlKCk7XG5cbiAgICAgICAgICAgIGlmIChjYXJ0cy5sZW5ndGggJiYgZGlmZiA8IDApIHtcbiAgICAgICAgICAgICAgICBpbml0Q2FydHNUaW1lc3RhbXAgPSArbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICBkaWZmID0gKGluaXRDYXJ0c1RpbWVzdGFtcCArIGxpbWl0TWludXRlcyAqIDYwMDAwKSAtIG5ldyBEYXRlKCk7XG5cbiAgICAgICAgICAgICAgICBjYXJ0cy5mb3JFYWNoKGNhcnQgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjYXJ0LnJlYWN0aXZhdGUoKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAvLyAkY2FydHNUaW1lcnMuYXR0cignZGF0YS1jYXJ0cy10aW1lcicsICcnKTtcbiAgICAgICAgICAgICAgICAvLyBUVy4kJCgnLnR3X19taW5pY2FydC1kZWxldGUnKS50cmlnZ2VyKCdjbGljaycpO1xuICAgICAgICAgICAgICAgIC8vIFRXQ2FydHNTdG9yYWdlLmNsZWFyKCk7XG4gICAgICAgICAgICAgICAgLy8gcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBtaW51dGVzID0gTWF0aC5mbG9vcihkaWZmIC8gNjAwMDApO1xuICAgICAgICAgICAgbGV0IHNlY29uZHMgPSBNYXRoLnJvdW5kKChkaWZmICUgNjAwMDApIC8gMTAwMCk7XG4gICAgICAgICAgICBzZWNvbmRzID0gc2Vjb25kcyA9PT0gNjAgPyA1OSA6IHNlY29uZHM7XG5cbiAgICAgICAgICAgIGxldCB0aW1lVHh0ID0gbWludXRlcyA8IDEwID8gJzAnICsgbWludXRlcyA6IG1pbnV0ZXM7XG4gICAgICAgICAgICB0aW1lVHh0ICs9ICc6JztcbiAgICAgICAgICAgIHRpbWVUeHQgKz0gc2Vjb25kcyA8IDEwID8gJzAnICsgc2Vjb25kcyA6IHNlY29uZHM7XG4gICAgICAgICAgICAkY2FydHNUaW1lcnMuYXR0cignZGF0YS1jYXJ0cy10aW1lcicsIHRpbWVUeHQpO1xuICAgICAgICB9LCAxMDAwKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0IGRhdGUgdG8gTW9zY293IFVUQ1xuICAgICAqXG4gICAgICogQHBhcmFtIHVuaXhUaW1lU3RhbXBcbiAgICAgKiBAcGFyYW0gb3B0aW9uc1xuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICovXG4gICAgbW9zY293RGF0ZSh1bml4VGltZVN0YW1wLCBvcHRpb25zID0ge30pIHtcbiAgICAgICAgdW5peFRpbWVTdGFtcCA9ICFpc05hTih1bml4VGltZVN0YW1wKSAmJiB1bml4VGltZVN0YW1wLnRvU3RyaW5nKCkubGVuZ3RoIDwgMTNcbiAgICAgICAgICAgID8gdW5peFRpbWVTdGFtcCAqIDEwMDBcbiAgICAgICAgICAgIDogdW5peFRpbWVTdGFtcDtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKHVuaXhUaW1lU3RhbXApLnRvTG9jYWxlRGF0ZVN0cmluZygncnUtUlUnLCBPYmplY3QuYXNzaWduKHt9LCBvcHRpb25zLCB7dGltZVpvbmU6ICdFdXJvcGUvTW9zY293J30pKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSB1bml4VGltZVN0YW1wXG4gICAgICogQHBhcmFtIG9wdGlvbnNcbiAgICAgKiBAcGFyYW0gY3V0TWlsbGlzZWNvbmRzXG4gICAgICogQHJldHVybnMge3N0cmluZ31cbiAgICAgKi9cbiAgICBtb3Njb3dUaW1lKHVuaXhUaW1lU3RhbXAsIG9wdGlvbnMgPSB7fSwgY3V0TWlsbGlzZWNvbmRzID0gdHJ1ZSkge1xuICAgICAgICB1bml4VGltZVN0YW1wID0gIWlzTmFOKHVuaXhUaW1lU3RhbXApICYmIHVuaXhUaW1lU3RhbXAudG9TdHJpbmcoKS5sZW5ndGggPCAxM1xuICAgICAgICAgICAgPyB1bml4VGltZVN0YW1wICogMTAwMFxuICAgICAgICAgICAgOiB1bml4VGltZVN0YW1wO1xuXG4gICAgICAgIGxldCBkYXRlID0gbmV3IERhdGUodW5peFRpbWVTdGFtcCkudG9Mb2NhbGVUaW1lU3RyaW5nKCdydS1SVScsIE9iamVjdC5hc3NpZ24oe30sIG9wdGlvbnMsIHt0aW1lWm9uZTogJ0V1cm9wZS9Nb3Njb3cnfSkpO1xuICAgICAgICBpZiAoY3V0TWlsbGlzZWNvbmRzKSB7XG4gICAgICAgICAgICBkYXRlID0gZGF0ZS5yZXBsYWNlKC8oXFxkKzpcXGQrKTpcXGQrJC8sICckMScpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBwYXJhbSBwYXJhbXNcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZXNwb25zZT59XG4gICAgICovXG4gICAgZmV0Y2gocGFyYW1zKSB7XG4gICAgICAgIHBhcmFtcy52ZXJzaW9uID0gcGFyYW1zLnZlcnNpb24gfHwgdGhpcy52ZXJzaW9uO1xuICAgICAgICBsZXQgZGVidWcgPSBwYXJhbXMuZGVidWcgfHwgZmFsc2U7XG4gICAgICAgIGxldCBtZXRob2QgPSBwYXJhbXMubWV0aG9kID8gcGFyYW1zLm1ldGhvZC50b1VwcGVyQ2FzZSgpIDogJ0dFVCc7XG4gICAgICAgIGxldCB1cmwgPSB0eXBlb2YgcGFyYW1zID09PSAnc3RyaW5nJyA/IHBhcmFtcyA6IChwYXJhbXMudXJsIHx8IFRXLnVybCk7XG4gICAgICAgIGxldCBmZXRjaERhdGEgPSB7XG4gICAgICAgICAgICBtZXRob2Q6IG1ldGhvZCxcbiAgICAgICAgICAgIGhlYWRlcnM6IHBhcmFtcy5oZWFkZXJzIHx8IHt9LFxuICAgICAgICAgICAgc2lnbmFsOiBwYXJhbXMuc2lnbmFsLFxuICAgICAgICAgICAgbW9kZTogJ25vLWNvcnMnLFxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChtZXRob2QgPT09ICdHRVQnKSB7XG4gICAgICAgICAgICB1cmwgKz0gdXJsLmluY2x1ZGVzKCc/JykgPyAnJicgOiAnPyc7XG4gICAgICAgICAgICB1cmwgKz0gKG5ldyBVUkxTZWFyY2hQYXJhbXMocGFyYW1zLmRhdGEpKS50b1N0cmluZygpO1xuICAgICAgICAgICAgdXJsID0gdXJsLnJlcGxhY2UoL1s/Jl0kLywgJycpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgYm9keSA9IHR5cGVvZiBwYXJhbXMuZGF0YSA9PT0gJ3N0cmluZycgPyBwYXJhbXMuZGF0YSA6IEpTT04uc3RyaW5naWZ5KHBhcmFtcy5kYXRhKTtcbiAgICAgICAgICAgIGZldGNoRGF0YS5ib2R5ID0gYm9keSB8fCB1bmRlZmluZWQ7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZmV0Y2godXJsLCBmZXRjaERhdGEpXG4gICAgICAgICAgICAudGhlbihhc3luYyByZXNwb25zZSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGRlYnVnKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGNsb25lID0gcmVzcG9uc2UuY2xvbmUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3Jlc3BvbnNlVGV4dDogJywgYXdhaXQgY2xvbmUudGV4dCgpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgX2luaXQoaHRtbCkge1xuICAgICAgICB0aGlzLiQuaHRtbChodG1sKTtcblxuICAgICAgICB0aGlzLiR0b29sdGlwID0gVFcuJCQoJyN0dy10b29sdGlwJyk7XG4gICAgICAgIHRoaXMuJGxvYWRlciA9IFRXLiQkKCcjdHctbG9hZGVyJyk7XG4gICAgICAgIHRoaXMuJGhlYWRlciA9IFRXLiQkKCcjdHctaGVhZGVyJyk7XG4gICAgICAgIHRoaXMuJHR3U3ZnTWFwID0gVFcuJCQoJyN0dy1zdmctbWFwJyk7XG4gICAgICAgIHRoaXMuJGNvbmZpcm0gPSBUVy4kJCgnI3R3LWNvbmZpcm0nKTtcbiAgICAgICAgdGhpcy4kdG9vbHMgPSBUVy4kJCgnI3R3LXRvb2xzJyk7XG4gICAgICAgIHRoaXMuJGNhbGVuZGFyID0gVFcuJCQoJyN0dy1jYWxlbmRhcicpLmVtcHR5KCk7XG4gICAgICAgIHRoaXMuJG1vcmVQb3B1cCA9IFRXLiQkKCcjdHctbW9yZS1wb3B1cCcpO1xuICAgICAgICB0aGlzLiRtaW5pY2FydCA9IFRXLiQkKCcudHdfX21pbmljYXJ0Jyk7XG4gICAgICAgIHRoaXMuJG11bHRpQ2hlY2tvdXRCdG4gPSBUVy4kJCgnLnR3LW11bHRpLWNoZWNrb3V0LWJ0bicpO1xuXG4gICAgICAgIHRoaXMuJG11bHRpQ2hlY2tvdXRCdG4ub24oJ2NsaWNrJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgVFcub3BlblBhbmVsKCd0dy1tdWx0aS1jaGVja291dCcpO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnVwZGF0ZU1hc2tFbGVtZW50cygpO1xuICAgICAgICB0aGlzLnVwZGF0ZUdsb2JhbENhcnRUb3RhbFFudCgpO1xuXG4gICAgICAgIHRoaXMuY2hlY2tBdmFpbGFibGVTY2hlbWVab29tID0gKCkgPT4ge1xuICAgICAgICAgICAgaWYgKCFUVy5zY2hlbWUpe1xuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3Qgem9vbSA9ICtUVy5zY2hlbWUuZ2V0Wm9vbSgpLnRvRml4ZWQoMik7XG4gICAgICAgICAgICBjb25zdCBtYXhab29tID0gVFcuc2NoZW1lLmdldE1heFpvb20oKTtcbiAgICAgICAgICAgIGNvbnN0IG1pblpvb20gPSBUVy5zY2hlbWUuZ2V0TWluWm9vbSgpO1xuXG4gICAgICAgICAgICBUVy4kJCgnLnR3X19zY2hlbWUtem9vbS1pbicpLnRvZ2dsZUNsYXNzKCctLWRpc2FibGVkJywgbWF4Wm9vbSA8PSB6b29tKTtcbiAgICAgICAgICAgIFRXLiQkKCcudHdfX3NjaGVtZS16b29tLW91dCcpLnRvZ2dsZUNsYXNzKCctLWRpc2FibGVkJywgbWluWm9vbSA+PSB6b29tKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMucmVzaXplU3ZnID0gKHJlaW5pdFNpemVzID0gZmFsc2UpID0+IHtcbiAgICAgICAgICAgIGlmIChUVy5zY2hlbWVJbml0ICYmIFRXLnNjaGVtZUxpYiA9PT0gVFcuU0NIRU1FX0xJQl9QQU5aT09NICYmICEgVFcuc2NoZW1lSW1hZ2VJbml0KXtcbiAgICAgICAgICAgICAgICBjb25zdCB6b29tID0gK1RXLnNjaGVtZS5nZXRab29tKCkudG9GaXhlZCgyKTtcbiAgICAgICAgICAgICAgICBjb25zdCBtaW5ab29tID0gK1RXLnNjaGVtZS5nZXRNaW5ab29tKCkudG9GaXhlZCgyKTtcblxuICAgICAgICAgICAgICAgIGlmIChtaW5ab29tID49IHpvb20gfHwgem9vbSA+IG1pblpvb20gJiYgem9vbSA8PSBtaW5ab29tICsgVFcuc2NoZW1lWm9vbVN0ZXApe1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuc2V0TWluWm9vbShUVy5zY2hlbWVJbml0TWluWm9vbSk7XG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS56b29tQnkoMSk7XG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5pbnZhbGlkYXRlU2l6ZSgpO1xuICAgICAgICAgICAgICAgICAgICByZWluaXRTaXplcyA9IHRydWU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLnJlc2l6ZSgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChyZWluaXRTaXplcyl7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHNjaGVtZVNpemVzID0gVFcuc2NoZW1lPy5nZXRTaXplcygpO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChzY2hlbWVTaXplcyl7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgbmV3Wm9vbSA9IHpvb207IFxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgdmlld3BvcnRSZWN0ID0gVFcuc2NoZW1lSW5uZXJWaWV3cG9ydC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGhlYWRlckhlaWdodCA9IFRXLiRoZWFkZXIub3V0ZXJIZWlnaHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG1pbmljYXJ0SGVpZ2h0ID0gVFcuJG1pbmljYXJ0LmZpbHRlcignLmFjdGl2ZScpLm91dGVySGVpZ2h0KCkgfHwgMDtcblxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2NoZW1lT2Zmc2V0VG9wID0gVFcuc2NoZW1lLmdldFBhbigpLnk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzY2hlbWVPZmZzZXRUb3AgPD0gaGVhZGVySGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLnBhbkJ5KHt4OiAwLCB5OiBoZWFkZXJIZWlnaHQgLSBzY2hlbWVPZmZzZXRUb3B9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHZpZXdwb3J0UmVjdC53aWR0aCA+IHNjaGVtZVNpemVzLndpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgd1pvb20gPSAoc2NoZW1lU2l6ZXMud2lkdGggLyB2aWV3cG9ydFJlY3Qud2lkdGgpLnRvRml4ZWQoMik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbmV3Wm9vbSA9IHdab29tIDwgbmV3Wm9vbSA/IG5ld1pvb20gKiB3Wm9vbSA6IG5ld1pvb207XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpbGUod1pvb20gPCBuZXdab29tKXtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3Wm9vbSAtPSBUVy5zY2hlbWVab29tU3RlcDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2aWV3cG9ydFJlY3QuaGVpZ2h0ID4gKHNjaGVtZVNpemVzLmhlaWdodCAtIGhlYWRlckhlaWdodCAtIG1pbmljYXJ0SGVpZ2h0KSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGhab29tID0gKChzY2hlbWVTaXplcy5oZWlnaHQgLSBoZWFkZXJIZWlnaHQgLSBtaW5pY2FydEhlaWdodCkgLyB2aWV3cG9ydFJlY3QuaGVpZ2h0KS50b0ZpeGVkKDIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWxlKGhab29tIDwgbmV3Wm9vbSl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld1pvb20gLT0gVFcuc2NoZW1lWm9vbVN0ZXA7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbmV3T2Zmc2V0ID0gLShUVy5zY2hlbWUuZ2V0UGFuKCkueSAtIChUVy5zY2hlbWUuZ2V0UGFuKCkueSAqIG5ld1pvb20pKSA7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLnBhbkJ5KHt4OiAwLCB5OiBuZXdPZmZzZXQgKyAxMH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuem9vbUJ5KG5ld1pvb20pO1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLnNldE1pblpvb20oK25ld1pvb20udG9GaXhlZCgyKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFRXLmNoZWNrQXZhaWxhYmxlU2NoZW1lWm9vbSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5yZXNpemVDYWxsYmFjayA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChUVy5yZXNpemVDYWxsYmFja1RpbWVvdXQpO1xuXG4gICAgICAgICAgICBUVy5pc01vYmlsZVdpZHRoID0gd2luZG93LmlubmVyV2lkdGggPCA3Njg7XG4gICAgICAgICAgICBUVy5pc1RvdWNoRGV2aWNlID0gKCgnb250b3VjaHN0YXJ0JyBpbiB3aW5kb3cpIHx8IChuYXZpZ2F0b3IubWF4VG91Y2hQb2ludHMgJiAweEZGKSB8fCAobmF2aWdhdG9yLm1zTWF4VG91Y2hQb2ludHMgPiAwKSk7XG5cbiAgICAgICAgICAgIFRXLnJlc2l6ZUNhbGxiYWNrVGltZW91dCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGFzc0xpc3QudG9nZ2xlKCdpcy1tb2JpbGUnLCBUVy5pc01vYmlsZVdpZHRoKTtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LnRvZ2dsZSgnaXMtdG91Y2gtZGV2aWNlJywgVFcuaXNUb3VjaERldmljZSk7XG5cbiAgICAgICAgICAgICAgICBUVy5yZXNpemVTdmcoKTtcbiAgICAgICAgICAgICAgICBUVy51cGRhdGVGbG9hdGluZ01hcmdpbnMoKTtcbiAgICAgICAgICAgIH0sIDIwMCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmhhc2hjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBUVy5vcGVuQnlIYXNoKGxvY2F0aW9uLmhhc2gpO1xuICAgICAgICB9XG5cbiAgICAgICAgJCh3aW5kb3cpLm9uKCdyZXNpemUnLCB0aGlzLnJlc2l6ZUNhbGxiYWNrKS50cmlnZ2VyKCdyZXNpemUnKTtcbiAgICAgICAgJCh3aW5kb3cpLm9uKCdoYXNoY2hhbmdlJywgdGhpcy5oYXNoY2hhbmdlKTtcbiAgICAgICAgXG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNobW92ZScsIChlKSA9PiB7XG4gICAgICAgICAgICB3aW5kb3cubW92ZVRvdWNoZXNDbnQgPSBlLnRvdWNoZXM/Lmxlbmd0aCB8fCBlLmNoYW5nZWRUb3VjaGVzPy5sZW5ndGggfHwgZS50YXJnZXRUb3VjaGVzPy5sZW5ndGggfHwgMTtcbiAgICAgICAgfSwge3Bhc3NpdmU6IHRydWV9KTtcblxuICAgICAgICBUVy4kbW9yZVBvcHVwLm9uKCdtb3VzZWRvd24nLCAnLnR3X19jbG9zZScsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJG1vcmVQb3B1cC5vbignaW5wdXQnLCAnLnR3X19wb3B1cC1jaGFuZ2VyLXFudCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBsZXQgJGlucHV0ID0gJCh0aGlzKTtcbiAgICAgICAgICAgIGxldCBjYXJ0SXRlbU9iID0gbmV3IFRXQ2FydEl0ZW0oVFcubGFzdENsaWNrZWRUaWNrZXRFbGVtKTtcbiAgICAgICAgICAgIGxldCBjYXJ0SXRlbSA9IFRXLmNhcnRJbnN0Lml0ZW1zLmZpbmQoaXRlbSA9PiBpdGVtLmlkID09PSBjYXJ0SXRlbU9iLmlkKTtcbiAgICAgICAgICAgIGNvbnN0IG1heCA9ICskaW5wdXQuYXR0cignZGF0YS1tYXgnKTtcbiAgICAgICAgICAgIC8vIGNvbnN0IG1heCA9IDEwO1xuICAgICAgICAgICAgbGV0IHZhbHVlID0gTWF0aC5hYnMoKyRpbnB1dC52YWwoKSk7XG5cbiAgICAgICAgICAgIGlmICh2YWx1ZSA8IDApIHtcbiAgICAgICAgICAgICAgICB2YWx1ZSA9IDA7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlID4gbWF4KSB7XG4gICAgICAgICAgICAgICAgdmFsdWUgPSBtYXg7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGZyZWVDb3VudCA9IG1heCAtIHZhbHVlO1xuXG4gICAgICAgICAgICBpZiAoIWNhcnRJdGVtKSB7XG4gICAgICAgICAgICAgICAgY2FydEl0ZW1PYi5xbnQgPSB2YWx1ZSB8fCAxO1xuICAgICAgICAgICAgICAgIFRXLmNhcnRJbnN0LmFkZChjYXJ0SXRlbU9iKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWUgPiAwKSB7XG4gICAgICAgICAgICAgICAgVFcuY2FydEluc3QudXBkYXRlKGNhcnRJdGVtLmlkLCB7cW50OiB2YWx1ZX0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBUVy5jYXJ0SW5zdC5kZWxldGUoY2FydEl0ZW0uaWQpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAkaW5wdXQucGFyZW50cygnLnR3X19wb3B1cC1jaGFuZ2VyJykuZmluZCgnW2RhdGEtZnJlZS1jb3VudF0nKS50ZXh0KGZyZWVDb3VudCk7XG4gICAgICAgICAgICAkaW5wdXQucHJldignLnR3X19wb3B1cC1jaGFuZ2VyLWJ0bicpWzBdLnRvZ2dsZUF0dHJpYnV0ZSgnZGlzYWJsZWQnLCB2YWx1ZSA8PSAwKTtcbiAgICAgICAgICAgICRpbnB1dC5uZXh0KCcudHdfX3BvcHVwLWNoYW5nZXItYnRuJylbMF0udG9nZ2xlQXR0cmlidXRlKCdkaXNhYmxlZCcsIGZyZWVDb3VudCA9PT0gMCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHRvdGFsU3VtID0gdmFsdWUgKiBjYXJ0SXRlbU9iLnByaWNlO1xuICAgICAgICAgICAgVFcuJG1vcmVQb3B1cC5maW5kKCdbZGF0YS1zdW1dJykudGV4dCh0b3RhbFN1bSA/IGAke3RvdGFsU3VtfSDigr1gIDogJ9C/0L7QtCDQt9Cw0LrQsNC3Jyk7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJ1tkYXRhLXRpY2tldHNdJykudGV4dCh2YWx1ZSArICcgJyArIFRXLmRlY2xPZk51bSh2YWx1ZSwgWyfQsdC40LvQtdGCJywgJ9Cx0LjQu9C10YLQsCcsICfQsdC40LvQtdGC0L7QsiddKSk7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJy50d19fcG9wdXAtcW50LWFsZXJ0JykudG9nZ2xlQ2xhc3MoJ2FjdGl2ZScsIHZhbHVlID49IG1heCkuZmluZCgnW2RhdGEtb25lLWhhbmQtbWF4XScpLnRleHQobWF4KTtcbiAgICAgICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnLnR3X19wb3B1cC1idXknKS50b2dnbGVDbGFzcygndHctaGlkZGVuJywgIXZhbHVlKTtcblxuICAgICAgICAgICAgJGlucHV0LnZhbCh2YWx1ZSk7XG5cbiAgICAgICAgICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG4gICAgICAgIH0pXG5cbiAgICAgICAgVFcuJG1vcmVQb3B1cC5vbignbW91c2Vkb3duJywgJy50d19fcG9wdXAtY2hhbmdlci1idG4nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgbGV0IHN0ZXAgPSBwYXJzZUludCh0aGlzLmRhdGFzZXQuc3RlcCk7XG4gICAgICAgICAgICBsZXQgJGlucHV0ID0gJCh0aGlzKS5wYXJlbnQoKS5maW5kKCcudHdfX3BvcHVwLWNoYW5nZXItcW50Jyk7XG4gICAgICAgICAgICBsZXQgbmV3UW50ID0gcGFyc2VJbnQoJGlucHV0LnZhbCgpKSArIHN0ZXA7XG5cbiAgICAgICAgICAgICRpbnB1dC52YWwobmV3UW50KS50cmlnZ2VyKCdpbnB1dCcpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kbW9yZVBvcHVwLm9uKCdjbGljaycsICcudHdfX3BvcHVwLWJ1eScsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIGxldCBjYXJ0SXRlbU9iID0gbmV3IFRXQ2FydEl0ZW0oVFcubGFzdENsaWNrZWRUaWNrZXRFbGVtKTtcbiAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAvLyBpZiAoVFcuY2FydEluc3QuaXRlbXMuZmluZEluZGV4KGl0ZW0gPT4gaXRlbS5pZCA9PT0gY2FydEl0ZW1PYi5pZCkgPT09IC0xKSB7XG4gICAgICAgICAgICAvLyAgICAgVFcuY2FydEluc3QuYWRkKGNhcnRJdGVtT2IpO1xuICAgICAgICAgICAgLy8gICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG4gICAgICAgICAgICAvLyB9XG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gVFcuJG1vcmVQb3B1cC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gVFcub3BlblBhbmVsKCd0dy1jaGVja291dCcpO1xuXG4gICAgICAgICAgICAvLyBUVy4kJCgnLmpzLXR3LWJ0bi10by1jaGVja291dCcpLnRyaWdnZXIoJ2NsaWNrJyk7XG5cbiAgICAgICAgICAgIFRXLiRtb3JlUG9wdXAucmVtb3ZlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdjbGljaycsICcudHdfX251bWJlci1pbnB1dC11cCwgLnR3X19udW1iZXItaW5wdXQtZG93bicsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zdCBpbnB1dCA9IHRoaXMucGFyZW50RWxlbWVudC5xdWVyeVNlbGVjdG9yKCdpbnB1dCcpO1xuICAgICAgICAgICAgdGhpcy5jbGFzc0xpc3QuY29udGFpbnMoJ3R3X19udW1iZXItaW5wdXQtdXAnKVxuICAgICAgICAgICAgICAgID8gaW5wdXQuc3RlcFVwKClcbiAgICAgICAgICAgICAgICA6IGlucHV0LnN0ZXBEb3duKClcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignY2xpY2snLCAnLmpzLW9wZW4taGFzaCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBpZiAobG9jYXRpb24uaGFzaCA9PT0gbmV3IFVSTCh0aGlzLmhyZWYpLmhhc2gpIHtcbiAgICAgICAgICAgICAgICBUVy5vcGVuUGFuZWwoJ3R3LXNjaGVtZScpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBUVy5vcGVuQnlIYXNoKGxvY2F0aW9uLmhhc2gpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWRvd24nLCAnI3R3LW1haW4tY2xvc2UnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIFRXLmNsb3NlUGFuZWwoKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignY2xpY2snLCAnLnR3X19zY2hlbWUtdG8tdGFibGUtYnRuJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgVFcuJCQoJy50d19fc2NoZW1lLXRvLXRhYmxlLWJ0bicpLmFkZENsYXNzKCd0dy1oaWRkZW4nKTtcbiAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctdGlja2V0cycsIHttaXNzTG9hZENvbmZpZzogdHJ1ZX0pO1xuICAgICAgICAgICAgVFcucmVkcmF3TWluaWNhcnQoKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgJy50d19fc2NoZW1lLXpvb20taW4nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgVFc/LnNjaGVtZUlubmVyVmlld3BvcnQ/LmNsYXNzTGlzdC5hZGQoJ3Ntb290aCcpO1xuICAgICAgICAgICAgVFcuc2NoZW1lLnpvb21JbigpO1xuICAgICAgICAgICAgVFcuY2hlY2tBdmFpbGFibGVTY2hlbWVab29tKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ21vdXNlZG93bicsICcudHdfX3NjaGVtZS16b29tLW91dCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICBUVz8uc2NoZW1lSW5uZXJWaWV3cG9ydD8uY2xhc3NMaXN0LmFkZCgnc21vb3RoJyk7XG4gICAgICAgICAgICBUVy5zY2hlbWUuem9vbU91dCgpO1xuICAgICAgICAgICAgVFcuY2hlY2tBdmFpbGFibGVTY2hlbWVab29tKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ2NsaWNrJywgJy50d19fdGlja2V0cy10by1zY2hlbWUnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBsZXQgaW1hZ2VVcmwgPSB0aGlzLmdldEF0dHJpYnV0ZSgnZGF0YS1pbWFnZScpO1xuICAgICAgICAgICAgVFcub3BlblBhbmVsKCd0dy1zY2hlbWUnKTsgLy/RgdC90LDRh9Cw0LvQsCDQvdGD0LbQvdC+INC+0YLQutGA0YvRgtGMINC/0LDQvdC10LvRjCwg0YfRgtC+0LHRiyDQv9GA0L7RgNC40YHQvtCy0LDQu9C40YHRjCDQsdC70L7QutC4XG4gICAgICAgICAgICBUVy4kJCgnLnR3X19zY2hlbWUtdG8tdGFibGUtYnRuJykucmVtb3ZlQ2xhc3MoJ3R3LWhpZGRlbicpO1xuXG4gICAgICAgICAgICBpZiAoIVRXLnNjaGVtZUltYWdlSW5pdCB8fCAhVFcuc2NoZW1lIHx8ICFUVy5zY2hlbWVTdmdPdmVybGF5KSB7XG4gICAgICAgICAgICAgICAgaWYgKFRXLnNjaGVtZUxpYiA9PT0gVFcuU0NIRU1FX0xJQl9MRUFGTEVUKSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZVN2Z092ZXJsYXk/LnJlbW92ZSgpLnJlbW92ZUV2ZW50TGlzdGVuZXIoKTtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lPy5yZW1vdmUoKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjb25zdCBtYXBCbG9jayA9IFRXLiR0d1N2Z01hcC5nZXQoMCk7XG4gICAgICAgICAgICAgICAgVFcuJHR3U3ZnTWFwLmVtcHR5KCk7XG5cbiAgICAgICAgICAgICAgICAvL9Cy0YHQtdCz0LTQsCBsZWFmbGV0LCDQu9C40LHQviDQt9Cw0YHRg9C90YPRgtGMIGltZyDQuCBvYmplY3Qg0LjQu9C4IHN2Zywg0YfRgtC+0LHRiyDRgNCw0LHQvtGC0LDRgtGMINGBIHBhbnpvb21cbiAgICAgICAgICAgICAgICBUVy5zY2hlbWUgPSBMLm1hcChtYXBCbG9jaywge1xuICAgICAgICAgICAgICAgICAgICBtaW5ab29tOiAxLFxuICAgICAgICAgICAgICAgICAgICBtYXhab29tOiAzLFxuICAgICAgICAgICAgICAgICAgICB6b29tOiAxLFxuICAgICAgICAgICAgICAgICAgICBjZW50ZXI6IFswLCAwXSxcbiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRpb25Db250cm9sOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgem9vbUNvbnRyb2w6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBzY3JvbGxXaGVlbFpvb206IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRvdWJsZUNsaWNrWm9vbTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGNsb3NlUG9wdXBPbkNsaWNrOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgdGFwSG9sZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGJveFpvb206IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBrZXlib2FyZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIGZhZGVBbmltYXRpb246IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBpbmVydGlhOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgYm91bmNlQXRab29tTGltaXRzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5TaW1wbGUsXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBUVy5zY2hlbWVJbWFnZUluaXQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIFRXLnNjaGVtZUxpYiA9IFRXLlNDSEVNRV9MSUJfTEVBRkxFVDtcblxuICAgICAgICAgICAgICAgIGNvbnN0IGdldEJvdW5kcyA9ICh3aWR0aCA9IDUwMCwgaGVpZ2h0ID0gNTAwKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBzb3V0aFdlc3QgPSBUVy5zY2hlbWUudW5wcm9qZWN0KFswLCBoZWlnaHRdLCBUVy5zY2hlbWUuZ2V0TWF4Wm9vbSgpKTtcbiAgICAgICAgICAgICAgICAgICAgbGV0IG5vcnRoRWFzdCA9IFRXLnNjaGVtZS51bnByb2plY3QoW3dpZHRoLCAwXSwgVFcuc2NoZW1lLmdldE1heFpvb20oKSk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoc291dGhXZXN0LCBub3J0aEVhc3QpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIFRXLnNjaGVtZVN2Z092ZXJsYXkgPSBMLmltYWdlT3ZlcmxheShpbWFnZVVybCwgZ2V0Qm91bmRzKCkpLmFkZFRvKFRXLnNjaGVtZSk7XG5cbiAgICAgICAgICAgICAgICBUVy5zY2hlbWVTdmdPdmVybGF5Ll9pbWFnZS5vbmxvYWQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGggPSB0aGlzLm5hdHVyYWxIZWlnaHQ7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHcgPSB0aGlzLm5hdHVyYWxXaWR0aDtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgbWFwUmVjdCA9IG1hcEJsb2NrLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCByYXRpb0ggPSBNYXRoLmNlaWwoaCAvIG1hcFJlY3QuaGVpZ2h0KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmF0aW9XID0gTWF0aC5jZWlsKHcgLyBtYXBSZWN0LndpZHRoKTtcbiAgICAgICAgICAgICAgICAgICAgbGV0IG1heFpvb20gPSBUVy5zY2hlbWUuZ2V0TWF4Wm9vbSgpO1xuICAgICAgICAgICAgICAgICAgICBsZXQgdXBkYXRlTWluWm9vbSA9IGZhbHNlO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChyYXRpb1cgPiBtYXhab29tIHx8IHJhdGlvSCA+IG1heFpvb20pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5zZXRNYXhab29tKE1hdGgubWF4KHJhdGlvVywgcmF0aW9IKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVNaW5ab29tID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG5ld0JvdW5kcyA9IGdldEJvdW5kcyh3LCBoKTtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lU3ZnT3ZlcmxheS5zZXRCb3VuZHMobmV3Qm91bmRzKTtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLmZpdEJvdW5kcyhuZXdCb3VuZHMsIHthbmltYXRlOiBmYWxzZX0pO1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuc2V0TWF4Qm91bmRzKG5ld0JvdW5kcyk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHJhdGlvSCA9PT0gMSAmJiByYXRpb1cgPT09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5zZXRNaW5ab29tKFRXLnNjaGVtZS5nZXRNYXhab29tKCkgLSAxLCB7YW5pbWF0ZTogZmFsc2V9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5zZXRNYXhab29tKFRXLnNjaGVtZS5nZXRNYXhab29tKCkgKyAxLCB7YW5pbWF0ZTogZmFsc2V9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5zZXRab29tKG1heFpvb20sIHthbmltYXRlOiBmYWxzZX0pO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHVwZGF0ZU1pblpvb20pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5zZXRNaW5ab29tKFRXLnNjaGVtZS5nZXRab29tKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICAvLyBUVy4kJCgnLnR3X190aWNrZXRzJykucmVtb3ZlQXR0cignZGF0YS1zZWN0b3Itb3BlbicpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdjbGljaycsICcudHdfX3RpY2tldHMtcGxhY2UnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjb25zdCBpZCA9IHRoaXMuZ2V0QXR0cmlidXRlKCdkYXRhLWlkJyk7XG4gICAgICAgICAgICBjb25zdCBzZWN0b3JJZCA9IHRoaXMuZ2V0QXR0cmlidXRlKCdkYXRhLXNlY3Rvci1pZCcpO1xuICAgICAgICAgICAgY29uc3QgcGxhY2UgPSBUVy5jb25maWcuc2VjdG9ycy5maW5kKHNlY3RvciA9PiBzZWN0b3IuaWQgPT0gc2VjdG9ySWQpLnBsYWNlcy5maW5kKGl0ZW0gPT4gaXRlbS5pZCA9PT0gaWQpO1xuICAgICAgICAgICAgY29uc3QgY2FydEl0ZW1PYiA9IG5ldyBUV0NhcnRJdGVtKHsuLi5wbGFjZSwgX2RvbUVsZW06IHRoaXN9KTtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0Q2FydEl0ZW0gPSBUVy5jYXJ0SW5zdC5pdGVtcy5maW5kKGl0ZW0gPT4gaXRlbS5pZCA9PT0gY2FydEl0ZW1PYi5pZCk7XG5cbiAgICAgICAgICAgIGlmICghZXhpc3RDYXJ0SXRlbSkge1xuICAgICAgICAgICAgICAgIFRXLmNhcnRJbnN0LmFkZChjYXJ0SXRlbU9iKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyB0aGlzLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpOyAvL9GD0LTQsNC70LXQvdC40LUg0L/RgNC+0LjRgdGF0L7QtNC40YIg0LLQvdGD0YLRgNC4ICBUVy5jYXJ0SW5zdC5kZWxldGVcbiAgICAgICAgICAgICAgICBUVy5jYXJ0SW5zdC5kZWxldGUoZXhpc3RDYXJ0SXRlbS5pZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ2NsaWNrJywgJy50d19fdGlja2V0cy10by1zZWN0b3JzJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgVFcuJCQoJy50d19fdGlja2V0cycpLnJlbW92ZUF0dHIoJ2RhdGEtc2VjdG9yLW9wZW4nKVxuICAgICAgICAgICAgICAgIC5maW5kKCcudHdfX3RpY2tldHMtc2VjdG9yLW9wZW5lcicpLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignY2xpY2snLCAnLnR3X190aWNrZXRzLXNlY3Rvci1vcGVuZXInLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjb25zdCAkc2VsZkJ0biA9ICQodGhpcykudG9nZ2xlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgY29uc3QgJHBhbmVsID0gJHNlbGZCdG4ucGFyZW50cygnLnR3X190aWNrZXRzJyk7XG4gICAgICAgICAgICBjb25zdCAkc2VjdG9yID0gJHNlbGZCdG4ucGFyZW50cygnLnR3X190aWNrZXRzLXNlY3RvcicpO1xuICAgICAgICAgICAgbGV0IHNlY3RvcklkID0gJHNlY3Rvci5kYXRhKCdpZCcpO1xuICAgICAgICAgICAgbGV0IHNlY3RvcklzT3BlbmVkID0gJHNlbGZCdG4uaGFzQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgbGV0IGlzTGFuZGluZ01vZGUgPSAkcGFuZWwuaXMoJ1tkYXRhLWxhbmRpbmctbW9kZV0nKTtcbiAgICAgICAgICAgIGNvbnN0IHNlY3RvckRhdGEgPSBUVy5jb25maWcuc2VjdG9ycy5maW5kKHNlY3RvciA9PiBzZWN0b3IuaWQgPT0gc2VjdG9ySWQpIHx8IHt9O1xuICAgICAgICAgICAgbGV0ICRyb3dzQmxvY2sgPSAkKCc8ZGl2IGNsYXNzPVwidHdfX3RpY2tldHMtcm93c1wiPjwvZGl2PicpO1xuICAgICAgICAgICAgbGV0IHBsYWNlSXRlbXMgPSBbXTtcblxuICAgICAgICAgICAgaWYgKGlzTGFuZGluZ01vZGUpIHtcbiAgICAgICAgICAgICAgICBpZiAoISRzZWN0b3IuZmluZCgnLnR3X190aWNrZXRzLXJvd3MnKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgJHNlY3Rvci5hcHBlbmQoJHJvd3NCbG9jayk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAkcGFuZWwuZmluZCgnLnR3X190aWNrZXRzLXJvd3MnKS5yZW1vdmUoKTtcbiAgICAgICAgICAgICAgICAkcGFuZWwuZmluZCgnLnR3X190aWNrZXRzLXNlY3RvcnMnKS5hZnRlcigkcm93c0Jsb2NrKTtcblxuICAgICAgICAgICAgICAgIGlmIChzZWN0b3JJc09wZW5lZCkge1xuICAgICAgICAgICAgICAgICAgICAkcGFuZWwuYXR0cignZGF0YS1zZWN0b3Itb3BlbicsIHNlY3RvcklkKS5maW5kKCcudHdfX3RpY2tldHMtc2VjdG9yLW5hbWUnKS5odG1sKHNlY3RvckRhdGEubmFtZSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgJHBhbmVsLnJlbW92ZUF0dHIoJ2RhdGEtc2VjdG9yLW9wZW4nKS5maW5kKCcudHdfX3RpY2tldHMtc2VjdG9yLW5hbWUnKS5odG1sKCcnKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICRzZWN0b3IuZmluZCgnLnR3X190aWNrZXRzLXJvd3MnKS50b2dnbGVDbGFzcygndHctaGlkZGVuJywgIXNlY3RvcklzT3BlbmVkKTtcblxuICAgICAgICAgICAgaWYgKHNlY3RvcklzT3BlbmVkICYmIHNlY3RvckRhdGEucGxhY2VzKSB7XG4gICAgICAgICAgICAgICAgbGV0IHJvdztcbiAgICAgICAgICAgICAgICBsZXQgJHJvd0l0ZW07XG4gICAgICAgICAgICAgICAgbGV0IGluZGV4ID0gMDtcbiAgICAgICAgICAgICAgICBsZXQgcGxhY2VPYiA9IHNlY3RvckRhdGEucGxhY2VzW2luZGV4XSB8fCBudWxsO1xuXG4gICAgICAgICAgICAgICAgd2hpbGUgKHBsYWNlT2IpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNlY3RvckRhdGEuZGlzcGxheV9tYXhfY291bnQgJiYgaW5kZXggKyAxID49IHNlY3RvckRhdGEuZGlzcGxheV9tYXhfY291bnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChwbGFjZU9iLnJvdyAhPT0gcm93KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkcm93SXRlbSA9ICQoJzxkaXY+Jywge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjbGFzcyc6IFwidHdfX3RpY2tldHMtcm93XCIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtY2FwdGlvbic6IFwi0KDRj9C0XCIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtcm93JzogcGxhY2VPYi5yb3csXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICRyb3dzQmxvY2suYXBwZW5kKCRyb3dJdGVtKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICgkcm93SXRlbSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgJHBsYWNlSXRlbSA9ICQoJzxzcGFuPicsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY2xhc3MnOiBcInR3X190aWNrZXRzLXBsYWNlXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtcHJpY2UnOiBwbGFjZU9iLnByaWNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYXRhLXBsYWNlJzogcGxhY2VPYi5wbGFjZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1pZCc6IHBsYWNlT2IuaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtc2VjdG9yLWlkJzogc2VjdG9ySWQsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS50ZXh0KHBsYWNlT2IucGxhY2UpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAkcm93SXRlbS5hcHBlbmQoJHBsYWNlSXRlbSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBwbGFjZUl0ZW1zLnB1c2goJHBsYWNlSXRlbS5nZXQoMCkpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgcm93ID0gcGxhY2VPYi5yb3c7XG5cbiAgICAgICAgICAgICAgICAgICAgcGxhY2VPYiA9IHNlY3RvckRhdGEucGxhY2VzWysraW5kZXhdO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIFRXLl9pbml0RmlsdGVyKCcudHdfX3RpY2tldHMtZmlsdGVyJywgcGxhY2VJdGVtcywge1xuICAgICAgICAgICAgICAgICAgICBjb2xvcml6ZUFmdGVySW5pdDogdHJ1ZSxcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICRwYW5lbC5maW5kKCcudHdfX3RpY2tldHMtcm93JykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCAkc2VsZiA9ICQodGhpcyk7XG4gICAgICAgICAgICAgICAgICAgIGlmICghJHNlbGYuZmluZCgnLnR3X190aWNrZXRzLXBsYWNlcycpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJHNlbGYud3JhcElubmVyKCc8ZGl2IGNsYXNzPVwidHdfX3RpY2tldHMtcGxhY2VzXCI+PC9kaXY+Jyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVFcuY2FydEluc3QuYWN0dWFsaXplKCk7XG4gICAgICAgICAgICBUVy5yZWRyYXdNaW5pY2FydCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWRvd24nLCAnLnR3X19zY2hlbWUtYmFjaywgLmpzLXR3LWNoZWNrb3V0LWNsb3NlJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG5cbiAgICAgICAgICAgIGlmIChUVy5ldmVudFBhcmFtcy5wYXJ0SWQpIHtcbiAgICAgICAgICAgICAgICBUVy5nZXRFdmVudCh7cGFydElkOiAnJ30sIHRydWUsIHRydWUpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVFcuY2xvc2VQYW5lbCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWVudGVyJywgJy50d19fbWluaWNhcnQtY291bnQnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBUVy4kbWluaWNhcnQuZmluZCgnLnR3X19taW5pY2FydC1pdGVtcycpLmFkZENsYXNzKCdmbG9hdGluZycpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWxlYXZlJywgJy50d19fbWluaWNhcnQtY291bnQsIC50d19fbWluaWNhcnQtYnRuLCAudHdfX21pbmljYXJ0LWl0ZW1zJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmIChlLnJlbGF0ZWRUYXJnZXQgJiYgIWUucmVsYXRlZFRhcmdldC5jbG9zZXN0KCcudHdfX21pbmljYXJ0LWl0ZW1zJykgJiYgIWUucmVsYXRlZFRhcmdldC5jbG9zZXN0KCcudHdfX21pbmljYXJ0LWJ0bicpKSB7XG4gICAgICAgICAgICAgICAgVFcuJG1pbmljYXJ0LmZpbmQoJy50d19fbWluaWNhcnQtaXRlbXMnKS5yZW1vdmVDbGFzcygnZmxvYXRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgJy50d19fbWluaWNhcnQtZGVsZXRlJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG5cbiAgICAgICAgICAgIFRXLmNhcnRJbnN0Py5yZXNldCgpO1xuICAgICAgICAgICAgVFcuJHRvb2x0aXAuaGlkZSgpO1xuICAgICAgICAgICAgVFcuJG1vcmVQb3B1cC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG5cbiAgICAgICAgICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ21vdXNlZG93bicsICcuanMtcmVzZXQtY2FydHMnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgVFcuY2FydEluc3Q/LnJlc2V0KCk7XG4gICAgICAgICAgICBUV0NhcnRzU3RvcmFnZS5jbGVhcigpO1xuXG4gICAgICAgICAgICBUVy51cGRhdGVDaGVja291dCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWRvd24nLCAnLmpzLXJlc2V0LWNhcnQnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgY29uc3QgY2FydElkID0gdGhpcy5jbG9zZXN0KCdbZGF0YS1jYXJ0XScpLmdldEF0dHJpYnV0ZSgnZGF0YS1jYXJ0Jyk7XG4gICAgICAgICAgICBjb25zdCBjYXJ0ID0gVFcuY2FydEluc3QgJiYgVFcuY2FydEluc3QuaWQgPT0gY2FydElkID8gVFcuY2FydEluc3QgOiBUV0NhcnRzU3RvcmFnZS5maW5kQ2FydChjYXJ0SWQpO1xuICAgICAgICAgICAgY2FydD8ucmVzZXQoKTtcblxuICAgICAgICAgICAgVFcudXBkYXRlQ2hlY2tvdXQoKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJHRvb2x0aXAub24oJ3RvdWNoZW5kJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgVFcuJHRvb2x0aXAuaGlkZSgpO1xuICAgICAgICB9KTtcblxuXG4gICAgICAgIFRXLiQub24oJ21vdXNlb3ZlcicsICcjdHctc3ZnLW1hcCBzdmc6bm90KC4tLWRyYWdnaW5nKTpub3QoLi0tZGVsYXktZHJhZ2dpbmcpIC50aWNrZXRzX2F2YWlsJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmICghVFcuaXNUb3VjaERldmljZSkge1xuICAgICAgICAgICAgICAgIFRXLnNob3dUb29sdGlwKHRoaXMpO1xuICAgICAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgICAgIGUudGFyZ2V0LmNsYXNzTGlzdC5hZGQoJy0taG92ZXInKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2VvdXQnLCAnI3R3LXN2Zy1tYXAgc3ZnIC50aWNrZXRzX2F2YWlsJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmICghVFcuaXNUb3VjaERldmljZSkge1xuICAgICAgICAgICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcblxuICAgICAgICAgICAgICAgIGUudGFyZ2V0LmNsYXNzTGlzdC5yZW1vdmUoJy0taG92ZXInKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gaWYgKFRXLnNjaGVtZUxpYiA9PT0gVFcuU0NIRU1FX0xJQl9MRUFGTEVUKXtcbiAgICAgICAgICAgIC8vICAgICBUVy5zY2hlbWUuZHJhZ2dpbmcuZW5hYmxlKCk7XG4gICAgICAgICAgICAvLyB9XG4gICAgICAgIH0pO1xuXG5cbiAgICAgICAgLy8gVFcuJC5vbignbW91c2Vkb3duJywgJyN0dy1zdmctbWFwIHN2ZyAudGlja2V0c19hdmFpbCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIC8vICAgICBpZiAoVFcuc2NoZW1lTGliID09PSBUVy5TQ0hFTUVfTElCX0xFQUZMRVQpe1xuICAgICAgICAvLyAgICAgICAgIFRXLnNjaGVtZS5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gICAgICAgIC8vICAgICB9XG4gICAgICAgIC8vIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ21vdXNlZG93bicsICcjdHctc3ZnLW1hcCBzdmcnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgaWYgKFRXLmlzVG91Y2hEZXZpY2UpIHtcbiAgICAgICAgICAgICAgICBUVy4kdG9vbHRpcC5oaWRlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vdG9kbyDQv9GA0L7RgtC10YHRgtC40YLRjCBjbGljayDRgdC+0LHRi9GC0LjQtSDRgSDQvtGB0YLQsNC90L7QstC60L7QuSBwcmV2ZW50RGVmYXVsdCBhbmQgcHJvcG9nYXRpb24sINCi0LDQutC20LUg0L/RgNC+0YLQtdGB0YLQuNGC0Ywg0LXRgdC70Lgg0LvQuCDRgNCw0LfQvdC40YbQsCDQutC+0LPQtNCwINGB0L7QsdGL0YLQuNGPINC90LDQstC10YjQuNCy0LDRjtGC0YHRjyDQv9GA0Y/QvNC+INC90LAg0Y3Qu9C10LzQtdC90YLRiywg0LAg0L3QtSDRgNC+0LTQuNGC0LXQu9GPXG4gICAgICAgIFRXLiQub24oJ21vdXNldXAnLCAnI3R3LXN2Zy1tYXAgc3ZnOm5vdCguLS1kZWxheS1kcmFnZ2luZykgLnRpY2tldHNfYXZhaWwnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgaWYgKFRXLnNjaGVtZUxpYiA9PT0gVFcuU0NIRU1FX0xJQl9MRUFGTEVUKSB7XG4gICAgICAgICAgICAgICAgVFcuc2NoZW1lLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsZXQgY2FydEl0ZW1PYiA9IG5ldyBUV0NhcnRJdGVtKHRoaXMpO1xuXG4gICAgICAgICAgICAvLyBpZiAoY2FydEl0ZW1PYi5ncm91cCAhPT0gJzAnKSB7XG4gICAgICAgICAgICAvLyAgICAgY29uc3QgZWxlbXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKGBbZGF0YS16cj1cIiR7Y2FydEl0ZW1PYi5ncm91cH1cIl1gKTtcbiAgICAgICAgICAgIC8vICAgICBpZiAoZWxlbXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAvLyAgICAgICAgIGVsZW1zLmZvckVhY2goKGVsZW0pID0+IHNlbGVjdFRpY2tldChlbGVtKSk7XG4gICAgICAgICAgICAvLyAgICAgfVxuICAgICAgICAgICAgLy8gfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzZWxlY3RUaWNrZXQodGhpcyk7XG4gICAgICAgICAgICAvLyB9XG4gICAgICAgIH0pO1xuXG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgJy50d19fbWluaWNhcnQtaXRlbS1kZWxldGUnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgY29uc3QgaXRlbUlkID0gJCh0aGlzKS5wYXJlbnRzKCdbZGF0YS1jYXJ0LWl0ZW1dJykuYXR0cignZGF0YS1jYXJ0LWl0ZW0nKTtcbiAgICAgICAgICAgIFRXLmNhcnRJbnN0Py5kZWxldGUoaXRlbUlkKTtcbiAgICAgICAgICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ21vdXNlZG93bicsICcuanMtdHctY2hlY2tvdXQtZGVsZXRlLWl0ZW0nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgY29uc3QgJGl0ZW0gPSAkKHRoaXMpLnBhcmVudHMoJ1tkYXRhLWNhcnQtaXRlbV0nKTtcbiAgICAgICAgICAgIGNvbnN0IGNhcnRJZCA9ICRpdGVtLnBhcmVudHMoJ1tkYXRhLWNhcnRdJykuYXR0cignZGF0YS1jYXJ0Jyk7XG4gICAgICAgICAgICBjb25zdCBjaGVja291dFBhbmVsSWQgPSAkaXRlbS5wYXJlbnRzKCcudHdfX2NoZWNrb3V0JykuYXR0cignaWQnKTtcblxuICAgICAgICAgICAgY29uc3QgY2FydCA9IFRXLmNhcnRJbnN0ICYmIFRXLmNhcnRJbnN0LmlkID09IGNhcnRJZFxuICAgICAgICAgICAgICAgID8gVFcuY2FydEluc3RcbiAgICAgICAgICAgICAgICA6IFRXQ2FydHNTdG9yYWdlLmZpbmRDYXJ0KGNhcnRJZCk7XG5cbiAgICAgICAgICAgIGNhcnQ/LmRlbGV0ZSgkaXRlbS5hdHRyKCdkYXRhLWNhcnQtaXRlbScpKTtcbiAgICAgICAgICAgIFRXLnVwZGF0ZUNoZWNrb3V0KGNoZWNrb3V0UGFuZWxJZCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ21vdXNlZG93bicsICcudHctc3VtbWFyeScsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICBlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO1xuXG4gICAgICAgICAgICAkKHRoaXMpLnBhcmVudHMoJy50dy1kZXRhaWxzJykudG9nZ2xlQ2xhc3MoJ29wZW4nKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgJy5qcy10dy1idG4tdG8tY2hlY2tvdXQnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgVFcubWV0cmlrYVJlYWNoR29hbCgnd2lkZ2V0X2J0bi10by1jaGVja291dF9jbGljaycpO1xuXG4gICAgICAgICAgICAvL3RvZG8g0YHQtNC10LvQsNGC0Ywg0LXQtNC40L3Rg9GOXG5cbiAgICAgICAgICAgIC8vIGlmIChUV0NhcnRzU3RvcmFnZS5jYXJ0c0NvdW50KCkgPiAxKSB7XG4gICAgICAgICAgICBUVy5vcGVuUGFuZWwoJ3R3LW11bHRpLWNoZWNrb3V0Jyk7XG4gICAgICAgICAgICAvLyB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gICAgIFRXLm9wZW5QYW5lbCgndHctY2hlY2tvdXQnKTtcbiAgICAgICAgICAgIC8vIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbigna2V5dXAnLCAnLnR3X19jaGVja291dC1mb3JtJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgaWYgKCFUVy5ibG9ja0NoZWNrb3V0Rm9ybUZpbGxNZXRyaWthKXtcbiAgICAgICAgICAgICAgICBUVy5tZXRyaWthUmVhY2hHb2FsKCd3aWRnZXRfZm9ybV9maWxsJyk7XG5cbiAgICAgICAgICAgICAgICBUVy5ibG9ja0NoZWNrb3V0Rm9ybUZpbGxNZXRyaWthID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbigna2V5dXAgY2hhbmdlJywgJy50d19fY2hlY2tvdXQtZm9ybSBpbnB1dCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnZhbHVlLmxlbmd0aCAmJiAhdGhpcy5jaGVja1ZhbGlkaXR5KCkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNldEN1c3RvbVZhbGlkaXR5KCcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignc3VibWl0JywgJ2Zvcm0udHdfX2NoZWNrb3V0LWZvcm0nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdtb3VzZWRvd24nLCAnLmpzLXR3LWJ0bi10by1wYXltZW50JywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG5cbiAgICAgICAgICAgIGNvbnN0ICRjaGVja291dCA9ICQodGhpcykucGFyZW50cygnLnR3X19jaGVja291dCcpO1xuICAgICAgICAgICAgY29uc3QgJGZvcm0gPSAkY2hlY2tvdXQuZmluZCgnZm9ybS50d19fY2hlY2tvdXQtZm9ybScpO1xuICAgICAgICAgICAgY29uc3Qgc2VyaWFsaXplRGF0YSA9ICRmb3JtLnNlcmlhbGl6ZUFycmF5KCk7XG4gICAgICAgICAgICBjb25zdCAkaW52YWxpZEVsZW1zID0gJGZvcm0uZmluZCgnOmludmFsaWQnKTtcbiAgICAgICAgICAgIGNvbnN0IGNhcnRJdGVtcyA9ICRjaGVja291dFswXS5xdWVyeVNlbGVjdG9yQWxsKCcudHctY2FydC1pdGVtOm5vdCgudHctaGlkZGVuKScpO1xuICAgICAgICAgICAgY29uc3QgaGFzRGF0YUZpZWxkcyA9IFRXLmNhcnRJbnN0Py5kb2N1bWVudEZpZWxkcyA/PyAkY2hlY2tvdXQuZmluZCgnW2RhdGEtdmlld2VyLWRhdGFdOnZpc2libGUnKS5sZW5ndGg7XG4gICAgICAgICAgICBjb25zdCB1c2Vyc0RhdGEgPSBbXTtcbiAgICAgICAgICAgIGxldCBpc1ZhbGlkVmlld2VyRGF0YSA9IHRydWU7XG4gICAgICAgICAgICBsZXQgZGF0YUZpZWxkcyA9IFtdO1xuXG4gICAgICAgICAgICBpZiAoY2FydEl0ZW1zLmxlbmd0aCAmJiBoYXNEYXRhRmllbGRzKSB7XG4gICAgICAgICAgICAgICAgY2FydEl0ZW1zLmZvckVhY2goKGNhcnRJdGVtLCBpbmRleCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCB2aWV3ZXJEYXRhID0gY2FydEl0ZW0ucXVlcnlTZWxlY3RvckFsbCgnW2RhdGEtdmlld2VyLWRhdGEtY29udGVudF0nKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAodmlld2VyRGF0YS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdlckRhdGEuZm9yRWFjaCgoaXRlbSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGZpZWxkc1ZpZXdlckRhdGEgPSBpdGVtLnF1ZXJ5U2VsZWN0b3JBbGwoJ2lucHV0W25hbWVdJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZmllbGRzVmlld2VyRGF0YS5sZW5ndGgpIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZHNWaWV3ZXJEYXRhLmZvckVhY2goKGZpZWxkKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBuYW1lID0gZmllbGQuZ2V0QXR0cmlidXRlKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWVsZFZhbHVlID0gZmllbGQudmFsdWUudHJpbSgpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgaXNWYWxpZCA9IGZpZWxkVmFsdWUubGVuZ3RoID4gMCAmJiBmaWVsZC5yZXBvcnRWYWxpZGl0eSgpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRmllbGRzLnB1c2goZmllbGQpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNWYWxpZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldCB2YWx1ZSA9IGZpZWxkLnZhbHVlLnRyaW0oKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpZWxkLmNsYXNzTGlzdC5yZW1vdmUoJ2ludmFsaWQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2Vyc0RhdGFbaW5kZXhdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi51c2Vyc0RhdGFbaW5kZXhdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmFtZV06IHZhbHVlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgdmlld2VyRGF0YSA9IGZpZWxkLmNsb3Nlc3QoJ1tkYXRhLXZpZXdlci1kYXRhXScpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgY2hlY2tvdXRDYXJ0cyA9IGNhcnRJdGVtLmNsb3Nlc3QoJy50d19fY2hlY2tvdXQtY2FydHMnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBjYXJ0RGV0YWlscyA9IGNhcnRJdGVtLmNsb3Nlc3QoJy5qcy10dy1jaGVja291dC1jYXJ0LWRldGFpbHMnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXJ0RGV0YWlscy5jbGFzc0xpc3QuYWRkKCdvcGVuJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrb3V0Q2FydHM/LnNjcm9sbEludG9WaWV3KHsgYmVoYXZpb3I6ICdzbW9vdGgnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlld2VyRGF0YS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZC5jbGFzc0xpc3QuYWRkKCdpbnZhbGlkJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNWYWxpZFZpZXdlckRhdGEgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoISRpbnZhbGlkRWxlbXMubGVuZ3RoICYmIGlzVmFsaWRWaWV3ZXJEYXRhKSB7XG4gICAgICAgICAgICAgICAgVFcubWV0cmlrYVJlYWNoR29hbCgnd2lkZ2V0X2J1dHRvbl9wYXlfY2xpY2snKTtcblxuICAgICAgICAgICAgICAgIGxldCBwYXJhbXMgPSB7fTtcbiAgICAgICAgICAgICAgICBsZXQgcmVzZXRPcmRlckNhcnRzRnVuYztcbiAgICAgICAgICAgICAgICBsZXQgY2FydHMgPSBPYmplY3QuZW50cmllcyhUV0NhcnRzU3RvcmFnZS5hbGwodHJ1ZSkpO1xuICAgICAgICAgICAgICAgIGxldCBvcmRlcnMgPSBbXTtcblxuICAgICAgICAgICAgICAgIGlmIChoYXNEYXRhRmllbGRzKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhcnRzID0gY2FydHMubWFwKChjYXJ0KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGNhcnQgPT09ICdzdHJpbmcnKSByZXR1cm4gY2FydDtcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBpdGVtcyA9IFtdO1xuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNhcnRbMV0uaXRlbXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbXMgPSBjYXJ0WzFdLml0ZW1zLm1hcCgoaXRlbSwgaW5kZXhJdGVtKSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi5pdGVtLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi51c2Vyc0RhdGFbaW5kZXhJdGVtXVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FydFswXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7IC4uLmNhcnRbMV0sIGl0ZW1zIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgW2NhcnRJZCwgY2FydF0gb2YgY2FydHMpIHtcbiAgICAgICAgICAgICAgICAgICAgb3JkZXJzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQ6IGNhcnQuX2V2ZW50LFxuICAgICAgICAgICAgICAgICAgICAgICAgdGlja2V0czogY2FydC5pdGVtcyxcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcGFyYW1zID0ge1xuICAgICAgICAgICAgICAgICAgICBhY3Rpb246ICdkb19vcmRlcnMnLFxuICAgICAgICAgICAgICAgICAgICBwYW5lbFR5cGU6ICdjaGVja291dCcsXG4gICAgICAgICAgICAgICAgICAgIG9yZGVyczogb3JkZXJzXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKCRjaGVja291dC5hdHRyKCdpZCcpLmluY2x1ZGVzKCd0dy1tdWx0aS1jaGVja291dCcpICYmIFRXQ2FydHNTdG9yYWdlLmNhcnRzQ291bnQoKSA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzZXRPcmRlckNhcnRzRnVuYyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXQ2FydHNTdG9yYWdlLmNsZWFyKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5jYXJ0SW5zdCA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXNldE9yZGVyQ2FydHNGdW5jID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuY2FydEluc3QucmVzZXQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICQuZWFjaChzZXJpYWxpemVEYXRhLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIHBhcmFtc1t0aGlzLm5hbWVdID0gdGhpcy52YWx1ZTtcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHBhcmFtcy5lcnJvcnMgPSBUVy5lcnJvcnM7XG5cbiAgICAgICAgICAgICAgICBUVy5mZXRjaCh7XG4gICAgICAgICAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiBwYXJhbXMsXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4ocmVzcG9uc2UgPT4gcmVzcG9uc2UuanNvbigpKVxuICAgICAgICAgICAgICAgICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh3aW5kb3cuc2VuZFN1Y2Nlc3NTdGF0aXN0aWNSZXF1ZXN0ICYmIHR5cGVvZiB3aW5kb3cuc2VuZFN1Y2Nlc3NTdGF0aXN0aWNSZXF1ZXN0ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2luZG93LnNlbmRTdWNjZXNzU3RhdGlzdGljUmVxdWVzdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghZGF0YS5zdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgRXJyb3IoJ9Cd0LUg0YPQtNCw0LvQvtGB0Ywg0L7RhNC+0YDQvNC40YLRjCDQt9Cw0LrQsNC3Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICRmb3JtLmdldCgwKS5yZXNldCgpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZGF0YS5yZWRpcmVjdFVybCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRXLmVycm9ycyA9IFtdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0ICRyZWRpcmVjdEZvcm0gPSAkKCc8Zm9ybT4nLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhY3Rpb24nOiBkYXRhLnJlZGlyZWN0VXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnbWV0aG9kJzogZGF0YS5tZXRob2QgfHwgJ3Bvc3QnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCBba2V5LCB2YWxdIG9mIE9iamVjdC5lbnRyaWVzKGRhdGEucmVkaXJlY3RQYXJhbXMgfHwgW10pKXtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlZGlyZWN0Rm9ybS5hcHBlbmQoJCgnPGlucHV0PicsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IGtleSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiB2YWwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnaGlkZGVuJ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJCgnYm9keScpLmFwcGVuZCgkcmVkaXJlY3RGb3JtLmhpZGUoKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlZGlyZWN0Rm9ybS50cmlnZ2VyKCdzdWJtaXQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcub3BlblBhbmVsKCdwYXltZW50LXN1Y2Nlc3MtcGFnZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5maW5kKCdbZGF0YS1uYW1lXScpLnRleHQoZGF0YS5uYW1lKS5lbmQoKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZmluZCgnW2RhdGEtb3JkZXItaWRdJykudGV4dChkYXRhLm51bWJlcikuZW5kKClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmZpbmQoJ1tkYXRhLWVtYWlsXScpLnRleHQoZGF0YS5lbWFpbCkuZW5kKClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzZXRPcmRlckNhcnRzRnVuYygpO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuZXJyb3JzLnB1c2goZS50b1N0cmluZygpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctcmVxdWVzdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYWxlcnQoJ9Ce0YjQuNCx0LrQsCDQvtGC0L/RgNCw0LLQutC4INC00LDQvdC90YvRhSEg0J/QvtC/0YDQvtCx0YPQudGC0LUg0L7QsdC90L7QstC40YLRjCDRgdGC0YDQsNC90LjRhtGDLicpO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgJGludmFsaWRFbGVtcy5maXJzdCgpLnRyaWdnZXIoJ2ZvY3VzJyk7XG5cbiAgICAgICAgICAgICAgICBpZiAoZGF0YUZpZWxkcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGludmFsaWRJdGVtID0gZGF0YUZpZWxkcy5maW5kKChpdGVtKSA9PiBpdGVtLmNsYXNzTGlzdC5jb250YWlucygnaW52YWxpZCcpKVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChpbnZhbGlkSXRlbSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgdmFsdWUgPSBpbnZhbGlkSXRlbS52YWx1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG5hbWUgPSBpbnZhbGlkSXRlbS5nZXRBdHRyaWJ1dGUoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG5leHRFbGVtID0gaW52YWxpZEl0ZW0ubmV4dEVsZW1lbnRTaWJsaW5nO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2RvY3VtZW50JyAmJiB2YWx1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFuZXh0RWxlbS5jbGFzc0xpc3QuY29udGFpbnMoJ3R3LWVycm9yLW1lc3NhZ2UnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBlcnJvckVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yRWxlbWVudC5jbGFzc0xpc3QuYWRkKCd0dy1lcnJvci1tZXNzYWdlJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yRWxlbWVudC50ZXh0Q29udGVudCA9ICfQndC+0LzQtdGAINC00L7QutGD0LzQtdC90YLQsCDQvdC1INC80L7QttC10YIg0YHQvtCy0L/QsNC00LDRgtGMINGBINC00YDRg9Cz0LjQvCc7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludmFsaWRJdGVtLmFmdGVyKGVycm9yRWxlbWVudCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBlcnJvck1lc3NhZ2VzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLnR3LWVycm9yLW1lc3NhZ2UnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlcnJvck1lc3NhZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JNZXNzYWdlcy5mb3JFYWNoKChpdGVtKSA9PiBpdGVtLnJlbW92ZSgpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChUVy5pc01vYmlsZVdpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaW52YWxpZEl0ZW0uc2Nyb2xsSW50b1ZpZXcoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJlaGF2aW9yOiAnc21vb3RoJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2NrOiAnY2VudGVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlubGluZTogJ3N0YXJ0J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZXJyb3JNZXNzYWdlcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy50dy1lcnJvci1tZXNzYWdlJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlcnJvck1lc3NhZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvck1lc3NhZ2VzLmZvckVhY2goKGl0ZW0pID0+IGl0ZW0ucmVtb3ZlKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChUVy5pc01vYmlsZVdpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgICRjaGVja291dC5nZXQoMCkuc2Nyb2xsKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGJlaGF2aW9yOiAnc21vb3RoJ1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ2NsaWNrJywgJy5qcy10dy1naXZlLXJlcXVlc3QnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjb25zdCAkZm9ybSA9ICQodGhpcykucGFyZW50cygnZm9ybScpO1xuICAgICAgICAgICAgY29uc3Qgc2VyaWFsaXplRGF0YSA9ICRmb3JtLnNlcmlhbGl6ZUFycmF5KCk7XG5cbiAgICAgICAgICAgICRmb3JtLmZpbmQoJ1tkYXRhLXJlcXVpcmVkLWdyb3VwXScpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGxldCAkZ3JvdXAgPSAkZm9ybS5maW5kKGBbZGF0YS1yZXF1aXJlZC1ncm91cD1cIiR7dGhpcy5kYXRhc2V0LnJlcXVpcmVkR3JvdXB9XCJdYCk7XG4gICAgICAgICAgICAgICAgbGV0IGdyb3VwSXNWYWxpZCA9ICRncm91cC5maWx0ZXIoKGluZGV4LCBlbCkgPT4gZWwudmFsdWUubGVuZ3RoID4gMCkubGVuZ3RoID4gMDtcblxuICAgICAgICAgICAgICAgIGlmICghZ3JvdXBJc1ZhbGlkKSB7XG4gICAgICAgICAgICAgICAgICAgICRncm91cC5wcm9wKCdyZXF1aXJlZCcsIHRydWUpLmFkZENsYXNzKCdpbnZhbGlkJykub25lKCdpbnB1dCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICRncm91cC5wcm9wKCdyZXF1aXJlZCcsIGZhbHNlKS5yZW1vdmVDbGFzcygnaW52YWxpZCcpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgbGV0ICRpbnZhbGlkRWxlbXMgPSAkZm9ybS5maW5kKCc6aW52YWxpZCcpO1xuXG4gICAgICAgICAgICBpZiAoISRpbnZhbGlkRWxlbXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgbGV0IHBhcmFtcyA9IHtcbiAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiAnZG9fb3JkZXJzJyxcbiAgICAgICAgICAgICAgICAgICAgcGFuZWxUeXBlOiAncmVxdWVzdEZvcm0nLFxuICAgICAgICAgICAgICAgICAgICBvcmRlcnM6IFt7XG4gICAgICAgICAgICAgICAgICAgICAgICBldmVudDogVFcuZXZlbnRQYXJhbXMsXG4gICAgICAgICAgICAgICAgICAgIH1dXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgJC5lYWNoKHNlcmlhbGl6ZURhdGEsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgcGFyYW1zW3RoaXMubmFtZV0gPSB0aGlzLnZhbHVlO1xuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgcGFyYW1zLmVycm9ycyA9IFRXLmVycm9ycztcblxuICAgICAgICAgICAgICAgIFRXLmZldGNoKHtcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHBhcmFtcyxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAudGhlbihyZXNwb25zZSA9PiByZXNwb25zZS5qc29uKCkpXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGRhdGEgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFkYXRhLnN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBFcnJvcign0J3QtSDRg9C00LDQu9C+0YHRjCDQvtGE0L7RgNC80LjRgtGMINC30LDQutCw0Lcg0LjQtyDRhNC+0YDQvNGLINC30LDRj9Cy0LrQuCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5lcnJvcnMgPSBbXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgJGZvcm0uYWRkQ2xhc3MoJ3N1Ym1pdHRlZCcpO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5lcnJvcnMucHVzaChlLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFsZXJ0KCfQntGI0LjQsdC60LAg0L7RgtC/0YDQsNCy0LrQuCDQtNCw0L3QvdGL0YUhINCf0L7Qv9GA0L7QsdGD0LnRgtC1INC+0LHQvdC+0LLQuNGC0Ywg0YHRgtGA0LDQvdC40YbRgy4nKTtcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICA7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICRpbnZhbGlkRWxlbXMuZmlyc3QoKS50cmlnZ2VyKCdmb2N1cycpO1xuXG4gICAgICAgICAgICAgICAgaWYgKFRXLmlzTW9iaWxlV2lkdGgpIHtcbiAgICAgICAgICAgICAgICAgICAgJGZvcm0ucGFyZW50KCkuZ2V0KDApLnNjcm9sbCh7XG4gICAgICAgICAgICAgICAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBiZWhhdmlvcjogJ3Ntb290aCdcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdjaGFuZ2UnLCAnLmpzLXJlcXVlc3QtZGF0ZS1zZWxlY3QnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBjb25zdCAkc2VsZWN0ZWRPcHRpb24gPSAkKHRoaXMpLmNoaWxkcmVuKCc6c2VsZWN0ZWQnKS5maXJzdCgpO1xuICAgICAgICAgICAgaWYgKCRzZWxlY3RlZE9wdGlvbi5hdHRyKCdkYXRhLXRpY2tldHMtY291bnQnKSA+IDApIHtcbiAgICAgICAgICAgICAgICBUVy5vcGVuUGFuZWwoJ3R3LXNjaGVtZScpO1xuICAgICAgICAgICAgICAgIFRXLmdldEV2ZW50KHtcbiAgICAgICAgICAgICAgICAgICAgZGF0ZTogJHNlbGVjdGVkT3B0aW9uLnZhbCgpLFxuICAgICAgICAgICAgICAgICAgICBoYWxsSWQ6ICRzZWxlY3RlZE9wdGlvbi5hdHRyKCdkYXRhLWhhbGwtaWQnKSxcbiAgICAgICAgICAgICAgICAgICAgaGFsbE5hbWU6ICRzZWxlY3RlZE9wdGlvbi5hdHRyKCdkYXRhLWhhbGwtbmFtZScpLFxuICAgICAgICAgICAgICAgICAgICBwbGFjZUlkOiAkc2VsZWN0ZWRPcHRpb24uYXR0cignZGF0YS1wbGFjZS1pZCcpLFxuICAgICAgICAgICAgICAgICAgICBwbGFjZU5hbWU6ICRzZWxlY3RlZE9wdGlvbi5hdHRyKCdkYXRhLXBsYWNlLW5hbWUnKSxcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgJy5qcy1yZXF1ZXN0LXJlc2V0LWZvcm0nLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgVFcuY2xvc2VQYW5lbCgpO1xuICAgICAgICAgICAgVFcuJCQoJy50d19fcmVxdWVzdC1mb3JtJykucmVtb3ZlQ2xhc3MoJ3N1Ym1pdHRlZCcpLmdldCgwKS5yZXNldCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdjbGljaycsICcuanMtdHctcGFnZS1saW5rJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIFRXLm9wZW5QYW5lbCh0aGlzLmhyZWYuc3BsaXQoJyMnKVsxXSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ2NoYW5nZScsICdbZGF0YS12aWV3ZXItZGF0YS1jb250ZW50XScsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zdCBjYXJ0cyA9IFRXQ2FydHNTdG9yYWdlLmFsbCh0cnVlKTtcbiAgICAgICAgICAgIGNvbnN0IGNhcnRJZCA9ICQodGhpcykuY2xvc2VzdCgnW2RhdGEtY2FydF0nKS5hdHRyKCdkYXRhLWNhcnQnKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgY29uc3QgY2FydEluc3QgPSBjYXJ0c1tjYXJ0SWRdO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjb25zdCBjYXJ0SXRlbUlkID0gZS50YXJnZXQuY2xvc2VzdCgnW2RhdGEtY2FydC1pdGVtXScpPy5nZXRBdHRyaWJ1dGUoJ2RhdGEtY2FydC1pdGVtJyk7XG4gICAgICAgICAgICBjb25zdCBjYXJ0SXRlbSA9IGNhcnRJbnN0Lml0ZW1zLmZpbmQoZmllbGRzID0+IGZpZWxkcy5pZCA9PT0gY2FydEl0ZW1JZCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmIChjYXJ0SXRlbSl7XG4gICAgICAgICAgICAgICAgY29uc3QgZG9jdW1lbnRBbHJlYWR5VXNlZCA9ICEhIGNhcnRJbnN0Lml0ZW1zLmZpbHRlcihpdGVtID0+IGl0ZW0uaWQgIT09IGNhcnRJdGVtLmlkICYmIGl0ZW0uZG9jdW1lbnREYXRhPy5kb2N1bWVudC5yZXBsYWNlKC9cXHMrL2csICcnKSA9PT0gZS50YXJnZXQudmFsdWUucmVwbGFjZSgvXFxzKy9nLCAnJykpLmxlbmd0aDtcbiAgICAgICAgICAgICAgICBpZiAoZS50YXJnZXQubmFtZSA9PT0gJ2RvY3VtZW50JyAmJiBkb2N1bWVudEFscmVhZHlVc2VkKXtcbiAgICAgICAgICAgICAgICAgICAgYWxlcnQoJ9Ci0LDQutC40LUg0L/QsNGB0L/QvtGA0YLQvdGL0LUg0LTQsNC90L3Ri9C1INGD0LbQtSDQuNGB0L/QvtC70YzQt9GD0Y7RgtGB0Y8g0LIg0LTRgNGD0LPQvtC8INCx0LjQu9C10YLQtSEnKTtcbiAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQuY2xhc3NMaXN0LmFkZCgnaW52YWxpZCcpO1xuICAgICAgICAgICAgICAgICAgICBlLnRhcmdldC52YWx1ZSA9ICcnO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhcnRJdGVtLmRvY3VtZW50RGF0YSA/Pz0ge307XG4gICAgICAgICAgICAgICAgY2FydEl0ZW0uZG9jdW1lbnREYXRhW2UudGFyZ2V0Lm5hbWVdID0gZS50YXJnZXQudmFsdWU7XG4gICAgICAgICAgICAgICAgY2FydEluc3QudXBkYXRlKGNhcnRJdGVtSWQsIHtcbiAgICAgICAgICAgICAgICAgICAgZG9jdW1lbnREYXRhOiBjYXJ0SXRlbS5kb2N1bWVudERhdGFcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignY2xpY2snLCAnLmpzLWRldGFpbHMgLmpzLXN1bW1hcnknLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoICEgVFcuJCQoJy50d19faGVhZGVyLWRhdGUtc2VsZWN0IC50d19faGVhZGVyLWRhdGUtb3B0aW9uJykubGVuZ3RoKXtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNvbnN0ICRkZXRhaWxzID0gJCh0aGlzKS5wYXJlbnRzKCcuanMtZGV0YWlscycpO1xuXG4gICAgICAgICAgICAkZGV0YWlscy50b2dnbGVDbGFzcygnb3BlbicpO1xuXG4gICAgICAgICAgICBpZiAoJGRldGFpbHMuaGFzQ2xhc3MoJ29wZW4nKSkge1xuICAgICAgICAgICAgICAgIFRXLiQub25lKCdjbGljaycsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgJGRldGFpbHMucmVtb3ZlQ2xhc3MoJ29wZW4nKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSW5pdCBsaXN0ZW5lcnMgZm9yIGNhbGVuZGFyIHBhbmVsXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqL1xuICAgIF9pbml0Q2FsZW5kYXJMaXN0ZW5lcnMoKSB7XG4gICAgICAgIFRXLiQub24oJ2NsaWNrJywgJy50d19faGVhZGVyLXBpY2tlcjpub3QoLi0tbG9ja2VkKScsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGlmIChUVy4kY2FsZW5kYXIuaGFzQ2xhc3MoJ2FjdGl2ZScpKSB7XG4gICAgICAgICAgICAgICAgVFcuY2xvc2VQYW5lbCgndHctY2FsZW5kYXInKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgVFcuZ2V0Q2FsZW5kYXIodHJ1ZSwgZmFsc2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBUVy4kLm9uKCdjbGljaycsICcudHdfX2NhbGVuZGFyLW1vbnRoJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgbGV0ICRtb250aCA9ICQodGhpcykuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgJG1vbnRoLnNpYmxpbmdzKCkucmVtb3ZlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgY29uc3QgbW9udGhOdW1iZXIgPSAkbW9udGguZGF0YSgnbW9udGgnKTtcblxuICAgICAgICAgICAgVFcuJCQoYC50d19fY2FsZW5kYXItZGF5c1tkYXRhLW1vbnRoPVwiJHttb250aE51bWJlcn1cIl1gKS5hZGRDbGFzcygnYWN0aXZlJylcbiAgICAgICAgICAgICAgICAuc2libGluZ3MoKS5yZW1vdmVDbGFzcygnYWN0aXZlJylcbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignY2xpY2snLCAnLnR3X19jYWxlbmRhci1kYXkuYXZhaWxhYmxlJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgY29uc3QgJHNlbGYgPSAkKHRoaXMpO1xuICAgICAgICAgICAgY29uc3QgZGF0ZSA9ICRzZWxmLmRhdGEoJ2RhdGUnKTtcblxuICAgICAgICAgICAgY29uc3QgJHBpY2tlcnMgPSBUVy4kY2FsZW5kYXIuZmluZCgnLnR3LWRhdGVwaWNrZXInKS5hZGRDbGFzcygndHctaGlkZGVuJylcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGBbZGF0YS1kYXRlPVwiJHtkYXRlfVwiXWApLnJlbW92ZUNsYXNzKCd0dy1oaWRkZW4nKVxuICAgICAgICAgICAgO1xuXG4gICAgICAgICAgICBpZiAoJHBpY2tlcnMubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgICAgICAgJHBpY2tlcnMuZmlyc3QoKS50cmlnZ2VyKCdjbGljaycpO1xuICAgICAgICAgICAgICAgIC8vIH0gZWxzZSBpZiAoJHBpY2tlcnMubGVuZ3RoID4gMSAmJiBUVy5pc01vYmlsZVdpZHRoKSB7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCRwaWNrZXJzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICBUVy4kY2FsZW5kYXIuZ2V0KDApLnNjcm9sbCh7XG4gICAgICAgICAgICAgICAgICAgIHRvcDogJHBpY2tlcnMuZmlyc3QoKS5wb3NpdGlvbigpLnRvcCxcbiAgICAgICAgICAgICAgICAgICAgYmVoYXZpb3I6ICdzbW9vdGgnXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICQoJy50d19fY2FsZW5kYXItc2VsZWN0LWNhcHRpb24nKS50b2dnbGUoJHBpY2tlcnMubGVuZ3RoID4gMCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIFRXLiQub24oJ2NsaWNrJywgJy5qcy10dy10aW1lLXBpY2tlcicsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zdCAkc2VsZiA9ICQodGhpcyk7XG4gICAgICAgICAgICBUVy4kJCgnLnR3X19oZWFkZXItZGF0ZScpLnByb3AoJ29wZW4nLCBmYWxzZSk7XG5cbiAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctc2NoZW1lJyk7XG4gICAgICAgICAgICBUVy5nZXRFdmVudCh7XG4gICAgICAgICAgICAgICAgZGF0ZTogJHNlbGYuYXR0cignZGF0YS10aW1lc3RhbXAnKSxcbiAgICAgICAgICAgICAgICBuYW1lOiAkc2VsZi5hdHRyKCdkYXRhLWV2ZW50LWRhdGUtbmFtZScpIHx8ICRzZWxmLmF0dHIoJ2RhdGEtZXZlbnQtbmFtZScpLFxuICAgICAgICAgICAgICAgIHBsYWNlSWQ6ICRzZWxmLmF0dHIoJ2RhdGEtcGxhY2UtaWQnKSxcbiAgICAgICAgICAgICAgICBoYWxsSWQ6ICRzZWxmLmF0dHIoJ2RhdGEtaGFsbC1pZCcpLFxuICAgICAgICAgICAgICAgIGhhbGxOYW1lOiAkc2VsZi5hdHRyKCdkYXRhLWhhbGwtbmFtZScpLFxuICAgICAgICAgICAgICAgIHBsYWNlTmFtZTogJHNlbGYuYXR0cignZGF0YS1wbGFjZS1uYW1lJyksXG4gICAgICAgICAgICAgICAgcGFydElkOiAnJyxcbiAgICAgICAgICAgIH0pO1xuXG5cbiAgICAgICAgICAgIC8vIGFsdGVybmF0aXZlIHdheSBmb3IgY2hhbmdlIG1hcCB3aXRoIHJlbG9hZCB3aWRnZXRcbiAgICAgICAgICAgIC8vIFRXLnJlbG9hZCh7XG4gICAgICAgICAgICAvLyAgICAgZGF0ZTogJHNlbGYuYXR0cignZGF0YS10aW1lc3RhbXAnKSxcbiAgICAgICAgICAgIC8vICAgICBwbGFjZUlkOiAkc2VsZi5hdHRyKCdkYXRhLXBsYWNlLWlkJyksXG4gICAgICAgICAgICAvLyAgICAgaGFsbElkOiAkc2VsZi5hdHRyKCdkYXRhLWhhbGwtaWQnKSxcbiAgICAgICAgICAgIC8vICAgICBoYWxsTmFtZTogJHNlbGYuYXR0cignZGF0YS1oYWxsLW5hbWUnKSxcbiAgICAgICAgICAgIC8vICAgICBwbGFjZU5hbWU6ICRzZWxmLmF0dHIoJ2RhdGEtcGxhY2UtbmFtZScpLFxuICAgICAgICAgICAgLy8gICAgIHBhcnRJZDogJycsXG4gICAgICAgICAgICAvLyB9KTtcbiAgICAgICAgfSk7XG5cbiAgICB9XG5cbiAgICBfaW5pdEZpbHRlcih1bmlxdWVTZWxlY3RvciwgZmlsdGVyaW5nRG9tRWxlbWVudHMgPSBbXSwgYWRkaXRpb25hbFBhcmFtcyA9IHt9KSB7XG4gICAgICAgIFRXLmZpbHRlciA9IE9iamVjdC5hc3NpZ24oVFcuZmlsdGVyLCBhZGRpdGlvbmFsUGFyYW1zLCB7dW5pcXVlU2VsZWN0b3I6IHVuaXF1ZVNlbGVjdG9yfSk7XG4gICAgICAgIFRXLmZpbHRlci4kID0gVFcuJCQodW5pcXVlU2VsZWN0b3IpO1xuICAgICAgICBUVy5maWx0ZXIuJHNjcm9sbGVyID0gVFcuZmlsdGVyLiQuZmluZCgnLicgKyBUVy5maWx0ZXIuc2Nyb2xsZXJDbGFzcyk7XG4gICAgICAgIFRXLmZpbHRlci4kZ2V0QnV0dG9ucyA9IChqcUZpbHRlciA9ICcnKSA9PiB7XG4gICAgICAgICAgICBsZXQgJHJlcyA9IFRXLmZpbHRlci4kLmZpbmQoJy4nICsgVFcuZmlsdGVyLmJ0bkNsYXNzKTtcbiAgICAgICAgICAgIHJldHVybiBqcUZpbHRlciA/ICRyZXMuZmlsdGVyKGpxRmlsdGVyKSA6ICRyZXM7XG4gICAgICAgIH07XG5cbiAgICAgICAgY29uc3QgZ2V0RGl2aXNpb25UZXh0QnlWYWwgPSAodmFsLCBwb3N0Zml4ID0gJycpID0+IHtcbiAgICAgICAgICAgIGlmIChpc05hTih2YWwpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuICh2YWwgPCAyMDAwMCA/IHZhbCA6IHZhbCAvIDEwMDAgKyAnINGC0YvRgScpICsgcG9zdGZpeFxuICAgICAgICB9O1xuXG4gICAgICAgIFRXLmZpbHRlci4kZ2V0QnV0dG9ucygpLmVhY2goZnVuY3Rpb24gKGluZGV4LCBidG4pIHtcbiAgICAgICAgICAgIGNvbnN0IG5leHQgPSB0aGlzLm5leHRFbGVtZW50U2libGluZztcbiAgICAgICAgICAgIGxldCBwcmljZVRvID0gK2J0bi5nZXRBdHRyaWJ1dGUoJ2RhdGEtcHJpY2UtdG8nKTtcblxuICAgICAgICAgICAgaWYgKCFwcmljZVRvKSB7XG4gICAgICAgICAgICAgICAgcHJpY2VUbyA9IG5leHRcbiAgICAgICAgICAgICAgICAgICAgPyBuZXh0LmdldEF0dHJpYnV0ZSgnZGF0YS1wcmljZS1mcm9tJykgLSAxXG4gICAgICAgICAgICAgICAgICAgIDogSW5maW5pdHk7XG4gICAgICAgICAgICAgICAgYnRuLnNldEF0dHJpYnV0ZSgnZGF0YS1wcmljZS10bycsIHByaWNlVG8pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtcyA9IHt9O1xuXG4gICAgICAgIGZvciAoY29uc3QgcGxhY2VFbGVtIG9mIGZpbHRlcmluZ0RvbUVsZW1lbnRzKSB7XG4gICAgICAgICAgICBjb25zdCB0aWNrZXRQcmljZSA9ICtwbGFjZUVsZW0uZ2V0QXR0cmlidXRlKCdwcmljZScpIHx8ICtwbGFjZUVsZW0uZ2V0QXR0cmlidXRlKCdkYXRhLXByaWNlJyk7XG4gICAgICAgICAgICBUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtc1t0aWNrZXRQcmljZV0gPSBUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtc1t0aWNrZXRQcmljZV0gfHwgW107XG4gICAgICAgICAgICBUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtc1t0aWNrZXRQcmljZV0ucHVzaChwbGFjZUVsZW0pO1xuXG5cbiAgICAgICAgICAgIGlmICh0aWNrZXRQcmljZSA8IFRXLmZpbHRlci5taW5QcmljZSB8fCAhVFcuZmlsdGVyLm1pblByaWNlKSB7XG4gICAgICAgICAgICAgICAgVFcuZmlsdGVyLm1pblByaWNlID0gdGlja2V0UHJpY2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodGlja2V0UHJpY2UgPiBUVy5maWx0ZXIubWF4UHJpY2UpIHtcbiAgICAgICAgICAgICAgICBUVy5maWx0ZXIubWF4UHJpY2UgPSB0aWNrZXRQcmljZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHVzZUxvYWRlZEZpbHRlckJ0bnMgPSBPYmplY3Qua2V5cyhUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtcykubGVuZ3RoID4gMTQ7XG4gICAgICAgIFRXLmZpbHRlci4kZ2V0QnV0dG9ucygnOm5vdChbZGF0YS1kZWZhdWx0XSknKS5yZW1vdmUoKTtcblxuICAgICAgICBpZiAoIXVzZUxvYWRlZEZpbHRlckJ0bnMpIHtcbiAgICAgICAgICAgIHdpbmRvdy5UV2ZpbHRlckRlZmF1bHRCdG5zID0gd2luZG93LlRXZmlsdGVyRGVmYXVsdEJ0bnMgfHwgVFcuZmlsdGVyLiRnZXRCdXR0b25zKCdbZGF0YS1kZWZhdWx0XScpO1xuICAgICAgICAgICAgd2luZG93LlRXZmlsdGVyRGVmYXVsdEJ0bnMuZGV0YWNoKCk7XG5cbiAgICAgICAgICAgIGZvciAoY29uc3QgcHJpY2Ugb2YgT2JqZWN0LmtleXMoVFcuZmlsdGVyLnByaWNlR3JvdXBzRWxlbXMpKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihwcmljZSkgJiYgcHJpY2UgPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLmZpbHRlci4kc2Nyb2xsZXIuYXBwZW5kKFxuICAgICAgICAgICAgICAgICAgICAgICAgJCgnPGRpdj4nLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NsYXNzJzogVFcuZmlsdGVyLmJ0bkNsYXNzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYXRhLXByaWNlLWZyb20nOiBwcmljZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1wcmljZS10byc6IHByaWNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIH0pLnRleHQocGFyc2VGbG9hdChwcmljZSkudG9Mb2NhbGVTdHJpbmcoJ3J1LVJVJykpXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS50ZXh0KGdldERpdmlzaW9uVGV4dEJ5VmFsKHByaWNlLCAnKycpKVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICghVFcuZmlsdGVyLiRnZXRCdXR0b25zKCkubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgVFcuZmlsdGVyLiRzY3JvbGxlci5hcHBlbmQod2luZG93LlRXZmlsdGVyRGVmYXVsdEJ0bnMpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBmaXJzdEJ0blByaWNlRnJvbSA9IFRXLmZpbHRlci4kZ2V0QnV0dG9ucygpLmZpcnN0KCkuYXR0cignZGF0YS1wcmljZS1mcm9tJyk7XG5cbiAgICAgICAgICAgIGlmIChUVy5maWx0ZXIubWluUHJpY2UgPCBmaXJzdEJ0blByaWNlRnJvbSkge1xuICAgICAgICAgICAgICAgIGNvbnN0ICRidG4gPSAkKCc8ZGl2PicsIHtcbiAgICAgICAgICAgICAgICAgICAgJ2NsYXNzJzogVFcuZmlsdGVyLmJ0bkNsYXNzLFxuICAgICAgICAgICAgICAgICAgICAnZGF0YS1wcmljZS1mcm9tJzogVFcuZmlsdGVyLm1pblByaWNlLFxuICAgICAgICAgICAgICAgICAgICAnZGF0YS1wcmljZS10byc6IGZpcnN0QnRuUHJpY2VGcm9tIC0gMSxcbiAgICAgICAgICAgICAgICAgICAgLy8gfSkudGV4dCgn0L7RgiAnICsgZ2V0RGl2aXNpb25UZXh0QnlWYWwoVFcuZmlsdGVyLm1pblByaWNlKSkpO1xuICAgICAgICAgICAgICAgIH0pLnRleHQoZ2V0RGl2aXNpb25UZXh0QnlWYWwoVFcuZmlsdGVyLm1pblByaWNlLCAnKycpKTtcblxuICAgICAgICAgICAgICAgIFRXLmZpbHRlci4kc2Nyb2xsZXIucHJlcGVuZCgkYnRuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIFRXLmZpbHRlci52aXNpYmxlQnRuc1FudCA9IDA7XG4gICAgICAgIFRXLmZpbHRlci4kZ2V0QnV0dG9ucygpLmVhY2goZnVuY3Rpb24gKGluZGV4LCBidG4pIHtcbiAgICAgICAgICAgIGNvbnN0IHByaWNlRnJvbSA9ICtidG4uZ2V0QXR0cmlidXRlKCdkYXRhLXByaWNlLWZyb20nKTtcbiAgICAgICAgICAgIGNvbnN0IHByaWNlVG8gPSArYnRuLmdldEF0dHJpYnV0ZSgnZGF0YS1wcmljZS10bycpO1xuXG4gICAgICAgICAgICBjb25zdCBjb2xvciA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKGJ0biwgJzpiZWZvcmUnKS5nZXRQcm9wZXJ0eVZhbHVlKCdiYWNrZ3JvdW5kLWNvbG9yJyk7XG4gICAgICAgICAgICBsZXQgY291bnQgPSAwO1xuICAgICAgICAgICAgbGV0IGlzU3ZnID0gZmFsc2U7XG5cbiAgICAgICAgICAgIGZvciAoY29uc3QgW3ByaWNlLCBncm91cF0gb2YgT2JqZWN0LmVudHJpZXMoVFcuZmlsdGVyLnByaWNlR3JvdXBzRWxlbXMpKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihwcmljZSkgJiYgcHJpY2UgPj0gcHJpY2VGcm9tICYmIHByaWNlIDw9IHByaWNlVG8pIHtcbiAgICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCBwbGFjZUVsIG9mIGdyb3VwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb3VudCsrO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoVFcuZmlsdGVyLmNvbG9yaXplQWZ0ZXJJbml0ICYmIGNvbG9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNTdmcgPSBpc1N2ZyB8fCAhIXBsYWNlRWwuY2xvc2VzdCgnc3ZnJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNTdmcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxhY2VFbC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCBjb2xvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYWNlRWwuc2V0QXR0cmlidXRlKCdzdHJva2UnLCBjb2xvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxhY2VFbC5zdHlsZS5ib3JkZXJDb2xvciA9IGNvbG9yO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgJChidG4pLmF0dHIoJ2RhdGEtY291bnRlcicsIGNvdW50KTtcblxuICAgICAgICAgICAgaWYgKGNvdW50KSB7XG4gICAgICAgICAgICAgICAgVFcuZmlsdGVyLnZpc2libGVCdG5zUW50Kys7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBUVy5maWx0ZXIuJFxuICAgICAgICAgICAgLnRvZ2dsZUNsYXNzKCd0dy1oaWRkZW4nLCAhVFcuZmlsdGVyLnZpc2libGUgfHwgVFcuZmlsdGVyLnZpc2libGVCdG5zUW50IDwgMilcbiAgICAgICAgICAgIC5hdHRyKCdkYXRhLXZpc2libGUtcW50JywgVFcuZmlsdGVyLnZpc2libGVCdG5zUW50KVxuICAgICAgICAgICAgLmF0dHIoJ2RhdGEtZGVmYXVsdC1tb2RlJywgdXNlTG9hZGVkRmlsdGVyQnRucylcbiAgICAgICAgO1xuICAgIH1cblxuICAgIF9pbml0RmlsdGVyc0xpc3RlbmVycygpIHtcbiAgICAgICAgVFcuJC5vbignd2hlZWwnLCBgLiR7VFcuZmlsdGVyLnNjcm9sbGVyQ2xhc3N9YCwgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGlmICghVFcuaXNUb3VjaERldmljZSkge1xuICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNjcm9sbExlZnQgKz0gZS5vcmlnaW5hbEV2ZW50LmRlbHRhWTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSkub24oJ21vdXNlZG93bicsIGAuJHtUVy5maWx0ZXIuc2Nyb2xsZXJDbGFzc31gLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgaWYgKCFUVy5pc1RvdWNoRGV2aWNlICYmIHRoaXMuY2xpZW50V2lkdGggPCB0aGlzLnNjcm9sbFdpZHRoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jbGFzc0xpc3QuYWRkKCctLWRyYWdpbml0Jyk7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXRBdHRyaWJ1dGUoJ3N0YXJ0WCcsIGUucGFnZVggLSB0aGlzLm9mZnNldExlZnQpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0QXR0cmlidXRlKCdzY3JvbGxMZWZ0JywgdGhpcy5zY3JvbGxMZWZ0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSkub24oJ21vdXNlbW92ZScsIGAuJHtUVy5maWx0ZXIuc2Nyb2xsZXJDbGFzc30uLS1kcmFnaW5pdGAsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBpZiAoIVRXLmlzVG91Y2hEZXZpY2UpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNsYXNzTGlzdC5hZGQoJy0tZHJhZ2dpbmcnKTtcbiAgICAgICAgICAgICAgICBjb25zdCB4ID0gZS5wYWdlWCAtIHRoaXMub2Zmc2V0TGVmdDtcbiAgICAgICAgICAgICAgICBjb25zdCBzY3JvbGwgPSB4IC0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3N0YXJ0WCcpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2Nyb2xsTGVmdCA9IHRoaXMuZ2V0QXR0cmlidXRlKCdzY3JvbGxMZWZ0JykgLSBzY3JvbGw7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pLm9uKCdtb3VzZXVwIG1vdXNlbGVhdmUnLCBgLiR7VFcuZmlsdGVyLnNjcm9sbGVyQ2xhc3N9Li0tZHJhZ2luaXRgLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgaWYgKCFUVy5pc1RvdWNoRGV2aWNlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jbGFzc0xpc3QucmVtb3ZlKCctLWRyYWdpbml0Jyk7XG4gICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY2xhc3NMaXN0LnJlbW92ZSgnLS1kcmFnZ2luZycpO1xuICAgICAgICAgICAgICAgIH0sIDUwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgVFcuJC5vbignbW91c2Vkb3duJywgYC4ke1RXLmZpbHRlci5idG5DbGFzc31gLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgLy8gZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgaWYgKCFUVy5maWx0ZXIubXVsdGlwbGVNb2RlKSB7XG4gICAgICAgICAgICAgICAgW10uZm9yRWFjaC5jYWxsKHRoaXMucGFyZW50Tm9kZS5jaGlsZHJlbiwgZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmNsYXNzTGlzdC50b2dnbGUoJ2FjdGl2ZScpO1xuXG4gICAgICAgICAgICBsZXQgZGl2aXNpb25zID0gW107XG5cbiAgICAgICAgICAgIFRXLmZpbHRlci4kZ2V0QnV0dG9ucygnLmFjdGl2ZScpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGRpdmlzaW9ucy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgZnJvbTogK3RoaXMuZ2V0QXR0cmlidXRlKCdkYXRhLXByaWNlLWZyb20nKSxcbiAgICAgICAgICAgICAgICAgICAgdG86ICt0aGlzLmdldEF0dHJpYnV0ZSgnZGF0YS1wcmljZS10bycpLFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgZm9yIChjb25zdCBbcHJpY2UsIGVsZW1lbnRzXSBvZiBPYmplY3QuZW50cmllcyhUVy5maWx0ZXIucHJpY2VHcm91cHNFbGVtcykpIHtcbiAgICAgICAgICAgICAgICBsZXQgZmlsdGVyZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIGlmIChkaXZpc2lvbnMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGZpbHRlcmVkID0gISFkaXZpc2lvbnMuZmluZChkaXZpc2lvbiA9PiBwcmljZSA+PSBkaXZpc2lvbi5mcm9tICYmIHByaWNlIDw9IGRpdmlzaW9uLnRvKTtcbiAgICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGVsZW1lbnRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZSgnZGF0YS1maWx0ZXJlZCcsICtmaWx0ZXJlZCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZWxlbWVudHMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW1lbnQucmVtb3ZlQXR0cmlidXRlKCdkYXRhLWZpbHRlcmVkJylcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG5cbiAgICAvKipcbiAgICAgKiDQktGL0LHQvtGAINGB0LvQvtCy0LAg0YEg0L/RgNCw0LLQuNC70YzQvdGL0Lwg0L7QutC+0L3Rh9Cw0L3QuNC10Lwg0LIg0LfQsNCy0LjRgdC40LzQvtGB0YLQuCDQvtGCINC60L7Quy3QstCwXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbnVtYmVyXG4gICAgICogQHBhcmFtIHdvcmRzXG4gICAgICogQHJldHVybnMgeyp9XG4gICAgICovXG4gICAgZGVjbE9mTnVtKG51bWJlciwgd29yZHMpIHtcbiAgICAgICAgcmV0dXJuIHdvcmRzWyhudW1iZXIgJSAxMDAgPiA0ICYmIG51bWJlciAlIDEwMCA8IDIwKSA/IDIgOiBbMiwgMCwgMSwgMSwgMSwgMl1bKG51bWJlciAlIDEwIDwgNSkgPyBNYXRoLmFicyhudW1iZXIpICUgMTAgOiA1XV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSnF1ZXJ5IGZpbmQgaGVscGVyXG4gICAgICpcbiAgICAgKiBAcGFyYW0gc2VsZWN0b3JcbiAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgKi9cbiAgICAkJChzZWxlY3Rvcikge1xuICAgICAgICByZXR1cm4gVFcuJC5maW5kKHNlbGVjdG9yKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDQodCx0YDQsNGB0YvQstCw0LXRgiBldmVudFBhcmFtcyDQtNC+INC/0YDQsNCy0LjQu9GM0L3QvtC5INC40LfQvdCw0YfQsNC70YzQvdC+0Lkg0YHRgtGA0YPQutGC0YPRgNGLXG4gICAgICovXG4gICAgcmVzZXRFdmVudFBhcmFtcygpIHtcbiAgICAgICAgT2JqZWN0LmtleXMoVFcuZXZlbnRQYXJhbXMpLmZvckVhY2goa2V5ID0+IFRXLmV2ZW50UGFyYW1zW2tleV0gPSAnJylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyB3aWRnZXRcbiAgICAgKlxuICAgICAqIEBwYXJhbSBldmVudFBhcmFtc1xuICAgICAqIEBwYXJhbSBvbmx5Q2FsZW5kYXJcbiAgICAgKi9cbiAgICBvcGVuKGV2ZW50UGFyYW1zLCBvbmx5Q2FsZW5kYXIgPSBmYWxzZSkge1xuICAgICAgICBvbmx5Q2FsZW5kYXIgPSArb25seUNhbGVuZGFyO1xuICAgICAgICB3aW5kb3cuc2F2ZWRTY3JvbGxUb3AgPSB3aW5kb3cuc2Nyb2xsWTtcbiAgICAgICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5hZGQoJ3R3LW9wZW5lZCcpO1xuXG4gICAgICAgIGNvbnN0IGFjdGl2YXRlV2lkZ2V0ID0gKCkgPT4ge1xuICAgICAgICAgICAgaWYgKFRXLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgICAgICAgICAgICBUVy4kaGVhZGVyLmhpZGUoKTtcbiAgICAgICAgICAgICAgICBpZiAoVFcuc2NoZW1lSW5pdCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUVy5yZWxvYWQob25seUNhbGVuZGFyKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICBUVy5wcmV2QWN0aXZlUGFuZWxJZCA9IG51bGw7XG5cbiAgICAgICAgICAgICAgICAgICAgVFcucmVzZXRFdmVudFBhcmFtcygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKFRXLmV2ZW50UGFyYW1zLCBldmVudFBhcmFtcyk7XG5cbiAgICAgICAgICAgICAgICBpZiAob25seUNhbGVuZGFyKSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLmdldENhbGVuZGFyKHRydWUsIHRydWUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctc2NoZW1lJyk7XG4gICAgICAgICAgICAgICAgICAgIFRXLmdldEV2ZW50KGV2ZW50UGFyYW1zKTtcbiAgICAgICAgICAgICAgICAgICAgVFcuZ2V0Q2FsZW5kYXIoZmFsc2UsIHRydWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2V0VGltZW91dChhY3RpdmF0ZVdpZGdldCwgMTAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBhY3RpdmF0ZVdpZGdldCgpO1xuICAgIH1cblxuICAgIG9wZW5CeUhhc2goaGFzaCkge1xuICAgICAgICBoYXNoID0gaGFzaC5yZXBsYWNlKC9eIy8sICcnKTtcbiAgICAgICAgY29uc3QgZGVjb2RlZEhhc2ggPSBiNjREZWNvZGUoaGFzaCk7XG5cbiAgICAgICAgaWYgKHR5cGVvZiBkZWNvZGVkSGFzaCAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBsZXQgaGFzaEtleXMgPSBPYmplY3Qua2V5cyhkZWNvZGVkSGFzaCkuc29ydCgpO1xuICAgICAgICBsZXQgdmFsaWRLZXlzID0gT2JqZWN0LmtleXMoVFcuZXZlbnRQYXJhbXMpLnNvcnQoKTtcblxuICAgICAgICBpZiAoaGFzaEtleXMuZXZlcnkoa2V5ID0+IHZhbGlkS2V5cy5pbmNsdWRlcyhrZXkpKSkge1xuICAgICAgICAgICAgVFcub3BlbihkZWNvZGVkSGFzaCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZSB3aWRnZXRcbiAgICAgKi9cbiAgICBjbG9zZSgpIHtcbiAgICAgICAgaWYgKHdpbmRvdy5sb2NhdGlvbi5oYXNoKSB7XG4gICAgICAgICAgICBoaXN0b3J5LnJlcGxhY2VTdGF0ZShudWxsLCBudWxsLCB3aW5kb3cubG9jYXRpb24uaHJlZi5zcGxpdCgnIycpWzBdKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFRXLiQkKCcudHdfX3BhbmVsLmFjdGl2ZScpLnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcblxuICAgICAgICBUVy5wcmV2QWN0aXZlUGFuZWxJZCA9IG51bGw7XG4gICAgICAgIFRXLmF2YWlsYWJsZVRpY2tldHNDaXJjbGVFbGVtcyA9IFtdO1xuICAgICAgICBUVy5ldmVudFBhcmFtcy5wYXJ0SWQgPSAnJztcbiAgICAgICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ3R3LW9wZW5lZCcpO1xuXG4gICAgICAgIHdpbmRvdy5zY3JvbGxUbygwLCB3aW5kb3cuc2F2ZWRTY3JvbGxUb3ApO1xuXG4gICAgICAgIGlmIChUVy5zY2hlbWVMaWIgPT09IFRXLlNDSEVNRV9MSUJfTEVBRkxFVCkge1xuICAgICAgICAgICAgVFcuc2NoZW1lU3ZnT3ZlcmxheT8ucmVtb3ZlKCkucmVtb3ZlRXZlbnRMaXN0ZW5lcigpO1xuICAgICAgICAgICAgVFcuc2NoZW1lPy5yZW1vdmUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFRXLiR0d1N2Z01hcC5lbXB0eSgpO1xuICAgICAgICBUVy5zY2hlbWVJbml0ID0gZmFsc2U7XG4gICAgICAgIFRXLnNjaGVtZUltYWdlSW5pdCA9IGZhbHNlO1xuICAgICAgICBUVy5hdmFpbGFibGVUaWNrZXRzQ2lyY2xlRWxlbXMgPSBbXTtcbiAgICAgICAgVFcuY2xlYXJFdmVudFBhcmFtcygpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZCB3aWRnZXRcbiAgICAgKi9cbiAgICByZWxvYWQob25seUNhbGVuZGFyID0gZmFsc2UpIHtcbiAgICAgICAgY29uc3Qgc2VsZWN0b3IgPSBUVy5zZWxlY3RvcjtcbiAgICAgICAgY29uc3QgZXZlbnRQYXJhbXMgPSB7Li4uVFcuZXZlbnRQYXJhbXMsIHBhcnRJZDogJyd9O1xuICAgICAgICBsZXQgaW5pdFBhcmFtcyA9IHt9O1xuXG4gICAgICAgIGluaXRQYXJhbXMub25Jbml0aWFsaXplZCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHdpbmRvdy5UVy5vcGVuKGV2ZW50UGFyYW1zLCBvbmx5Q2FsZW5kYXIpO1xuICAgICAgICB9XG4gICAgICAgIGluaXRQYXJhbXMudXJsID0gVFcudXJsO1xuXG4gICAgICAgIFRXLmRlc3RydWN0KCk7XG5cbiAgICAgICAgd2luZG93LlRXID0gbmV3IHRoaXMuY29uc3RydWN0b3Ioc2VsZWN0b3IsIGluaXRQYXJhbXMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEN1c3RvbSBkZXN0cnVjdG9yXG4gICAgICovXG4gICAgZGVzdHJ1Y3QoKSB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwoVFcuY2FydHNUaW1lckZ1bmMpO1xuICAgICAgICAkKHdpbmRvdykub2ZmKCdyZXNpemUnLCBUVy5yZXNpemVDYWxsYmFjaykub2ZmKCdoYXNoY2hhbmdlJywgVFcuaGFzaGNoYW5nZSk7XG4gICAgICAgIFRXLiQub2ZmKCd0b3VjaG1vdmUnKTtcbiAgICAgICAgVFcuJC5lbXB0eSgpLm9mZigpO1xuICAgICAgICBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgndHctb3BlbmVkJyk7XG5cbiAgICAgICAgZGVsZXRlIHdpbmRvdy5UVztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAcGFyYW0gcGFuZWxJZFxuICAgICAqIEBwYXJhbSBwYXJhbXNcbiAgICAgKiBAcmV0dXJucyB7Kn1cbiAgICAgKi9cbiAgICBvcGVuUGFuZWwocGFuZWxJZCwgcGFyYW1zID0ge30pIHtcbiAgICAgICAgY29uc3QgJHByZXZQYW5lbCA9IFRXLiQkKCcudHdfX3BhbmVsLmFjdGl2ZScpO1xuICAgICAgICBsZXQgJG5leHRQYW5lbCA9IFRXLiQkKCcjJyArIHBhbmVsSWQpO1xuXG4gICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gJHByZXZQYW5lbC5hdHRyKCdpZCcpIHx8IG51bGw7XG4gICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbFNjcm9sbFRvcCA9ICRwcmV2UGFuZWwuc2Nyb2xsVG9wKCk7XG5cbiAgICAgICAgVFcuJHRvb2xzLnJlbW92ZUNsYXNzKCd0dy1oaWRkZW4nKTtcbiAgICAgICAgVFcuJG1pbmljYXJ0LnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICAgICAgVFcuJGhlYWRlci5yZW1vdmVDbGFzcygnZmxvYXRpbmcnKTtcblxuICAgICAgICBpZiAocGFuZWxJZCA9PT0gJ3R3LXNjaGVtZScpIHtcbiAgICAgICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gbnVsbDtcbiAgICAgICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcbiAgICAgICAgICAgIFRXLiRtb3JlUG9wdXAucmVtb3ZlQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgVFcuJGhlYWRlci5hZGRDbGFzcygnZmxvYXRpbmcnKTtcbiAgICAgICAgICAgIFRXLiRoZWFkZXIuc2hvdygpO1xuICAgICAgICAgICAgVFcucmVkcmF3TWluaWNhcnQoKTtcbiAgICAgICAgfSBlbHNlIGlmIChwYW5lbElkID09PSAndHctY2FsZW5kYXInKSB7XG4gICAgICAgICAgICBUVy4kaGVhZGVyLnNob3coKTtcbiAgICAgICAgICAgICRuZXh0UGFuZWwuZmluZCgnLnR3LWRhdGVwaWNrZXInKS5hZGRDbGFzcygndHctaGlkZGVuJyk7XG5cbiAgICAgICAgICAgIGNvbnN0ICRtb250aHMgPSAkbmV4dFBhbmVsLmZpbmQoJy50d19fY2FsZW5kYXItbW9udGgnKTtcblxuICAgICAgICAgICAgaWYgKCEkbW9udGhzLmZpbHRlcignLmFjdGl2ZScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICRtb250aHMuZmlyc3QoKS50cmlnZ2VyKCdjbGljaycpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgIH0gZWxzZSBpZiAocGFuZWxJZCA9PT0gJ3R3LWNoZWNrb3V0Jykge1xuICAgICAgICAgICAgaWYgKHBhbmVsSWQgIT09IFRXLnByZXZBY3RpdmVQYW5lbElkKSB7XG4gICAgICAgICAgICAgICAgVFcudXBkYXRlQ2hlY2tvdXQocGFuZWxJZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAocGFuZWxJZCA9PT0gJ3R3LW11bHRpLWNoZWNrb3V0Jykge1xuICAgICAgICAgICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5hZGQoJ3R3LW9wZW5lZCcpO1xuICAgICAgICAgICAgVFcuJGhlYWRlci5oaWRlKCk7XG4gICAgICAgICAgICBUVy4kdG9vbHMuYWRkQ2xhc3MoJ3R3LWhpZGRlbicpO1xuICAgICAgICAgICAgaWYgKCEkbmV4dFBhbmVsLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGNvbnN0ICRjaGVja291dCA9IFRXLiQkKCcjdHctY2hlY2tvdXQnKTtcbiAgICAgICAgICAgICAgICAkbmV4dFBhbmVsID0gJGNoZWNrb3V0LmNsb25lKCkuaW5zZXJ0QWZ0ZXIoJGNoZWNrb3V0KS5hdHRyKCdpZCcsIHBhbmVsSWQpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAocGFuZWxJZCAhPT0gVFcucHJldkFjdGl2ZVBhbmVsSWQpIHtcbiAgICAgICAgICAgICAgICBUVy51cGRhdGVDaGVja291dChwYW5lbElkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChwYW5lbElkID09PSAndHctcGF5bWVudCcpIHtcbiAgICAgICAgICAgIFRXLiRoZWFkZXIuaGlkZSgpO1xuICAgICAgICB9IGVsc2UgaWYgKHBhbmVsSWQgPT09ICd0dy1yZXF1ZXN0Jykge1xuICAgICAgICAgICAgVFcucHJldkFjdGl2ZVBhbmVsSWQgPSBudWxsO1xuICAgICAgICAgICAgVFcuJGhlYWRlci5oaWRlKCkvLy5hZGRDbGFzcygnY2VudGVyZWQnKTtcbiAgICAgICAgICAgIFRXLmxvYWRSZXF1ZXN0RXZlbnREYXRlcygpO1xuICAgICAgICB9IGVsc2UgaWYgKHBhbmVsSWQgPT09ICd0dy10aWNrZXRzJykge1xuICAgICAgICAgICAgaWYgKCFwYXJhbXMubWlzc0xvYWRDb25maWcpIHtcbiAgICAgICAgICAgICAgICBUVy5sb2FkQ29uZmlnKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBUVy5yZWRyYXdNaW5pY2FydCgpO1xuICAgICAgICAgICAgVFcuJGhlYWRlci5zaG93KCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBUVy4kaGVhZGVyLmhpZGUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgICRuZXh0UGFuZWwuYWRkQ2xhc3MoJ2FjdGl2ZScpLnNpYmxpbmdzKCcudHdfX3BhbmVsJykucmVtb3ZlQ2xhc3MoJ2FjdGl2ZScpO1xuXG4gICAgICAgIGlmIChwYXJhbXMuc2Nyb2xsVG9wKSB7XG4gICAgICAgICAgICAkbmV4dFBhbmVsLmdldCgwKS5zY3JvbGxUbygwLCBwYXJhbXMuc2Nyb2xsVG9wKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAkbmV4dFBhbmVsO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENsb3NlIGFjdGl2ZSBwYW5lbCBvZiB3aWRnZXQgYW5kIG9wZW4gcHJldmlvdXMgcGFuZWwgaWYgZXhpc3RcbiAgICAgKi9cbiAgICBjbG9zZVBhbmVsKGFjdGl2ZVBhbmVsSWQgPSBudWxsKSB7XG4gICAgICAgIGNvbnN0ICRhY3RpdmVQYW5lbCA9IGFjdGl2ZVBhbmVsSWQgPyBUVy4kJChgIyR7YWN0aXZlUGFuZWxJZH0uYWN0aXZlYCkgOiBUVy4kJCgnLnR3X19wYW5lbC5hY3RpdmUnKTtcbiAgICAgICAgaWYgKCEkYWN0aXZlUGFuZWwubGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBhY3RpdmVQYW5lbElkID0gYWN0aXZlUGFuZWxJZCB8fCAkYWN0aXZlUGFuZWwuYXR0cignaWQnKTtcblxuICAgICAgICBpZiAoYWN0aXZlUGFuZWxJZCA9PT0gJ3R3LXNjaGVtZScpIHtcbiAgICAgICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gbnVsbDtcblxuICAgICAgICAgICAgaWYgKFRXLmV2ZW50UGFyYW1zLnBhcnRJZCkge1xuICAgICAgICAgICAgICAgIFRXLmdldEV2ZW50KHtwYXJ0SWQ6ICcnfSk7XG5cbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoYWN0aXZlUGFuZWxJZCA9PT0gJ3BheW1lbnQtc3VjY2Vzcy1wYWdlJykge1xuICAgICAgICAgICAgLy8gVFcucHJldkFjdGl2ZVBhbmVsSWQgPSBUVy5zY2hlbWVJbml0ID8gJ3R3LXNjaGVtZScgOiBUVy5wcmV2QWN0aXZlUGFuZWxJZDtcbiAgICAgICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gbnVsbDtcbiAgICAgICAgfSBlbHNlIGlmIChhY3RpdmVQYW5lbElkID09PSAndHctcmVxdWVzdCcpIHtcbiAgICAgICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gbnVsbDtcbiAgICAgICAgfSBlbHNlIGlmIChhY3RpdmVQYW5lbElkID09PSAndHctcGF5bWVudCcpIHtcbiAgICAgICAgICAgICRhY3RpdmVQYW5lbC5maW5kKCdpZnJhbWUnKS5hdHRyKCdzcmMnLCAnJyk7XG4gICAgICAgICAgICAvLyBUVy5wcmV2QWN0aXZlUGFuZWxJZCA9IFRXLnNjaGVtZUluaXQgPyAndHctc2NoZW1lJyA6IFRXLnByZXZBY3RpdmVQYW5lbElkO1xuICAgICAgICAgICAgVFcucHJldkFjdGl2ZVBhbmVsSWQgPSBudWxsO1xuICAgICAgICB9IGVsc2UgaWYgKGFjdGl2ZVBhbmVsSWQgPT09ICd0dy1tdWx0aS1jaGVja291dCcpIHtcbiAgICAgICAgICAgIFRXLiRoZWFkZXIudG9nZ2xlKCEhVFcucHJldkFjdGl2ZVBhbmVsSWQpO1xuICAgICAgICAgICAgVFcuJHRvb2xzLnJlbW92ZUNsYXNzKCd0dy1oaWRkZW4nKTtcbiAgICAgICAgfSBlbHNlIGlmIChhY3RpdmVQYW5lbElkID09PSAndHctdGlja2V0cycpIHtcbiAgICAgICAgICAgIFRXLnByZXZBY3RpdmVQYW5lbElkID0gbnVsbDtcbiAgICAgICAgfSBlbHNlIGlmIChhY3RpdmVQYW5lbElkID09PSAndHctY2FsZW5kYXInICYmICFUVy5wcmV2QWN0aXZlUGFuZWxJZCkge1xuICAgICAgICAgICAgVFcuZmlsdGVyPy4kPy5hZGRDbGFzcygndHctaGlkZGVuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoVFcucHJldkFjdGl2ZVBhbmVsSWQpIHtcbiAgICAgICAgICAgIFRXLm9wZW5QYW5lbChUVy5wcmV2QWN0aXZlUGFuZWxJZCwge3Njcm9sbFRvcDogVFcucHJldkFjdGl2ZVBhbmVsU2Nyb2xsVG9wfSk7XG4gICAgICAgICAgICBUVy5wcmV2QWN0aXZlUGFuZWxJZCA9IFRXLnNjaGVtZUluaXQgPyAndHctc2NoZW1lJyA6IG51bGw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBmaW5pc2hXaWRnZXQgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBba2V5LCBhYm9ydENvbnRyb2xsZXJdIG9mIE9iamVjdC5lbnRyaWVzKFRXLmFib3J0Q29udHJvbGxlcnMpKSB7XG4gICAgICAgICAgICAgICAgICAgIGFib3J0Q29udHJvbGxlci5hYm9ydCgpO1xuICAgICAgICAgICAgICAgICAgICBUVy5hYm9ydENvbnRyb2xsZXJzW2tleV0gPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGFjdGl2ZVBhbmVsSWQgIT09ICd0dy1jYWxlbmRhcicgJiYgVFcub25seUNhbGVuZGFyKSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctY2FsZW5kYXInLCB7c2Nyb2xsVG9wOiBUVy5wcmV2QWN0aXZlUGFuZWxTY3JvbGxUb3B9KTtcbiAgICAgICAgICAgICAgICAgICAgVFcucHJldkFjdGl2ZVBhbmVsSWQgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgVFcuY2xvc2UoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGFjdGl2ZVBhbmVsSWQgPT09ICd0dy1tdWx0aS1jaGVja291dCcpIHtcbiAgICAgICAgICAgICAgICBmaW5pc2hXaWRnZXQoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgVFcuY29uZmlybSgn0JLRiyDQtNC10LnRgdGC0LLQuNGC0LXQu9GM0L3QviDRhdC+0YLQuNGC0LUg0LfQsNC60YDRi9GC0Ywg0L7QutC90L4g0L/QvtC60YPQv9C60Lgg0LHQuNC70LXRgtC+0LI/JywgZmluaXNoV2lkZ2V0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERpc3BsYXkgdG9vbHRpcCBmb3IgbWFwIHRpY2tldFxuICAgICAqXG4gICAgICogQHBhcmFtIGVsZW1cbiAgICAgKi9cbiAgICBzaG93VG9vbHRpcChlbGVtKSB7XG4gICAgICAgIFRXLmxhc3RIb3ZlclRpY2tldEVsZW0gPSBlbGVtO1xuICAgICAgICBjb25zdCBhdHRycyA9IE9iamVjdC5mcm9tRW50cmllcyhBcnJheS5mcm9tKGVsZW0uYXR0cmlidXRlcykubWFwKGl0ZW0gPT4gW2l0ZW0ubmFtZSwgaXRlbS52YWx1ZV0pKTtcblxuICAgICAgICBjb25zdCBzZWN0b3JOYW1lID0gYXR0cnNbJ3BsYWNlLW5hbWUnXTtcbiAgICAgICAgY29uc3Qgcm93ID0gYXR0cnNbJ3JvdyddO1xuICAgICAgICBjb25zdCBwbGFjZSA9IGF0dHJzWydwbGFjZSddO1xuICAgICAgICBjb25zdCBlVGlja2V0ID0gcGFyc2VJbnQoYXR0cnNbJ2RhdGEtZXRpY2tldCddKTtcbiAgICAgICAgY29uc3QgYnlTZWN0b3IgPSArYXR0cnNbJ2RhdGEtcW50LW1vcmUnXTtcbiAgICAgICAgY29uc3Qgc2VjdG9yUW50ID0gK2F0dHJzWydkYXRhLXNlY3Rvci1xbnQnXTtcbiAgICAgICAgY29uc3QgcHJpY2VNaW4gPSBOdW1iZXIoYXR0cnNbJ2RhdGEtcHJpY2UtbWluJ10pO1xuICAgICAgICBjb25zdCBwcmljZU1heCA9IE51bWJlcihhdHRyc1snZGF0YS1wcmljZS1tYXgnXSk7XG4gICAgICAgIGNvbnN0IG1vcmVUZXh0ID0gYXR0cnNbJ2RhdGEtcW50LW1vcmUtdGV4dCddO1xuICAgICAgICBjb25zdCBuZWVkUGFzc3BvcnQgPSArYXR0cnNbJ2RhdGEtcGFzc3BvcnQnXTtcbiAgICAgICAgY29uc3QgYnlTZWN0b3JQcmljZUZvclRleHQgPSBhdHRyc1snZGF0YS1wcmljZS1mb3InXTtcbiAgICAgICAgY29uc3QgcHJpY2VEaXJlY3QgPSBhdHRyc1sncHJpY2UtZGlyZWN0J107XG4gICAgICAgIGNvbnN0IGlzR2lmdCA9ICsoYXR0cnNbJ2RhdGEtZ2lmdCddIHx8ICcnKTtcblxuICAgICAgICBsZXQgcHJpY2UgPSBhdHRyc1sncHJpY2UnXTtcblxuICAgICAgICBUVy4kdG9vbHRpcC5maW5kKCdbZGF0YS10aWNrZXQtdHlwZV0nKS50ZXh0KGVUaWNrZXQgPyBcItGN0LvQtdC60YLRgNC+0L3QvdGL0LlcIiA6IFwi0LHRg9C80LDQttC90YvQuVwiKTtcbiAgICAgICAgVFcuJHRvb2x0aXAuZmluZCgnW2RhdGEtbW9yZS10ZXh0XScpLmh0bWwobW9yZVRleHQpO1xuICAgICAgICBUVy4kdG9vbHRpcC5maW5kKCdbZGF0YS1uZWVkLXBhc3Nwb3J0XScpLnRvZ2dsZSghIW5lZWRQYXNzcG9ydCk7XG4gICAgICAgIGNvbnN0ICRmcmVlQ291bnRFbCA9IFRXLiR0b29sdGlwLmZpbmQoJ1tkYXRhLWZyZWUtcGxhY2UtY291bnRdJyk7XG5cbiAgICAgICAgaWYgKGJ5U2VjdG9yKSB7XG4gICAgICAgICAgICBUVy4kdG9vbHRpcC5maW5kKCdbZGF0YS10aWNrZXQtcG9zaXRpb25dJykuaGlkZSgpO1xuICAgICAgICAgICAgVFcuJHRvb2x0aXAuZmluZCgnW2RhdGEtc2VjdG9yLW5hbWVdJykuaHRtbCgnPGI+JyArIHNlY3Rvck5hbWUgKyAnPC9iPicpO1xuXG4gICAgICAgICAgICBpZiAoaXNHaWZ0KXtcbiAgICAgICAgICAgICAgICAkZnJlZUNvdW50RWwudGV4dChUVy5jYXJ0SW5zdC50aWNrZXRzQ291bnQoZmFsc2UpIC0gVFcuY2FydEluc3QuZ2lmdHNDb3VudCgpKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgJGZyZWVDb3VudEVsLnRleHQoc2VjdG9yUW50KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgJGZyZWVDb3VudEVsLnBhcmVudCgpLnRvZ2dsZSghIXNlY3RvclFudCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZXQgdGlja2V0UG9zaXRpb25UZXh0ID0gJyc7XG4gICAgICAgICAgICBpZiAocm93ICYmIHJvdyAhPT0gJy0nKSB7XG4gICAgICAgICAgICAgICAgdGlja2V0UG9zaXRpb25UZXh0ICs9IGA8Yj4ke3Jvd308L2I+INGA0Y/QtCDigKIgYDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwbGFjZSAmJiBwbGFjZSAhPT0gJy0nKSB7XG4gICAgICAgICAgICAgICAgdGlja2V0UG9zaXRpb25UZXh0ICs9IGA8Yj4ke3BsYWNlfTwvYj4g0LzQtdGB0YLQvmA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBUVy4kdG9vbHRpcC5maW5kKCdbZGF0YS10aWNrZXQtcG9zaXRpb25dJykuaHRtbCh0aWNrZXRQb3NpdGlvblRleHQpLnNob3coKTtcbiAgICAgICAgICAgIFRXLiR0b29sdGlwLmZpbmQoJ1tkYXRhLXNlY3Rvci1uYW1lXScpLmh0bWwoc2VjdG9yTmFtZSk7XG4gICAgICAgICAgICAkZnJlZUNvdW50RWwucGFyZW50KCkuaGlkZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGJ5U2VjdG9yUHJpY2VGb3JUZXh0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICRmcmVlQ291bnRFbC5wYXJlbnQoKS5oaWRlKClcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBwcmljZVRleHQgPSAnJztcblxuICAgICAgICBpZiAocHJpY2VNaW4gJiYgcHJpY2VNaW4gPT09IHByaWNlTWF4KSB7XG4gICAgICAgICAgICBwcmljZSA9IHByaWNlTWluO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHByaWNlRGlyZWN0KSB7XG4gICAgICAgICAgICBwcmljZVRleHQgPSBgJHtwcmljZURpcmVjdH0g4oK9YDtcbiAgICAgICAgfSBlbHNlIGlmIChwcmljZU1pbiA+IDAgJiYgcHJpY2VNYXggPiAwICYmIHByaWNlTWluIDwgcHJpY2VNYXgpIHtcbiAgICAgICAgICAgIHByaWNlVGV4dCA9IGJ5U2VjdG9yUHJpY2VGb3JUZXh0ICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICA/IGAke3ByaWNlfSDigr1gXG4gICAgICAgICAgICAgICAgOiBgJHtwcmljZU1pbn0gLSAke3ByaWNlTWF4fSDigr1gO1xuICAgICAgICB9IGVsc2UgaWYgKCFpc05hTihwcmljZSkpIHtcbiAgICAgICAgICAgIHByaWNlVGV4dCA9IGAke3ByaWNlfSDigr1gO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcHJpY2VUZXh0ID0gcHJpY2U7XG4gICAgICAgIH1cblxuICAgICAgICBUVy4kdG9vbHRpcC5maW5kKCdbZGF0YS1wcmljZV0nKS50ZXh0KHByaWNlVGV4dCk7XG4gICAgICAgIFRXLiR0b29sdGlwLnNob3coKTtcblxuICAgICAgICBUVy51cGRhdGVUb29sdGlwUG9zaXRpb24oKTtcbiAgICB9XG5cbiAgICBsb2FkZXIoZGlzcGxheSA9IHRydWUsIGFsZXJ0VGltZW91dERlbGF5KSB7XG4gICAgICAgIFRXLiRsb2FkZXIudG9nZ2xlQ2xhc3MoJ3R3LWhpZGRlbicsICFkaXNwbGF5KTtcbiAgICAgICAgY2xlYXJUaW1lb3V0KFRXLmxvYWRlclRpbWVvdXRJZCk7XG4gICAgfVxuXG4gICAgc2VuZFRnTXNnKCl7XG4gICAgICAgIGNvbnN0IHNjaGVtZUxvYWRlZCA9IHdpbmRvdy5zY2hlbWVMb2FkRmluaXNoICYmIHdpbmRvdy5zY2hlbWVMb2FkRmluaXNoID4gd2luZG93LnNjaGVtZUxvYWRTdGFydDtcbiAgICAgICAgd2luZG93LnNjaGVtZUxvYWRGaW5pc2ggPSB3aW5kb3cuc2NoZW1lTG9hZEZpbmlzaCB8fCArIG5ldyBEYXRlKCk7XG5cbiAgICAgICAgY29uc3QgbG9hZFRpbWVTZWMgPSBNYXRoLnJvdW5kKCh3aW5kb3cuc2NoZW1lTG9hZEZpbmlzaCAtIHdpbmRvdy5zY2hlbWVMb2FkU3RhcnQpIC8gMTAwMCk7XG5cbiAgICAgICAgaWYgKHNjaGVtZUxvYWRlZCAmJiBsb2FkVGltZVNlYyA8IDEwKXtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBtc2cgPSAnJztcblxuXG4gICAgICAgIGlmIChsb2FkVGltZVNlYyl7XG4gICAgICAgICAgICBtc2cgKz0gYFxcbtCS0YDQtdC80Y8g0LfQsNCz0YDRg9C30LrQuCAtICR7bG9hZFRpbWVTZWN9INGB0LXQumA7XG4gICAgICAgIH1cblxuICAgICAgICBtc2cgKz0gJ1xcbtCe0YjQuNCx0LrQuDogJyArIChUVy5lcnJvcnMuam9pbignLCAnKSB8fCAn0L3QtdGCJyk7XG5cbiAgICAgICAgZm9yKGNvbnN0IFtwYXJhbSwgdmFsXSBvZiBPYmplY3QuZW50cmllcyhUVy5ldmVudFBhcmFtcykpe1xuICAgICAgICAgICAgaWYgKHZhbC5sZW5ndGgpe1xuICAgICAgICAgICAgICAgIG1zZyArPSBgXFxuJHtwYXJhbX09JHt2YWx9YDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBtc2cgKz0gJ1xcbicgKyB3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudDtcbiAgICAgICAgbXNnICs9ICdcXG4nICsgbG9jYXRpb24uaHJlZjtcblxuICAgICAgICBUVy5mZXRjaCh7XG4gICAgICAgICAgICB1cmw6ICdodHRwczovL2JpbGV0c2VydmlzLmFnZW5jeS90ZWxlL3RlbGVncmFtX3RlY2gucGhwP2NvZGU9YmlsZXRzZXJ2aXMyMDIyJnNlbmQ9MSZ0ZXh0PTc6ICcgKyBlbmNvZGVVUklDb21wb25lbnQobXNnKSxcbiAgICAgICAgfSkuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgICB9KVxuICAgIH1cblxuICAgIHVwZGF0ZVRvb2x0aXBQb3NpdGlvbigpIHtcbiAgICAgICAgaWYgKCFUVy5sYXN0SG92ZXJUaWNrZXRFbGVtKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZWxlbUNsaWVudFJlY3QgPSBUVy5sYXN0SG92ZXJUaWNrZXRFbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCB0b29sdGlwV2lkdGggPSBwYXJzZUludChUVy4kdG9vbHRpcC5vdXRlcldpZHRoKCkpO1xuICAgICAgICBjb25zdCB0b29sdGlwSGVpZ2h0ID0gcGFyc2VJbnQoVFcuJHRvb2x0aXAub3V0ZXJIZWlnaHQoKSk7XG5cbiAgICAgICAgbGV0IHBvc1gsIHBvc1k7XG5cbiAgICAgICAgLy8gcG9zWCA9IGVsZW1DbGllbnRSZWN0LmxlZnQgLSB0b29sdGlwV2lkdGggLyAyO1xuICAgICAgICBwb3NYID0gZWxlbUNsaWVudFJlY3QubGVmdCAtIHRvb2x0aXBXaWR0aCAvIDIgKyBlbGVtQ2xpZW50UmVjdC53aWR0aCAvIDI7XG4gICAgICAgIHBvc1kgPSBlbGVtQ2xpZW50UmVjdC50b3AgLSB0b29sdGlwSGVpZ2h0IC0gMTU7XG5cbiAgICAgICAgaWYgKHBvc1ggPCAwKSB7XG4gICAgICAgICAgICBwb3NYID0gMDtcbiAgICAgICAgfSBlbHNlIGlmIChwb3NYICsgdG9vbHRpcFdpZHRoID4gd2luZG93LmlubmVyV2lkdGgpIHtcbiAgICAgICAgICAgIHBvc1ggPSB3aW5kb3cuaW5uZXJXaWR0aCAtIHRvb2x0aXBXaWR0aDtcbiAgICAgICAgfVxuXG4gICAgICAgIFRXLiR0b29sdGlwLmNzcyh7XG4gICAgICAgICAgICAndHJhbnNmb3JtJzogYHRyYW5zbGF0ZSgke3Bvc1h9cHgsICR7cG9zWX1weClgLFxuICAgICAgICB9KTtcblxuICAgICAgICBjb25zdCAkY29ybmVyID0gVFcuJHRvb2x0aXAuZmluZCgnLnR3X19zY2hlbWUtdG9vbHRpcC1jb3JuZXInKTtcblxuICAgICAgICAkY29ybmVyLmNzcyh7XG4gICAgICAgICAgICAndHJhbnNmb3JtJzogYHRyYW5zbGF0ZSgke2VsZW1DbGllbnRSZWN0LmxlZnQgLSBUVy4kdG9vbHRpcC5vZmZzZXQoKS5sZWZ0ICsgZWxlbUNsaWVudFJlY3Qud2lkdGggLyAyIC0gJGNvcm5lci53aWR0aCgpIC8gMn1weCkgcm90YXRlKDQ1ZGVnKWBcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZGluZyBhbmQgZHJhd2luZyBldmVudCBtYXBcbiAgICAgKlxuICAgICAqIEBwYXJhbSBldmVudFBhcmFtc1xuICAgICAqIEBwYXJhbSBzaG93TWFwXG4gICAgICovXG4gICAgZ2V0RXZlbnQoZXZlbnRQYXJhbXMsIHNob3dNYXAgPSB0cnVlKSB7XG4gICAgICAgIFRXLmVycm9ycyA9IFtdO1xuICAgICAgICBUVy5ibG9ja0NoZWNrb3V0Rm9ybUZpbGxNZXRyaWthID0gZmFsc2U7XG5cbiAgICAgICAgT2JqZWN0LmFzc2lnbihUVy5ldmVudFBhcmFtcywgZXZlbnRQYXJhbXMpO1xuICAgICAgICBoaXN0b3J5LnJlcGxhY2VTdGF0ZShudWxsLCBudWxsLCB3aW5kb3cubG9jYXRpb24uaHJlZi5zcGxpdCgnIycpWzBdICsgJyMnICsgYjY0RW5jb2RlKFRXLmV2ZW50UGFyYW1zKSlcblxuICAgICAgICBUVy5hdmFpbGFibGVUaWNrZXRzQ2lyY2xlRWxlbXMgPSBbXTtcbiAgICAgICAgVFcubGFzdENsaWNrZWRUaWNrZXRFbGVtID0gbnVsbDtcbiAgICAgICAgVFcubGFzdEhvdmVyVGlja2V0RWxlbSA9IG51bGw7XG5cbiAgICAgICAgVFcudXBkYXRlSGVhZGVySW5mbyhUVy5ldmVudFBhcmFtcyk7XG5cbiAgICAgICAgVFcuJG1pbmljYXJ0LnJlbW92ZUNsYXNzKCdhY3RpdmUnKTtcbiAgICAgICAgaWYgKFRXLnNjaGVtZUxpYiA9PT0gVFcuU0NIRU1FX0xJQl9QQU5aT09NKSB7XG4gICAgICAgICAgICBUVy5zY2hlbWU/LmRpc2FibGVQYW4oKTtcbiAgICAgICAgfVxuICAgICAgICBUVy5sb2FkZXIodHJ1ZSk7XG4gICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcbiAgICAgICAgVFcuJG1vcmVQb3B1cC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG5cbiAgICAgICAgVFcuJCQoJy50d19fc2NoZW1lLXRvLXRhYmxlLWJ0bicpLmFkZENsYXNzKCd0dy1oaWRkZW4nKTtcbiAgICAgICAgVFcuJCQoJy50d19fdGlja2V0cycpLnJlbW92ZUF0dHIoJ2RhdGEtc2VjdG9yLW9wZW4nKS5maW5kKCcudHdfX3RpY2tldHMtc2VjdG9ycywgLnR3X190aWNrZXRzLXJvd3MnKS5lbXB0eSgpO1xuXG5cbiAgICAgICAgZm9yIChjb25zdCBba2V5LCBhYm9ydENvbnRyb2xsZXJdIG9mIE9iamVjdC5lbnRyaWVzKFRXLmFib3J0Q29udHJvbGxlcnMpKSB7XG4gICAgICAgICAgICBhYm9ydENvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICAgICAgICAgIFRXLmFib3J0Q29udHJvbGxlcnNba2V5XSA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBUVy5hYm9ydENvbnRyb2xsZXJzLnN2Zy5hYm9ydCgpO1xuICAgICAgICAvLyBUVy5hYm9ydENvbnRyb2xsZXJzLnN2ZyA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcblxuICAgICAgICBUVy5mZXRjaCh7XG4gICAgICAgICAgICBzaWduYWw6IFRXLmFib3J0Q29udHJvbGxlcnMuc3ZnLnNpZ25hbCxcbiAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICBhY3Rpb246ICdnZXRfc3ZnJyxcbiAgICAgICAgICAgICAgICB3aWRnZXQ6IDEsXG4gICAgICAgICAgICAgICAgZGF0ZTogVFcuZXZlbnRQYXJhbXMuZGF0ZSxcbiAgICAgICAgICAgICAgICBldmVudF9pZDogVFcuZXZlbnRQYXJhbXMuaWQsXG4gICAgICAgICAgICAgICAgcGxhY2VfaWQ6IFRXLmV2ZW50UGFyYW1zLnBsYWNlSWQsXG4gICAgICAgICAgICAgICAgaGFsbF9pZDogVFcuZXZlbnRQYXJhbXMuaGFsbElkLFxuICAgICAgICAgICAgICAgIHBhcnRfaWQ6IFRXLmV2ZW50UGFyYW1zLnBhcnRJZCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKGFzeW5jIHJlc3BvbnNlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocmVzcG9uc2UuaGVhZGVycy5nZXQoJ0NvbnRlbnQtVHlwZScpLmluY2x1ZGVzKCdpbWFnZS9zdmcreG1sJykpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZVRleHQgPSBhd2FpdCByZXNwb25zZS50ZXh0KCk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFyZXNwb25zZVRleHQudHJpbSgpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBFcnJvcignU1ZHIHJlc3BvbnNlINC/0YPRgdGCLi4uJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gSlNPTi5wYXJzZShyZXNwb25zZVRleHQpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoanNvbj8uZXJyb3IgfHwgIWpzb24/LnN2Zykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgRXJyb3IoanNvbj8uZXJyb3IgfHwgJ1NWRyDQvtGC0YHRg9GC0YHRgtCy0YPQtdGCLi4uJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oc3ZnRGF0YSA9PiB7XG4gICAgICAgICAgICAgICAgVFcudXBkYXRlSGVhZGVyRGF0ZXMoKTtcblxuICAgICAgICAgICAgICAgIGlmIChUVy5zY2hlbWVJbml0ICYmIFRXLnNjaGVtZVN2Z092ZXJsYXkpIHtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lU3ZnT3ZlcmxheS5yZW1vdmUoKS5yZW1vdmVFdmVudExpc3RlbmVyKCk7XG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZVN2Z092ZXJsYXkgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWVTdmdBdHRycyA9IHt9O1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuaW52YWxpZGF0ZVNpemUoZmFsc2UpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChUVy5zY2hlbWVJbWFnZUluaXQpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVFcuc2NoZW1lPy5kZXN0cm95KCk7XG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZUluaXQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lSW1hZ2VJbml0ID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgVFcuc2NoZW1lTGliID0gVFcuZGVmYXVsdFNjaGVtZUxpYjtcblxuICAgICAgICAgICAgICAgIFRXLiR0d1N2Z01hcC5lbXB0eSgpO1xuXG4gICAgICAgICAgICAgICAgY29uc3Qgc3ZnTWFwUmVjdCA9IFRXLiR0d1N2Z01hcC5nZXQoMCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cbiAgICAgICAgICAgICAgICBjb25zdCB0ZW1wRGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgICAgICAgICAgdGVtcERpdi5pbm5lckhUTUwgPSBzdmdEYXRhO1xuICAgICAgICAgICAgICAgIGNvbnN0IHN2Z0VsZW0gPSB0ZW1wRGl2LnF1ZXJ5U2VsZWN0b3IoJ3N2ZycpO1xuXG4gICAgICAgICAgICAgICAgVFcuc2NoZW1lU3ZnQXR0cnMgPSBPYmplY3QuZnJvbUVudHJpZXMoQXJyYXkuZnJvbShzdmdFbGVtLmF0dHJpYnV0ZXMpLm1hcChpdGVtID0+IFtpdGVtLm5hbWUsIGl0ZW0udmFsdWVdKSk7XG5cbiAgICAgICAgICAgICAgICAvLyBbZWxlbT1cInRydWVcIl0gLy/QtdGB0LvQuCDQvdC1IHRydWUgLCDRgtC+INGN0YLQviDQvdC1INC80LXRgdGC0L4sINCwINGB0LXQutGC0L7RgCDQuNC70Lgg0Y3Qu9C10LzQtdC90YIg0LTQtdC60L7RgNCwXG4gICAgICAgICAgICAgICAgVFcuYXZhaWxhYmxlVGlja2V0c0NpcmNsZUVsZW1zID0gVFcuc2NoZW1lU3ZnQXR0cnMuc2hlbWUgJiYgVFcuc2NoZW1lU3ZnQXR0cnMuc2hlbWUgPT09ICd0cnVlJ1xuICAgICAgICAgICAgICAgICAgICA/IHN2Z0VsZW0ucXVlcnlTZWxlY3RvckFsbCgnLnRpY2tldHNfYXZhaWwnKVxuICAgICAgICAgICAgICAgICAgICA6IHN2Z0VsZW0ucXVlcnlTZWxlY3RvckFsbCgnLnRpY2tldHNfYXZhaWxbZWxlbT1cInRydWVcIl0nKVxuICAgICAgICAgICAgICAgIDtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHN2Z1RpY2tldEVsZW1PYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKChtdXRhdGlvbkxpc3QsIG9ic2VydmVyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3QgbXV0YXRpb24gb2YgbXV0YXRpb25MaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAobXV0YXRpb24udHlwZSA9PT0gJ2F0dHJpYnV0ZXMnICYmIG11dGF0aW9uLmF0dHJpYnV0ZU5hbWUgPT09ICdjbGFzcycgJiYgbXV0YXRpb24udGFyZ2V0LnIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgc3Ryb2tlV2lkdGggPSBtdXRhdGlvbi50YXJnZXQuci5iYXNlVmFsLnZhbHVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZVdpZHRoID0gTWF0aC5jZWlsKHN0cm9rZVdpZHRoIC8gMik7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobXV0YXRpb24udGFyZ2V0LmNsYXNzTGlzdC5jb250YWlucygnYWN0aXZlJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRpb24udGFyZ2V0LnNldEF0dHJpYnV0ZSgnc3Ryb2tlLXdpZHRoJywgc3Ryb2tlV2lkdGgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0aW9uLnRhcmdldC5yZW1vdmVBdHRyaWJ1dGUoJ3N0cm9rZS13aWR0aCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChtdXRhdGlvbi50YXJnZXQuY2xhc3NMaXN0LmNvbnRhaW5zKCctLWhvdmVyJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRpb24udGFyZ2V0LnNldEF0dHJpYnV0ZSgnc3Ryb2tlLXdpZHRoJywgc3Ryb2tlV2lkdGgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoIW11dGF0aW9uLnRhcmdldC5jbGFzc0xpc3QuY29udGFpbnMoJ2FjdGl2ZScpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0aW9uLnRhcmdldC5yZW1vdmVBdHRyaWJ1dGUoJ3N0cm9rZS13aWR0aCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtIG9mIFRXLmF2YWlsYWJsZVRpY2tldHNDaXJjbGVFbGVtcykge1xuICAgICAgICAgICAgICAgICAgICBzdmdUaWNrZXRFbGVtT2JzZXJ2ZXIub2JzZXJ2ZShlbGVtLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhdHRyaWJ1dGVzOiB0cnVlXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChUVy5zY2hlbWVTdmdBdHRycy5ub190aWNrZXRzID09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKFRXLnNjaGVtZVN2Z0F0dHJzLm5vX3RpY2tldHNfdHlwZSA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuZXJyb3JzLnB1c2goJ9Cd0LXRgiDQsdC40LvQtdGC0L7QsiDQstC+0L7QsdGJ0LUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctcmVxdWVzdCcpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKFRXLnNjaGVtZVN2Z0F0dHJzLm5vX3RpY2tldHNfdHlwZSA9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5lcnJvcnMucHVzaCgn0JHQuNC70LXRgtGLINC/0YDQuNGB0YPRgtGB0YLQstGD0Y7Rgiwg0L3QviDQvdC10YIg0YHQvtCy0L/QsNC00LXQvdC40Lkg0L/QviDRgdGF0LXQvNC1Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5vcGVuUGFuZWwoJ3R3LXRpY2tldHMnKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBUVy4kdHdTdmdNYXAuZ2V0KDApLnJlcGxhY2VDaGlsZHJlbihzdmdFbGVtKTtcblxuICAgICAgICAgICAgICAgIC8vINC00LvRjyDQutC+0YDRgNC10LrRgtC90L7QuSDRgNCw0LHQvtGC0YsgaG92ZXIg0L3Rg9C20L3QviDQsdGD0LTQtdGCINGD0L/QsNC60L7QstCw0YLRjCDRjdGC0Lgg0YLQtdCz0Lgg0LIgPGc+INC4INGBINC90LjQvCDRgNCw0LHQvtGC0LDRgtGMLCDRgtC+0LPQtNCwINC90YPQttC90L4g0LHRg9C00LXRgiDQvdCw0LLQtdGA0L3QvtC1INC90LAgbW91c2VPdmVyINGB0L7Qt9C00LDQstCw0YLRjCDQuNGFINCy0YHQtS3RgtCw0LrQuCwg0YfRgtC+0LEg0L3QtSDQv9C10YDQtdC90LDQs9GA0YPQttCw0YLRjCBET01cblxuICAgICAgICAgICAgICAgIC8vIFRXLmF2YWlsYWJsZVRpY2tldHNDaXJjbGVFbGVtcy5mb3JFYWNoKGVsZW0gPT4ge1xuICAgICAgICAgICAgICAgIC8vICAgICBjb25zdCBwbGFjZU51bSA9IGVsZW0uZ2V0QXR0cmlidXRlKCdwbGFjZScpO1xuICAgICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgICAgLy8gICAgIGlmIChwbGFjZU51bSl7XG4gICAgICAgICAgICAgICAgLy8gICAgICAgICBjb25zdCB0ZXh0VGFnID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycsICd0ZXh0Jyk7XG4gICAgICAgICAgICAgICAgLy8gICAgICAgICB0ZXh0VGFnLnNldEF0dHJpYnV0ZU5TKG51bGwsICd4JywgZWxlbS5nZXRBdHRyaWJ1dGUoJ2N4JykpO1xuICAgICAgICAgICAgICAgIC8vICAgICAgICAgdGV4dFRhZy5zZXRBdHRyaWJ1dGVOUyhudWxsLCAneScsIGVsZW0uZ2V0QXR0cmlidXRlKCdjeScpKTtcbiAgICAgICAgICAgICAgICAvLyAgICAgICAgIHRleHRUYWcuc2V0QXR0cmlidXRlTlMobnVsbCwgJ2ZvbnQtc2l6ZScsIGVsZW0uZ2V0QXR0cmlidXRlKCdyJykgKyAncHgnKTtcbiAgICAgICAgICAgICAgICAvLyAgICAgICAgIHRleHRUYWcuc2V0QXR0cmlidXRlTlMobnVsbCwgJ2ZpbGwnLCAnIzAwMCcpO1xuICAgICAgICAgICAgICAgIC8vICAgICAgICAgdGV4dFRhZy5zZXRBdHRyaWJ1dGVOUyhudWxsLCAnZHknLCAnMC4zZW0nKTtcbiAgICAgICAgICAgICAgICAvLyAgICAgICAgIHRleHRUYWcuc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RleHQtYW5jaG9yJywgJ21pZGRsZScpO1xuICAgICAgICAgICAgICAgIC8vICAgICAgICAgdGV4dFRhZy5jbGFzc0xpc3QuYWRkKCdwbGFjZS1jYXB0aW9uJyk7XG4gICAgICAgICAgICAgICAgLy8gICAgICAgICB0ZXh0VGFnLmlubmVySFRNTCA9IHBsYWNlTnVtO1xuICAgICAgICAgICAgICAgIC8vICAgICAgICAgZWxlbS5hZnRlcih0ZXh0VGFnKTtcbiAgICAgICAgICAgICAgICAvLyAgICAgfVxuICAgICAgICAgICAgICAgIC8vIH0pO1xuXG4gICAgICAgICAgICAgICAgY29uc3Qgdmlld0JveCA9IFRXLnNjaGVtZVN2Z0F0dHJzLnZpZXdCb3g7XG4gICAgICAgICAgICAgICAgbGV0IGxhdExuZ0Nvcm5lcjI7XG5cbiAgICAgICAgICAgICAgICBpZiAodmlld0JveCkge1xuICAgICAgICAgICAgICAgICAgICBsYXRMbmdDb3JuZXIyID0gW1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFyc2VJbnQodmlld0JveC5zcGxpdCgnICcpWzJdKSAvIDIsXG4gICAgICAgICAgICAgICAgICAgICAgICBwYXJzZUludCh2aWV3Qm94LnNwbGl0KCcgJylbM10pIC8gMlxuICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGxhdExuZ0Nvcm5lcjIgPSBbXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXBSZWN0LndpZHRoIC8gMixcbiAgICAgICAgICAgICAgICAgICAgICAgIG1hcFJlY3QuaGVpZ2h0IC8gMlxuICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGxldCBib3VuZHMgPSBbXG4gICAgICAgICAgICAgICAgICAgIFswLCAwXSxcbiAgICAgICAgICAgICAgICAgICAgbGF0TG5nQ29ybmVyMixcbiAgICAgICAgICAgICAgICBdXG5cbiAgICAgICAgICAgICAgICBUVy5faW5pdEZpbHRlcignLnR3X19zY2hlbWUtZGl2aXNpb25zJywgVFcuYXZhaWxhYmxlVGlja2V0c0NpcmNsZUVsZW1zLCB7XG4gICAgICAgICAgICAgICAgICAgIHZpc2libGU6ICFUVy5zY2hlbWVTdmdBdHRycy5ub3ByaWNlZmlsZXIsXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBUVy4kJCgnLnR3X19zY2hlbWUtYmFjaycpLnRvZ2dsZUNsYXNzKCd0dy1oaWRkZW4nLCAhVFcuZXZlbnRQYXJhbXMucGFydElkKTtcblxuICAgICAgICAgICAgICAgIFRXLiRtaW5pY2FydC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgICAgICAgICAgVFcuY2xvc2VQYW5lbCgndHctY2hlY2tvdXQnKTtcbiAgICAgICAgICAgICAgICBUVy4kdG9vbHRpcC5oaWRlKCk7XG4gICAgICAgICAgICAgICAgVFcuY2FydEluc3QgPSBuZXcgVFdDYXJ0KFRXLmV2ZW50UGFyYW1zLCB7ZG9jdW1lbnRGaWVsZHM6IFRXLnNjaGVtZVN2Z0F0dHJzWydkYXRhLWZpZWxkcyddfSk7XG4gICAgICAgICAgICAgICAgVFcuY2FydEluc3QuYWN0dWFsaXplKCk7XG4gICAgICAgICAgICAgICAgVFcucmVkcmF3TWluaWNhcnQoKTtcblxuICAgICAgICAgICAgICAgIGlmICghVFcuc2NoZW1lSW5pdCAmJiBUVy5zY2hlbWVMaWIgPT09IFRXLlNDSEVNRV9MSUJfTEVBRkxFVCkge1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUgPSBMLm1hcCgndHctc3ZnLW1hcCcsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHpvb21EZWx0YTogMSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1pblpvb206IDEsXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXhab29tOiA2LFxuICAgICAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRpb25Db250cm9sOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHpvb21Db250cm9sOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFdoZWVsWm9vbTogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvdWJsZUNsaWNrWm9vbTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgICBjbG9zZVBvcHVwT25DbGljazogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgICB0YXBIb2xkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGJveFpvb206IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAga2V5Ym9hcmQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gem9vbUFuaW1hdGlvbjogISBMLkJyb3dzZXIubW9iaWxlLFxuICAgICAgICAgICAgICAgICAgICAgICAgem9vbUFuaW1hdGlvbjogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgICBmYWRlQW5pbWF0aW9uOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGluZXJ0aWE6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgYm91bmNlQXRab29tTGltaXRzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGZ1bGxzY3JlZW5Db250cm9sOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gZnVsbHNjcmVlbkNvbnRyb2xPcHRpb25zOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAgICAgZnVsbHNjcmVlbkVsZW1lbnQ6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyN0dy1zY2hlbWUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIH0sXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5vbignbW92ZXN0YXJ0JywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLm9uKCdtb3ZlZW5kJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChUVy4kdG9vbHRpcC5pcygnOnZpc2libGUnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVy5zaG93VG9vbHRpcChUVy5sYXN0SG92ZXJUaWNrZXRFbGVtKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lSW5pdCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChUVy5zY2hlbWVMaWIgPT09IFRXLlNDSEVNRV9MSUJfUEFOWk9PTSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUVy5zY2hlbWU/LmRlc3Ryb3koKTtcblxuICAgICAgICAgICAgICAgICAgICBjb25zdCBoYW1tZXJUb3VjaEV2ZW50c0hhbmRsZXIgPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYWx0RXZlbnRMaXN0ZW5lcnM6IFsndG91Y2hzdGFydCcsICd0b3VjaGVuZCcsICd0b3VjaG1vdmUnLCAndG91Y2hsZWF2ZScsICd0b3VjaGNhbmNlbCddLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgaW5zdGFuY2UgPSBvcHRpb25zLmluc3RhbmNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgaW5pdGlhbFNjYWxlID0gMVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHBhbm5lZFggPSAwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgcGFubmVkWSA9IDBcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEluaXQgSGFtbWVyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTGlzdGVuIG9ubHkgZm9yIHBvaW50ZXIgYW5kIHRvdWNoIGV2ZW50c1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaGFtbWVyID0gSGFtbWVyKG9wdGlvbnMuc3ZnRWxlbWVudCwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dENsYXNzOiBIYW1tZXIuU1VQUE9SVF9QT0lOVEVSX0VWRU5UUyA/IEhhbW1lci5Qb2ludGVyRXZlbnRJbnB1dCA6IEhhbW1lci5Ub3VjaElucHV0XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEVuYWJsZSBwaW5jaFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaGFtbWVyLmdldCgncGluY2gnKS5zZXQoe2VuYWJsZTogdHJ1ZX0pXG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAvLyBIYW5kbGUgZG91YmxlIHRhcFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoaXMuaGFtbWVyLm9uKCdkb3VibGV0YXAnLCBmdW5jdGlvbihldil7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gICAgIGluc3RhbmNlLnpvb21JbigpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gfSlcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEhhbmRsZSBwYW5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmhhbW1lci5vbigncGFuc3RhcnQgcGFubW92ZScsIGZ1bmN0aW9uIChldikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL3RvZG8g0LLQvtC30LzQvtC20L3QviDQtNC70Y8gcGFuc3RhcnQg0L3Rg9C20L3QviDQtNCw0LLQsNGC0YwgcmV0dXJuIGZhbHNlLCDRh9GC0L7QsdGLINC90LUg0YDQsNC30YDQtdGI0LDQu9C+0YHRjCBwYW5tb3ZlINC60L7Qs9C00LAg0LIg0YLQsNGH0LUg0L3QsNGF0L7QtNC40YLRgdGPINC00YAg0Y3Qu9C10LzQtdC90YJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKFRXLmlzVG91Y2hEZXZpY2UgJiYgd2luZG93Lm1vdmVUb3VjaGVzQ250IDw9IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9uIHBhbiBzdGFydCByZXNldCBwYW5uZWQgdmFyaWFibGVzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZXYudHlwZSA9PT0gJ3BhbnN0YXJ0Jykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbm5lZFggPSAwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFubmVkWSA9IDBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVy4kdG9vbHRpcC5oaWRlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBhbiBvbmx5IHRoZSBkaWZmZXJlbmNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnN0YW5jZS5wYW5CeSh7eDogZXYuZGVsdGFYIC0gcGFubmVkWCwgeTogZXYuZGVsdGFZIC0gcGFubmVkWX0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5uZWRYID0gZXYuZGVsdGFYXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5uZWRZID0gZXYuZGVsdGFZXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSGFuZGxlIHBpbmNoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5oYW1tZXIub24oJ3BpbmNoc3RhcnQgcGluY2htb3ZlJywgZnVuY3Rpb24gKGV2KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9uIHBpbmNoIHN0YXJ0IHJlbWVtYmVyIGluaXRpYWwgem9vbVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZXYudHlwZSA9PT0gJ3BpbmNoc3RhcnQnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0aWFsU2NhbGUgPSBpbnN0YW5jZS5nZXRab29tKClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlLnpvb21BdFBvaW50KGluaXRpYWxTY2FsZSAqIGV2LnNjYWxlLCB7eDogZXYuY2VudGVyLngsIHk6IGV2LmNlbnRlci55fSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlLnpvb21BdFBvaW50KGluaXRpYWxTY2FsZSAqIGV2LnNjYWxlLCB7eDogZXYuY2VudGVyLngsIHk6IGV2LmNlbnRlci55fSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBQcmV2ZW50IG1vdmluZyB0aGUgcGFnZSBvbiBzb21lIGRldmljZXMgd2hlbiBwYW5uaW5nIG92ZXIgU1ZHXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5zdmdFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNobW92ZScsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7IHBhc3NpdmU6IGZhbHNlIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3Ryb3k6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmhhbW1lci5kZXN0cm95KClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIHN2Z0VsZW0uc3R5bGUud2lkdGggPSBzdmdNYXBSZWN0LndpZHRoICsgJ3B4JztcbiAgICAgICAgICAgICAgICAgICAgLy8gc3ZnRWxlbS5zdHlsZS5oZWlnaHQgPSBzdmdNYXBSZWN0LmhlaWdodCArICdweCc7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gVFcuJCQoJyN0dy1zdmctbWFwJykuZ2V0KDApLmNvbnRlbnREb2N1bWVudC53cml0ZShzdmdFbGVtLm91dGVySFRNTCk7XG5cbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lID0gc3ZnUGFuWm9vbSgnI3R3LXN2Zy1tYXAgc3ZnJywge1xuICAgICAgICAgICAgICAgICAgICAgICAgem9vbUVuYWJsZWQ6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sSWNvbnNFbmFibGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcjogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhbkVuYWJsZWQ6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYmxDbGlja1pvb21FbmFibGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1vdXNlV2hlZWxab29tRW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXZlbnRNb3VzZUV2ZW50c0RlZmF1bHQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgem9vbVNjYWxlU2Vuc2l0aXZpdHk6IChUVy5zY2hlbWVab29tU3RlcCA9IC4zKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1pblpvb206IChUVy5zY2hlbWVJbml0TWluWm9vbSA9IDAuNCksXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXhab29tOiAxMDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250YWluOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpdDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlZnJlc2hSYXRlOiAnYXV0bycsXG4gICAgICAgICAgICAgICAgICAgICAgICBiZWZvcmVab29tOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVFcuJHRvb2x0aXAuZmlsdGVyKCc6dmlzaWJsZScpLmhpZGUoKS5hZGRDbGFzcygnc2hvdy1hZnRlci16b29tJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgb25ab29tOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcuY2hlY2tBdmFpbGFibGVTY2hlbWVab29tKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcudXBkYXRlVG9vbHRpcFBvc2l0aW9uKCk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBpZiAoICEgVFcuaXNUb3VjaERldmljZSl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KFRXLnNtb290aFpvb21UaW1lb3V0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVy5zbW9vdGhab29tVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVz8uc2NoZW1lSW5uZXJWaWV3cG9ydD8uY2xhc3NMaXN0LnJlbW92ZSgnc21vb3RoJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgNzAwKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgYmVmb3JlUGFuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgb25QYW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBvblVwZGF0ZWRDVE06IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBjdXN0b21FdmVudHNIYW5kbGVyOiBoYW1tZXJUb3VjaEV2ZW50c0hhbmRsZXIsXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZS5yZXNldCgpO1xuXG4gICAgICAgICAgICAgICAgICAgIFRXLnNjaGVtZUlubmVyVmlld3BvcnQgPSBzdmdFbGVtLnF1ZXJ5U2VsZWN0b3IoJzpzY29wZSA+IC5zdmctcGFuLXpvb21fdmlld3BvcnQnKTtcblxuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWVJbml0ID0gdHJ1ZTtcblxuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuaW52YWxpZGF0ZVNpemUoKTtcbiAgICAgICAgICAgICAgICAgICAgVFcucmVzaXplU3ZnKHRydWUpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChUVy5zY2hlbWVMaWIgPT09IFRXLlNDSEVNRV9MSUJfTEVBRkxFVCkge1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWVTdmdPdmVybGF5ID0gTC5zdmdPdmVybGF5KHN2Z0RvY0xheWVyLFxuICAgICAgICAgICAgICAgICAgICAgICAgYm91bmRzLFxuICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyYWN0aXZlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBidWJibGluZ01vdXNlRXZlbnRzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKS5hZGRUbyhUVy5zY2hlbWUpO1xuXG5cbiAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLmZpdEJvdW5kcyhib3VuZHMsIHthbmltYXRlOiBmYWxzZX0pO1xuICAgICAgICAgICAgICAgICAgICBUVy5zY2hlbWUuc2V0TWF4Qm91bmRzKGJvdW5kcyk7XG5cbiAgICAgICAgICAgICAgICAgICAgbGV0IHN2Z0NvbnRlbnRXaWR0aDtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHN2Z0NvbnRlbnRIZWlnaHQ7XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3ZnSW5uZXJHcm91cEVsZW0gPSBzdmdFbGVtLnF1ZXJ5U2VsZWN0b3IoJzpzY29wZSA+IGc6b25seS1jaGlsZCcpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoc3ZnSW5uZXJHcm91cEVsZW0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBzdmdJbm5lclJlY3QgPSBzdmdJbm5lckdyb3VwRWxlbS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN2Z0NvbnRlbnRXaWR0aCA9IHN2Z0lubmVyUmVjdC53aWR0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN2Z0NvbnRlbnRIZWlnaHQgPSBzdmdJbm5lclJlY3QuaGVpZ2h0O1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgc3ZnQ29udGVudFdpZHRoID0gcGFyc2VJbnQoc3ZnRWxlbS5zdHlsZS53aWR0aCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdmdDb250ZW50SGVpZ2h0ID0gcGFyc2VJbnQoc3ZnRWxlbS5zdHlsZS5oZWlnaHQpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHN2Z0NvbnRlbnRIZWlnaHQgKiAyIDw9IG1hcFJlY3QuaGVpZ2h0ICYmIHN2Z0NvbnRlbnRXaWR0aCAqIDIgPD0gbWFwUmVjdC53aWR0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuc2NoZW1lLnNldFpvb20oMiwge2FuaW1hdGU6IGZhbHNlfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBUVy5sb2FkZXIoZmFsc2UpO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5jYXRjaChlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoZS50b1N0cmluZygpLmluY2x1ZGVzKCdBYm9ydEVycm9yJykpe1xuICAgICAgICAgICAgICAgICAgICBUVy5lcnJvcnMucHVzaCgn0JfQsNCz0YDRg9C30LrRgyDQtNCw0L3QvdGL0YUg0LLQuNC00LbQtdGC0LAg0L/RgNC10YDQstCw0LvQuCcpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIFRXLmVycm9ycy5wdXNoKGUudG9TdHJpbmcoKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbnNvbGUudHJhY2UoZSk7XG4gICAgICAgICAgICAgICAgVFcub3BlblBhbmVsKCd0dy1yZXF1ZXN0Jyk7XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgICAgICAgIFRXLmxvYWRlcihmYWxzZSk7XG4gICAgICAgICAgICAgICAgVFcuc2VuZFRnTXNnKCk7XG4gICAgICAgICAgICB9KVxuICAgICAgICA7XG4gICAgfVxuXG4gICAgdXBkYXRlSGVhZGVySW5mbygpIHtcbiAgICAgICAgVFcuJCQoJy50d19faGVhZGVyLWNhcHRpb24nKS5hdHRyKCdkYXRhLXBsYXRmb3JtJywgVFcuZXZlbnRQYXJhbXMucGxhY2VOYW1lKS5hdHRyKCdkYXRhLWhhbGwnLCBUVy5ldmVudFBhcmFtcy5oYWxsTmFtZSk7XG4gICAgICAgIFRXLiQkKCcudHdfX2hlYWRlci10aXRsZScpLmh0bWwoVFcuZXZlbnRQYXJhbXMubmFtZSkuc2hvdygpO1xuICAgICAgICBUVy4kJCgnLnR3X19oZWFkZXItc3VidGl0bGUnKS5odG1sKFRXLmV2ZW50UGFyYW1zLmRlc2MpO1xuXG4gICAgICAgIGNvbnN0IHBpY2tlclRpbWVzdGFtcCA9IFRXLmV2ZW50UGFyYW1zLmRhdGU7XG4gICAgICAgIGNvbnN0ICRwaWNrZXIgPSBUVy4kJCgnLnR3X19oZWFkZXItcGlja2VyJykuYXR0cignZGF0YS10aW1lc3RhbXAnLCBwaWNrZXJUaW1lc3RhbXAgfHwgJycpO1xuXG4gICAgICAgIGlmIChwaWNrZXJUaW1lc3RhbXApIHtcbiAgICAgICAgICAgIGNvbnN0IGRheSA9IFRXLm1vc2Nvd0RhdGUocGlja2VyVGltZXN0YW1wLCB7ZGF5OiAnMi1kaWdpdCd9KTtcbiAgICAgICAgICAgIGNvbnN0IHdlZWtkYXkgPSBUVy5tb3Njb3dEYXRlKHBpY2tlclRpbWVzdGFtcCwge3dlZWtkYXk6ICdzaG9ydCd9KTtcbiAgICAgICAgICAgIGNvbnN0IHRpbWUgPSBUVy5tb3Njb3dUaW1lKHBpY2tlclRpbWVzdGFtcCk7XG4gICAgICAgICAgICBjb25zdCBtb250aCA9IFRXLm1vc2Nvd0RhdGUocGlja2VyVGltZXN0YW1wLCB7bW9udGg6ICdsb25nJ30pXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoL9GMJC8sICfRjycpXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoL9GCJC8sICfRgtCwJylcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgv0LkkLywgJ9GPJylcblxuICAgICAgICAgICAgJHBpY2tlci5maW5kKCdbZGF0YS1kYXldJykudGV4dChkYXkpO1xuICAgICAgICAgICAgJHBpY2tlci5maW5kKCdbZGF0YS13ZWVrZGF5XScpLnRleHQod2Vla2RheS50b1VwcGVyQ2FzZSgpKTtcbiAgICAgICAgICAgICRwaWNrZXIuZmluZCgnW2RhdGEtdGltZV0nKS50ZXh0KHRpbWUpO1xuICAgICAgICAgICAgJHBpY2tlci5maW5kKCdbZGF0YS1tb250aF0nKS50ZXh0KG1vbnRoLnRvVXBwZXJDYXNlKCkpO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIGxvYWRDb25maWcoc2hvd0xvYWRlciA9IHRydWUpe1xuICAgICAgICBzaG93TG9hZGVyICYmIFRXLmxvYWRlcih0cnVlKTtcbiAgICAgICAgZm9yIChjb25zdCBba2V5LCBhYm9ydENvbnRyb2xsZXJdIG9mIE9iamVjdC5lbnRyaWVzKFRXLmFib3J0Q29udHJvbGxlcnMpKSB7XG4gICAgICAgICAgICBhYm9ydENvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICAgICAgICAgIFRXLmFib3J0Q29udHJvbGxlcnNba2V5XSA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBUVy5mZXRjaCh7XG4gICAgICAgICAgICBzaWduYWw6IFRXLmFib3J0Q29udHJvbGxlcnMuY29uZmlnLnNpZ25hbCxcbiAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICBhY3Rpb246ICdnZXRfY29uZmlnJyxcbiAgICAgICAgICAgICAgICB3aWRnZXQ6IDEsXG4gICAgICAgICAgICAgICAgZGF0ZTogVFcuZXZlbnRQYXJhbXMuZGF0ZSxcbiAgICAgICAgICAgICAgICBldmVudF9pZDogVFcuZXZlbnRQYXJhbXMuaWQsXG4gICAgICAgICAgICAgICAgcGxhY2VfaWQ6IFRXLmV2ZW50UGFyYW1zLnBsYWNlSWQsXG4gICAgICAgICAgICAgICAgaGFsbF9pZDogVFcuZXZlbnRQYXJhbXMuaGFsbElkLFxuICAgICAgICAgICAgICAgIHBhcnRfaWQ6IFRXLmV2ZW50UGFyYW1zLnBhcnRJZCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKGFzeW5jIHJlc3BvbnNlID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGpzb24/LmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IEVycm9yKGpzb24/LmVycm9yIHx8ICfQlNCw0L3QvdGL0LUgZ2V0X2NvbmZpZyDQvtGC0YHRg9GC0YHRgtCy0YPRjtGCLi4uJyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIGpzb247XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oY29uZmlnID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWNvbmZpZy50aWNrZXRzX2NvdW50KSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IEVycm9yKGDQn9GD0YHRgtC+0LkgdGlja2V0c19jb3VudCDQsiBnZXRfY29uZmlnYCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFRXLmNvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgICAgICAgICAvLyBUVy4kJCgnLnR3X19zY2hlbWUtYXMtdGFibGUtYnRuJykuc2hvdygpO1xuICAgICAgICAgICAgICAgIFRXLiQkKCcudHdfX3RpY2tldHMtc2VjdG9ycycpLmh0bWwoJycpO1xuXG4gICAgICAgICAgICAgICAgaWYgKFRXLmNvbmZpZy5zZWN0b3JzKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgc2VjdG9yRGF0YV0gb2YgT2JqZWN0LmVudHJpZXMoVFcuY29uZmlnLnNlY3RvcnMpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBzZWN0b3JJZCA9IHNlY3RvckRhdGEuaWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCAkc2VjdG9ySXRlbSA9ICQoJzxkaXY+Jywge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjbGFzcyc6ICd0d19fdGlja2V0cy1zZWN0b3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYXRhLWlkJzogc2VjdG9ySWQsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcHJpY2VGcm9tID0gc2VjdG9yRGF0YS5wbGFjZXNNaW5QcmljZSB8fCBzZWN0b3JEYXRhLnBsYWNlcy5yZWR1Y2UoKG1pblByaWNlLCBwbGFjZSkgPT4gK3BsYWNlLnByaWNlIDwgbWluUHJpY2UgPyArcGxhY2UucHJpY2UgOiBtaW5QcmljZSwgSW5maW5pdHkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcGxhY2VzUW50ID0gc2VjdG9yRGF0YS5wbGFjZXNRbnQgfHwgc2VjdG9yRGF0YS5wbGFjZXMubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAkc2VjdG9ySXRlbS5hcHBlbmQoJChgPHNwYW4gZGF0YS1zZWN0b3ItY2VsbD4ke3NlY3RvckRhdGEubmFtZX08L3NwYW4+YCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJHNlY3Rvckl0ZW0uYXBwZW5kKCQoYDxzcGFuIGRhdGEtY291bnQtY2VsbD4ke3BsYWNlc1FudH0g0YjRgjwvc3Bhbj5gKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAocHJpY2VGcm9tKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJHNlY3Rvckl0ZW0uYXBwZW5kKCQoYDxzcGFuIGRhdGEtcHJpY2UtY2VsbD7QvtGCICR7cHJpY2VGcm9tfSDigr08L3NwYW4+YCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkc2VjdG9ySXRlbS5hcHBlbmQoJChgPHNwYW4gZGF0YS1wcmljZS1jZWxsPtCf0L7QtCDQt9Cw0LrQsNC3PC9zcGFuPmApKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGJ0blR4dCA9ICfQktGL0LHRgNCw0YLRjCDQsdC40LvQtdGC0YsnO1xuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGJ0blNtVGV4dCA9IHByaWNlRnJvbSA+IDAgPyBg0L7RgiAke3ByaWNlRnJvbX0g4oK9YCA6IGJ0blR4dDtcbiAgICAgICAgICAgICAgICAgICAgICAgICRzZWN0b3JJdGVtLmFwcGVuZCgkKGA8c3BhbiBkYXRhLWJ0bi1jZWxsPjxzcGFuIGNsYXNzPVwidHctYnRuIHR3LWJ0bi0tc20gdHdfX3RpY2tldHMtc2VjdG9yLW9wZW5lclwiIGRhdGEtdGV4dD1cIiR7YnRuVHh0fVwiIGRhdGEtc20tdGV4dD1cIiR7YnRuU21UZXh0fVwiPjwvc3Bhbj48L3NwYW4+YCkpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBUVy4kJCgnLnR3X190aWNrZXRzLXNlY3RvcnMnKS5hcHBlbmQoJHNlY3Rvckl0ZW0pO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5jb25maWcuYWxsUGxhY2VzID0gVFcuY29uZmlnLmFsbFBsYWNlcyB8fCBbXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHNlY3RvckRhdGEucGxhY2VzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbS5pZCA9IFRXLmdlbmVyYXRlVGlja2V0SWQoaXRlbS5yb3csIGl0ZW0ucGxhY2UsIHNlY3RvckRhdGEubmFtZSwgY29uZmlnLmV2ZW50LmlkLCBjb25maWcuZXZlbnQuZGF0ZSwgY29uZmlnLnBsYWNlLmlkLCBjb25maWcuaGFsbC5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbS5wbGFjZU5hbWUgPSBzZWN0b3JEYXRhLm5hbWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgVFcuY29uZmlnLmFsbFBsYWNlcy5wdXNoKGl0ZW0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgVFcuJCQoJy50d19fdGlja2V0cy10by1zY2hlbWUnKS5hdHRyKCdkYXRhLWltYWdlJywgVFcuY29uZmlnWydoYWxsJ11bJ3NjaGVtZSddIHx8ICcnKTtcbiAgICAgICAgICAgICAgICBUVy4kJCgnLnR3X190aWNrZXRzLWZvb3RlcicpLnJlbW92ZUNsYXNzKCd0dy1oaWRkZW4nKTtcblxuICAgICAgICAgICAgICAgIFRXLmNhcnRJbnN0ID0gbmV3IFRXQ2FydChUVy5ldmVudFBhcmFtcywge2RvY3VtZW50RmllbGRzOiBUVy5jb25maWc/LmZpZWxkc30pO1xuICAgICAgICAgICAgICAgIFRXLmNhcnRJbnN0LmFjdHVhbGl6ZSgpO1xuICAgICAgICAgICAgICAgIFRXLnJlZHJhd01pbmljYXJ0KCk7XG5cbiAgICAgICAgICAgICAgICBUVy5sb2FkZXIoZmFsc2UpO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5jYXRjaChlID0+IHtcbiAgICAgICAgICAgICAgICBUVy5lcnJvcnMucHVzaChlLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctcmVxdWVzdCcpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGUpO1xuICAgICAgICAgICAgfSlcbiAgICB9XG5cbiAgICB1cGRhdGVIZWFkZXJEYXRlcygpIHtcbiAgICAgICAgY29uc3QgJGhlYWRlckRhdGVTZWxlY3QgPSBUVy4kaGVhZGVyLmZpbmQoJy50d19faGVhZGVyLWRhdGUtc2VsZWN0Jyk7XG4gICAgICAgICRoZWFkZXJEYXRlU2VsZWN0LmVtcHR5KCk7XG5cbiAgICAgICAgY29uc3QgZXZEYXkgPSBUVy5tb3Njb3dEYXRlKFRXLmV2ZW50UGFyYW1zLmRhdGUsIHtkYXk6ICdudW1lcmljJ30pO1xuICAgICAgICBjb25zdCBldk1vbnRoID0gVFcubW9zY293RGF0ZShUVy5ldmVudFBhcmFtcy5kYXRlLCB7bW9udGg6ICdsb25nJ30pXG4gICAgICAgICAgICAucmVwbGFjZSgv0YwkL2ksICfRjycpXG4gICAgICAgICAgICAucmVwbGFjZSgv0YIkL2ksICfRgtCwJylcbiAgICAgICAgICAgIC5yZXBsYWNlKC/QuSQvaSwgJ9GPJylcbiAgICAgICAgO1xuICAgICAgICBjb25zdCAkaGVhZGVyRGF0ZU1haW5PcHRpb24gPSBUVy4kaGVhZGVyLmZpbmQoJy50d19faGVhZGVyLWRhdGUtb3B0aW9uJyk7XG4gICAgICAgICRoZWFkZXJEYXRlTWFpbk9wdGlvbi5maW5kKCdbZGF0YS13ZWVrLWRheV0nKS50ZXh0KFRXLm1vc2Nvd0RhdGUoVFcuZXZlbnRQYXJhbXMuZGF0ZSwge3dlZWtkYXk6ICdzaG9ydCd9KS50b1VwcGVyQ2FzZSgpKTtcbiAgICAgICAgJGhlYWRlckRhdGVNYWluT3B0aW9uLmZpbmQoJ1tkYXRhLWRhdGVdJykudGV4dChgJHtldkRheX0gJHtldk1vbnRofWApO1xuICAgICAgICAkaGVhZGVyRGF0ZU1haW5PcHRpb24uZmluZCgnW2RhdGEtdGltZV0nKS50ZXh0KFRXLm1vc2Nvd1RpbWUoVFcuZXZlbnRQYXJhbXMuZGF0ZSwge1xuICAgICAgICAgICAgaG91cjogJzItZGlnaXQnLFxuICAgICAgICAgICAgbWludXRlOiAnMi1kaWdpdCdcbiAgICAgICAgfSkpO1xuXG4gICAgICAgIHJldHVybiBUVy5mZXRjaCh7XG4gICAgICAgICAgICBzaWduYWw6IFRXLmFib3J0Q29udHJvbGxlcnMuaGVhZGVyRGF0ZXMuc2lnbmFsLFxuICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgIGFjdGlvbjogJ2dldF9ldmVudF9kYXRlcycsXG4gICAgICAgICAgICAgICAgZXZlbnRfaWQ6IFRXLmV2ZW50UGFyYW1zLmlkLFxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oYXN5bmMgcmVzcG9uc2UgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGpzb24gPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG5cbiAgICAgICAgICAgICAgICBpZiAoanNvbj8uZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgRXJyb3IoanNvbj8uZXJyb3IgfHwgJ9CU0LDQvdC90YvQtSDQv9C+INC00LDRgtCw0Lwg0YHQvtCx0YvRgtC40Y8g0LTQu9GPINC80LjQvdC4LdC60LDQu9C10L3QtNCw0YDRjyDQvtGC0YHRg9GC0YHRgtCy0YPRjtGCLi4uJyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIGpzb247XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oanNvbiA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgaGFzQW55RGF0ZXMgPSAhKE9iamVjdC5rZXlzKGpzb24uZGF0ZXMpLmxlbmd0aCA9PT0gMSAmJiBqc29uLmRhdGVzW1RXLmV2ZW50UGFyYW1zLmRhdGVdIHx8ICFPYmplY3Qua2V5cyhqc29uLmRhdGVzKS5sZW5ndGgpO1xuXG4gICAgICAgICAgICAgICAgVFcuJGhlYWRlci5maW5kKCcudHdfX2hlYWRlci1kYXRlLXRvZ2dsZScpLnRvZ2dsZShoYXNBbnlEYXRlcyk7XG4gICAgICAgICAgICAgICAgVFcuJGhlYWRlci5maW5kKCcudHdfX2hlYWRlci1vdGhlci1kYXRlcycpLnRvZ2dsZUNsYXNzKCd0dy1oaWRkZW4nLCAhaGFzQW55RGF0ZXMpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGhhc0FueURhdGVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3QgW2RhdGVJbnQsIGRhdGVJbmZvXSBvZiBPYmplY3QuZW50cmllcyhqc29uLmRhdGVzKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGRhdGVJbnQgPT0gVFcuZXZlbnRQYXJhbXMuZGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB3ZWVrZGF5U2hvcnQgPSBUVy5tb3Njb3dEYXRlKGRhdGVJbnQsIHt3ZWVrZGF5OiAnc2hvcnQnfSkudG9VcHBlckNhc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRheSA9IFRXLm1vc2Nvd0RhdGUoZGF0ZUludCwge2RheTogJ251bWVyaWMnfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB0aW1lID0gVFcubW9zY293VGltZShkYXRlSW50LCB7aG91cjogJzItZGlnaXQnLCBtaW51dGU6ICcyLWRpZ2l0J30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbW9udGggPSBUVy5tb3Njb3dEYXRlKGRhdGVJbnQsIHttb250aDogJ2xvbmcnfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgv0YwkL2ksICfRjycpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoL9GCJC9pLCAn0YLQsCcpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoL9C5JC9pLCAn0Y8nKVxuICAgICAgICAgICAgICAgICAgICAgICAgO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCAkb3B0aW9uID0gJChgPGRpdiBjbGFzcz1cInR3X19oZWFkZXItZGF0ZS1vcHRpb24ganMtdHctdGltZS1waWNrZXJcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBkYXRhLXdlZWstZGF5PiR7d2Vla2RheVNob3J0fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBkYXRhLWRhdGU+JHtkYXl9ICR7bW9udGh9PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGRhdGEtdGltZT4ke3RpbWV9PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+YCk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICRvcHRpb24uYXR0cih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtdGltZXN0YW1wJzogZGF0ZUludCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1ldmVudC1uYW1lJzoganNvbi5ldmVudF9uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYXRhLXBsYWNlLWlkJzogZGF0ZUluZm8ucGxhY2VfaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtaGFsbC1pZCc6IGRhdGVJbmZvLmhhbGxfaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtaGFsbC1uYW1lJzogZGF0ZUluZm8uaGFsbF9uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYXRhLXBsYWNlLW5hbWUnOiBkYXRlSW5mby5wbGFjZV9uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgICAgICAgICAgICAgJGhlYWRlckRhdGVTZWxlY3QuYXBwZW5kKCRvcHRpb24pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICB9XG5cbiAgICBsb2FkUmVxdWVzdEV2ZW50RGF0ZXMoc2hvd0xvYWRlciA9IHRydWUpe1xuICAgICAgICBzaG93TG9hZGVyICYmIFRXLmxvYWRlcih0cnVlKTtcbiAgICAgICAgXG4gICAgICAgIGZvciAoY29uc3QgW2tleSwgYWJvcnRDb250cm9sbGVyXSBvZiBPYmplY3QuZW50cmllcyhUVy5hYm9ydENvbnRyb2xsZXJzKSkge1xuICAgICAgICAgICAgYWJvcnRDb250cm9sbGVyLmFib3J0KCk7XG4gICAgICAgICAgICBUVy5hYm9ydENvbnRyb2xsZXJzW2tleV0gPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gVFcuZmV0Y2goe1xuICAgICAgICAgICAgc2lnbmFsOiBUVy5hYm9ydENvbnRyb2xsZXJzLmV2ZW50RGF0ZXMuc2lnbmFsLFxuICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgIGFjdGlvbjogJ2dldF9ldmVudF9kYXRlcycsXG4gICAgICAgICAgICAgICAgZXZlbnRfaWQ6IFRXLmV2ZW50UGFyYW1zLmlkLFxuICAgICAgICAgICAgICAgIC8vIHBsYWNlX2lkOiBUVy5ldmVudFBhcmFtcy5wbGFjZUlkLFxuICAgICAgICAgICAgICAgIC8vIGhhbGxfaWQ6IFRXLmV2ZW50UGFyYW1zLmhhbGxJZCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKGFzeW5jIHJlc3BvbnNlID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGpzb24/LmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IEVycm9yKGpzb24/LmVycm9yIHx8ICfQlNCw0L3QvdGL0LUg0L/QviDQtNCw0YLQsNC8INGB0L7QsdGL0YLQuNGPINC+0YLRgdGD0YLRgdGC0LLRg9GO0YIuLi4nKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4ganNvbjtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAudGhlbihqc29uID0+IHtcbiAgICAgICAgICAgICAgICBqc29uLmRhdGVzID0ganNvbi5kYXRlcyB8fCB7fTtcbiAgICAgICAgICAgICAgICBjb25zdCAkc2VsZWN0ID0gVFcuJCQoJy5qcy1yZXF1ZXN0LWRhdGUtc2VsZWN0Jyk7XG4gICAgICAgICAgICAgICAgJHNlbGVjdC5jaGlsZHJlbigpLmZpcnN0KCkucHJvcCgnc2VsZWN0ZWQnLCB0cnVlKS5zaWJsaW5ncygpLnJlbW92ZSgpO1xuICAgICAgICAgICAgICAgIGxldCBoYXNBbnlQbGFjZXMgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICBsZXQgcHJldkRhdGVFdmVudEluZm87XG5cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IFtkYXRlSW50LCBldmVudEluZm9dIG9mIE9iamVjdC5lbnRyaWVzKGpzb24uZGF0ZXMpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBsYWNlID0gZXZlbnRJbmZvLnBsYWNlX2NpdHk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGxvY2FsZURhdGUgPSBUVy5tb3Njb3dEYXRlKGRhdGVJbnQsIHt5ZWFyOiAnMi1kaWdpdCcsIG1vbnRoOiAnMi1kaWdpdCcsIGRheTogJzItZGlnaXQnfSk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHdlZWtkYXkgPSBUVy5tb3Njb3dEYXRlKGRhdGVJbnQsIHt3ZWVrZGF5OiAnbG9uZyd9KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgdGltZSA9IFRXLm1vc2Nvd1RpbWUoZGF0ZUludCwge2hvdXI6ICcyLWRpZ2l0JywgbWludXRlOiAnMi1kaWdpdCd9KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgJG9wdGlvbiA9ICQoJzxvcHRpb24+Jywge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGRhdGVJbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS10aWNrZXRzLWNvdW50JzogZXZlbnRJbmZvLnRpY2tldHNfY291bnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1oYWxsLWlkJzogZXZlbnRJbmZvLmhhbGxfaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1oYWxsLW5hbWUnOiBldmVudEluZm8uaGFsbF9uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgJ2RhdGEtcGxhY2UtaWQnOiBldmVudEluZm8ucGxhY2VfaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAnZGF0YS1wbGFjZS1uYW1lJzogZXZlbnRJbmZvLnBsYWNlX25hbWUsXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgICRvcHRpb24udGV4dChgJHtsb2NhbGVEYXRlfSAke3RpbWV9ICgke3dlZWtkYXl9KSwgJHtwbGFjZX1gKS5wcm9wKCdzZWxlY3RlZCcsIGRhdGVJbnQgPT0gVFcuZXZlbnRQYXJhbXMuZGF0ZSB8fCBPYmplY3Qua2V5cyhqc29uLmRhdGVzKS5sZW5ndGggPT0gMSk7XG4gICAgICAgICAgICAgICAgICAgICRzZWxlY3QuYXBwZW5kKCRvcHRpb24pO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChwcmV2RGF0ZUV2ZW50SW5mbyAmJiAocHJldkRhdGVFdmVudEluZm8ucGxhY2VfaWQgIT09IGV2ZW50SW5mby5wbGFjZV9pZCB8fCBwcmV2RGF0ZUV2ZW50SW5mby5oYWxsX2lkICE9PSBldmVudEluZm8uaGFsbF9pZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhc0FueVBsYWNlcyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBwcmV2RGF0ZUV2ZW50SW5mbyA9IGV2ZW50SW5mbztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAkc2VsZWN0LnByb3AoJ2Rpc2FibGVkJywgIU9iamVjdC5rZXlzKGpzb24uZGF0ZXMpLmxlbmd0aCkudG9nZ2xlKE9iamVjdC5rZXlzKGpzb24uZGF0ZXMpLmxlbmd0aCA+IDApO1xuXG4gICAgICAgICAgICAgICAgaWYgKGhhc0FueVBsYWNlcykge1xuICAgICAgICAgICAgICAgICAgICBUVy4kaGVhZGVyLmZpbmQoJy50d19faGVhZGVyLWNhcHRpb24nKS5hdHRyKCdkYXRhLXBsYXRmb3JtJywgJycpLmF0dHIoJ2RhdGEtaGFsbCcsICcnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBUVy5sb2FkZXIoZmFsc2UpO1xuXG4gICAgICAgICAgICAgICAgZmV0Y2goYGh0dHBzOi8vc3ZnLWdlbi5ydS9fcmVxdWVzdC5waHA/Y29kZT1tb3MyMDE5JmRhdGU9JHtUVy5ldmVudFBhcmFtcy5kYXRlfSZldmVudF9pZD0ke1RXLmV2ZW50UGFyYW1zLmlkfSZwbGFjZV9pZD0ke1RXLmV2ZW50UGFyYW1zLnBsYWNlSWR9JmhhbGxfaWQ9JHtUVy5ldmVudFBhcmFtcy5oYWxsSWR9Jmhvc3Q9JHtsb2NhdGlvbi5ob3N0fWApO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5jYXRjaChlID0+IHtcbiAgICAgICAgICAgICAgICBhbGVydCgn0JjQt9Cy0LjQvdC40YLQtSwg0LLRgNC10LzQtdC90L3QviDQvtGC0YHRg9GC0YHRgtCy0YPQtdGCINC40L3RhNC+0YDQvNCw0YbQuNGPINC/0L4g0LTQsNGC0LDQvCDQstGL0LHRgNCw0L3QvdC+0LPQviDRgdC+0LHRi9GC0LjRjy4g0J/QvtC/0YDQvtCx0YPQudGC0LUg0L/QvtGB0LzQvtGC0YDQtdGC0Ywg0L/QvtC30LbQtScpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGUpO1xuICAgICAgICAgICAgfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb2FkIGFuZCBkaXNwbGF5IG9mIGNhbGVuZGFyXG4gICAgICpcbiAgICAgKiBAcGFyYW0gb3BlblxuICAgICAqIEBwYXJhbSByZWxvYWRcbiAgICAgKi9cbiAgICBnZXRDYWxlbmRhcihvcGVuID0gdHJ1ZSwgcmVsb2FkID0gdHJ1ZSkge1xuICAgICAgICBjb25zdCBkb0ZldGNoID0gVFcuJGNhbGVuZGFyLmlzKCc6ZW1wdHknKSB8fCByZWxvYWQ7XG5cbiAgICAgICAgaWYgKG9wZW4gJiYgIWRvRmV0Y2gpIHtcbiAgICAgICAgICAgIFRXLm9wZW5QYW5lbCgndHctY2FsZW5kYXInKTtcbiAgICAgICAgfSBlbHNlIGlmIChkb0ZldGNoKSB7XG4gICAgICAgICAgICBpZiAob3Blbikge1xuICAgICAgICAgICAgICAgIFRXLmxvYWRlcih0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIGNvbnN0IGFjdGlvbiA9ICdnZXRfY2FsZW5kYXInO1xuICAgICAgICAgICAgY29uc3QgYWN0aW9uID0gJ2dldF9zaW1wbGVfY2FsZW5kYXInO1xuICAgICAgICAgICAgVFcuZmV0Y2goe1xuICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiBhY3Rpb24sXG4gICAgICAgICAgICAgICAgICAgIGRhdGU6IFRXLmV2ZW50UGFyYW1zLmRhdGUsXG4gICAgICAgICAgICAgICAgICAgIGV2ZW50X2lkOiBUVy5ldmVudFBhcmFtcy5pZCxcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VfaWQ6IFRXLmV2ZW50UGFyYW1zLnBsYWNlSWQsXG4gICAgICAgICAgICAgICAgICAgIGhhbGxfaWQ6IFRXLmV2ZW50UGFyYW1zLmhhbGxJZCxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC50aGVuKHJlc3BvbnNlID0+IHJlc3BvbnNlLmpzb24oKSlcbiAgICAgICAgICAgICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFkYXRhLnN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IEVycm9yKCdzdWNjZXNzIGlzIG5vdCB0cnVlJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBUVy4kY2FsZW5kYXIuaHRtbChkYXRhLmh0bWwpO1xuICAgICAgICAgICAgICAgICAgICBUVy4kJCgnLnR3X19oZWFkZXItcGlja2VyJykudG9nZ2xlQ2xhc3MoJy0tbG9ja2VkJywgVFcuZXZlbnRQYXJhbXMuaWQudG9TdHJpbmcoKS5pbmNsdWRlcygnLScpKVxuICAgICAgICAgICAgICAgICAgICBUVy4kaGVhZGVyLmZpbmQoJy50d19faGVhZGVyLW90aGVyLWRhdGVzJykudG9nZ2xlQ2xhc3MoJ3R3LWhpZGRlbicsIHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKG9wZW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLnVwZGF0ZUhlYWRlckluZm8oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLmxvYWRlcihmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBUVy5vcGVuUGFuZWwoJ3R3LWNhbGVuZGFyJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoYWN0aW9uID09PSAnZ2V0X3NpbXBsZV9jYWxlbmRhcicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLiRjYWxlbmRhci5hZGRDbGFzcygndHdfX2NhbGVuZGFyLXNpbXBsZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgVFcuJGNhbGVuZGFyLmZpbmQoJy50d19fY2FsZW5kYXItc2ltcGxlLXRpdGxlJykuaHRtbChUVy5ldmVudFBhcmFtcy5uYW1lKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRXLiQkKCcudHdfX2hlYWRlci10aXRsZScpLmhpZGUoKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGFsZXJ0KCfQntGI0LjQsdC60LAg0L7RgtC/0YDQsNCy0LrQuCDQtNCw0L3QvdGL0YUhINCf0L7Qv9GA0L7QsdGD0LnRgtC1INC+0LHQvdC+0LLQuNGC0Ywg0YHRgtGA0LDQvdC40YbRgy4nKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXBkYXRlRmxvYXRpbmdNYXJnaW5zKCkge1xuICAgICAgICBjb25zdCAkYWJzb2x1dGVQb3NpdGlvbkZvb3RlckVsZW1lbnRzID0gJCgnLnR3X19zY2hlbWUtYmFjaywgLnR3X19zY2hlbWUtdG8tdGFibGUtYnRuLCAudHdfX3RpY2tldHMtZm9vdGVyJykuY3NzKCdtYXJnaW4tYm90dG9tJywgJycpO1xuICAgICAgICAkYWJzb2x1dGVQb3NpdGlvbkZvb3RlckVsZW1lbnRzLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgY29uc3QgY3VyVmFsdWUgPSBwYXJzZUludCgkKHRoaXMpLmNzcygnbWFyZ2luLWJvdHRvbScpKTtcbiAgICAgICAgICAgICQodGhpcykuY3NzKCdtYXJnaW4tYm90dG9tJywgY3VyVmFsdWUgKyBwYXJzZUludChUVy4kbWluaWNhcnQuZmlsdGVyKCdhY3RpdmUnKS5vdXRlckhlaWdodCgpKSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJlZHJhd01pbmljYXJ0KCkge1xuICAgICAgICBjb25zdCAkaXRlbXMgPSBUVy4kbWluaWNhcnQuZmluZCgnLnR3X19taW5pY2FydC1pdGVtcycpLmVtcHR5KCk7XG5cbiAgICAgICAgaWYgKCFUVy5jYXJ0SW5zdCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGlja2V0c0NvdW50ID0gVFcuY2FydEluc3Q/LnRpY2tldHNDb3VudCgpO1xuXG4gICAgICAgIGlmICh0aWNrZXRzQ291bnQgPiAwKSB7XG4gICAgICAgICAgICBjb25zdCB0aWNrZXRzQ291bnRDYXB0aW9uID0gVFcuZGVjbE9mTnVtKHRpY2tldHNDb3VudCwgWyfQsdC40LvQtdGCJywgJ9Cx0LjQu9C10YLQsCcsICfQsdC40LvQtdGC0L7QsiddKTtcblxuICAgICAgICAgICAgVFcuJG1pbmljYXJ0LmZpbmQoJ1tkYXRhLWNhcnQtcW50XScpLnRleHQodGlja2V0c0NvdW50ICsgJyAnICsgdGlja2V0c0NvdW50Q2FwdGlvbik7XG5cbiAgICAgICAgICAgIGNvbnN0IHN1bSA9IFRXLmNhcnRJbnN0LnN1bSgpO1xuICAgICAgICAgICAgbGV0IHByZWZpeDtcbiAgICAgICAgICAgIGxldCBzdW1UZXh0O1xuXG4gICAgICAgICAgICBpZiAoIWlzTmFOKHN1bSkpIHtcbiAgICAgICAgICAgICAgICBwcmVmaXggPSAnJztcbiAgICAgICAgICAgICAgICBzdW1UZXh0ID0gYCR7c3VtfSDigr1gO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwcmVmaXggPSAnJztcbiAgICAgICAgICAgICAgICBzdW1UZXh0ID0gJ9Cf0L7QtCDQt9Cw0LrQsNC3JztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVFcuJG1pbmljYXJ0LmZpbmQoJ1tkYXRhLWNhcnQtc3VtXScpLmF0dHIoJ2RhdGEtY2FydC1zdW0nLCBzdW1UZXh0KS5hdHRyKCdkYXRhLWNhcnQtc3VtLXByZWZpeCcsIHByZWZpeCk7XG4gICAgICAgICAgICBUVy4kbWluaWNhcnQuYWRkQ2xhc3MoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgVFcudXBkYXRlRmxvYXRpbmdNYXJnaW5zKCk7XG5cbiAgICAgICAgICAgIGZvciAoY29uc3QgY2FydEl0ZW0gb2YgVFcuY2FydEluc3QuaXRlbXMpIHtcbiAgICAgICAgICAgICAgICBjb25zdCAkaXRlbSA9ICQoYDxkaXYgY2xhc3M9XCJ0d19fbWluaWNhcnQtaXRlbVwiIGRhdGEtY2FydC1pdGVtPVwiJHtjYXJ0SXRlbS5pZH1cIj48L2Rpdj5gKTtcbiAgICAgICAgICAgICAgICBjb25zdCByb3cgPSBjYXJ0SXRlbS5yb3cucmVwbGFjZSgvXlstMF0kLywgJycpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHBsYWNlID0gY2FydEl0ZW0ucGxhY2UucmVwbGFjZSgvXlstMF0kLywgJycpO1xuXG4gICAgICAgICAgICAgICAgJGl0ZW0uYXBwZW5kKGA8c3BhbiBkYXRhLXNlY3Rvcj1cIiR7Y2FydEl0ZW0ucGxhY2VOYW1lfVwiPjwvc3Bhbj5gKTtcblxuICAgICAgICAgICAgICAgIGlmIChyb3cpIHtcbiAgICAgICAgICAgICAgICAgICAgJGl0ZW0uYXBwZW5kKGA8c3BhbiBkYXRhLXJvdz1cIiR7cm93fVwiPiDRgNGP0LQ8L3NwYW4+YCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChwbGFjZSkge1xuICAgICAgICAgICAgICAgICAgICAkaXRlbS5hcHBlbmQoYDxzcGFuIGRhdGEtcGxhY2U9XCIke3BsYWNlfVwiPiDQvNC10YHRgtC+PC9zcGFuPmApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY2FydEl0ZW0ucW50ID4gMSkge1xuICAgICAgICAgICAgICAgICAgICAkaXRlbS5hcHBlbmQoYDxzcGFuIGRhdGEtcW50PVwiJHtjYXJ0SXRlbS5xbnR9XCI+INGI0YI8L3NwYW4+YCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgJGl0ZW0uYXBwZW5kKGA8c3Bhbj48aSBjbGFzcz1cInR3X19jbG9zZSB0d19fbWluaWNhcnQtaXRlbS1kZWxldGVcIj48L2k+PC9zcGFuPmApO1xuICAgICAgICAgICAgICAgICRpdGVtcy5hcHBlbmQoJGl0ZW0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVFcuJHRvb2x0aXAuaGlkZSgpO1xuICAgICAgICAgICAgVFcuY2FydEluc3Q/LnJlc2V0KCk7XG5cbiAgICAgICAgICAgIFRXLiRtaW5pY2FydC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG4gICAgICAgICAgICBUVy5jbG9zZVBhbmVsKCd0dy1jaGVja291dCcpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXBkYXRlTWFza0VsZW1lbnRzKCkge1xuICAgICAgICBpZiAoJCgpLmlucHV0bWFzaykge1xuICAgICAgICAgICAgVFcuJCQoJ2lucHV0W2RhdGEtbWFzaz1cInBob25lXCJdJylcbiAgICAgICAgICAgICAgICAuY3NzKCdwYXR0ZXJuJywgXCJcXCs3XFxzKlxcKFxcZHszfVxcKVxccypcXGR7M30tXFxkezJ9LVxcZHsyfVwiKVxuICAgICAgICAgICAgICAgIC5pbnB1dG1hc2soe1xuICAgICAgICAgICAgICAgICAgICBtYXNrOiAnKzcgKDk5OSkgOTk5LTk5LTk5JyxcbiAgICAgICAgICAgICAgICAgICAgc2hvd01hc2tPbkhvdmVyOiBmYWxzZSxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXBkYXRlQ2hlY2tvdXQoY2hlY2tvdXRQYW5lbElkKSB7XG4gICAgICAgIGNoZWNrb3V0UGFuZWxJZCA9IGNoZWNrb3V0UGFuZWxJZCB8fCBUVy4kJCgnLnR3X19jaGVja291dC5hY3RpdmUnKS5hdHRyKCdpZCcpIHx8ICd0dy1jaGVja291dCc7XG5cbiAgICAgICAgY29uc3QgJGNoZWNrb3V0ID0gVFcuJCQoJyMnICsgY2hlY2tvdXRQYW5lbElkKTtcbiAgICAgICAgY29uc3QgJGRyYXduQ2FydHMgPSAkY2hlY2tvdXQuZmluZCgnW2RhdGEtY2FydF06bm90KC50dy1oaWRkZW4pJyk7XG4gICAgICAgIGNvbnN0IG9wZW5lZENhcnRzID0gW107XG5cbiAgICAgICAgJGRyYXduQ2FydHMuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBpZiAoJCh0aGlzKS5maW5kKCcuanMtdHctY2hlY2tvdXQtY2FydC1kZXRhaWxzW29wZW5dJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgb3BlbmVkQ2FydHMucHVzaCh0aGlzLmdldEF0dHJpYnV0ZSgnZGF0YS1jYXJ0JykpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgJGRyYXduQ2FydHMucmVtb3ZlKCk7XG5cbiAgICAgICAgbGV0IHRpY2tldHNEZWxpdmVyeSA9ICfQrdC70LXQutGC0YDQvtC90L3Ri9C5INCx0LjQu9C10YInO1xuICAgICAgICBsZXQgYWxsb3dQYXkgPSBmYWxzZTtcbiAgICAgICAgbGV0IGhhc1BhcGVyVGlja2V0ID0gZmFsc2U7XG4gICAgICAgIGxldCB0b3RhbFN1bSA9IDA7XG4gICAgICAgIGxldCB0b3RhbFFudCA9IDA7XG5cbiAgICAgICAgY29uc3QgcGFyc2VEYXRhRmllbGRzID0gKGlucHV0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYWlycyA9IGlucHV0LnNwbGl0KFwifFwiKTtcblxuICAgICAgICAgICAgcmV0dXJuIHBhaXJzLm1hcChwYWlyID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBbbmFtZSwgcGxhY2Vob2xkZXIsIHR5cGUsIG9wdGlvbnNdID0gcGFpci5zcGxpdChcIjtcIik7XG4gICAgICAgICAgICAgICAgbGV0IGZvcm1hdE9wdGlvbnMgPSBbXTtcblxuICAgICAgICAgICAgICAgIGlmICh0eXBlID09PSAnc2VsZWN0JyAmJiBvcHRpb25zKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvcm1hdE9wdGlvbnMgPSBvcHRpb25zLnNwbGl0KFwiIVwiKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcixcbiAgICAgICAgICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9uczogZm9ybWF0T3B0aW9uc1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgY29uc3QgZmlsbENhcnREYXRhID0gKGNhcnRJbnN0YW5jZSkgPT4ge1xuICAgICAgICAgICAgaWYgKCFjYXJ0SW5zdGFuY2UuaXNFbXB0eSgpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgJGNhcnRUcGwgPSAkY2hlY2tvdXQuZmluZCgnW2RhdGEtY2FydD1cIlwiXS50dy1oaWRkZW4nKTtcbiAgICAgICAgICAgICAgICBjb25zdCAkY2FydCA9XG4gICAgICAgICAgICAgICAgICAgICRjYXJ0VHBsXG4gICAgICAgICAgICAgICAgICAgICAgICAuY2xvbmUoKVxuICAgICAgICAgICAgICAgICAgICAgICAgLmF0dHIoJ2RhdGEtY2FydCcsIGNhcnRJbnN0YW5jZS5pZClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5yZW1vdmVDbGFzcygndHctaGlkZGVuJylcbiAgICAgICAgICAgICAgICAgICAgICAgIC5pbnNlcnRBZnRlcigkY2FydFRwbCk7XG4gICAgICAgICAgICAgICAgaWYgKG9wZW5lZENhcnRzLmluY2x1ZGVzKGNhcnRJbnN0YW5jZS5pZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgJGNhcnQuZmluZCgnLmpzLXR3LWNoZWNrb3V0LWNhcnQtZGV0YWlscycpLnByb3AoJ29wZW4nLCB0cnVlKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjb25zdCAkY2FydExpc3QgPSAkY2FydC5maW5kKCdbZGF0YS1jYXJ0LWxpc3RdJyk7XG4gICAgICAgICAgICAgICAgY29uc3QgY2FydFN1bSA9IGNhcnRJbnN0YW5jZS5zdW0oKTtcbiAgICAgICAgICAgICAgICBjb25zdCB0aWNrZXRzQ291bnQgPSBjYXJ0SW5zdGFuY2UudGlja2V0c0NvdW50KCk7XG4gICAgICAgICAgICAgICAgY29uc3QgdGlja2V0c0NvdW50Q2FwdGlvbiA9IFRXLmRlY2xPZk51bSh0aWNrZXRzQ291bnQsIFsn0LHQuNC70LXRgicsICfQsdC40LvQtdGC0LAnLCAn0LHQuNC70LXRgtC+0LInXSk7XG4gICAgICAgICAgICAgICAgY29uc3QgY2FydFN1bVRleHQgPSAhaXNOYU4oY2FydFN1bSkgPyBjYXJ0U3VtICsgJ+KCvScgOiAn0J/QvtC0INC30LDQutCw0LcnO1xuXG4gICAgICAgICAgICAgICAgdG90YWxTdW0gKz0gY2FydFN1bTtcbiAgICAgICAgICAgICAgICB0b3RhbFFudCArPSB0aWNrZXRzQ291bnQ7XG5cbiAgICAgICAgICAgICAgICBjb25zdCBkYXkgPSBUVy5tb3Njb3dEYXRlKGNhcnRJbnN0YW5jZS5ldmVudC5kYXRlLCB7ZGF5OiAnMi1kaWdpdCd9KTtcbiAgICAgICAgICAgICAgICBjb25zdCB3ZWVrZGF5ID0gVFcubW9zY293RGF0ZShjYXJ0SW5zdGFuY2UuZXZlbnQuZGF0ZSwge3dlZWtkYXk6ICdzaG9ydCd9KTtcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lID0gVFcubW9zY293VGltZShjYXJ0SW5zdGFuY2UuZXZlbnQuZGF0ZSk7XG4gICAgICAgICAgICAgICAgY29uc3QgbW9udGggPSBUVy5tb3Njb3dEYXRlKGNhcnRJbnN0YW5jZS5ldmVudC5kYXRlLCB7bW9udGg6ICdsb25nJ30pXG4gICAgICAgICAgICAgICAgICAgIC5yZXBsYWNlKC/RjCQvLCAn0Y8nKVxuICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgv0YIkLywgJ9GC0LAnKVxuICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgv0LkkLywgJ9GPJylcblxuXG4gICAgICAgICAgICAgICAgY29uc3QgJGNhcnRFdmVudERhdGUgPSAkY2FydC5maW5kKCdbZGF0YS1jYXJ0LWRhdGVdJyk7XG4gICAgICAgICAgICAgICAgJGNhcnRFdmVudERhdGUuZmluZCgnW2RhdGEtd2Vla2RheV0nKS50ZXh0KHdlZWtkYXkudG9VcHBlckNhc2UoKSk7XG4gICAgICAgICAgICAgICAgJGNhcnRFdmVudERhdGUuZmluZCgnW2RhdGEtZGF0ZV0nKS50ZXh0KGRheSArICcgJyArIG1vbnRoKTtcbiAgICAgICAgICAgICAgICAkY2FydEV2ZW50RGF0ZS5maW5kKCdbZGF0YS10aW1lXScpLnRleHQodGltZSk7XG5cbiAgICAgICAgICAgICAgICAkY2FydC5maW5kKCdbZGF0YS1jYXJ0LWV2ZW50XScpLmh0bWwoY2FydEluc3RhbmNlLmV2ZW50Lm5hbWUpO1xuICAgICAgICAgICAgICAgICRjYXJ0LmZpbmQoJ1tkYXRhLWNhcnQtcGxhdGZvcm1dJykuYXR0cignZGF0YS1jYXJ0LXBsYXRmb3JtJywgY2FydEluc3RhbmNlLmV2ZW50LnBsYWNlTmFtZSk7XG4gICAgICAgICAgICAgICAgJGNhcnQuZmluZCgnW2RhdGEtY2FydC1oYWxsXScpLmF0dHIoJ2RhdGEtY2FydC1oYWxsJywgY2FydEluc3RhbmNlLmV2ZW50LmhhbGxOYW1lKTtcblxuICAgICAgICAgICAgICAgICRjYXJ0LmZpbmQoJ1tkYXRhLWNhcnQtc3VtXScpLnRleHQoY2FydFN1bVRleHQpO1xuICAgICAgICAgICAgICAgICRjYXJ0LmZpbmQoJ1tkYXRhLWNhcnQtdGlja2V0c10nKS50ZXh0KHRpY2tldHNDb3VudCArICcgJyArIHRpY2tldHNDb3VudENhcHRpb24gKyAnOiAnKTtcbiAgICAgICAgICAgICAgICAkY2FydC5maW5kKCcuanMtb3Blbi1oYXNoJykuYXR0cignaHJlZicsICcjJyArIGI2NEVuY29kZShjYXJ0SW5zdGFuY2UuZXZlbnQpKTtcblxuICAgICAgICAgICAgICAgICRjYXJ0TGlzdC5maW5kKCdbZGF0YS1jYXJ0LWl0ZW1dOm5vdCgudHctaGlkZGVuKScpLnJlbW92ZSgpO1xuXG4gICAgICAgICAgICAgICAgbGV0IGkgPSAxXG5cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGNhcnRJdGVtIG9mIGNhcnRJbnN0YW5jZS5pdGVtcykge1xuICAgICAgICAgICAgICAgICAgICBsZXQgcHJpY2UgPSBwYXJzZUludChjYXJ0SXRlbS5wcmljZSkgKiBjYXJ0SXRlbS5xbnQ7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHN1bVRleHQgPSAhY2FydEl0ZW0ub25PcmRlciA/IHByaWNlLnRvTG9jYWxlU3RyaW5nKCkgOiAn0J/QvtC0INC30LDQutCw0LcnXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGNhcnRJdGVtLmFsbG93UGF5bWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgYWxsb3dQYXkgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgbGV0ICRjYXJ0SXRlbUVsID1cbiAgICAgICAgICAgICAgICAgICAgICAgICRjYXJ0TGlzdFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5maW5kKCdbZGF0YS1jYXJ0LWl0ZW1dLnR3LWhpZGRlbicpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLmNsb25lKClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAucmVtb3ZlQ2xhc3MoJ3R3LWhpZGRlbicpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLmF0dHIoJ2RhdGEtY2FydC1pdGVtJywgY2FydEl0ZW0uaWQpO1xuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHZpZXdlckRhdGEgPSAkY2FydEl0ZW1FbC5maW5kKCdbZGF0YS12aWV3ZXItZGF0YV0nKTtcblxuICAgICAgICAgICAgICAgICAgICAkY2FydEl0ZW1FbC5maW5kKCdbZGF0YS1jYXJ0LWl0ZW0taW5kZXhdJykudGV4dChgJHtpfS5gKTtcbiAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICBpZiAoY2FydEl0ZW0uaXNHaWZ0KXtcbiAgICAgICAgICAgICAgICAgICAgICAgICRjYXJ0SXRlbUVsLmZpbmQoJ1tkYXRhLXBsYWNlXScpLnBhcmVudCgpLmh0bWwoY2FydEl0ZW0ucGxhY2VOYW1lICsgJyAtICcgKyBjYXJ0SXRlbS5xbnQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJGNhcnRJdGVtRWwuZmluZCgnW2RhdGEtc2VjdG9yLW5hbWVdJykucGFyZW50KCkuaHRtbCgnJyk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkY2FydEl0ZW1FbC5maW5kKCdbZGF0YS1zZWN0b3ItbmFtZV0nKS5odG1sKGNhcnRJdGVtLnBsYWNlTmFtZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkY2FydEl0ZW1FbC5maW5kKCdbZGF0YS1yb3ddJykudGV4dChjYXJ0SXRlbS5yb3cpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJGNhcnRJdGVtRWwuZmluZCgnW2RhdGEtcGxhY2VdJykudGV4dChjYXJ0SXRlbS5wbGFjZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAkY2FydEl0ZW1FbC5maW5kKCdbZGF0YS1zdW1dJykudGV4dChzdW1UZXh0KTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIWNhcnRJdGVtLmVUaWNrZXQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRpY2tldHNEZWxpdmVyeSA9ICfQkdGD0LzQsNC20L3Ri9C5INCx0LjQu9C10YIgLSDQmtGD0YDRjNC10YDRgdC60LDRjyDQtNC+0YHRgtCw0LLQutCwJztcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhc1BhcGVyVGlja2V0ID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGFGaWVsZHMgPSBjYXJ0SW5zdGFuY2UuZG9jdW1lbnRGaWVsZHM7XG4gICAgICAgICAgICAgICAgICAgIGlmIChkYXRhRmllbGRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWVsZFN0cnVjdCA9IHBhcnNlRGF0YUZpZWxkcyhkYXRhRmllbGRzKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHZpZXdlckRhdGEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBoZWFkZXIgPSB2aWV3ZXJEYXRhLmZpbmQoJ1tkYXRhLXZpZXdlci1kYXRhLWhlYWRlcl0nKVswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gdmlld2VyRGF0YS5maW5kKCdbZGF0YS12aWV3ZXItZGF0YS1jb250ZW50XScpWzBdO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhlYWRlciAmJiBjb250ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHNhdmVkRG9jdW1lbnREYXRhID0gY2FydEl0ZW0uZG9jdW1lbnREYXRhIHx8IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWVsZHNIVE1MID0gZmllbGRTdHJ1Y3QubWFwKGl0ZW0gPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRWYWx1ZSA9IHNhdmVkRG9jdW1lbnREYXRhW2l0ZW0ubmFtZV0gfHwgJyc7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpdGVtPy5vcHRpb25zKSByZXR1cm4gJyc7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGl0ZW0ub3B0aW9ucy5tYXAob3B0aW9uID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChgPGxpPiR7b3B0aW9ufTwvbGk+YClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLmpvaW4oJycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0ICRpbnB1dCA9ICQoYFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPVwidGV4dFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPVwiJHtzZWxlY3RlZFZhbHVlfVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9XCIke2l0ZW0ubmFtZX1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cIiR7aXRlbS5wbGFjZWhvbGRlcn0gKlwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoJGlucHV0LmF0dHIoJ25hbWUnKSA9PT0gJ2Zpbycpe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRpbnB1dC5hdHRyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybjogXCJeW0EtWmEtetCQLdCv0LAt0Y/QgdGRXSsoPzogW0EtWmEtetCQLdCv0LAt0Y/QgdGRXSspKiRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU6IFwi0JLQstC10LTQuNGC0LUg0LrQvtGA0YDQtdC60YLQvdC+0LUg0KTQmNCeXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCAkc2VsZWN0ID0gJChgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidHctc2VsZWN0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEtc2VsZWN0PVwibWFpblwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiaGlkZGVuXCIgbmFtZT1cIiR7aXRlbS5uYW1lfVwiIHZhbHVlPVwiJHtzZWxlY3RlZFZhbHVlfVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LXNlbGVjdF9fZmllbGRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctc2VsZWN0X19oZWFkXCIgZGF0YS1zZWxlY3Q9XCJoZWFkXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHctc2VsZWN0X192YWx1ZVwiIGRhdGEtc2VsZWN0PVwidmFsdWVcIj4ke3NlbGVjdGVkVmFsdWUgfHwgKGl0ZW0ucGxhY2Vob2xkZXIgKyAnIConKX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzdmdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidHctc2VsZWN0X19pY29uXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGg9XCIyNFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQ9XCIyNVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWV3Qm94PVwiMCAwIDI0IDI1XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGw9XCJub25lXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkPVwiTTE2IDEwLjVMMTIgMTQuNUw4IDEwLjVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1cIiMyNDI0MjRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZS13aWR0aD1cIjEuMlwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlLWxpbmVjYXA9XCJzcXVhcmVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LXNlbGVjdF9fYm9keVwiIGRhdGEtc2VsZWN0PVwiYm9keVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dWwgY2xhc3M9XCJ0dy1zZWxlY3RfX2xpc3QgbGlzdC1yZXNldFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHsgb3B0aW9ucygpIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC91bD5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGl0ZW0udHlwZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3RleHQnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gJGlucHV0WzBdLm91dGVySFRNTDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzZWxlY3QnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gJHNlbGVjdFswXS5vdXRlckhUTUw7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuICRpbnB1dFswXS5vdXRlckhUTUw7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pLmpvaW4oXCJcIik7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudC5pbnNlcnRBZGphY2VudEhUTUwoJ2JlZm9yZWVuZCcsIGZpZWxkc0hUTUwpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGZpZWxkcyA9IGNvbnRlbnQucXVlcnlTZWxlY3RvckFsbCgnaW5wdXQnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWV3ZXJEYXRhWzBdLmNsYXNzTGlzdC50b2dnbGUoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZmllbGRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRzLmZvckVhY2goKGZpZWxkKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGQuYWRkRXZlbnRMaXN0ZW5lcignYmx1cicsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZC5jbGFzc0xpc3QudG9nZ2xlKCdpbnZhbGlkJywgISBldmVudC50YXJnZXQudmFsdWUubGVuZ3RoKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodmlld2VyRGF0YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmlld2VyRGF0YS5yZW1vdmUoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG5cbiAgICAgICAgICAgICAgICAgICAgJGNhcnRMaXN0LmFwcGVuZCgkY2FydEl0ZW1FbCk7XG5cbiAgICAgICAgICAgICAgICAgICAgaSsrO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHR3U2VsZWN0KClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGVja291dFBhbmVsSWQgPT09ICd0dy1tdWx0aS1jaGVja291dCcpIHtcbiAgICAgICAgICAgIFRXLnVwZGF0ZU1hc2tFbGVtZW50cygpO1xuICAgICAgICAgICAgVFcuJGhlYWRlci5oaWRlKCk7XG5cbiAgICAgICAgICAgIGNvbnN0IGNhcnRzID0gVFdDYXJ0c1N0b3JhZ2UuYWxsKHRydWUpO1xuICAgICAgICAgICAgY29uc3QgYXJDYXJ0cyA9IE9iamVjdC52YWx1ZXMoY2FydHMpLnNvcnQoKGEsIGIpID0+IGIuY3JlYXRlZEF0IC0gYS5jcmVhdGVkQXQpO1xuXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNhcnQgb2YgYXJDYXJ0cykge1xuICAgICAgICAgICAgICAgIGZpbGxDYXJ0RGF0YShjYXJ0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFUV0NhcnRzU3RvcmFnZS5nZXRBbGxUb3RhbFFudCgpKSB7XG4gICAgICAgICAgICAgICAgVFcuY2xvc2VQYW5lbChjaGVja291dFBhbmVsSWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKCFUVy5jYXJ0SW5zdCkge1xuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgVFcuJGhlYWRlci5zaG93KCk7XG5cbiAgICAgICAgICAgIGZpbGxDYXJ0RGF0YShUVy5jYXJ0SW5zdCk7XG5cbiAgICAgICAgICAgIGlmIChUVy5jYXJ0SW5zdC5pc0VtcHR5KCkpIHtcbiAgICAgICAgICAgICAgICBUVy5jbG9zZVBhbmVsKGNoZWNrb3V0UGFuZWxJZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAkY2hlY2tvdXQuZmluZCgnW2RhdGEtdG90YWwtcW50XScpLnRleHQodG90YWxRbnQpO1xuICAgICAgICBUVy51cGRhdGVHbG9iYWxDYXJ0VG90YWxRbnQoKTtcblxuICAgICAgICBjb25zdCB0b3RhbFN1bVRleHQgPSAhaXNOYU4odG90YWxTdW0pXG4gICAgICAgICAgICA/IHRvdGFsU3VtICsgJyDigr0nXG4gICAgICAgICAgICA6ICfQn9C+0LQg0LfQsNC60LDQtyc7XG4gICAgICAgICRjaGVja291dC5maW5kKCdbZGF0YS10b3RhbC1zdW1dJykudGV4dCh0b3RhbFN1bVRleHQpO1xuXG5cbiAgICAgICAgaWYgKGFsbG93UGF5ICYmIHBhcnNlSW50KFRXLnNjaGVtZVN2Z0F0dHJzLm5vX3RpY2tldHMpICE9PSAxKSB7XG4gICAgICAgICAgICAkY2hlY2tvdXQuZmluZCgnLmpzLXR3LWJ0bi10by1wYXltZW50JykuYXR0cignZGF0YS10ZXh0JywgJ9Ce0L/Qu9Cw0YLQuNGC0Ywg0LfQsNC60LDQtzonKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICRjaGVja291dC5maW5kKCcuanMtdHctYnRuLXRvLXBheW1lbnQnKS5hdHRyKCdkYXRhLXRleHQnLCAn0J7RhNC+0YDQvNC40YLRjCDQt9Cw0Y/QstC60YMnKS5maW5kKCdbZGF0YS10b3RhbC1zdW1dJykudGV4dCgnJyk7XG4gICAgICAgIH1cblxuICAgICAgICAkY2hlY2tvdXQuZmluZCgnLnR3X19jaGVja291dC1vZmYtdnBuJykudG9nZ2xlKGFsbG93UGF5KTtcbiAgICAgICAgJGNoZWNrb3V0LmZpbmQoJ1tkYXRhLXBhcGVyLXRpY2tldC1ibG9jaz1cIjFcIl0nKS50b2dnbGUoaGFzUGFwZXJUaWNrZXQpO1xuICAgICAgICAkY2hlY2tvdXQuZmluZCgnW2RhdGEtcGFwZXItdGlja2V0LWJsb2NrPVwiMFwiXScpLnRvZ2dsZSghaGFzUGFwZXJUaWNrZXQpO1xuICAgICAgICAkY2hlY2tvdXQuZmluZCgnW25hbWU9XCJkZWxpdmVyeV9hZGRyZXNzXCJdJykucHJvcCgncmVxdWlyZWQnLCBoYXNQYXBlclRpY2tldCk7XG4gICAgICAgICRjaGVja291dC5maW5kKCdbZGF0YS1kZWxpdmVyeS10eXBlXScpLmh0bWwodGlja2V0c0RlbGl2ZXJ5KTtcbiAgICB9XG5cbiAgICB1cGRhdGVHbG9iYWxDYXJ0VG90YWxRbnQoKSB7XG4gICAgICAgIGNvbnN0IHFudCA9IFRXQ2FydHNTdG9yYWdlLmdldEFsbFRvdGFsUW50KCk7XG4gICAgICAgIFRXLiRtdWx0aUNoZWNrb3V0QnRuPy5hdHRyKCdkYXRhLXFudCcsIHFudCk7XG4gICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zZXRBdHRyaWJ1dGUoJ2RhdGEtZ2xvYmFsLWNhcnQtdG90YWwtcW50JywgcW50KTtcbiAgICB9XG5cbiAgICBnZW5lcmF0ZVRpY2tldElkKHJvdyA9ICcnLCBwbGFjZSA9ICcnLCBzZWN0b3JOYW1lID0gJycsIGV2dElkID0gVFcuZXZlbnRQYXJhbXMuaWQsIGV2dERhdGUgPSBUVy5ldmVudFBhcmFtcy5kYXRlLCBhcmVhSWQgPSBUVy5ldmVudFBhcmFtcy5wbGFjZUlkLCBoYWxsSWQgPSBUVy5ldmVudFBhcmFtcy5oYWxsSWQpIHtcbiAgICAgICAgbGV0IHN0ciA9IFtldnRJZCwgZXZ0RGF0ZSwgYXJlYUlkLCBoYWxsSWQsIHNlY3Rvck5hbWUsIHJvdywgcGxhY2VdLmpvaW4oJycpO1xuICAgICAgICByZXR1cm4gYjY0RW5jb2RlKHN0cik7XG4gICAgfVxuXG4gICAgY29uZmlybSh0ZXh0LCBjYWxsYmFjaykge1xuICAgICAgICBUVy4kY29uZmlybS5yZW1vdmVDbGFzcygndHctaGlkZGVuJylcbiAgICAgICAgVFcuJGNvbmZpcm0uZmluZCgnI3R3LWNvbmZpcm0tdGV4dCcpLmh0bWwodGV4dClcbiAgICAgICAgVFcuJGNvbmZpcm0uZmluZCgnI3R3LWNvbmZpcm0tc3VjY2VzcycpLm9mZignY2xpY2snKS5vbmUoJ2NsaWNrJywgKCkgPT4ge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBjYWxsYmFjayA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBUVy4kY29uZmlybS5hZGRDbGFzcygndHctaGlkZGVuJyk7XG4gICAgICAgIH0pXG4gICAgICAgIFRXLiRjb25maXJtLmZpbmQoJyN0dy1jb25maXJtLWNhbmNlbCcpLm9mZignY2xpY2snKS5vbmUoJ2NsaWNrJywgKCkgPT4gVFcuJGNvbmZpcm0uYWRkQ2xhc3MoJ3R3LWhpZGRlbicpKVxuICAgIH1cblxuICAgIG1ldHJpa2FSZWFjaEdvYWwoa2V5KSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IFtrZXksIG9ial0gb2YgT2JqZWN0LmVudHJpZXMod2luZG93LllhKSkge1xuICAgICAgICAgICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aCgnTWV0cmlrYScpICYmIHR5cGVvZiBvYmouY291bnRlcnMgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnltKG9iai5jb3VudGVycygpWzBdLmlkLCAncmVhY2hHb2FsJywga2V5KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBGYWlsZWQgc2VuZCB5YW5kZXggbWV0cmlrYSByZWFjaEdvYWwgJHtrZXl9YCwgZSk7XG4gICAgICAgIH1cblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZmV0Y2goYGh0dHBzOi8vc3ZnLWdlbi5ydS9zdGF0LnBocD9jb2RlPW1vczIwMTkmZGF0ZT0ke1RXLmV2ZW50UGFyYW1zLmRhdGV9JmV2ZW50X2lkPSR7VFcuZXZlbnRQYXJhbXMuaWR9JnBsYWNlX2lkPSR7VFcuZXZlbnRQYXJhbXMucGxhY2VJZH0maGFsbF9pZD0ke1RXLmV2ZW50UGFyYW1zLmhhbGxJZH0maG9zdD0ke2xvY2F0aW9uLmhvc3RuYW1lfSZldmVudF90eXBlPSR7a2V5fWApXG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBGYWlsZWQgc2VuZCBjdXN0b20gbWV0cmlrYSBzdGF0ICR7a2V5fWAsIGUpO1xuICAgICAgICB9XG4gICAgfVxufVxuXG5mdW5jdGlvbiBiNjRFbmNvZGUoaW5wdXQpIHtcbiAgICBpbnB1dCA9IHR5cGVvZiBpbnB1dCA9PT0gJ29iamVjdCcgPyBKU09OLnN0cmluZ2lmeShpbnB1dCkgOiBpbnB1dC50b1N0cmluZygpO1xuXG4gICAgcmV0dXJuIGJ0b2EoZW5jb2RlVVJJQ29tcG9uZW50KGlucHV0KS5yZXBsYWNlKC8lKFswLTlBLUZdezJ9KS9nLFxuICAgICAgICBmdW5jdGlvbiB0b1NvbGlkQnl0ZXMobWF0Y2gsIHAxKSB7XG4gICAgICAgICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZSgnMHgnICsgcDEpO1xuICAgICAgICB9KVxuICAgICk7XG59XG5cbmZ1bmN0aW9uIGI2NERlY29kZShzdHIpIHtcbiAgICBsZXQgcmVzdWx0O1xuXG4gICAgdHJ5IHtcbiAgICAgICAgcmVzdWx0ID0gZGVjb2RlVVJJQ29tcG9uZW50KGF0b2Ioc3RyKS5zcGxpdCgnJykubWFwKGZ1bmN0aW9uIChjKSB7XG4gICAgICAgICAgICByZXR1cm4gJyUnICsgKCcwMCcgKyBjLmNoYXJDb2RlQXQoMCkudG9TdHJpbmcoMTYpKS5zbGljZSgtMik7XG4gICAgICAgIH0pLmpvaW4oJycpKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGUpO1xuICAgICAgICByZXN1bHQgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIHJlc3VsdCAmJiBKU09OLnN0cmluZ2lmeShKU09OLnBhcnNlKHJlc3VsdCkpID09PSByZXN1bHQgPyBKU09OLnBhcnNlKHJlc3VsdCkgOiByZXN1bHQ7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmxvZyhlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufVxuXG5mdW5jdGlvbiBzZWxlY3RUaWNrZXQoZWxlbSkge1xuICAgIGNvbnN0IHNlY3RvcklkID0gZWxlbS5nZXRBdHRyaWJ1dGUoJ2RhdGEtY2xpY2snKTtcbiAgICBUVy5sYXN0Q2xpY2tlZFRpY2tldEVsZW0gPSBlbGVtO1xuXG4gICAgaWYgKHNlY3RvcklkKSB7XG4gICAgICAgIFRXLmdldEV2ZW50KHtwYXJ0SWQ6IHNlY3RvcklkfSk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBsZXQgY2FydEl0ZW1PYiA9IG5ldyBUV0NhcnRJdGVtKGVsZW0pO1xuXG4gICAgLy90b2RvINC80L7QttC10YIg0L/QtdGA0LXQstC10YHRgtC4INC90LAg0LDRgtGA0LjQsdGD0YLRiyDQutC+0YDQt9C40L3Riz9cblxuICAgIGxldCBieVNlY3RvclRleHQgPSBlbGVtLmdldEF0dHJpYnV0ZSgnZGF0YS1xbnQtbW9yZS10ZXh0JykgfHwgJyc7XG4gICAgbGV0IGJ5U2VjdG9yUHJpY2VGb3JUZXh0ID0gZWxlbS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcHJpY2UtZm9yJykgfHwgJyc7XG4gICAgbGV0IGV4aXN0Q2FydEl0ZW0gPSBUVy5jYXJ0SW5zdC5pdGVtcy5maW5kKGl0ZW0gPT4gaXRlbS5pZCA9PT0gY2FydEl0ZW1PYi5pZCk7XG5cbiAgICBpZiAoY2FydEl0ZW1PYi5ieVNlY3Rvcikge1xuICAgICAgICBsZXQgcHJpY2VUZXh0ID0gIWNhcnRJdGVtT2Iub25PcmRlclxuICAgICAgICAgICAgPyBjYXJ0SXRlbU9iLnByaWNlICsgJyDigr0nXG4gICAgICAgICAgICA6ICfQn9C+0LQg0LfQsNC60LDQtydcbiAgICAgICAgO1xuXG4gICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnW2RhdGEtdGl0bGVdJykuaHRtbChjYXJ0SXRlbU9iLnBsYWNlTmFtZSk7XG4gICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnW2RhdGEtZGVzY10nKS5odG1sKGJ5U2VjdG9yVGV4dCk7XG4gICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnW2RhdGEtcHJpY2VdJykudGV4dChwcmljZVRleHQpO1xuICAgICAgICBjb25zdCBzZWN0b3JRbnQgPSBjYXJ0SXRlbU9iLmRvbUVsZW0uZ2V0QXR0cmlidXRlKCdkYXRhLXNlY3Rvci1xbnQnKTtcblxuICAgICAgICBsZXQgaW5wdXRNYXggPSBzZWN0b3JRbnQ7XG5cbiAgICAgICAgaWYgKGNhcnRJdGVtT2IuaXNHaWZ0KSB7XG4gICAgICAgICAgICBpbnB1dE1heCA9IFRXLmNhcnRJbnN0LnRpY2tldHNDb3VudChmYWxzZSk7XG5cbiAgICAgICAgICAgIGlmICghIGlucHV0TWF4KXtcbiAgICAgICAgICAgICAgICBhbGVydCgn0KfRgtC+0LHRiyDQstGL0LHRgNCw0YLRjCDQv9C+0LTQsNGA0L7Quiwg0L3Rg9C20L3QviDQtNC+0LHQsNCy0LjRgtGMINCx0LjQu9C10YLRiyDQsiDQutC+0YDQt9C40L3RgyEnKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBjdXJyZW50UW50ID0gZXhpc3RDYXJ0SXRlbT8ucW50IHx8IDA7XG4gICAgICAgIGNvbnN0IGZyZWVDb3VudCA9IGlucHV0TWF4IC0gY3VycmVudFFudDtcblxuICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJ1tkYXRhLWZyZWUtY291bnRdJykuaHRtbChmcmVlQ291bnQpO1xuICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJy50d19fcG9wdXAtY2hhbmdlci1idG4nKS5yZW1vdmVBdHRyKCdkaXNhYmxlZCcpO1xuXG4gICAgICAgIGlmIChieVNlY3RvclByaWNlRm9yVGV4dCAhPT0gJycpIHtcbiAgICAgICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnLnR3X19wb3B1cC1wcmljZS1vbmUnKS5oaWRlKCk7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJy50d19fcG9wdXAtcHJpY2Utc2VjdG9yJykuaHRtbChieVNlY3RvclByaWNlRm9yVGV4dCkuc2hvdygpO1xuICAgICAgICAgICAgVFcuJG1vcmVQb3B1cC5maW5kKCcudHdfX3BvcHVwLWNoYW5nZXInKS5oaWRlKCk7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJ1tkYXRhLXRpY2tldHNdJykudGV4dCgnJyk7XG5cbiAgICAgICAgICAgIC8vIFRXLiRtb3JlUG9wdXAuZmluZCgnLnR3X19wb3B1cC1idXknKS5yZW1vdmVDbGFzcygndHctaGlkZGVuJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBUVy4kbW9yZVBvcHVwLmZpbmQoJy50d19fcG9wdXAtcHJpY2Utb25lJykuc2hvdygpO1xuICAgICAgICAgICAgVFcuJG1vcmVQb3B1cC5maW5kKCcudHdfX3BvcHVwLXByaWNlLXNlY3RvcicpLmhpZGUoKTtcbiAgICAgICAgICAgIFRXLiRtb3JlUG9wdXAuZmluZCgnLnR3X19wb3B1cC1jaGFuZ2VyJykuc2hvdygpO1xuICAgICAgICAgICAgVFcuJG1vcmVQb3B1cC5maW5kKCcudHdfX3BvcHVwLWNoYW5nZXItcW50JylcbiAgICAgICAgICAgICAgICAudmFsKGN1cnJlbnRRbnQpXG4gICAgICAgICAgICAgICAgLmF0dHIoJ2RhdGEtbWF4JywgaW5wdXRNYXgpXG4gICAgICAgICAgICAgICAgLnRyaWdnZXIoJ2lucHV0JylcbiAgICAgICAgICAgIDtcblxuICAgICAgICAgICAgLy8gVFcuJG1vcmVQb3B1cC5maW5kKCcudHdfX3BvcHVwLWJ1eScpLmFkZENsYXNzKCd0dy1oaWRkZW4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFRXLiR0b29sdGlwLmhpZGUoKTtcbiAgICAgICAgVFcuJG1vcmVQb3B1cC5hZGRDbGFzcygnYWN0aXZlJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgVFcuJG1vcmVQb3B1cC5yZW1vdmVDbGFzcygnYWN0aXZlJyk7XG5cbiAgICAgICAgaWYgKCFleGlzdENhcnRJdGVtKSB7XG4gICAgICAgICAgICBUVy5jYXJ0SW5zdC5hZGQoY2FydEl0ZW1PYik7XG4gICAgICAgICAgICBlbGVtLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpO1xuXG4gICAgICAgICAgICBUVy5zaG93VG9vbHRpcChlbGVtKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIFRXLmNhcnRJbnN0LmRlbGV0ZShleGlzdENhcnRJdGVtLmlkKTtcblxuICAgICAgICAgICAgaWYgKFRXLmlzVG91Y2hEZXZpY2UpIHtcbiAgICAgICAgICAgICAgICBUVy4kdG9vbHRpcC5oaWRlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBUVy5yZWRyYXdNaW5pY2FydCgpO1xuICAgIH1cbn1cblxubGV0IGhhc0xpc3RlbmVyID0gZmFsc2VcblxuZnVuY3Rpb24gdHdTZWxlY3QoKSB7XG4gICAgY29uc3QgbWFpbnMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdbZGF0YS1zZWxlY3Q9XCJtYWluXCJdJyk7XG5cbiAgICBpZiAoIW1haW5zLmxlbmd0aCkgcmV0dXJuO1xuXG4gICAgY29uc3QgcmVtb3ZlQWN0aXZlID0gKGVsZW1zKSA9PiB7XG4gICAgICAgIGVsZW1zLmZvckVhY2gobWFpbiA9PiBtYWluLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpKTtcbiAgICB9O1xuXG4gICAgaWYgKGhhc0xpc3RlbmVyKSByZXR1cm47XG5cbiAgICBoYXNMaXN0ZW5lciA9IHRydWVcblxuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGV2ZW50KSA9PiB7XG4gICAgICAgIGlmIChldmVudC50YXJnZXQuY2xvc2VzdCgnW2RhdGEtc2VsZWN0PVwibWFpblwiXScpKSB7XG4gICAgICAgICAgICBtYWlucy5mb3JFYWNoKG1haW4gPT4ge1xuICAgICAgICAgICAgICAgIGlmIChtYWluICE9PSBldmVudC50YXJnZXQuY2xvc2VzdCgnW2RhdGEtc2VsZWN0PVwibWFpblwiXScpKVxuICAgICAgICAgICAgICAgICAgICBtYWluLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICBjb25zdCBzZWxlY3QgPSBldmVudC50YXJnZXQuY2xvc2VzdCgnW2RhdGEtc2VsZWN0PVwibWFpblwiXScpLFxuICAgICAgICAgICAgICAgIHZhbHVlID0gc2VsZWN0LnF1ZXJ5U2VsZWN0b3IoJ1tkYXRhLXNlbGVjdD1cInZhbHVlXCJdJyksXG4gICAgICAgICAgICAgICAgbGlzdEl0ZW1zID0gc2VsZWN0LnF1ZXJ5U2VsZWN0b3JBbGwoJ2xpJylcblxuICAgICAgICAgICAgaWYgKGV2ZW50LnRhcmdldC5jbG9zZXN0KCdbZGF0YS1zZWxlY3Q9XCJoZWFkXCJdJykpIHtcbiAgICAgICAgICAgICAgICBzZWxlY3QuY2xhc3NMaXN0LnRvZ2dsZSgnYWN0aXZlJylcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGV2ZW50LnRhcmdldC5jbG9zZXN0KCdsaScpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbGlzdEl0ZW0gPSBldmVudC50YXJnZXQuY2xvc2VzdCgnbGknKVxuXG4gICAgICAgICAgICAgICAgaWYgKCFsaXN0SXRlbS5jbGFzc0xpc3QuY29udGFpbnMoJ2FjdGl2ZScpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlbW92ZUFjdGl2ZShsaXN0SXRlbXMpXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlLnRleHRDb250ZW50ID0gbGlzdEl0ZW0udGV4dENvbnRlbnRcbiAgICAgICAgICAgICAgICAgICAgbGlzdEl0ZW0uY2xhc3NMaXN0LmFkZCgnYWN0aXZlJylcbiAgICAgICAgICAgICAgICAgICAgJChzZWxlY3QpLmNoaWxkcmVuKGBpbnB1dFt0eXBlPWhpZGRlbl1gKS52YWwobGlzdEl0ZW0udGV4dENvbnRlbnQpLnRyaWdnZXIoJ2NoYW5nZScpLnJlbW92ZUNsYXNzKCdpbnZhbGlkJyk7XG4gICAgICAgICAgICAgICAgICAgIHNlbGVjdC5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJlbW92ZUFjdGl2ZShtYWlucylcbiAgICAgICAgfVxuICAgIH0pXG59XG5cbnR3U2VsZWN0KCkiXX0=
